initial
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
// ----------------------------------------- //
|
||||
// File generated by VPC //
|
||||
// ----------------------------------------- //
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_gamesettingsmgr.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_gamesettingsmgr.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_gamesettingsmgr.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_main.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_main.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_main.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_richpresence.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_richpresence.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_richpresence.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_titledata.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_titledata.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_titledata.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\game\shared\cstrike15\gametypes.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\game\shared\cstrike15\gametypes.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\game\shared\cstrike15\gametypes.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\common\platforminputdevice.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\common\platforminputdevice.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\common\platforminputdevice.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\common\debug_dll_check.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\common\debug_dll_check.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\common\debug_dll_check.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\tier0\memoverride.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\tier0\memoverride.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\tier0\memoverride.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// ----------------------------------------- //
|
||||
// File generated by VPC //
|
||||
// ----------------------------------------- //
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_gamesettingsmgr.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_gamesettingsmgr.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_gamesettingsmgr.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_main.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_main.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_main.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_richpresence.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_richpresence.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_richpresence.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_titledata.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_titledata.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\cstrike15\mm_title_titledata.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\game\shared\cstrike15\gametypes.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\game\shared\cstrike15\gametypes.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\game\shared\cstrike15\gametypes.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\common\platforminputdevice.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\common\platforminputdevice.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\common\platforminputdevice.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\common\debug_dll_check.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\common\debug_dll_check.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\common\debug_dll_check.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\tier0\memoverride.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\tier0\memoverride.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\tier0\memoverride.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
202
matchmaking/_vpc_/manifest_matchmakingbase/win32/manifest.txt
Normal file
202
matchmaking/_vpc_/manifest_matchmakingbase/win32/manifest.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
// ----------------------------------------- //
|
||||
// File generated by VPC //
|
||||
// ----------------------------------------- //
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\matchmakingqos.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\matchmakingqos.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\matchmakingqos.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_events.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_events.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_events.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_extensions.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_extensions.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_extensions.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_framework.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_framework.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_framework.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_netmsgcontroller.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_netmsgcontroller.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_netmsgcontroller.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_session.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_session.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_session.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_voice.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_voice.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_voice.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\steam_apihook.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\steam_apihook.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\steam_apihook.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\steam_datacenterjobs.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\steam_datacenterjobs.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\steam_datacenterjobs.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\steam_lobbyapi.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\steam_lobbyapi.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\steam_lobbyapi.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\x360_xlsp_cmd.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\x360_xlsp_cmd.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\x360_xlsp_cmd.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\ds_searcher.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\ds_searcher.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\ds_searcher.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\match_searcher.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\match_searcher.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\match_searcher.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_netmgr.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_netmgr.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_netmgr.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_offline_custom.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_offline_custom.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_offline_custom.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_online_client.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_online_client.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_online_client.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_online_host.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_online_host.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_online_host.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_online_search.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_online_search.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_online_search.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_online_teamsearch.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_online_teamsearch.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_session_online_teamsearch.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\sys_session.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\sys_session.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\sys_session.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\filesystem_helpers.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\filesystem_helpers.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\filesystem_helpers.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\main.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\main.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\main.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\datacenter.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\datacenter.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\datacenter.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\leaderboards.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\leaderboards.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\leaderboards.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\matchsystem.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\matchsystem.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\matchsystem.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_dlc.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_dlc.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_dlc.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\player.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\player.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\player.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\playermanager.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\playermanager.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\playermanager.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\playerrankingdata.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\playerrankingdata.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\playerrankingdata.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\searchmanager.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\searchmanager.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\searchmanager.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\servermanager.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\servermanager.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\servermanager.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\extkeyvalues.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\extkeyvalues.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\extkeyvalues.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
130
matchmaking/_vpc_/manifest_matchmakingbase_ds/win32/manifest.txt
Normal file
130
matchmaking/_vpc_/manifest_matchmakingbase_ds/win32/manifest.txt
Normal file
@@ -0,0 +1,130 @@
|
||||
// ----------------------------------------- //
|
||||
// File generated by VPC //
|
||||
// ----------------------------------------- //
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\matchmakingqos.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\matchmakingqos.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\matchmakingqos.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_events.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_events.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_events.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_extensions.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_extensions.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_extensions.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_framework.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_framework.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_framework.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_netmsgcontroller.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_netmsgcontroller.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_netmsgcontroller.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_voice.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_voice.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_voice.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\steam_apihook.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\steam_apihook.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\steam_apihook.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\steam_datacenterjobs.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\steam_datacenterjobs.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\steam_datacenterjobs.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\steam_lobbyapi.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\steam_lobbyapi.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\steam_lobbyapi.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\x360_xlsp_cmd.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\x360_xlsp_cmd.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\x360_xlsp_cmd.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_netmgr.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_netmgr.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_netmgr.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\filesystem_helpers.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\filesystem_helpers.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\filesystem_helpers.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\main.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\main.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\main.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\datacenter.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\datacenter.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\datacenter.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\matchsystem.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\matchsystem.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\matchsystem.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\mm_dlc.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\mm_dlc.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\mm_dlc.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\playermanager.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\playermanager.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\playermanager.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\playerrankingdata.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\playerrankingdata.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\playerrankingdata.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\servermanager.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\servermanager.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\servermanager.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\matchmaking\extkeyvalues.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\matchmaking\extkeyvalues.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\matchmaking\extkeyvalues.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
510
matchmaking/cstrike15/mm_title.cpp
Normal file
510
matchmaking/cstrike15/mm_title.cpp
Normal file
@@ -0,0 +1,510 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "mm_title_richpresence.h"
|
||||
#include "csgo.spa.h"
|
||||
|
||||
#ifdef _PS3
|
||||
#include <netex/net.h>
|
||||
#include <netex/libnetctl.h>
|
||||
#endif
|
||||
|
||||
#include "fmtstr.h"
|
||||
#include "gametypes/igametypes.h"
|
||||
#include "netmessages_signon.h"
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
extern IGameTypes *g_pGameTypes;
|
||||
|
||||
|
||||
CMatchTitle::CMatchTitle()
|
||||
{
|
||||
}
|
||||
|
||||
CMatchTitle::~CMatchTitle()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Init / shutdown
|
||||
//
|
||||
|
||||
InitReturnVal_t CMatchTitle::Init()
|
||||
{
|
||||
if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
|
||||
{
|
||||
mgr->AddListener( this, "server_pre_shutdown", false );
|
||||
mgr->AddListener( this, "game_newmap", false );
|
||||
mgr->AddListener( this, "finale_start", false );
|
||||
mgr->AddListener( this, "round_start", false );
|
||||
mgr->AddListener( this, "round_end", false );
|
||||
mgr->AddListener( this, "difficulty_changed", false );
|
||||
mgr->AddListener( this, "player_connect", false );
|
||||
mgr->AddListener( this, "player_disconnect", false );
|
||||
}
|
||||
|
||||
#ifdef _GAMECONSOLE
|
||||
// Initialize Title Update version
|
||||
extern ConVar mm_tu_string;
|
||||
mm_tu_string.SetValue( "00000000" );
|
||||
#endif
|
||||
|
||||
g_pGameTypes->Initialize();
|
||||
|
||||
return INIT_OK;
|
||||
}
|
||||
|
||||
void CMatchTitle::Shutdown()
|
||||
{
|
||||
if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
|
||||
{
|
||||
mgr->RemoveListener( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
uint64 CMatchTitle::GetTitleID()
|
||||
{
|
||||
#ifdef _X360
|
||||
return TITLEID_COUNTER_STRIKE__GO;
|
||||
#elif !defined( SWDS ) && !defined( NO_STEAM )
|
||||
static uint64 uiAppID = 0ull;
|
||||
if ( !uiAppID && steamapicontext && steamapicontext->SteamUtils() )
|
||||
{
|
||||
uiAppID = steamapicontext->SteamUtils()->GetAppID();
|
||||
}
|
||||
return uiAppID;
|
||||
#else
|
||||
return 0ull;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64 CMatchTitle::GetTitleServiceID()
|
||||
{
|
||||
#ifdef _X360
|
||||
return 0x45410880ull; // Left 4 Dead 1 Service ID
|
||||
#else
|
||||
return 0ull;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _PS3
|
||||
|
||||
uint64 CMatchTitle::GetTitleSettingsFlags()
|
||||
{
|
||||
return MATCHTITLE_SETTING_MULTIPLAYER
|
||||
| MATCHTITLE_VOICE_INGAME
|
||||
| MATCHTITLE_FORCE_PSN_NAMES
|
||||
| MATCHTITLE_PLAYERMGR_DISABLED
|
||||
| MATCHTITLE_SERVERMGR_DISABLED;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
uint64 CMatchTitle::GetTitleSettingsFlags()
|
||||
{
|
||||
return MATCHTITLE_SETTING_MULTIPLAYER
|
||||
| MATCHTITLE_VOICE_INGAME
|
||||
| MATCHTITLE_PLAYERMGR_ALLFRIENDS
|
||||
#if !defined( CSTRIKE15 )
|
||||
| MATCHTITLE_SETTING_NODEDICATED
|
||||
| MATCHTITLE_PLAYERMGR_DISABLED
|
||||
| MATCHTITLE_SERVERMGR_DISABLED
|
||||
| MATCHTITLE_INVITE_ONLY_SINGLE_USER
|
||||
#else
|
||||
| MATCHTITLE_PLAYERMGR_FRIENDREQS
|
||||
#endif // !CSTRIKE15
|
||||
;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _PS3
|
||||
void *g_pMatchTitle_NetMemory;
|
||||
#endif
|
||||
|
||||
void CMatchTitle::PrepareNetStartupParams( void *pNetStartupParams )
|
||||
{
|
||||
#ifdef _X360
|
||||
XNetStartupParams &xnsp = *( XNetStartupParams * ) pNetStartupParams;
|
||||
|
||||
xnsp.cfgQosDataLimitDiv4 = 64; // 256 bytes
|
||||
|
||||
// Increase outstanding QoS responses significantly
|
||||
// See: https://forums.xboxlive.com/AnswerPage.aspx?qid=493b207b-66b9-42bc-b23d-ddc306e09749&tgt=1
|
||||
xnsp.cfgQosSrvMaxSimultaneousResponses = 255;
|
||||
|
||||
xnsp.cfgSockDefaultRecvBufsizeInK = 64; // Increase receive size for UDP to 64k
|
||||
xnsp.cfgSockDefaultSendBufsizeInK = 64; // Keep send size at 64k too
|
||||
|
||||
int numGamePlayersMax = GetTotalNumPlayersSupported();
|
||||
|
||||
int numConnections = 4 * ( numGamePlayersMax - 1 );
|
||||
// - the max number of connections to members of your game party
|
||||
// - the max number of connections to members of your social party
|
||||
// - the max number of connections to a pending game party (if you are joining a new one ).
|
||||
// - matchmakings client info structure also creates a connection per client for the lobby.
|
||||
|
||||
// 1 - the main game session
|
||||
int numTotalConnections = 1 + numConnections;
|
||||
|
||||
// 29 - total Connections (XNADDR/XNKID pairs) ,using 5 sessions (XNKID/XNKEY pairs).
|
||||
|
||||
xnsp.cfgKeyRegMax = 16; //adding some extra room because of lazy dealocation of these pairs.
|
||||
xnsp.cfgSecRegMax = MAX( 64, numTotalConnections ); //adding some extra room because of lazy dealocation of these pairs.
|
||||
|
||||
xnsp.cfgSockMaxDgramSockets = xnsp.cfgSecRegMax;
|
||||
xnsp.cfgSockMaxStreamSockets = xnsp.cfgSecRegMax;
|
||||
#endif
|
||||
|
||||
#if defined( _PS3 ) && defined( NO_STEAM )
|
||||
MEM_ALLOC_CREDIT_( "NO_STEAM: CMatchTitle::PrepareNetStartupParams" );
|
||||
sys_net_initialize_parameter_t &snip = *( sys_net_initialize_parameter_t * ) pNetStartupParams;
|
||||
|
||||
snip.memory_size = 512 * 1024;
|
||||
snip.memory = malloc( snip.memory_size ); // alternatively this can be a global array
|
||||
|
||||
g_pMatchTitle_NetMemory = snip.memory; // bookmark the memory address for later inspection if necessary
|
||||
#endif
|
||||
}
|
||||
|
||||
int CMatchTitle::GetTotalNumPlayersSupported()
|
||||
{
|
||||
#ifdef _GAMECONSOLE
|
||||
// [jason] Rounding this up to 16, since this was the value used on cstrike15 xbla. Among other things, this
|
||||
// controls how many remote talker voice channels are reserved for each client in XHV
|
||||
return 16;
|
||||
#else
|
||||
return 64; // On PC this is not limited, return max number dedicated servers can ever run with
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get a guest player name
|
||||
char const * CMatchTitle::GetGuestPlayerName( int iUserIndex )
|
||||
{
|
||||
if ( vgui::ILocalize *pLocalize = g_pMatchExtensions->GetILocalize() )
|
||||
{
|
||||
if ( wchar_t* wStringTableEntry = pLocalize->Find( "#SFUI_LocalPlayer" ) )
|
||||
{
|
||||
static char szName[ MAX_PLAYER_NAME_LENGTH ] = {0};
|
||||
pLocalize->ConvertUnicodeToANSI( wStringTableEntry, szName, ARRAYSIZE( szName ) );
|
||||
return szName;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// Sets up all necessary client-side convars and user info before
|
||||
// connecting to server
|
||||
void CMatchTitle::PrepareClientForConnect( KeyValues *pSettings )
|
||||
{
|
||||
#ifndef SWDS
|
||||
int numPlayers = 1;
|
||||
#ifdef _GAMECONSOLE
|
||||
numPlayers = XBX_GetNumGameUsers();
|
||||
#endif
|
||||
|
||||
//
|
||||
// Now we set the convars
|
||||
//
|
||||
|
||||
for ( int k = 0; k < numPlayers; ++ k )
|
||||
{
|
||||
int iController = k;
|
||||
#ifdef _GAMECONSOLE
|
||||
iController = XBX_GetUserId( k );
|
||||
#endif
|
||||
IPlayerLocal *pPlayerLocal = g_pPlayerManager->GetLocalPlayer( iController );
|
||||
if ( !pPlayerLocal )
|
||||
continue;
|
||||
|
||||
// Set "name"
|
||||
static SplitScreenConVarRef s_cl_name( "name" );
|
||||
char const *szName = pPlayerLocal->GetName();
|
||||
s_cl_name.SetValue( k, szName );
|
||||
|
||||
// Set "networkid_force"
|
||||
if ( IsX360() )
|
||||
{
|
||||
static SplitScreenConVarRef s_networkid_force( "networkid_force" );
|
||||
uint64 xid = pPlayerLocal->GetXUID();
|
||||
s_networkid_force.SetValue( k, CFmtStr( "%08X:%08X:", uint32( xid >> 32 ), uint32( xid ) ) );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CMatchTitle::StartServerMap( KeyValues *pSettings )
|
||||
{
|
||||
int numPlayers = 1;
|
||||
#ifdef _GAMECONSOLE
|
||||
numPlayers = XBX_GetNumGameUsers();
|
||||
#endif
|
||||
|
||||
char const *szMap = pSettings->GetString( "game/map", NULL );
|
||||
if ( !szMap )
|
||||
return false;
|
||||
|
||||
// Check that we have the server interface and that the map is valid
|
||||
if ( !g_pMatchExtensions->GetIVEngineServer() )
|
||||
return false;
|
||||
if ( !g_pMatchExtensions->GetIVEngineServer()->IsMapValid( szMap ) )
|
||||
return false;
|
||||
|
||||
//
|
||||
// Prepare game dll reservation package
|
||||
//
|
||||
KeyValues *pGameDllReserve = g_pMatchFramework->GetMatchNetworkMsgController()->PackageGameDetailsForReservation( pSettings );
|
||||
KeyValues::AutoDelete autodelete( pGameDllReserve );
|
||||
|
||||
pGameDllReserve->SetString( "map/mapcommand", ( numPlayers <= 1 ) ? "map" : "ss_map" );
|
||||
|
||||
if ( !Q_stricmp( "commentary", pSettings->GetString( "options/play", "" ) ) )
|
||||
pGameDllReserve->SetString( "map/mapcommand", "map_commentary" );
|
||||
|
||||
// Run map based off the faked reservation packet
|
||||
g_pMatchExtensions->GetIVEngineClient()->StartLoadingScreenForKeyValues( pGameDllReserve );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static KeyValues * GetCurrentMatchSessionSettings()
|
||||
{
|
||||
IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
return pIMatchSession ? pIMatchSession->GetSessionSettings() : NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// <Vitaliy> July-2012
|
||||
// CS:GO hack: training map has been implemented storing progress in convars instead of
|
||||
// properly storing it in game profile data, force storing the convars into profile
|
||||
// when the training session finishes here
|
||||
// Please, do not use this approach in future code.
|
||||
//
|
||||
bool g_bPlayingTrainingMap = false;
|
||||
|
||||
void CMatchTitle::OnEvent( KeyValues *pEvent )
|
||||
{
|
||||
char const *szEvent = pEvent->GetName();
|
||||
|
||||
if ( !Q_stricmp( "OnPlayerRemoved", szEvent ) ||
|
||||
!Q_stricmp( "OnPlayerUpdated", szEvent ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), NULL );
|
||||
}
|
||||
else if ( !Q_stricmp( "OnPlayerMachinesConnected", szEvent ) ||
|
||||
!Q_stricmp( "OnPlayerMachinesDisconnected", szEvent ) )
|
||||
{
|
||||
// Player counts changed on host, update aggregate fields
|
||||
IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pMatchSession )
|
||||
return;
|
||||
KeyValues *kvPackage = new KeyValues( "Update" );
|
||||
if ( KeyValues *kvUpdate = kvPackage->FindKey( "update", true ) )
|
||||
{
|
||||
extern void UpdateAggregateMembersSettings( KeyValues *pFullGameSettings, KeyValues *pUpdate );
|
||||
UpdateAggregateMembersSettings( pMatchSession->GetSessionSettings(), kvUpdate );
|
||||
}
|
||||
pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( kvPackage ) );
|
||||
}
|
||||
else if ( !Q_stricmp( "OnMatchSessionUpdate", szEvent ) )
|
||||
{
|
||||
if ( !Q_stricmp( pEvent->GetString( "state" ), "updated" ) )
|
||||
{
|
||||
if ( KeyValues *kvUpdate = pEvent->FindKey( "update" ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), kvUpdate );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( pEvent->GetString( "state" ), "created" ) ||
|
||||
!Q_stricmp( pEvent->GetString( "state" ), "ready" ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), NULL );
|
||||
if ( IMatchSession *pSession = g_pMatchFramework->GetMatchSession() )
|
||||
{
|
||||
if ( !Q_stricmp( "training", pSession->GetSessionSettings()->GetString( "game/type" ) ) )
|
||||
g_bPlayingTrainingMap = true;
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( pEvent->GetString( "state" ), "closed" ) )
|
||||
{
|
||||
if ( g_bPlayingTrainingMap )
|
||||
{
|
||||
g_bPlayingTrainingMap = false;
|
||||
g_pMatchExtensions->GetIVEngineClient()->ClientCmd_Unrestricted( CFmtStr( "host_writeconfig_ss %d", XBX_GetPrimaryUserId() ) );
|
||||
}
|
||||
MM_Title_RichPresence_Update( NULL, NULL );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( szEvent, "Client::CmdKeyValues" ) )
|
||||
{
|
||||
KeyValues *pCmd = pEvent->GetFirstTrueSubKey();
|
||||
if ( !pCmd )
|
||||
return;
|
||||
char const *szCmd = pCmd->GetName();
|
||||
if ( !Q_stricmp( "ExtendedServerInfo", szCmd ) )
|
||||
{
|
||||
KeyValuesDumpAsDevMsg( pCmd, 2, 1 );
|
||||
g_pGameTypes->SetAndParseExtendedServerInfo( pCmd );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnEngineClientSignonStateChange", szEvent ) )
|
||||
{
|
||||
int iOldState = pEvent->GetInt( "old", 0 );
|
||||
int iNewState = pEvent->GetInt( "new", 0 );
|
||||
|
||||
if (
|
||||
( iOldState >= SIGNONSTATE_CONNECTED && // disconnect
|
||||
iNewState < SIGNONSTATE_CONNECTED ) ||
|
||||
( iOldState < SIGNONSTATE_FULL && // full connect
|
||||
iNewState >= SIGNONSTATE_FULL )
|
||||
)
|
||||
{
|
||||
MM_Title_RichPresence_Update( NULL, NULL );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnEngineDisconnectReason", szEvent ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( NULL, NULL );
|
||||
}
|
||||
else if ( !Q_stricmp( "OnEngineEndGame", szEvent ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( NULL, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
int CMatchTitle::GetEventDebugID( void )
|
||||
{
|
||||
return EVENT_DEBUG_ID_INIT;
|
||||
}
|
||||
|
||||
void CMatchTitle::FireGameEvent( IGameEvent *pIGameEvent )
|
||||
{
|
||||
// Parse the game event
|
||||
char const *szGameEvent = pIGameEvent->GetName();
|
||||
if ( !szGameEvent || !*szGameEvent )
|
||||
return;
|
||||
|
||||
if ( !Q_stricmp( "round_start", szGameEvent ) ||
|
||||
!Q_stricmp( "round_end", szGameEvent ) ||
|
||||
!Q_stricmp( "game_newmap", szGameEvent ) ||
|
||||
!Q_stricmp( "player_connect", szGameEvent ) ||
|
||||
!Q_stricmp( "player_disconnect", szGameEvent ) )
|
||||
{ // Update rich presence
|
||||
MM_Title_RichPresence_Update( NULL, NULL );
|
||||
}
|
||||
|
||||
// Check if the current match session is on an active game server
|
||||
IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pMatchSession )
|
||||
return;
|
||||
KeyValues *pSessionSettings = pMatchSession->GetSessionSettings();
|
||||
char const *szGameServer = pSessionSettings->GetString( "server/server", "" );
|
||||
char const *szSystemLock = pSessionSettings->GetString( "system/lock", "" );
|
||||
if ( ( !szGameServer || !*szGameServer ) &&
|
||||
( !szSystemLock || !*szSystemLock ) )
|
||||
return;
|
||||
|
||||
// Also don't run on the client when there's a host
|
||||
char const *szSessionType = pMatchSession->GetSessionSystemData()->GetString( "type", NULL );
|
||||
if ( szSessionType && !Q_stricmp( szSessionType, "client" ) )
|
||||
return;
|
||||
|
||||
if ( !Q_stricmp( "round_start", szGameEvent ) )
|
||||
{
|
||||
KeyValues *kvUpdate = KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" state game "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
KeyValues::AutoDelete autodelete( kvUpdate );
|
||||
|
||||
pMatchSession->UpdateSessionSettings( kvUpdate );
|
||||
}
|
||||
else if ( !Q_stricmp( "round_end", szGameEvent ) )
|
||||
{
|
||||
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
||||
"OnProfilesWriteOpportunity", "reason", "checkpoint"
|
||||
) );
|
||||
}
|
||||
else if ( !Q_stricmp( "finale_start", szGameEvent ) )
|
||||
{
|
||||
pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" state finale "
|
||||
" } "
|
||||
" } "
|
||||
) ) );
|
||||
}
|
||||
else if ( !Q_stricmp( "game_newmap", szGameEvent ) )
|
||||
{
|
||||
const char *szMapName = pIGameEvent->GetString( "mapname", "" );
|
||||
|
||||
KeyValues *kvUpdate = KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" state game "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
KeyValues::AutoDelete autodelete( kvUpdate );
|
||||
|
||||
Assert( szMapName && *szMapName );
|
||||
if ( szMapName && *szMapName )
|
||||
{
|
||||
kvUpdate->SetString( "update/game/map", szMapName );
|
||||
|
||||
char const *szWorkshopMap = g_pMatchExtensions->GetIVEngineClient()->GetLevelNameShort();
|
||||
if ( StringHasPrefix( szWorkshopMap, "workshop" ) )
|
||||
{
|
||||
size_t nLenMapName = Q_strlen( szMapName );
|
||||
size_t nShortMapName = Q_strlen( szWorkshopMap );
|
||||
if ( ( nShortMapName >= nLenMapName ) &&
|
||||
!Q_stricmp( szWorkshopMap + nShortMapName - nLenMapName, szMapName ) )
|
||||
// Use the name of the workshop map
|
||||
kvUpdate->SetString( "update/game/map", szWorkshopMap );
|
||||
}
|
||||
}
|
||||
|
||||
pMatchSession->UpdateSessionSettings( kvUpdate );
|
||||
|
||||
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
||||
"OnProfilesWriteOpportunity", "reason", "checkpoint"
|
||||
) );
|
||||
}
|
||||
else if ( !Q_stricmp( "server_pre_shutdown", szGameEvent ) )
|
||||
{
|
||||
char const *szReason = pIGameEvent->GetString( "reason", "quit" );
|
||||
if ( !Q_stricmp( szReason, "quit" ) )
|
||||
{
|
||||
DevMsg( "Received server_pre_shutdown notification - server is shutting down...\n" );
|
||||
|
||||
// Transform the server shutdown event into game end event
|
||||
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
||||
"OnEngineDisconnectReason", "reason", "Server shutting down"
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
89
matchmaking/cstrike15/mm_title.h
Normal file
89
matchmaking/cstrike15/mm_title.h
Normal file
@@ -0,0 +1,89 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_TITLE_H
|
||||
#define MM_TITLE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "../mm_framework.h"
|
||||
|
||||
class CMatchTitle :
|
||||
public IMatchTitle,
|
||||
public IMatchEventsSink,
|
||||
public IGameEventListener2
|
||||
{
|
||||
// Methods exposed for the framework
|
||||
public:
|
||||
virtual InitReturnVal_t Init();
|
||||
virtual void Shutdown();
|
||||
|
||||
// Methods of IMatchTitle
|
||||
public:
|
||||
// Title ID
|
||||
virtual uint64 GetTitleID();
|
||||
|
||||
// Service ID for XLSP
|
||||
virtual uint64 GetTitleServiceID();
|
||||
|
||||
// Describe title settings using a bitwise combination of flags
|
||||
virtual uint64 GetTitleSettingsFlags();
|
||||
|
||||
// Prepare network startup params for the title
|
||||
virtual void PrepareNetStartupParams( void *pNetStartupParams );
|
||||
|
||||
// Get total number of players supported by the title
|
||||
virtual int GetTotalNumPlayersSupported();
|
||||
|
||||
// Get a guest player name
|
||||
virtual char const * GetGuestPlayerName( int iUserIndex );
|
||||
|
||||
// Decipher title data fields
|
||||
virtual TitleDataFieldsDescription_t const * DescribeTitleDataStorage();
|
||||
|
||||
// Title achievements
|
||||
virtual TitleAchievementsDescription_t const * DescribeTitleAchievements();
|
||||
|
||||
// Title avatar awards
|
||||
virtual TitleAvatarAwardsDescription_t const * DescribeTitleAvatarAwards();
|
||||
|
||||
// Title DLC description
|
||||
virtual TitleDlcDescription_t const * DescribeTitleDlcs();
|
||||
|
||||
// Run every frame
|
||||
virtual void RunFrame() {}
|
||||
|
||||
// Title leaderboards
|
||||
virtual KeyValues * DescribeTitleLeaderboard( char const *szLeaderboardView );
|
||||
|
||||
// Sets up all necessary client-side convars and user info before
|
||||
// connecting to server
|
||||
virtual void PrepareClientForConnect( KeyValues *pSettings );
|
||||
|
||||
// Start up a listen server with the given settings
|
||||
virtual bool StartServerMap( KeyValues *pSettings );
|
||||
|
||||
// Methods of IMatchEventsSink
|
||||
public:
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
// Methods of IGameEventListener2
|
||||
public:
|
||||
// FireEvent is called by EventManager if event just occured
|
||||
// KeyValue memory will be freed by manager if not needed anymore
|
||||
virtual void FireGameEvent( IGameEvent *event );
|
||||
virtual int GetEventDebugID( void );
|
||||
|
||||
public:
|
||||
CMatchTitle();
|
||||
~CMatchTitle();
|
||||
};
|
||||
|
||||
// Match title singleton
|
||||
extern CMatchTitle *g_pMatchTitle;
|
||||
|
||||
#endif // MM_TITLE_H
|
||||
96
matchmaking/cstrike15/mm_title_contextvalues.h
Normal file
96
matchmaking/cstrike15/mm_title_contextvalues.h
Normal file
@@ -0,0 +1,96 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
#ifndef _MM_TITLE_CONTEXTVALUES_H
|
||||
#define _MM_TITLE_CONTEXTVALUES_H
|
||||
#pragma once
|
||||
|
||||
#include "csgo.spa.h"
|
||||
|
||||
static ContextValue_t g_MapGroupContexts[] =
|
||||
{
|
||||
{ "mg_bomb", CONTEXT_CSS_MAP_GROUP_BOMB_MG },
|
||||
{ "mg_hostage", CONTEXT_CSS_MAP_GROUP_HOSTAGE_MG },
|
||||
{ "mg_demolition", CONTEXT_CSS_MAP_GROUP_DEMOLITION_MG },
|
||||
{ "mg_armsrace", CONTEXT_CSS_MAP_GROUP_ARMSRACE_MG },
|
||||
{ "mg_de_aztec", CONTEXT_CSS_MAP_GROUP_AZTEC_MG },
|
||||
{ "mg_ar_baggage", CONTEXT_CSS_MAP_GROUP_BAGGAGE_MG },
|
||||
{ "mg_de_bank", CONTEXT_CSS_MAP_GROUP_BANK_MG },
|
||||
{ "mg_de_dust", CONTEXT_CSS_MAP_GROUP_DUST_MG },
|
||||
{ "mg_de_dust2", CONTEXT_CSS_MAP_GROUP_DUST2_MG },
|
||||
{ "mg_de_inferno", CONTEXT_CSS_MAP_GROUP_INFERNO_MG },
|
||||
{ "mg_cs_italy", CONTEXT_CSS_MAP_GROUP_ITALY_MG },
|
||||
{ "mg_de_lake", CONTEXT_CSS_MAP_GROUP_LAKE_MG },
|
||||
{ "mg_de_nuke", CONTEXT_CSS_MAP_GROUP_NUKE_MG },
|
||||
{ "mg_cs_office", CONTEXT_CSS_MAP_GROUP_OFFICE_MG },
|
||||
{ "mg_de_safehouse", CONTEXT_CSS_MAP_GROUP_SAFEHOUSE_MG },
|
||||
{ "mg_ar_shoots", CONTEXT_CSS_MAP_GROUP_SHOOTS_MG },
|
||||
{ "mg_de_shorttrain", CONTEXT_CSS_MAP_GROUP_SHORTTRAIN_MG },
|
||||
{ "mg_de_stmarc", CONTEXT_CSS_MAP_GROUP_STMARC_MG },
|
||||
{ "mg_de_sugarcane", CONTEXT_CSS_MAP_GROUP_SUGARCANE_MG },
|
||||
{ "mg_de_train", CONTEXT_CSS_MAP_GROUP_TRAIN_MG },
|
||||
{ "mg_training", CONTEXT_CSS_MAP_GROUP_TRAINING_MG },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
static ContextValue_t g_LevelContexts[] =
|
||||
{
|
||||
{ "cs_italy", CONTEXT_CSS_LEVEL_ITALY },
|
||||
{ "cs_office", CONTEXT_CSS_LEVEL_OFFICE },
|
||||
{ "de_aztec", CONTEXT_CSS_LEVEL_AZTEC },
|
||||
{ "de_dust", CONTEXT_CSS_LEVEL_DUST },
|
||||
{ "de_dust2", CONTEXT_CSS_LEVEL_DUST2 },
|
||||
{ "de_inferno", CONTEXT_CSS_LEVEL_INFERNO },
|
||||
{ "de_nuke", CONTEXT_CSS_LEVEL_NUKE },
|
||||
{ "ar_baggage", CONTEXT_CSS_LEVEL_BAGGAGE },
|
||||
{ "ar_shoots", CONTEXT_CSS_LEVEL_SHOOTS },
|
||||
{ "de_lake", CONTEXT_CSS_LEVEL_LAKE },
|
||||
{ "de_bank", CONTEXT_CSS_LEVEL_BANK },
|
||||
{ "de_safehouse", CONTEXT_CSS_LEVEL_SAFEHOUSE },
|
||||
{ "de_sugarcane", CONTEXT_CSS_LEVEL_SUGARCANE },
|
||||
{ "de_stmarc", CONTEXT_CSS_LEVEL_STMARC },
|
||||
{ "de_shorttrain", CONTEXT_CSS_LEVEL_SHORTTRAIN },
|
||||
{ "de_train", CONTEXT_CSS_LEVEL_TRAIN },
|
||||
{ "training1", CONTEXT_CSS_LEVEL_TRAINING },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
static ContextValue_t g_GameModeContexts[] =
|
||||
{
|
||||
{ "casual", CONTEXT_CSS_GAME_MODE_CASUAL },
|
||||
{ "competitive", CONTEXT_CSS_GAME_MODE_COMPETITIVE },
|
||||
{ "gungameprogressive", CONTEXT_CSS_GAME_MODE_GUNGAMEPROGRESSIVE },
|
||||
{ "gungametrbomb", CONTEXT_CSS_GAME_MODE_GUNGAMEBOMB },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
static ContextValue_t g_GameModeAsNumberContexts[] =
|
||||
{
|
||||
{ "casual", 0 },
|
||||
{ "competitive", 1 },
|
||||
{ "competitive_unranked", 2 },
|
||||
{ "pro", 4 },
|
||||
{ "gungameprogressive", 10 },
|
||||
{ "gungameselect", 20 },
|
||||
{ "gungametrbomb", 30 },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
static ContextValue_t g_GameTypeContexts[] =
|
||||
{
|
||||
{ "classic", CONTEXT_CSS_GAME_TYPE_CLASSIC },
|
||||
{ "gungame", CONTEXT_CSS_GAME_TYPE_GUNGAME },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
static ContextValue_t g_PrivacyContexts[] =
|
||||
{
|
||||
{ "public", CONTEXT_CSS_PRIVACY_PUBLIC },
|
||||
{ "private", CONTEXT_CSS_PRIVACY_INVITE_ONLY },
|
||||
{ "friends", CONTEXT_CSS_PRIVACY_FRIENDS },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
#endif // _MM_TITLE_CONTEXTVALUES_H
|
||||
1645
matchmaking/cstrike15/mm_title_gamesettingsmgr.cpp
Normal file
1645
matchmaking/cstrike15/mm_title_gamesettingsmgr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
50
matchmaking/cstrike15/mm_title_main.cpp
Normal file
50
matchmaking/cstrike15/mm_title_main.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "../mm_title_main.h"
|
||||
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
LINK_MATCHMAKING_LIB();
|
||||
|
||||
static CMatchTitle g_MatchTitle;
|
||||
CMatchTitle *g_pMatchTitle = &g_MatchTitle;
|
||||
|
||||
IMatchTitle *g_pIMatchTitle = g_pMatchTitle;
|
||||
IMatchEventsSink *g_pIMatchTitleEventsSink = g_pMatchTitle;
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Init / shutdown
|
||||
//
|
||||
|
||||
InitReturnVal_t MM_Title_Init()
|
||||
{
|
||||
return g_pMatchTitle->Init();
|
||||
}
|
||||
|
||||
void MM_Title_Shutdown()
|
||||
{
|
||||
if ( g_pMatchTitle )
|
||||
g_pMatchTitle->Shutdown();
|
||||
}
|
||||
|
||||
#if defined( _X360 ) && defined( _DEBUG )
|
||||
CON_COMMAND( x_dbg_xgi, "Set X360 XGI debug output level" )
|
||||
{
|
||||
int iLevel = args.FindArgInt( "-level", -1 );
|
||||
if ( iLevel >= 0 )
|
||||
{
|
||||
XDebugSetSystemOutputLevel( HXAMAPP_XGI, iLevel );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
330
matchmaking/cstrike15/mm_title_richpresence.cpp
Normal file
330
matchmaking/cstrike15/mm_title_richpresence.cpp
Normal file
@@ -0,0 +1,330 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title_richpresence.h"
|
||||
#include "mm_title_contextvalues.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
static void SetAllUsersContext( DWORD dwContextId, DWORD dwValue, bool bAsync = true )
|
||||
{
|
||||
#ifdef _X360
|
||||
for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
|
||||
{
|
||||
if ( XBX_GetUserIsGuest( k ) )
|
||||
continue;
|
||||
int iCtrlr = XBX_GetUserId( k );
|
||||
|
||||
//if ( dwContextId == X_CONTEXT_PRESENCE ) DevMsg( "Set presence to %d for user %d\n", dwValue, iCtrlr );
|
||||
|
||||
if ( bAsync )
|
||||
XUserSetContextEx( iCtrlr, dwContextId, dwValue, MMX360_NewOverlappedDormant() );
|
||||
else
|
||||
XUserSetContext( iCtrlr, dwContextId, dwValue );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void SetAllUsersProperty( DWORD dwPropertyId, DWORD cbValue, void const *pvValue )
|
||||
{
|
||||
#ifdef _X360
|
||||
for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
|
||||
{
|
||||
if ( XBX_GetUserIsGuest( k ) )
|
||||
continue;
|
||||
int iCtrlr = XBX_GetUserId( k );
|
||||
XUserSetPropertyEx( iCtrlr, dwPropertyId, cbValue, pvValue, MMX360_NewOverlappedDormant() );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
KeyValues * MM_Title_RichPresence_PrepareForSessionCreate( KeyValues *pSettings )
|
||||
{
|
||||
SetAllUsersContext( X_CONTEXT_GAME_MODE, CONTEXT_GAME_MODE_CSS_GAME_MODE_MULTIPLAYER, false );
|
||||
|
||||
// matchmaking version
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
extern ConVar mm_title_debug_version;
|
||||
val = mm_title_debug_version.GetInt();
|
||||
SetAllUsersProperty( PROPERTY_CSS_MATCH_VERSION, sizeof( val ), &val );
|
||||
DevMsg( "PrepareForSessionCreate: matchmaking version %d\n", val );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void MM_Title_RichPresence_Update( KeyValues *pFullSettings, KeyValues *pUpdatedSettings )
|
||||
{
|
||||
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
||||
( void ) g_pMatchExtensions->GetIBaseClientDLL()->GetRichPresenceStatusString();
|
||||
#endif
|
||||
#ifdef _X360
|
||||
if ( !pFullSettings )
|
||||
{
|
||||
SetAllUsersContext( X_CONTEXT_PRESENCE, CONTEXT_PRESENCE_MAINMENU ); // main menu
|
||||
return;
|
||||
}
|
||||
|
||||
// Also set players information during initial rich presence update
|
||||
if ( !pUpdatedSettings && pFullSettings )
|
||||
{
|
||||
MM_Title_RichPresence_PlayersChanged( pFullSettings );
|
||||
|
||||
// Open slots
|
||||
int numSlots = pFullSettings->GetInt( "members/numSlots", XBX_GetNumGameUsers() );
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
val = numSlots;
|
||||
SetAllUsersProperty( PROPERTY_CSS_OPEN_SLOTS, sizeof( val ), &val );
|
||||
}
|
||||
|
||||
// Team slots
|
||||
int numTeamSlots = MAX( pFullSettings->GetInt( "members/numTSlotsFree", 0 ), pFullSettings->GetInt( "members/numCTSlotsFree", 0 ) );
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
val = numTeamSlots;
|
||||
SetAllUsersProperty( PROPERTY_CSS_MAX_OPEN_TEAM_SLOTS, sizeof( val ), &val );
|
||||
}
|
||||
|
||||
// Skill fields
|
||||
{
|
||||
static int val = 0; // must be valid for the async call
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_EXPERIENCE, sizeof( val ), &val );
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL0, sizeof( val ), &val );
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL1, sizeof( val ), &val );
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL2, sizeof( val ), &val );
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL3, sizeof( val ), &val );
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL4, sizeof( val ), &val );
|
||||
}
|
||||
|
||||
// Listen/dedicated server resolver
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
val = 0;
|
||||
extern ConVar mm_title_debug_dccheck;
|
||||
if ( mm_title_debug_dccheck.GetInt() )
|
||||
val = ( ( mm_title_debug_dccheck.GetInt() > 0 ) ? 0 : 1 );
|
||||
SetAllUsersProperty( PROPERTY_CSS_SEARCH_LISTEN_SERVER, sizeof( val ), &val );
|
||||
}
|
||||
}
|
||||
|
||||
// pUpdatedSettings = NULL when the session is created and all contexts need to be set
|
||||
KeyValues *pNewSettings = pUpdatedSettings ? pUpdatedSettings : pFullSettings;
|
||||
|
||||
// if ( KeyValues *kvVal = pNewSettings->FindKey( "game/dlcrequired" ) )
|
||||
// {
|
||||
// static int val[10]; // must be valid for the async call
|
||||
// uint64 uiDlcRequired = kvVal->GetUint64();
|
||||
// extern ConVar mm_matchmaking_dlcsquery;
|
||||
// for ( int k = 1; k <= mm_matchmaking_dlcsquery.GetInt(); ++ k )
|
||||
// {
|
||||
// val[k] = !!( uiDlcRequired & ( 1ull << k ) );
|
||||
// DevMsg( "DLC%d required: %d\n", k, val[k] );
|
||||
// SetAllUsersProperty( PROPERTY_REQUIRED_DLC1 - 1 + k, sizeof( val ), &val );
|
||||
// }
|
||||
// }
|
||||
|
||||
// Actual game type (classic, gungame)
|
||||
if ( char const *szGameType = pNewSettings->GetString( "game/type", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( CONTEXT_CSS_GAME_TYPE, g_GameTypeContexts->ScanValues( szGameType ) );
|
||||
}
|
||||
|
||||
// Game state
|
||||
if ( char const *szGameState = pNewSettings->GetString( "game/state", NULL ) )
|
||||
{
|
||||
if ( !V_stricmp( pFullSettings->GetString( "system/network" ), "offline" ) )
|
||||
SetAllUsersContext( CONTEXT_GAME_STATE, CONTEXT_GAME_STATE_SINGLE_PLAYER );
|
||||
else
|
||||
SetAllUsersContext( CONTEXT_GAME_STATE, ( !V_stricmp( "game", szGameState ) ) ? CONTEXT_GAME_STATE_MULTIPLAYER : CONTEXT_GAME_STATE_IN_MENUS );
|
||||
}
|
||||
|
||||
// Actual game mode (casual, competitive, pro, etc)
|
||||
if ( char const *szValue = pNewSettings->GetString( "game/mode", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( CONTEXT_CSS_GAME_MODE, g_GameModeContexts->ScanValues( szValue ) );
|
||||
|
||||
static int val; // must be valid for the async call
|
||||
val = g_GameModeAsNumberContexts->ScanValues( szValue );
|
||||
SetAllUsersProperty( PROPERTY_CSS_GAME_MODE_AS_NUMBER, sizeof( val ), &val );
|
||||
}
|
||||
|
||||
// MapGroup being used
|
||||
if ( char const *szMapGroupName = pNewSettings->GetString( "game/mapgroupname", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( CONTEXT_CSS_MAP_GROUP, g_MapGroupContexts->ScanValues( szMapGroupName ) );
|
||||
}
|
||||
|
||||
// Privacy
|
||||
if ( char const *szPrivacy = pNewSettings->GetString( "system/access", NULL ) )
|
||||
{
|
||||
SetAllUsersContext( CONTEXT_CSS_PRIVACY, g_PrivacyContexts->ScanValues( szPrivacy ) );
|
||||
}
|
||||
|
||||
// Listen server
|
||||
if ( char const *szListenServer = pNewSettings->GetString( "server/server", NULL ) )
|
||||
{
|
||||
static int val; // must be valid for the async call
|
||||
val = ( !V_stricmp( "listen", szListenServer ) ? 1 : 0 );
|
||||
extern ConVar mm_title_debug_dccheck;
|
||||
if ( mm_title_debug_dccheck.GetInt() )
|
||||
val = ( ( mm_title_debug_dccheck.GetInt() > 0 ) ? 0 : 1 );
|
||||
SetAllUsersProperty( PROPERTY_CSS_SEARCH_LISTEN_SERVER, sizeof( val ), &val );
|
||||
}
|
||||
|
||||
//
|
||||
// Determine Rich Presence Display
|
||||
//
|
||||
if ( char const *szGameModeForRichPresence = pFullSettings->GetString( "game/mode", NULL ) )
|
||||
{
|
||||
// Online/Offline
|
||||
if ( char const *szNetwork = pFullSettings->GetString( "system/network", NULL ) )
|
||||
{
|
||||
DWORD dwLevelPresence = CONTEXT_PRESENCE_MAINMENU;
|
||||
|
||||
if ( V_stricmp( "offline", szNetwork ) == 0 )
|
||||
{
|
||||
dwLevelPresence = CONTEXT_PRESENCE_SINGLEPLAYER;
|
||||
}
|
||||
else if ( !V_stricmp( "lobby", pFullSettings->GetString( "game/state" ) ) )
|
||||
{
|
||||
dwLevelPresence = CONTEXT_PRESENCE_LOBBY;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Privacy
|
||||
if ( char const *szPrivacy = pFullSettings->GetString( "system/access", NULL ) )
|
||||
{
|
||||
DWORD dwPrivacy = g_PrivacyContexts->ScanValues( szPrivacy );
|
||||
if ( dwPrivacy == CONTEXT_CSS_PRIVACY_PUBLIC )
|
||||
{
|
||||
// Public match
|
||||
|
||||
// See if there are any free slots
|
||||
int numSlots = pFullSettings->GetInt( "members/numSlots", 0 );
|
||||
int numPlayers = pFullSettings->GetInt( "members/numPlayers", 0 );
|
||||
|
||||
dwLevelPresence = (numSlots > numPlayers) ? CONTEXT_PRESENCE_MULTIPLAYER : CONTEXT_PRESENCE_MULTIPLAYER_NO_SLOTS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Private/invite only match
|
||||
dwLevelPresence = CONTEXT_PRESENCE_MULTIPLAYER_PRIVATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetAllUsersContext( X_CONTEXT_PRESENCE, dwLevelPresence );
|
||||
}
|
||||
|
||||
// Update the map being used
|
||||
DWORD dwMapRichPresence = pFullSettings->GetInt( "game/mapRichPresence", 0xFFFF );
|
||||
if ( dwMapRichPresence == 0xFFFF )
|
||||
{
|
||||
// We didn't have a richpresence context set in GameModes.txt so look it up based on the name
|
||||
if ( char const *szMapName = pFullSettings->GetString( "game/map", NULL ) )
|
||||
{
|
||||
dwMapRichPresence = g_LevelContexts->ScanValues( szMapName );
|
||||
}
|
||||
}
|
||||
if ( dwMapRichPresence != 0xFFFF )
|
||||
{
|
||||
SetAllUsersContext( CONTEXT_CSS_LEVEL, dwMapRichPresence );
|
||||
}
|
||||
}
|
||||
#endif // _X360
|
||||
}
|
||||
|
||||
void MM_Title_RichPresence_PlayersChanged( KeyValues *pFullSettings )
|
||||
{
|
||||
//#ifdef _X360
|
||||
// // Set the installed DLCs masks
|
||||
// static int val[10]; // must be valid for the async call
|
||||
// uint64 uiDlcInstalled = g_pMatchFramework->GetMatchSystem()->GetDlcManager()->GetDataInfo()->GetUint64( "@info/installed" );
|
||||
// extern ConVar mm_matchmaking_dlcsquery;
|
||||
// for ( int k = 1; k <= mm_matchmaking_dlcsquery.GetInt(); ++ k )
|
||||
// {
|
||||
// val[k] = !!( uiDlcInstalled & ( 1ull << k ) );
|
||||
// DevMsg( "DLC%d installed: %d\n", k, val[k] );
|
||||
// SetAllUsersProperty( PROPERTY_INSTALLED_DLC1 - 1 + k, sizeof( val[k] ), &val[k] );
|
||||
// }
|
||||
//#endif
|
||||
}
|
||||
|
||||
// Called by the client to notify matchmaking that it should update matchmaking properties based
|
||||
// on player distribution among the teams.
|
||||
void MM_Title_RichPresence_UpdateTeamPropertiesCSGO( KeyValues *pCurrentSettings, KeyValues *pTeamProperties )
|
||||
{
|
||||
#ifdef _X360
|
||||
int numSlots = pCurrentSettings->GetInt( "members/numSlots", 0 );
|
||||
int numPlayers = pTeamProperties->GetInt( "members/numPlayers", 0 );
|
||||
int numSpectators = pTeamProperties->GetInt( "members/numSpectators", 0 );
|
||||
int numExtraSpectatorSlots = pCurrentSettings->GetInt( "members/numExtraSpectatorSlots", 0 );
|
||||
int numFreeTSlots = pCurrentSettings->GetInt( "members/numTSlotsFree", 0 );
|
||||
int numFreeCTSlots = pCurrentSettings->GetInt( "members/numCTSlotsFree", 0 );
|
||||
|
||||
// Spectator overflow is computed in case we end up in a situation in which there are more
|
||||
// spectators than spectator slots. In that case, the extra spectators are counted against
|
||||
// the active player slots.
|
||||
int spectatorOverflow = numSpectators - numExtraSpectatorSlots;
|
||||
|
||||
|
||||
static int nPROPERTY_CSS_OPEN_SLOTS;
|
||||
nPROPERTY_CSS_OPEN_SLOTS = numSlots - numPlayers;
|
||||
if ( spectatorOverflow > 0 )
|
||||
{
|
||||
nPROPERTY_CSS_OPEN_SLOTS = numSlots - ( numPlayers - numExtraSpectatorSlots + spectatorOverflow );
|
||||
}
|
||||
SetAllUsersProperty( PROPERTY_CSS_OPEN_SLOTS, sizeof( nPROPERTY_CSS_OPEN_SLOTS ), &nPROPERTY_CSS_OPEN_SLOTS );
|
||||
|
||||
|
||||
|
||||
// post the average skill rank so matchmaking will work
|
||||
int avgRank = pTeamProperties->GetInt( "members/timeout", DEFAULT_NEW_PLAYER_ELO_RANK );
|
||||
static int nPROPERTY_CSS_AGGREGATE_SKILL0;
|
||||
nPROPERTY_CSS_AGGREGATE_SKILL0 = avgRank;
|
||||
SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL0, sizeof( nPROPERTY_CSS_AGGREGATE_SKILL0 ), &nPROPERTY_CSS_AGGREGATE_SKILL0 );
|
||||
|
||||
|
||||
// Post the maximum number of free slots on either team, for team matchmaking only
|
||||
static int nPROPERTY_CSS_MAX_OPEN_TEAM_SLOTS;
|
||||
nPROPERTY_CSS_MAX_OPEN_TEAM_SLOTS = max( numFreeTSlots, numFreeCTSlots );
|
||||
SetAllUsersProperty( PROPERTY_CSS_MAX_OPEN_TEAM_SLOTS, sizeof( nPROPERTY_CSS_MAX_OPEN_TEAM_SLOTS ), &nPROPERTY_CSS_MAX_OPEN_TEAM_SLOTS );
|
||||
|
||||
#elif defined( _PS3 )
|
||||
// This is hacky: we stuff the rank into system data so that if lobby migrates
|
||||
// to a new owner and the metadata wasn't correctly set by the previous owner
|
||||
// then the new owner will set it
|
||||
if ( IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession() )
|
||||
{
|
||||
KeyValues *kvSystemData = pMatchSession->GetSessionSystemData();
|
||||
if ( !kvSystemData )
|
||||
return;
|
||||
|
||||
int avgRank = pTeamProperties->GetInt( "members/timeout", DEFAULT_NEW_PLAYER_ELO_RANK );
|
||||
kvSystemData->SetInt( "timeout", avgRank );
|
||||
|
||||
int numOpenSlots = 10 - pTeamProperties->GetInt( "members/numPlayers" );
|
||||
if ( numOpenSlots < 0 )
|
||||
numOpenSlots = 0;
|
||||
|
||||
kvSystemData->SetInt( "numOpenSlots", numOpenSlots );
|
||||
|
||||
char const *szSessionType = kvSystemData->GetString( "type", NULL );
|
||||
|
||||
DevMsg( "Session timeout value=%d (%s)\n", avgRank, szSessionType ? szSessionType : "offline" );
|
||||
DevMsg( "Session numOpenSlots=%d (%s)\n", numOpenSlots, szSessionType ? szSessionType : "offline" );
|
||||
if ( szSessionType && !Q_stricmp( szSessionType, "client" ) )
|
||||
return; // don't run on clients for now, maybe later when we become owner
|
||||
|
||||
steamapicontext->SteamMatchmaking()->SetLobbyData( kvSystemData->GetUint64( "xuidReserve", 0ull ), "game:timeout", CFmtStr( "%u", avgRank ) );
|
||||
steamapicontext->SteamMatchmaking()->SetLobbyData( kvSystemData->GetUint64( "xuidReserve", 0ull ), "game:numOpenSlots", CFmtStr( "%u", numOpenSlots ) );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
21
matchmaking/cstrike15/mm_title_richpresence.h
Normal file
21
matchmaking/cstrike15/mm_title_richpresence.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_TITLE_RICHPRESENCE_H
|
||||
#define MM_TITLE_RICHPRESENCE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "mm_title.h"
|
||||
|
||||
void MM_Title_RichPresence_Update( KeyValues *pFullSettings, KeyValues *pUpdatedSettings );
|
||||
void MM_Title_RichPresence_UpdateTeamPropertiesCSGO( KeyValues *pCurrentSettings, KeyValues *pTeamProperties );
|
||||
void MM_Title_RichPresence_PlayersChanged( KeyValues *pFullSettings );
|
||||
|
||||
KeyValues * MM_Title_RichPresence_PrepareForSessionCreate( KeyValues *pSettings );
|
||||
|
||||
#endif // MM_TITLE_H
|
||||
557
matchmaking/cstrike15/mm_title_titledata.cpp
Normal file
557
matchmaking/cstrike15/mm_title_titledata.cpp
Normal file
@@ -0,0 +1,557 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "matchmaking/cstrike15/imatchext_cstrike15.h"
|
||||
#include "inputsystem/iinputsystem.h"
|
||||
#include "platforminputdevice.h"
|
||||
#include "netmessages_signon.h"
|
||||
|
||||
#ifndef NO_STEAM
|
||||
#include "steam/isteamuserstats.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
static ConVar cl_titledataversionblock1( "cl_titledataversionblock1", "14", FCVAR_DEVELOPMENTONLY, "stats for console title data block1 i/o version." );
|
||||
static ConVar cl_titledataversionblock2( "cl_titledataversionblock2", "8", FCVAR_DEVELOPMENTONLY, "stats for console title data block2 i/o version." );
|
||||
static ConVar cl_titledataversionblock3( "cl_titledataversionblock3", "48", FCVAR_DEVELOPMENTONLY, "stats for console title data block3 i/o version." );
|
||||
|
||||
static TitleDataFieldsDescription_t const * PrepareTitleDataStorageDescription()
|
||||
{
|
||||
|
||||
#if defined( _X360 )
|
||||
|
||||
#define TD_ENTRY( szName, nTD, eDataType, numBytesOffset ) \
|
||||
{ \
|
||||
TitleDataFieldsDescription_t aTDFD = { szName, TitleDataFieldsDescription_t::nTD, TitleDataFieldsDescription_t::eDataType, numBytesOffset }; \
|
||||
s_tdfd.AddToTail( aTDFD ); \
|
||||
if ( numBytesOffset >= XPROFILE_SETTING_MAX_SIZE ) \
|
||||
Warning( "\nnumBytesOffset %d is > XPROFILE_SETTING_MAX_SIZE in TD_ENTRY for %s\n\n", numBytesOffset, szName ); \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define TD_ENTRY( szName, nTD, eDataType, numBytesOffset ) \
|
||||
{ \
|
||||
TitleDataFieldsDescription_t aTDFD = { szName, TitleDataFieldsDescription_t::nTD, TitleDataFieldsDescription_t::eDataType, numBytesOffset }; \
|
||||
s_tdfd.AddToTail( aTDFD ); \
|
||||
}
|
||||
|
||||
#endif // _X360
|
||||
|
||||
static CUtlVector< TitleDataFieldsDescription_t > s_tdfd;
|
||||
|
||||
#if defined( _X360 )
|
||||
|
||||
// versioning info for the title data blocks
|
||||
char *pTitleDataBlock[3];
|
||||
pTitleDataBlock[0] = new char[30];
|
||||
pTitleDataBlock[1] = new char[30];
|
||||
pTitleDataBlock[2] = new char[30];
|
||||
Q_snprintf( pTitleDataBlock[0], 30, "TITLEDATA.BLOCK1.VERSION" );
|
||||
Q_snprintf( pTitleDataBlock[1], 30, "TITLEDATA.BLOCK2.VERSION" );
|
||||
Q_snprintf( pTitleDataBlock[2], 30, "TITLEDATA.BLOCK3.VERSION" );
|
||||
TD_ENTRY( pTitleDataBlock[0], DB_TD1, DT_uint16, offsetof( TitleData1, versionNumber ) );
|
||||
TD_ENTRY( pTitleDataBlock[1], DB_TD2, DT_uint16, offsetof( TitleData2, versionNumber ) );
|
||||
TD_ENTRY( pTitleDataBlock[2], DB_TD3, DT_uint16, offsetof( TitleData3, versionNumber ) );
|
||||
|
||||
// stats
|
||||
#define CFG( cppType, name ) \
|
||||
TD_ENTRY( "STATS.usr." #name, DB_TD1, DT_##cppType, offsetof( TitleData1, usrStats.name ) )
|
||||
#include "xlast_csgo/inc_stats_usr.inc"
|
||||
#undef CFG
|
||||
|
||||
// loadouts
|
||||
//we are using an array so we can pack this info as tight as possible to fit into the 1K block
|
||||
#define CFG( loadoutnum, equipmentnum ) \
|
||||
int numLoadouts = loadoutnum; \
|
||||
int numEquipmentSlots = equipmentnum;
|
||||
#include "xlast_csgo/inc_loadouts_usr.inc"
|
||||
#undef CFG
|
||||
|
||||
int loadoutDataIndex = 0;
|
||||
for ( int team=0; team<2; ++team )
|
||||
{
|
||||
char teamName[10];
|
||||
if ( team == 0 )
|
||||
Q_snprintf( teamName, 10, "CT" );
|
||||
else
|
||||
Q_snprintf( teamName, 10, "T" );
|
||||
for (int i=0; i<numLoadouts; ++i)
|
||||
{
|
||||
for (int j=0; j<numEquipmentSlots; ++j)
|
||||
{
|
||||
char *loadoutName = new char[30];
|
||||
Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.EQUIP%.1d.ID", teamName, i, j );
|
||||
TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
|
||||
loadoutName = new char[30];
|
||||
Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.EQUIP%.1d.QUANTITY", teamName, i, j );
|
||||
TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
|
||||
}
|
||||
char *loadoutName = new char[30];
|
||||
Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.PRIMARY", teamName, i );
|
||||
TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
|
||||
loadoutName = new char[30];
|
||||
Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.SECONDARY", teamName, i );
|
||||
TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
|
||||
loadoutName = new char[30];
|
||||
Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.FLAGS", teamName, i );
|
||||
TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
|
||||
}
|
||||
}
|
||||
|
||||
// medals
|
||||
#define CFG( buffer ) \
|
||||
for ( int i=0; i< buffer; ++i ) \
|
||||
{ \
|
||||
char *pAwardedName = new char[ 20 ]; \
|
||||
Q_snprintf( pAwardedName, 20, "MEDALS.AWARDED%.3d", i ); \
|
||||
TD_ENTRY( pAwardedName, DB_TD2, DT_uint8, offsetof( TitleData2, CSMedalsAwarded[i] ) ) \
|
||||
char *pMedalInfoName = new char[ 25 ]; \
|
||||
Q_snprintf( pMedalInfoName, 25, "MEDALS.MEDALINFO%.3d", i ); \
|
||||
TD_ENTRY( pMedalInfoName, DB_TD2, DT_uint32, offsetof( TitleData2, CSMedalsMedalInfo[i] ) ) \
|
||||
}
|
||||
#include "xlast_csgo/inc_medals_usr.inc"
|
||||
#undef CFG
|
||||
|
||||
#endif // _X360
|
||||
|
||||
#if defined( _GAMECONSOLE )
|
||||
|
||||
// system convars
|
||||
#define CFG( name, scfgType, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX "CFG.sys." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvSystem.name ) )
|
||||
#include "xlast_csgo/inc_gameconsole_settings_sys.inc"
|
||||
#undef CFG
|
||||
|
||||
// profile-specific convars
|
||||
#define CFG( name, scfgType, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX "CFG.usr." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUser.name ) ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX "CFG.usrSS." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUserSS.name ) )
|
||||
#include "xlast_csgo/inc_gameconsole_settings_usr.inc"
|
||||
#include "xlast_csgo/inc_gameconsole_device_specific_settings_usr.inc"
|
||||
#undef CFG
|
||||
|
||||
|
||||
// joystick bindings
|
||||
#define ACTION( name )
|
||||
#define BINDING( name, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX "BINDING." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, JoystickBindings.name ) )
|
||||
#include "xlast_csgo/inc_bindings_usr.inc"
|
||||
#undef BINDING
|
||||
|
||||
#if defined( _PS3 )
|
||||
|
||||
// PS3 also has keyboard bindings.
|
||||
#define BINDING( name, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX "BINDING." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, JoystickBindings.name ) )
|
||||
#include "xlast_csgo/inc_ps3_key_bindings_usr.inc"
|
||||
#undef BINDING
|
||||
|
||||
// For PS3 we have two additional sets of button bindings one for Sharp Shooter, the other for Move.
|
||||
// We also have a few device specific convar settings.
|
||||
|
||||
// PS Move specific bindings and settings
|
||||
#define BINDING( name, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX TITLE_DATA_DEVICE_MOVE_PREFIX "BINDING." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, JoystickBindings.PSMove.name ) )
|
||||
#include "xlast_csgo/inc_bindings_usr.inc"
|
||||
#undef BINDING
|
||||
|
||||
#define CFG( name, scfgType, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX TITLE_DATA_DEVICE_MOVE_PREFIX "CFG.usr." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUser.PSMove.name ) )
|
||||
#include "xlast_csgo/inc_gameconsole_device_specific_settings_usr.inc"
|
||||
#undef CFG
|
||||
|
||||
// Sharp Shooter specific bindings and settings
|
||||
#define BINDING( name, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX TITLE_DATA_DEVICE_SHARP_SHOOTER_PREFIX "BINDING." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, JoystickBindings.SharpShooter.name ) )
|
||||
#include "xlast_csgo/inc_bindings_usr.inc"
|
||||
#undef BINDING
|
||||
|
||||
#define CFG( name, scfgType, cppType ) \
|
||||
TD_ENTRY( TITLE_DATA_PREFIX TITLE_DATA_DEVICE_SHARP_SHOOTER_PREFIX "CFG.usr." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUser.SharpShooter.name ) )
|
||||
#include "xlast_csgo/inc_gameconsole_device_specific_settings_usr.inc"
|
||||
#undef CFG
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#undef ACTION
|
||||
|
||||
// Player Rankings by mode, controller, w/ optional history
|
||||
int rankIndex = 0;
|
||||
int numControllers = PlatformInputDevice::GetInputDeviceCountforPlatform();
|
||||
|
||||
for ( int m = 0; m < ELOTitleData::NUM_GAME_MODES_ELO_RANKED; m++ )
|
||||
{
|
||||
for ( int c = 1; c <= numControllers; c++ )
|
||||
{
|
||||
char *pRankingName = new char[ 30 ];
|
||||
V_snprintf( pRankingName, 30, TITLE_DATA_PREFIX "ELO.MODE%d.CTR%d", m, c );
|
||||
|
||||
TD_ENTRY( pRankingName, DB_TD3, DT_ELO, offsetof( TitleData3, playerRankingsData[ rankIndex ] ) );
|
||||
|
||||
rankIndex++;
|
||||
}
|
||||
|
||||
// Record the bracket and some info for calculating it. Only legal controllers are game console controllers.
|
||||
char *pBracketInfoName = new char[ 30 ];
|
||||
V_snprintf( pBracketInfoName, 30, TITLE_DATA_PREFIX"ELO.MODE%d.BRACKETINFO", m );
|
||||
TD_ENTRY( pBracketInfoName, DB_TD3, DT_uint16, offsetof( TitleData3, EloBracketInfo[ m ] ) );
|
||||
}
|
||||
|
||||
#endif // _GAMECONSOLE
|
||||
|
||||
#if defined ( _X360 )
|
||||
// matchmaking data
|
||||
#define CFG( cppType, name ) \
|
||||
TD_ENTRY( "MMDATA.usr." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, usrMMData.name ) )
|
||||
#include "xlast_csgo/inc_mmdata_usr.inc"
|
||||
#undef CFG
|
||||
#endif // #if defined ( _X360 )
|
||||
|
||||
// END MARKER
|
||||
TD_ENTRY( (const char*) NULL, DB_TD3, DT_0, 0 )
|
||||
|
||||
#undef TD_ENTRY
|
||||
|
||||
#if defined( _X360 )
|
||||
|
||||
COMPILE_TIME_ASSERT( sizeof( TitleData1 ) < XPROFILE_SETTING_MAX_SIZE );
|
||||
COMPILE_TIME_ASSERT( sizeof( TitleData2 ) < XPROFILE_SETTING_MAX_SIZE );
|
||||
COMPILE_TIME_ASSERT( sizeof( TitleData3 ) < XPROFILE_SETTING_MAX_SIZE );
|
||||
|
||||
#endif
|
||||
|
||||
return s_tdfd.Base();
|
||||
}
|
||||
|
||||
TitleDataFieldsDescription_t const * CMatchTitle::DescribeTitleDataStorage()
|
||||
{
|
||||
static TitleDataFieldsDescription_t const *s_pTDFD = PrepareTitleDataStorageDescription();
|
||||
return s_pTDFD;
|
||||
}
|
||||
|
||||
TitleAchievementsDescription_t const * CMatchTitle::DescribeTitleAchievements()
|
||||
{
|
||||
static TitleAchievementsDescription_t tad[] =
|
||||
{
|
||||
//#include "left4dead2.xhelp.achtitledesc.txt"
|
||||
// END MARKER
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
return tad;
|
||||
}
|
||||
|
||||
TitleAvatarAwardsDescription_t const * CMatchTitle::DescribeTitleAvatarAwards()
|
||||
{
|
||||
static TitleAvatarAwardsDescription_t taad[] =
|
||||
{
|
||||
//#include "left4dead2.xhelp.avawtitledesc.txt"
|
||||
// END MARKER
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
return taad;
|
||||
}
|
||||
|
||||
TitleDlcDescription_t const * CMatchTitle::DescribeTitleDlcs()
|
||||
{
|
||||
static TitleDlcDescription_t tdlcs[] =
|
||||
{
|
||||
//{ PORTAL2_DLCID_COOP_BOT_SKINS, PORTAL2_DLC_APPID_COOP_BOT_SKINS, PORTAL2_DLC_PKGID_COOP_BOT_SKINS, "DLC.0x12" },
|
||||
//{ PORTAL2_DLCID_COOP_BOT_HELMETS, PORTAL2_DLC_APPID_COOP_BOT_HELMETS, PORTAL2_DLC_PKGID_COOP_BOT_HELMETS, "DLC.0x13" },
|
||||
//{ PORTAL2_DLCID_COOP_BOT_ANTENNA, PORTAL2_DLC_APPID_COOP_BOT_ANTENNA, PORTAL2_DLC_PKGID_COOP_BOT_ANTENNA, "DLC.0x14" },
|
||||
// END MARKER
|
||||
{ 0, 0, 0 }
|
||||
};
|
||||
|
||||
return tdlcs;
|
||||
}
|
||||
|
||||
// Title leaderboards
|
||||
KeyValues * CMatchTitle::DescribeTitleLeaderboard( char const *szLeaderboardView )
|
||||
{
|
||||
#if !defined( NO_STEAM )
|
||||
if ( StringAfterPrefix( szLeaderboardView, "WINS_" ) )
|
||||
{
|
||||
if ( IsPC() || IsPS3() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score wins_ratio " // :score is the leaderboard value mapped to game name "besttime"
|
||||
" :payloadformat { " // This describes the payload format.
|
||||
" payload0 { "
|
||||
" :score total_wins"
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload1 { "
|
||||
" :score total_losses "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload2 { "
|
||||
" :score win_as_ct "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload3 { "
|
||||
" :score win_as_t "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload4 { "
|
||||
" :score loss_as_ct "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload5 { "
|
||||
" :score loss_as_t "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
|
||||
pSettings->SetString( ":scoreformula", "( payload0 / max( payload0 + payload1, 1 ) ) * ( min( payload0 + payload1, 20 ) / 20 ) * 10000000" );
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
|
||||
pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeNumeric ); // Note: this is actually 1/100th seconds type, Steam change pending
|
||||
pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodForceUpdate ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
else if ( StringAfterPrefix( szLeaderboardView, "CS_" ) )
|
||||
{
|
||||
if ( IsPC() || IsPS3() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score average_contribution " // :score is the leaderboard value mapped to game name "besttime"
|
||||
" :payloadformat { " // This describes the payload format.
|
||||
" payload0 { "
|
||||
" :score mvp_awards"
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload1 { "
|
||||
" :score rounds_played "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload2 { "
|
||||
" :score total_contribution "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload3 { "
|
||||
" :score kills "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload4 { "
|
||||
" :score deaths "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload5 { "
|
||||
" :score damage "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
|
||||
pSettings->SetString( ":scoreformula", "( payload2 / max( payload1, 1 ) )" );
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
|
||||
pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeNumeric ); // Note: this is actually 1/100th seconds type, Steam change pending
|
||||
pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodForceUpdate ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
else if ( StringAfterPrefix( szLeaderboardView, "KD_" ) )
|
||||
{
|
||||
if ( IsPC() || IsPS3() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score kd_ratio " // :score is the leaderboard value mapped to game name "besttime"
|
||||
" :payloadformat { " // This describes the payload format.
|
||||
" payload0 { "
|
||||
" :score kills"
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload1 { "
|
||||
" :score deaths "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload2 { "
|
||||
" :score rounds_played "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload3 { "
|
||||
" :score shots_fired "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload4 { "
|
||||
" :score head_shots "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload5 { "
|
||||
" :score shots_hit "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
|
||||
pSettings->SetString( ":scoreformula", "( payload0 / max( payload1, 1 ) ) * ( min( payload2, 20 ) / 20 ) * 10000000" );
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
|
||||
pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeNumeric ); // Note: this is actually 1/100th seconds type, Steam change pending
|
||||
pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodForceUpdate ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
else if ( StringAfterPrefix( szLeaderboardView, "STARS_" ) )
|
||||
{
|
||||
if ( IsPC() || IsPS3() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score numstars " // :score is the leaderboard value mapped to game name "besttime"
|
||||
" :scoresum 1 "
|
||||
" :payloadformat { " // This describes the payload format.
|
||||
" payload0 { "
|
||||
" :score bombs_planted "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload1 { "
|
||||
" :score bombs_detonated "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload2 { "
|
||||
" :score bombs_defused "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload3 { "
|
||||
" :score hostages_rescued "
|
||||
" :format int "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
|
||||
pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeNumeric ); // Note: this is actually 1/100th seconds type, Steam change pending
|
||||
pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodKeepBest ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
else if ( StringAfterPrefix( szLeaderboardView, "GP_" ) )
|
||||
{
|
||||
if ( IsPC() || IsPS3() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score num_rounds " // :score is the leaderboard value mapped to game name "besttime"
|
||||
" :scoresum 1 "
|
||||
" :payloadformat { " // This describes the payload format.
|
||||
" payload0 { "
|
||||
" :score time_played "
|
||||
" :format uint64 "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload1 { "
|
||||
" :score time_played_ct "
|
||||
" :format uint64 "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload2 { "
|
||||
" :score time_played_t "
|
||||
" :format uint64 "
|
||||
" :upload sum "
|
||||
" } "
|
||||
" payload3 { "
|
||||
" :score total_medals "
|
||||
" :format int "
|
||||
" :upload last " // the last value written is the authoritative value of total achievement medals unlocked
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
|
||||
pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeNumeric ); // Note: this is actually 1/100th seconds type, Steam change pending
|
||||
pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodKeepBest ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !NO_STEAM
|
||||
|
||||
/*
|
||||
// Check if this is a survival leaderboard
|
||||
if ( char const *szSurvivalMap = StringAfterPrefix( szLeaderboardView, "survival_" ) )
|
||||
{
|
||||
if ( IsX360() )
|
||||
{
|
||||
// Find the corresponding record in the mission script
|
||||
KeyValues *pSettings = new KeyValues( "settings" );
|
||||
KeyValues::AutoDelete autodelete_pSettings( pSettings );
|
||||
pSettings->SetString( "game/mode", "survival" );
|
||||
|
||||
KeyValues *pMissionInfo = NULL;
|
||||
KeyValues *pMapInfo = g_pMatchExtL4D->GetMapInfoByBspName( pSettings, szSurvivalMap, &pMissionInfo );
|
||||
if ( !pMapInfo || !pMissionInfo )
|
||||
return NULL;
|
||||
|
||||
// Find the leaderboard description in the map info
|
||||
KeyValues *pLbDesc = pMapInfo->FindKey( "x360leaderboard" );
|
||||
if ( !pLbDesc )
|
||||
return NULL;
|
||||
|
||||
// Insert the required keys
|
||||
pLbDesc = pLbDesc->MakeCopy();
|
||||
|
||||
static KeyValues *s_pRatingKey = KeyValues::FromString( ":rating", // X360 leaderboards are rated
|
||||
" name besttime " // game name of the rating field is "besttime"
|
||||
" type uint64 " // type is uint64
|
||||
" rule max" // rated field must be greater than cached value so that it can be written
|
||||
);
|
||||
pLbDesc->AddSubKey( s_pRatingKey->MakeCopy() );
|
||||
pLbDesc->SetString( "besttime/type", "uint64" );
|
||||
|
||||
return pLbDesc;
|
||||
}
|
||||
|
||||
if ( IsPC() || IsPS3() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score besttime " // :score is the leaderboard value mapped to game name "besttime"
|
||||
);
|
||||
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
|
||||
pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeTimeMilliSeconds ); // Note: this is actually 1/100th seconds type, Steam change pending
|
||||
pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodKeepBest ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
1177
matchmaking/datacenter.cpp
Normal file
1177
matchmaking/datacenter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
187
matchmaking/datacenter.h
Normal file
187
matchmaking/datacenter.h
Normal file
@@ -0,0 +1,187 @@
|
||||
//========= Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
#ifndef _DATACENTER_H_
|
||||
#define _DATACENTER_H_
|
||||
|
||||
#include "utlvector.h"
|
||||
#include "utlmap.h"
|
||||
|
||||
#include "x360_xlsp_cmd.h"
|
||||
|
||||
class CDatacenterCmdBatchImpl;
|
||||
|
||||
class CDatacenter :
|
||||
public IDatacenter,
|
||||
public IMatchEventsSink
|
||||
{
|
||||
public :
|
||||
CDatacenter();
|
||||
virtual ~CDatacenter();
|
||||
|
||||
//
|
||||
// IDatacenter implementation
|
||||
//
|
||||
public:
|
||||
//
|
||||
// EnableUpdate
|
||||
// controls whether data can be updated in the background
|
||||
//
|
||||
virtual void EnableUpdate( bool bEnable );
|
||||
|
||||
//
|
||||
// GetStats
|
||||
// retrieves the last received datacenter stats
|
||||
//
|
||||
virtual KeyValues * GetDataInfo();
|
||||
virtual KeyValues * GetStats();
|
||||
|
||||
//
|
||||
// CreateCmdBatch
|
||||
// creates a new instance of cmd batch to communicate
|
||||
// with datacenter backend
|
||||
//
|
||||
virtual IDatacenterCmdBatch * CreateCmdBatch( bool bMustSupportPII );
|
||||
|
||||
//
|
||||
// CanReachDatacenter
|
||||
// returns true if we were able to establish a connection with the
|
||||
// datacenter backend regardless if it returned valid data or not.
|
||||
virtual bool CanReachDatacenter();
|
||||
|
||||
// IMatchEventsSink
|
||||
public:
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
protected:
|
||||
void StorageDeviceWriteInfo( int iCtrlr );
|
||||
void TrySaveInfoToUserStorage();
|
||||
void OnDatacenterInfoUpdated();
|
||||
|
||||
//
|
||||
// Interface for match system
|
||||
//
|
||||
public:
|
||||
void Update();
|
||||
void OnCmdBatchReleased( CDatacenterCmdBatchImpl *pCmdBatch );
|
||||
|
||||
protected:
|
||||
void RequestStart();
|
||||
void RequestUpdate();
|
||||
void RequestStop();
|
||||
|
||||
void PushAwayNextUpdate();
|
||||
|
||||
void OnStorageDeviceAvailable( int iCtrlr );
|
||||
|
||||
protected:
|
||||
KeyValues *m_pDataInfo;
|
||||
KeyValues *m_pInfoChunks;
|
||||
|
||||
#ifdef _X360
|
||||
CXlspConnection *m_pXlspConnection;
|
||||
CXlspConnectionCmdBatch *m_pXlspBatch;
|
||||
bool m_bStorageDeviceAvail[ XUSER_MAX_COUNT ];
|
||||
int m_nVersionStored;
|
||||
int m_nVersionApplied;
|
||||
int m_numDelayedMountAttempts;
|
||||
float m_flDcRequestDelayUntil;
|
||||
#elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR ) && !defined( SWDS )
|
||||
friend class CGCClientJobDataRequest;
|
||||
JobID_t m_JobIDDataRequest;
|
||||
#endif
|
||||
|
||||
float m_flNextSearchTime;
|
||||
bool m_bCanReachDatacenter;
|
||||
|
||||
enum State_t
|
||||
{
|
||||
STATE_IDLE,
|
||||
STATE_REQUESTING_DATA,
|
||||
STATE_REQUESTING_CHUNKS,
|
||||
STATE_PAUSED,
|
||||
};
|
||||
State_t m_eState;
|
||||
|
||||
CUtlVector< CDatacenterCmdBatchImpl * > m_arrCmdBatchObjects;
|
||||
};
|
||||
|
||||
class CDatacenterCmdBatchImpl : public IDatacenterCmdBatch
|
||||
{
|
||||
public:
|
||||
explicit CDatacenterCmdBatchImpl( CDatacenter *pParent, bool bMustSupportPII );
|
||||
|
||||
public:
|
||||
//
|
||||
// AddCommand
|
||||
// enqueues a command in command batch queue
|
||||
//
|
||||
virtual void AddCommand( KeyValues *pCommand );
|
||||
|
||||
//
|
||||
// IsFinished
|
||||
// whether command batch queue has finished running / error occurred
|
||||
//
|
||||
virtual bool IsFinished();
|
||||
|
||||
//
|
||||
// GetNumResults
|
||||
// returns number of results retrieved for which data is available
|
||||
//
|
||||
virtual int GetNumResults();
|
||||
|
||||
//
|
||||
// GetResult
|
||||
// returns the result by index
|
||||
//
|
||||
virtual KeyValues * GetResult( int idx );
|
||||
|
||||
//
|
||||
// Destroy
|
||||
// destroys the command batch object and all contained results
|
||||
//
|
||||
virtual void Destroy();
|
||||
|
||||
//
|
||||
// SetDestroyWhenFinished
|
||||
// destroys the command batch object automatically after
|
||||
// it finishes communication with datacenter
|
||||
//
|
||||
virtual void SetDestroyWhenFinished( bool bDestroyWhenFinished );
|
||||
|
||||
//
|
||||
// SetNumRetriesAllowedPerCmd
|
||||
// configures retry attempts per command
|
||||
//
|
||||
virtual void SetNumRetriesAllowedPerCmd( int numRetriesAllowed );
|
||||
|
||||
//
|
||||
// SetRetryCmdTimeout
|
||||
// configures retry timeout per command
|
||||
//
|
||||
virtual void SetRetryCmdTimeout( float flRetryCmdTimeout );
|
||||
|
||||
public:
|
||||
virtual void Update();
|
||||
|
||||
protected:
|
||||
#ifdef _X360
|
||||
CXlspConnection *m_pXlspConnection;
|
||||
CXlspConnectionCmdBatch *m_pXlspBatch;
|
||||
#endif
|
||||
|
||||
CDatacenter *m_pParent;
|
||||
CUtlVector< KeyValues * > m_arrCommands;
|
||||
|
||||
int m_numRetriesAllowedPerCmd;
|
||||
float m_flRetryCmdTimeout;
|
||||
bool m_bDestroyWhenFinished;
|
||||
bool m_bMustSupportPII;
|
||||
};
|
||||
|
||||
extern class CDatacenter *g_pDatacenter;
|
||||
|
||||
#endif // _DATACENTER_H_
|
||||
378
matchmaking/dota/mm_title.cpp
Normal file
378
matchmaking/dota/mm_title.cpp
Normal file
@@ -0,0 +1,378 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "mm_title_richpresence.h"
|
||||
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
CMatchTitle::CMatchTitle()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
CMatchTitle::~CMatchTitle()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Init / shutdown
|
||||
//
|
||||
|
||||
InitReturnVal_t CMatchTitle::Init()
|
||||
{
|
||||
if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
|
||||
{
|
||||
mgr->AddListener( this, "server_pre_shutdown", false );
|
||||
mgr->AddListener( this, "game_newmap", false );
|
||||
mgr->AddListener( this, "finale_start", false );
|
||||
mgr->AddListener( this, "round_start", false );
|
||||
mgr->AddListener( this, "round_end", false );
|
||||
mgr->AddListener( this, "difficulty_changed", false );
|
||||
}
|
||||
return INIT_OK;
|
||||
}
|
||||
|
||||
void CMatchTitle::Shutdown()
|
||||
{
|
||||
if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
|
||||
{
|
||||
mgr->RemoveListener( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
uint64 CMatchTitle::GetTitleID()
|
||||
{
|
||||
#ifdef _X360
|
||||
return 0xFFFFFFFFFFFFFFFF;
|
||||
#elif !defined( SWDS )
|
||||
static uint64 uiAppID = 0ull;
|
||||
if ( !uiAppID && steamapicontext && steamapicontext->SteamUtils() )
|
||||
{
|
||||
uiAppID = steamapicontext->SteamUtils()->GetAppID();
|
||||
}
|
||||
return uiAppID;
|
||||
#else
|
||||
return 0ull;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64 CMatchTitle::GetTitleServiceID()
|
||||
{
|
||||
#ifdef _X360
|
||||
return 0x45410880ull; // Left 4 Dead 1 Service ID
|
||||
#else
|
||||
return 0ull;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CMatchTitle::IsMultiplayer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMatchTitle::PrepareNetStartupParams( void *pNetStartupParams )
|
||||
{
|
||||
#ifdef _X360
|
||||
XNetStartupParams &xnsp = *( XNetStartupParams * ) pNetStartupParams;
|
||||
|
||||
xnsp.cfgQosDataLimitDiv4 = 64; // 256 bytes
|
||||
xnsp.cfgSockDefaultRecvBufsizeInK = 64; // Increase receive size for UDP to 64k
|
||||
xnsp.cfgSockDefaultSendBufsizeInK = 64; // Keep send size at 64k too
|
||||
|
||||
int numGamePlayersMax = GetTotalNumPlayersSupported();
|
||||
|
||||
int numConnections = 4 * ( numGamePlayersMax - 1 );
|
||||
// - the max number of connections to members of your game party
|
||||
// - the max number of connections to members of your social party
|
||||
// - the max number of connections to a pending game party (if you are joining a new one ).
|
||||
// - matchmakings client info structure also creates a connection per client for the lobby.
|
||||
|
||||
// 1 - the main game session
|
||||
int numTotalConnections = 1 + numConnections;
|
||||
|
||||
// 29 - total Connections (XNADDR/XNKID pairs) ,using 5 sessions (XNKID/XNKEY pairs).
|
||||
|
||||
xnsp.cfgKeyRegMax = 16; //adding some extra room because of lazy dealocation of these pairs.
|
||||
xnsp.cfgSecRegMax = MAX( 64, numTotalConnections ); //adding some extra room because of lazy dealocation of these pairs.
|
||||
|
||||
xnsp.cfgSockMaxDgramSockets = xnsp.cfgSecRegMax;
|
||||
xnsp.cfgSockMaxStreamSockets = xnsp.cfgSecRegMax;
|
||||
#endif
|
||||
}
|
||||
|
||||
int CMatchTitle::GetTotalNumPlayersSupported()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get a guest player name
|
||||
char const * CMatchTitle::GetGuestPlayerName( int iUserIndex )
|
||||
{
|
||||
if ( vgui::ILocalize *pLocalize = g_pMatchExtensions->GetILocalize() )
|
||||
{
|
||||
if ( wchar_t* wStringTableEntry = pLocalize->Find( "#L4D360UI_Character_Guest" ) )
|
||||
{
|
||||
static char szName[ MAX_PLAYER_NAME_LENGTH ] = {0};
|
||||
pLocalize->ConvertUnicodeToANSI( wStringTableEntry, szName, ARRAYSIZE( szName ) );
|
||||
return szName;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
// Sets up all necessary client-side convars and user info before
|
||||
// connecting to server
|
||||
void CMatchTitle::PrepareClientForConnect( KeyValues *pSettings )
|
||||
{
|
||||
#ifndef SWDS
|
||||
int numPlayers = 1;
|
||||
#ifdef _X360
|
||||
numPlayers = XBX_GetNumGameUsers();
|
||||
#endif
|
||||
|
||||
//
|
||||
// Now we set the convars
|
||||
//
|
||||
|
||||
for ( int k = 0; k < numPlayers; ++ k )
|
||||
{
|
||||
int iController = k;
|
||||
#ifdef _X360
|
||||
iController = XBX_GetUserId( k );
|
||||
#endif
|
||||
IPlayerLocal *pPlayerLocal = g_pPlayerManager->GetLocalPlayer( iController );
|
||||
if ( !pPlayerLocal )
|
||||
continue;
|
||||
|
||||
// Set "name"
|
||||
static SplitScreenConVarRef s_cl_name( "name" );
|
||||
char const *szName = pPlayerLocal->GetName();
|
||||
s_cl_name.SetValue( k, szName );
|
||||
|
||||
// Set "networkid_force"
|
||||
if ( IsX360() )
|
||||
{
|
||||
static SplitScreenConVarRef s_networkid_force( "networkid_force" );
|
||||
uint64 xid = pPlayerLocal->GetXUID();
|
||||
s_networkid_force.SetValue( k, CFmtStr( "%08X:%08X:", uint32( xid >> 32 ), uint32( xid ) ) );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CMatchTitle::StartServerMap( KeyValues *pSettings )
|
||||
{
|
||||
|
||||
int numPlayers = 1;
|
||||
#ifdef _X360
|
||||
numPlayers = XBX_GetNumGameUsers();
|
||||
#endif
|
||||
|
||||
char const *szMap = pSettings->GetString( "game/bspname", NULL );
|
||||
if ( !szMap )
|
||||
return false;
|
||||
|
||||
// Check that we have the server interface and that the map is valid
|
||||
if ( !g_pMatchExtensions->GetIVEngineServer() )
|
||||
return false;
|
||||
if ( !g_pMatchExtensions->GetIVEngineServer()->IsMapValid( szMap ) )
|
||||
return false;
|
||||
|
||||
//
|
||||
// Prepare game dll reservation package
|
||||
//
|
||||
KeyValues *pGameDllReserve = g_pMatchFramework->GetMatchNetworkMsgController()->PackageGameDetailsForReservation( pSettings );
|
||||
KeyValues::AutoDelete autodelete( pGameDllReserve );
|
||||
|
||||
pGameDllReserve->SetString( "map/mapcommand", ( numPlayers <= 1 ) ? "map" : "ss_map" );
|
||||
|
||||
if ( !Q_stricmp( "commentary", pSettings->GetString( "options/play", "" ) ) )
|
||||
pGameDllReserve->SetString( "map/mapcommand", "map_commentary" );
|
||||
|
||||
// Run map based off the faked reservation packet
|
||||
g_pMatchExtensions->GetIServerGameDLL()->ApplyGameSettings( pGameDllReserve );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static KeyValues * GetCurrentMatchSessionSettings()
|
||||
{
|
||||
IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
return pIMatchSession ? pIMatchSession->GetSessionSettings() : NULL;
|
||||
}
|
||||
|
||||
void CMatchTitle::OnEvent( KeyValues *pEvent )
|
||||
{
|
||||
char const *szEvent = pEvent->GetName();
|
||||
|
||||
if ( !Q_stricmp( "OnPlayerRemoved", szEvent ) ||
|
||||
!Q_stricmp( "OnPlayerUpdated", szEvent ) )
|
||||
{
|
||||
MM_Title_RichPresence_PlayersChanged( GetCurrentMatchSessionSettings() );
|
||||
}
|
||||
else if ( !Q_stricmp( "OnMatchSessionUpdate", szEvent ) )
|
||||
{
|
||||
if ( !Q_stricmp( pEvent->GetString( "state" ), "updated" ) )
|
||||
{
|
||||
if ( KeyValues *kvUpdate = pEvent->FindKey( "update" ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), kvUpdate );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( pEvent->GetString( "state" ), "created" ) ||
|
||||
!Q_stricmp( pEvent->GetString( "state" ), "ready" ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), NULL );
|
||||
}
|
||||
else if ( !Q_stricmp( pEvent->GetString( "state" ), "closed" ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( NULL, NULL );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "Client::CmdKeyValues", szEvent ) )
|
||||
{
|
||||
KeyValues *pCmd = pEvent->GetFirstTrueSubKey();
|
||||
if ( !pCmd )
|
||||
return;
|
||||
|
||||
char const *szCmd = pCmd->GetName();
|
||||
|
||||
if ( !Q_stricmp( "swapteam", szCmd ) )
|
||||
{
|
||||
if ( IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession() )
|
||||
{
|
||||
KeyValues *pRequest = new KeyValues( "Game::Team" );
|
||||
KeyValues::AutoDelete autodelete( pRequest );
|
||||
|
||||
// If a player is on infected team, then the avatar is infected
|
||||
char const *szTeam = pCmd->GetString( "team" );
|
||||
|
||||
// Parse out the team and avatar info
|
||||
pRequest->SetString( "run", "host" );
|
||||
pRequest->SetUint64( "xuid", pCmd->GetUint64( "xuid" ) );
|
||||
pRequest->SetString( "team", szTeam );
|
||||
|
||||
pIMatchSession->Command( pRequest );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
int CMatchTitle::GetEventDebugID( void )
|
||||
{
|
||||
return EVENT_DEBUG_ID_INIT;
|
||||
}
|
||||
|
||||
void CMatchTitle::FireGameEvent( IGameEvent *pIGameEvent )
|
||||
{
|
||||
// Check if the current match session is on an active game server
|
||||
IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pMatchSession )
|
||||
return;
|
||||
KeyValues *pSessionSettings = pMatchSession->GetSessionSettings();
|
||||
char const *szGameServer = pSessionSettings->GetString( "server/server", "" );
|
||||
char const *szSystemLock = pSessionSettings->GetString( "system/lock", "" );
|
||||
if ( ( !szGameServer || !*szGameServer ) &&
|
||||
( !szSystemLock || !*szSystemLock ) )
|
||||
return;
|
||||
|
||||
// Also don't run on the client when there's a host
|
||||
char const *szSessionType = pMatchSession->GetSessionSystemData()->GetString( "type", NULL );
|
||||
if ( szSessionType && !Q_stricmp( szSessionType, "client" ) )
|
||||
return;
|
||||
|
||||
// Parse the game event
|
||||
char const *szGameEvent = pIGameEvent->GetName();
|
||||
if ( !szGameEvent || !*szGameEvent )
|
||||
return;
|
||||
|
||||
if ( !Q_stricmp( "round_start", szGameEvent ) )
|
||||
{
|
||||
pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" state game "
|
||||
" } "
|
||||
" } "
|
||||
) ) );
|
||||
}
|
||||
else if ( !Q_stricmp( "finale_start", szGameEvent ) )
|
||||
{
|
||||
pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" state finale "
|
||||
" } "
|
||||
" } "
|
||||
) ) );
|
||||
}
|
||||
else if ( !Q_stricmp( "game_newmap", szGameEvent ) )
|
||||
{
|
||||
KeyValues *kvUpdate = KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" state game "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
KeyValues::AutoDelete autodelete( kvUpdate );
|
||||
|
||||
pMatchSession->UpdateSessionSettings( kvUpdate );
|
||||
}
|
||||
else if ( !Q_stricmp( "difficulty_changed", szGameEvent ) )
|
||||
{
|
||||
char const *szDifficulty = pIGameEvent->GetString( "strDifficulty", "normal" );
|
||||
|
||||
KeyValues *kvUpdate = KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" difficulty = "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
KeyValues::AutoDelete autodelete( kvUpdate );
|
||||
|
||||
kvUpdate->SetString( "update/game/difficulty", szDifficulty );
|
||||
|
||||
pMatchSession->UpdateSessionSettings( kvUpdate );
|
||||
}
|
||||
else if ( !Q_stricmp( "server_pre_shutdown", szGameEvent ) )
|
||||
{
|
||||
char const *szReason = pIGameEvent->GetString( "reason", "quit" );
|
||||
if ( !Q_stricmp( szReason, "quit" ) )
|
||||
{
|
||||
DevMsg( "Received server_pre_shutdown notification - server is shutting down...\n" );
|
||||
|
||||
// Transform the server shutdown event into game end event
|
||||
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
||||
"OnEngineDisconnectReason", "reason", "Server shutting down"
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
83
matchmaking/dota/mm_title.h
Normal file
83
matchmaking/dota/mm_title.h
Normal file
@@ -0,0 +1,83 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_TITLE_H
|
||||
#define MM_TITLE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "../mm_framework.h"
|
||||
|
||||
class CMatchTitle :
|
||||
public IMatchTitle,
|
||||
public IMatchEventsSink,
|
||||
public IGameEventListener2
|
||||
{
|
||||
// Methods exposed for the framework
|
||||
public:
|
||||
virtual InitReturnVal_t Init();
|
||||
virtual void Shutdown();
|
||||
|
||||
// Methods of IMatchTitle
|
||||
public:
|
||||
// Title ID
|
||||
virtual uint64 GetTitleID();
|
||||
|
||||
// Service ID for XLSP
|
||||
virtual uint64 GetTitleServiceID();
|
||||
|
||||
// Whether we are a single-player title or multi-player title
|
||||
virtual bool IsMultiplayer();
|
||||
|
||||
// Prepare network startup params for the title
|
||||
virtual void PrepareNetStartupParams( void *pNetStartupParams );
|
||||
|
||||
// Get total number of players supported by the title
|
||||
virtual int GetTotalNumPlayersSupported();
|
||||
|
||||
// Get a guest player name
|
||||
virtual char const * GetGuestPlayerName( int iUserIndex );
|
||||
|
||||
// Decipher title data fields
|
||||
virtual TitleDataFieldsDescription_t const * DescribeTitleDataStorage();
|
||||
|
||||
// Title achievements
|
||||
virtual TitleAchievementsDescription_t const * DescribeTitleAchievements();
|
||||
|
||||
// Title avatar awards
|
||||
virtual TitleAvatarAwardsDescription_t const * DescribeTitleAvatarAwards();
|
||||
|
||||
// Title leaderboards
|
||||
virtual KeyValues * DescribeTitleLeaderboard( char const *szLeaderboardView );
|
||||
|
||||
// Sets up all necessary client-side convars and user info before
|
||||
// connecting to server
|
||||
virtual void PrepareClientForConnect( KeyValues *pSettings );
|
||||
|
||||
// Start up a listen server with the given settings
|
||||
virtual bool StartServerMap( KeyValues *pSettings );
|
||||
|
||||
// Methods of IMatchEventsSink
|
||||
public:
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
// Methods of IGameEventListener2
|
||||
public:
|
||||
// FireEvent is called by EventManager if event just occured
|
||||
// KeyValue memory will be freed by manager if not needed anymore
|
||||
virtual void FireGameEvent( IGameEvent *event );
|
||||
virtual int GetEventDebugID( void );
|
||||
|
||||
public:
|
||||
CMatchTitle();
|
||||
~CMatchTitle();
|
||||
};
|
||||
|
||||
// Match title singleton
|
||||
extern CMatchTitle *g_pMatchTitle;
|
||||
|
||||
#endif // MM_TITLE_H
|
||||
372
matchmaking/dota/mm_title_gamesettingsmgr.cpp
Normal file
372
matchmaking/dota/mm_title_gamesettingsmgr.cpp
Normal file
@@ -0,0 +1,372 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "mm_title_richpresence.h"
|
||||
|
||||
#include "vstdlib/random.h"
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
class CMatchTitleGameSettingsMgr : public IMatchTitleGameSettingsMgr
|
||||
{
|
||||
public:
|
||||
// Extends server game details
|
||||
virtual void ExtendServerDetails( KeyValues *pDetails, KeyValues *pRequest );
|
||||
|
||||
// Adds the essential part of game details to be broadcast
|
||||
virtual void ExtendLobbyDetailsTemplate( KeyValues *pDetails, char const *szReason, KeyValues *pFullSettings );
|
||||
|
||||
// Extends game settings update packet for lobby transition,
|
||||
// either due to a migration or due to an endgame condition
|
||||
virtual void ExtendGameSettingsForLobbyTransition( KeyValues *pSettings, KeyValues *pSettingsUpdate, bool bEndGame );
|
||||
|
||||
|
||||
// Rolls up game details for matches grouping
|
||||
virtual KeyValues * RollupGameDetails( KeyValues *pDetails, KeyValues *pRollup, KeyValues *pQuery );
|
||||
|
||||
|
||||
// Defines session search keys for matchmaking
|
||||
virtual KeyValues * DefineSessionSearchKeys( KeyValues *pSettings );
|
||||
|
||||
// Defines dedicated server search key
|
||||
virtual KeyValues * DefineDedicatedSearchKeys( KeyValues *pSettings );
|
||||
|
||||
|
||||
// Initializes full game settings from potentially abbreviated game settings
|
||||
virtual void InitializeGameSettings( KeyValues *pSettings );
|
||||
|
||||
// Extends game settings update packet before it gets merged with
|
||||
// session settings and networked to remote clients
|
||||
virtual void ExtendGameSettingsUpdateKeys( KeyValues *pSettings, KeyValues *pUpdateDeleteKeys );
|
||||
|
||||
// Prepares system for session creation
|
||||
virtual KeyValues * PrepareForSessionCreate( KeyValues *pSettings );
|
||||
|
||||
|
||||
// Executes the command on the session settings, this function on host
|
||||
// is allowed to modify Members/Game subkeys and has to fill in modified players KeyValues
|
||||
// When running on a remote client "ppPlayersUpdated" is NULL and players cannot
|
||||
// be modified
|
||||
virtual void ExecuteCommand( KeyValues *pCommand, KeyValues *pSessionSystemData, KeyValues *pSettings, KeyValues **ppPlayersUpdated );
|
||||
|
||||
// Prepares the lobby for game or adjust settings of new players who
|
||||
// join a game in progress, this function is allowed to modify
|
||||
// Members/Game subkeys and has to fill in modified players KeyValues
|
||||
virtual void PrepareLobbyForGame( KeyValues *pSettings, KeyValues **ppPlayersUpdated );
|
||||
|
||||
// Prepares the host team lobby for game adjusting the game settings
|
||||
// this function is allowed to prepare modification package to update
|
||||
// Game subkeys.
|
||||
// Returns the update/delete package to be applied to session settings
|
||||
// and pushed to dependent two sesssion of the two teams.
|
||||
virtual KeyValues * PrepareTeamLinkForGame( KeyValues *pSettingsLocal, KeyValues *pSettingsRemote );
|
||||
};
|
||||
|
||||
CMatchTitleGameSettingsMgr g_MatchTitleGameSettingsMgr;
|
||||
IMatchTitleGameSettingsMgr *g_pIMatchTitleGameSettingsMgr = &g_MatchTitleGameSettingsMgr;
|
||||
|
||||
|
||||
//
|
||||
// Implementation of CMatchTitleGameSettingsMgr
|
||||
//
|
||||
|
||||
// Extends server game details
|
||||
void CMatchTitleGameSettingsMgr::ExtendServerDetails( KeyValues *pDetails, KeyValues *pRequest )
|
||||
{
|
||||
// Query server info
|
||||
INetSupport::ServerInfo_t si;
|
||||
g_pMatchExtensions->GetINetSupport()->GetServerInfo( &si );
|
||||
|
||||
// Server is always in game
|
||||
pDetails->SetString( "game/state", "game" );
|
||||
|
||||
//
|
||||
// Determine map info
|
||||
//
|
||||
{
|
||||
pDetails->SetString( "game/bspname", CFmtStr( "%s", si.m_szMapName ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the essential part of game details to be broadcast
|
||||
void CMatchTitleGameSettingsMgr::ExtendLobbyDetailsTemplate( KeyValues *pDetails, char const *szReason, KeyValues *pFullSettings )
|
||||
{
|
||||
static KeyValues *pkvExt = KeyValues::FromString(
|
||||
"settings",
|
||||
" game { "
|
||||
" bspname #empty# "
|
||||
" } "
|
||||
);
|
||||
|
||||
pDetails->MergeFrom( pkvExt, KeyValues::MERGE_KV_UPDATE );
|
||||
}
|
||||
|
||||
// Extends game settings update packet for lobby transition,
|
||||
// either due to a migration or due to an endgame condition
|
||||
void CMatchTitleGameSettingsMgr::ExtendGameSettingsForLobbyTransition( KeyValues *pSettings, KeyValues *pSettingsUpdate, bool bEndGame )
|
||||
{
|
||||
pSettingsUpdate->SetString( "game/state", "lobby" );
|
||||
}
|
||||
|
||||
// Rolls up game details for matches grouping
|
||||
KeyValues * CMatchTitleGameSettingsMgr::RollupGameDetails( KeyValues *pDetails, KeyValues *pRollup, KeyValues *pQuery )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Defines dedicated server search key
|
||||
KeyValues * CMatchTitleGameSettingsMgr::DefineDedicatedSearchKeys( KeyValues *pSettings )
|
||||
{
|
||||
if ( IsPC() )
|
||||
{
|
||||
static ConVarRef sv_search_key( "sv_search_key" );
|
||||
|
||||
KeyValues *pKeys = new KeyValues( "SearchKeys" );
|
||||
pKeys->SetString( "gametype", CFmtStr( "%s,sv_search_key_%s%d",
|
||||
"empty",
|
||||
sv_search_key.GetString(),
|
||||
g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() ) );
|
||||
return pKeys;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Defines session search keys for matchmaking
|
||||
KeyValues * CMatchTitleGameSettingsMgr::DefineSessionSearchKeys( KeyValues *pSettings )
|
||||
{
|
||||
MEM_ALLOC_CREDIT();
|
||||
KeyValues *pResult = new KeyValues( "SessionSearch" );
|
||||
|
||||
pResult->SetInt( "numPlayers", pSettings->GetInt( "members/numPlayers", XBX_GetNumGameUsers() ) );
|
||||
|
||||
/*
|
||||
char const *szGameMode = pSettings->GetString( "game/mode", "" );
|
||||
if ( IsX360() )
|
||||
{
|
||||
if ( char const *szValue = pSettings->GetString( "game/mode", NULL ) )
|
||||
{
|
||||
static ContextValue_t values[] = {
|
||||
{ "versus", SESSION_MATCH_QUERY_PUBLIC_STATE_C___SORT___CHAPTER },
|
||||
{ "teamversus", SESSION_MATCH_QUERY_TEAM_STATE_C_CHAPTER },
|
||||
{ "scavenge", SESSION_MATCH_QUERY_PUBLIC_STATE_C_CHAPTER___SORT___ROUNDS },
|
||||
{ "teamscavenge", SESSION_MATCH_QUERY_TEAM_STATE_C_CHAPTER_ROUNDS },
|
||||
{ "survival", SESSION_MATCH_QUERY_PUBLIC_STATE_C_CHAPTER },
|
||||
{ "coop", SESSION_MATCH_QUERY_PUBLIC_STATE_C_DIFF___SORT___CHAPTER },
|
||||
{ "realism", SESSION_MATCH_QUERY_PUBLIC_STATE_C_DIFF___SORT___CHAPTER },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
pResult->SetInt( "rule", values->ScanValues( szValue ) );
|
||||
}
|
||||
|
||||
// Set the matchmaking version
|
||||
pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_MMVERSION ), mm_matchmaking_version.GetInt() );
|
||||
|
||||
#ifdef _X360
|
||||
// Set the installed DLCs masks
|
||||
uint64 uiDlcsMask = MatchSession_GetDlcInstalledMask();
|
||||
for ( int k = 1; k <= mm_matchmaking_dlcsquery.GetInt(); ++ k )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_MMVERSION + k ), !!( uiDlcsMask & ( 1ull << k ) ) );
|
||||
}
|
||||
pResult->SetInt( "dlc1", PROPERTY_MMVERSION + 1 );
|
||||
pResult->SetInt( "dlcN", PROPERTY_MMVERSION + mm_matchmaking_dlcsquery.GetInt() );
|
||||
#endif
|
||||
|
||||
// X_CONTEXT_GAME_TYPE
|
||||
pResult->SetInt( CFmtStr( "Contexts/%d", X_CONTEXT_GAME_TYPE ), X_CONTEXT_GAME_TYPE_STANDARD );
|
||||
|
||||
// X_CONTEXT_GAME_MODE
|
||||
if ( char const *szValue = pSettings->GetString( "game/mode", NULL ) )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Contexts/%d", X_CONTEXT_GAME_MODE ), g_pcv_CONTEXT_GAME_MODE->ScanValues( szValue ) );
|
||||
}
|
||||
|
||||
if ( char const *szValue = pSettings->GetString( "game/state", NULL ) )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Contexts/%d", CONTEXT_STATE ), g_pcv_CONTEXT_STATE->ScanValues( szValue ) );
|
||||
}
|
||||
|
||||
if ( char const *szValue = pSettings->GetString( "game/difficulty", NULL ) )
|
||||
{
|
||||
if ( !Q_stricmp( "coop", szGameMode ) || !Q_stricmp( "realism", szGameMode ) )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Contexts/%d", CONTEXT_DIFFICULTY ), g_pcv_CONTEXT_DIFFICULTY->ScanValues( szValue ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( int val = pSettings->GetInt( "game/maxrounds" ) )
|
||||
{
|
||||
if ( !Q_stricmp( "scavenge", szGameMode ) || !Q_stricmp( "teamscavenge", szGameMode ) )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_MAXROUNDS ), val );
|
||||
}
|
||||
}
|
||||
|
||||
char const *szCampaign = pSettings->GetString( "game/campaign" );
|
||||
if ( *szCampaign )
|
||||
{
|
||||
DWORD dwContext = CONTEXT_CAMPAIGN_UNKNOWN;
|
||||
if ( KeyValues *pAllMissions = g_pMatchExtL4D->GetAllMissions() )
|
||||
{
|
||||
if ( KeyValues *pMission = pAllMissions->FindKey( szCampaign ) )
|
||||
{
|
||||
dwContext = pMission->GetInt( "x360ctx", dwContext );
|
||||
}
|
||||
}
|
||||
if ( dwContext != CONTEXT_CAMPAIGN_UNKNOWN )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Contexts/%d", CONTEXT_CAMPAIGN ), dwContext );
|
||||
}
|
||||
}
|
||||
|
||||
if ( int val = pSettings->GetInt( "game/chapter" ) )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_CHAPTER ), val );
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
*/
|
||||
{
|
||||
if ( char const *szValue = pSettings->GetString( "game/bspname", NULL ) )
|
||||
{
|
||||
pResult->SetString( "Filter=/game:bspname", szValue );
|
||||
}
|
||||
}
|
||||
|
||||
return pResult;
|
||||
}
|
||||
|
||||
// Initializes full game settings from potentially abbreviated game settings
|
||||
void CMatchTitleGameSettingsMgr::InitializeGameSettings( KeyValues *pSettings )
|
||||
{
|
||||
char const *szNetwork = pSettings->GetString( "system/network", "LIVE" );
|
||||
|
||||
pSettings->SetString( "game/state", "lobby" );
|
||||
|
||||
if ( KeyValues *kv = pSettings->FindKey( "Options", true ) )
|
||||
{
|
||||
kv->SetString( "server", "listen" );
|
||||
}
|
||||
|
||||
// Offline games don't need slots and player setup
|
||||
if ( !Q_stricmp( "offline", szNetwork ) )
|
||||
return;
|
||||
|
||||
//
|
||||
// Set the number of slots
|
||||
//
|
||||
int numSlots = 10;
|
||||
pSettings->SetInt( "members/numSlots", numSlots );
|
||||
}
|
||||
|
||||
// Extends game settings update packet before it gets merged with
|
||||
// session settings and networked to remote clients
|
||||
void CMatchTitleGameSettingsMgr::ExtendGameSettingsUpdateKeys( KeyValues *pSettings, KeyValues *pUpdateDeleteKeys )
|
||||
{
|
||||
}
|
||||
|
||||
// Prepares system for session creation
|
||||
KeyValues * CMatchTitleGameSettingsMgr::PrepareForSessionCreate( KeyValues *pSettings )
|
||||
{
|
||||
return MM_Title_RichPresence_PrepareForSessionCreate( pSettings );
|
||||
}
|
||||
|
||||
// Prepares the lobby for game or adjust settings of new players who
|
||||
// join a game in progress, this function is allowed to modify
|
||||
// Members/Game subkeys and has to write modified players XUIDs
|
||||
void CMatchTitleGameSettingsMgr::PrepareLobbyForGame( KeyValues *pSettings, KeyValues **ppPlayersUpdated )
|
||||
{
|
||||
// set player avatar/teams, etc
|
||||
}
|
||||
|
||||
// Prepares the host team lobby for game adjusting the game settings
|
||||
// this function is allowed to prepare modification package to update
|
||||
// Game subkeys.
|
||||
// Returns the update/delete package to be applied to session settings
|
||||
// and pushed to dependent two sesssion of the two teams.
|
||||
KeyValues * CMatchTitleGameSettingsMgr::PrepareTeamLinkForGame( KeyValues *pSettingsLocal, KeyValues *pSettingsRemote )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void OnRunCommand_TeamChange( KeyValues *pCommand, KeyValues *pSettings, KeyValues **ppPlayersUpdated )
|
||||
{
|
||||
MEM_ALLOC_CREDIT();
|
||||
XUID xuid = pCommand->GetUint64( "xuid" );
|
||||
char const *szTeam = pCommand->GetString( "team", "" );
|
||||
|
||||
KeyValues *pMembers = pSettings->FindKey( "members" );
|
||||
if ( !pMembers )
|
||||
return;
|
||||
|
||||
// Find the avatar that is going to be updated
|
||||
KeyValues *pPlayer = SessionMembersFindPlayer( pSettings, xuid );
|
||||
if ( !pPlayer )
|
||||
return;
|
||||
|
||||
// Check if the team is the same
|
||||
if ( !Q_stricmp( szTeam, pPlayer->GetString( "game/team", "" ) ) )
|
||||
return;
|
||||
|
||||
KeyValues *kvGame = pPlayer->FindKey( "game", true );
|
||||
if ( !kvGame )
|
||||
return;
|
||||
|
||||
// If desired avatar is blank, then no validation required
|
||||
if ( !*szTeam )
|
||||
{
|
||||
kvGame->SetString( "team", "random" );
|
||||
}
|
||||
else
|
||||
{
|
||||
kvGame->SetString( "team", szTeam );
|
||||
}
|
||||
|
||||
// Notify the sessions of a player update
|
||||
* ( ppPlayersUpdated ++ ) = pPlayer;
|
||||
}
|
||||
|
||||
// Executes the command on the session settings, this function on host
|
||||
// is allowed to modify Members/Game subkeys and has to fill in modified players KeyValues
|
||||
// When running on a remote client "ppPlayersUpdated" is NULL and players cannot
|
||||
// be modified
|
||||
void CMatchTitleGameSettingsMgr::ExecuteCommand( KeyValues *pCommand, KeyValues *pSessionSystemData, KeyValues *pSettings, KeyValues **ppPlayersUpdated )
|
||||
{
|
||||
char const *szCommand = pCommand->GetName();
|
||||
|
||||
if ( !Q_stricmp( "Game::Team", szCommand ) )
|
||||
{
|
||||
if ( !Q_stricmp( "host", pSessionSystemData->GetString( "type", "host" ) ) &&
|
||||
// !Q_stricmp( "lobby", pSessionSystemData->GetString( "state", "lobby" ) ) && - avatars also update when players change team ingame
|
||||
ppPlayersUpdated )
|
||||
{
|
||||
char const *szAvatar = pCommand->GetString( "team" );
|
||||
if ( !*szAvatar )
|
||||
{
|
||||
// Requesting random is only allowed in unlocked lobby
|
||||
if ( Q_stricmp( "lobby", pSessionSystemData->GetString( "state", "lobby" ) ) )
|
||||
return;
|
||||
if ( *pSettings->GetString( "system/lock" ) )
|
||||
return;
|
||||
}
|
||||
|
||||
OnRunCommand_TeamChange( pCommand, pSettings, ppPlayersUpdated );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
39
matchmaking/dota/mm_title_main.cpp
Normal file
39
matchmaking/dota/mm_title_main.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "../mm_title_main.h"
|
||||
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
LINK_MATCHMAKING_LIB();
|
||||
|
||||
static CMatchTitle g_MatchTitle;
|
||||
CMatchTitle *g_pMatchTitle = &g_MatchTitle;
|
||||
|
||||
IMatchTitle *g_pIMatchTitle = g_pMatchTitle;
|
||||
IMatchEventsSink *g_pIMatchTitleEventsSink = g_pMatchTitle;
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Init / shutdown
|
||||
//
|
||||
|
||||
InitReturnVal_t MM_Title_Init()
|
||||
{
|
||||
return g_pMatchTitle->Init();
|
||||
}
|
||||
|
||||
void MM_Title_Shutdown()
|
||||
{
|
||||
if ( g_pMatchTitle )
|
||||
g_pMatchTitle->Shutdown();
|
||||
}
|
||||
|
||||
52
matchmaking/dota/mm_title_richpresence.cpp
Normal file
52
matchmaking/dota/mm_title_richpresence.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title_richpresence.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
static void SetAllUsersContext( DWORD dwContextId, DWORD dwValue, bool bAsync = true )
|
||||
{
|
||||
#ifdef _X360
|
||||
for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
|
||||
{
|
||||
if ( XBX_GetUserIsGuest( k ) )
|
||||
continue;
|
||||
int iCtrlr = XBX_GetUserId( k );
|
||||
if ( bAsync )
|
||||
XUserSetContextEx( iCtrlr, dwContextId, dwValue, MMX360_NewOverlappedDormant() );
|
||||
else
|
||||
XUserSetContext( iCtrlr, dwContextId, dwValue );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void SetAllUsersProperty( DWORD dwPropertyId, DWORD cbValue, void const *pvValue )
|
||||
{
|
||||
#ifdef _X360
|
||||
for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
|
||||
{
|
||||
if ( XBX_GetUserIsGuest( k ) )
|
||||
continue;
|
||||
int iCtrlr = XBX_GetUserId( k );
|
||||
XUserSetPropertyEx( iCtrlr, dwPropertyId, cbValue, pvValue, MMX360_NewOverlappedDormant() );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
KeyValues * MM_Title_RichPresence_PrepareForSessionCreate( KeyValues *pSettings )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void MM_Title_RichPresence_Update( KeyValues *pFullSettings, KeyValues *pUpdatedSettings )
|
||||
{
|
||||
}
|
||||
|
||||
void MM_Title_RichPresence_PlayersChanged( KeyValues *pFullSettings )
|
||||
{
|
||||
}
|
||||
20
matchmaking/dota/mm_title_richpresence.h
Normal file
20
matchmaking/dota/mm_title_richpresence.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_TITLE_RICHPRESENCE_H
|
||||
#define MM_TITLE_RICHPRESENCE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "mm_title.h"
|
||||
|
||||
void MM_Title_RichPresence_Update( KeyValues *pFullSettings, KeyValues *pUpdatedSettings );
|
||||
void MM_Title_RichPresence_PlayersChanged( KeyValues *pFullSettings );
|
||||
|
||||
KeyValues * MM_Title_RichPresence_PrepareForSessionCreate( KeyValues *pSettings );
|
||||
|
||||
#endif // MM_TITLE_H
|
||||
113
matchmaking/dota/mm_title_titledata.cpp
Normal file
113
matchmaking/dota/mm_title_titledata.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
TitleDataFieldsDescription_t const * CMatchTitle::DescribeTitleDataStorage()
|
||||
{
|
||||
#define TD_ENTRY( szName, nTD, eDataType, numBytesOffset ) \
|
||||
{ szName, TitleDataFieldsDescription_t::nTD, TitleDataFieldsDescription_t::eDataType, numBytesOffset }
|
||||
|
||||
static TitleDataFieldsDescription_t tdfd[] =
|
||||
{
|
||||
#if 0
|
||||
TD_ENTRY( "TD1.Easy.Games.Total", DB_TD1, DT_U64, offsetof( TitleData1, mGames[0] ) ),
|
||||
TD_ENTRY( "TD1.Normal.Games.Total", DB_TD1, DT_U64, offsetof( TitleData1, mGames[1] ) ),
|
||||
TD_ENTRY( "TD1.Advanced.Games.Total", DB_TD1, DT_U64, offsetof( TitleData1, mGames[2] ) ),
|
||||
TD_ENTRY( "TD1.Expert.Games.Total", DB_TD1, DT_U64, offsetof( TitleData1, mGames[3] ) ),
|
||||
#endif
|
||||
TD_ENTRY( NULL, DB_TD1, DT_U8, 0 )
|
||||
};
|
||||
|
||||
#undef TD_ENTRY
|
||||
|
||||
return tdfd;
|
||||
}
|
||||
|
||||
TitleAchievementsDescription_t const * CMatchTitle::DescribeTitleAchievements()
|
||||
{
|
||||
static TitleAchievementsDescription_t tad[] =
|
||||
{
|
||||
//#include "left4dead2.xhelp.achtitledesc.txt"
|
||||
// END MARKER
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
return tad;
|
||||
}
|
||||
|
||||
TitleAvatarAwardsDescription_t const * CMatchTitle::DescribeTitleAvatarAwards()
|
||||
{
|
||||
static TitleAvatarAwardsDescription_t taad[] =
|
||||
{
|
||||
//#include "left4dead2.xhelp.avawtitledesc.txt"
|
||||
// END MARKER
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
return taad;
|
||||
}
|
||||
|
||||
// Title leaderboards
|
||||
KeyValues * CMatchTitle::DescribeTitleLeaderboard( char const *szLeaderboardView )
|
||||
{
|
||||
/*
|
||||
// Check if this is a survival leaderboard
|
||||
if ( char const *szSurvivalMap = StringAfterPrefix( szLeaderboardView, "survival_" ) )
|
||||
{
|
||||
if ( IsX360() )
|
||||
{
|
||||
// Find the corresponding record in the mission script
|
||||
KeyValues *pSettings = new KeyValues( "settings" );
|
||||
KeyValues::AutoDelete autodelete_pSettings( pSettings );
|
||||
pSettings->SetString( "game/mode", "survival" );
|
||||
|
||||
KeyValues *pMissionInfo = NULL;
|
||||
KeyValues *pMapInfo = g_pMatchExtL4D->GetMapInfoByBspName( pSettings, szSurvivalMap, &pMissionInfo );
|
||||
if ( !pMapInfo || !pMissionInfo )
|
||||
return NULL;
|
||||
|
||||
// Find the leaderboard description in the map info
|
||||
KeyValues *pLbDesc = pMapInfo->FindKey( "x360leaderboard" );
|
||||
if ( !pLbDesc )
|
||||
return NULL;
|
||||
|
||||
// Insert the required keys
|
||||
pLbDesc = pLbDesc->MakeCopy();
|
||||
|
||||
static KeyValues *s_pRatingKey = KeyValues::FromString( ":rating", // X360 leaderboards are rated
|
||||
" name besttime " // game name of the rating field is "besttime"
|
||||
" type uint64 " // type is uint64
|
||||
" rule max" // rated field must be greater than cached value so that it can be written
|
||||
);
|
||||
pLbDesc->AddSubKey( s_pRatingKey->MakeCopy() );
|
||||
pLbDesc->SetString( "besttime/type", "uint64" );
|
||||
|
||||
return pLbDesc;
|
||||
}
|
||||
|
||||
if ( IsPC() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score besttime " // :score is the leaderboard value mapped to game name "besttime"
|
||||
);
|
||||
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
|
||||
pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeTimeMilliSeconds ); // Note: this is actually 1/100th seconds type, Steam change pending
|
||||
pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodKeepBest ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
868
matchmaking/ds_searcher.cpp
Normal file
868
matchmaking/ds_searcher.cpp
Normal file
@@ -0,0 +1,868 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
#include "fmtstr.h"
|
||||
#include "vstdlib/random.h"
|
||||
|
||||
#include "protocol.h"
|
||||
#include "proto_oob.h"
|
||||
#include "bitbuf.h"
|
||||
#include "checksum_crc.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
ConVar mm_dedicated_allow( "mm_dedicated_allow", "1", FCVAR_DEVELOPMENTONLY, "1 = allow searches for dedicated servers" );
|
||||
ConVar mm_dedicated_fake( "mm_dedicated_fake", "0", FCVAR_DEVELOPMENTONLY, "1 = pretend like search is going, but abort after some time" );
|
||||
ConVar mm_dedicated_force_servers( "mm_dedicated_force_servers", "", FCVAR_RELEASE,
|
||||
"Comma delimited list of ip:port of servers used to search for dedicated servers instead of searching for public servers.\n"
|
||||
"Use syntax `publicip1:port|privateip1:port,publicip2:port|privateip2:port` if your server is behind NAT.\n"
|
||||
"If the server is behind NAT, you can specify `0.0.0.0|privateip:port` and if server port is in the list of `mm_server_search_lan_ports` its public address should be automatically detected." );
|
||||
ConVar mm_dedicated_ip( "mm_dedicated_ip", "", FCVAR_DEVELOPMENTONLY, "IP address of dedicated servers to consider available" );
|
||||
ConVar mm_dedicated_timeout_request( "mm_dedicated_timeout_request", "20", FCVAR_DEVELOPMENTONLY );
|
||||
ConVar mm_dedicated_search_maxping( "mm_dedicated_search_maxping", IsX360() ? "200" : "150", FCVAR_RELEASE | FCVAR_ARCHIVE, "Longest preferred ping to dedicated servers for games", true, 25.f, true, 350.f );
|
||||
ConVar mm_dedicated_search_maxresults( "mm_dedicated_search_maxresults", "75", FCVAR_DEVELOPMENTONLY );
|
||||
|
||||
extern ConVar mm_dedicated_xlsp_timeout;
|
||||
|
||||
CDsSearcher::CDsSearcher( KeyValues *pSettings, uint64 uiReserveCookie, IMatchSession *pMatchSession, uint64 ullCrypt ) :
|
||||
m_pSettings( pSettings ),
|
||||
m_uiReserveCookie( uiReserveCookie ),
|
||||
m_pReserveSettings( g_pMatchFramework->GetMatchNetworkMsgController()->PackageGameDetailsForReservation( m_pSettings ) ),
|
||||
m_autodelete_pReserveSettings( m_pReserveSettings ),
|
||||
#ifdef _X360
|
||||
m_pTitleServers( NULL ),
|
||||
#elif !defined( NO_STEAM )
|
||||
m_pServerListListener( NULL ),
|
||||
m_nSearchPass( 0 ),
|
||||
#endif
|
||||
m_eState( STATE_INIT ),
|
||||
m_flTimeout( 0.0f ),
|
||||
m_pAsyncOperation( NULL ),
|
||||
m_pMatchSession( pMatchSession ),
|
||||
m_ullCrypt( ullCrypt )
|
||||
{
|
||||
#ifdef _X360
|
||||
ZeroMemory( m_chDatacenterQuery, sizeof( m_chDatacenterQuery ) );
|
||||
ZeroMemory( &m_dc, sizeof( m_dc ) );
|
||||
#endif
|
||||
|
||||
DevMsg( "Created DS searcher\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings );
|
||||
|
||||
memset( &m_Result, 0, sizeof( m_Result ) );
|
||||
|
||||
// Build the reservation settings
|
||||
|
||||
// Load test
|
||||
m_bLoadTest = (m_pSettings->GetInt( "options/sv_load_test", 0) == 1);
|
||||
}
|
||||
|
||||
CDsSearcher::~CDsSearcher()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void CDsSearcher::Update()
|
||||
{
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_INIT:
|
||||
{
|
||||
char const *szNetwork = m_pSettings->GetString( "system/network", "" );
|
||||
char const *szServer = m_pSettings->GetString( "options/server", "listen" );
|
||||
if ( m_pSettings->GetString( "server/server", NULL ) )
|
||||
{
|
||||
InitWithKnownServer();
|
||||
}
|
||||
else if ( mm_dedicated_allow.GetBool() &&
|
||||
!Q_stricmp( "LIVE", szNetwork ) &&
|
||||
Q_stricmp( "listen", szServer ) &&
|
||||
!( g_pMatchFramework->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_SETTING_NODEDICATED ) )
|
||||
{
|
||||
InitDedicatedSearch();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eState = STATE_FINISHED;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_WAITING:
|
||||
{
|
||||
if ( Plat_FloatTime() > m_flTimeout )
|
||||
m_eState = STATE_FINISHED;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef _X360
|
||||
|
||||
case STATE_XLSP_ENUMERATE_DCS:
|
||||
m_pTitleServers->Update();
|
||||
if ( m_pTitleServers->IsSearchCompleted() )
|
||||
Xlsp_OnEnumerateDcsCompleted();
|
||||
break;
|
||||
|
||||
case STATE_XLSP_NEXT_DC:
|
||||
Xlsp_StartNextDc();
|
||||
break;
|
||||
|
||||
case STATE_XLSP_REQUESTING_SERVERS:
|
||||
if ( Plat_FloatTime() > m_flTimeout )
|
||||
{
|
||||
DevWarning( "XLSP datacenter `%s` timed out.\n", m_dc.m_szGatewayName );
|
||||
m_dc.Destroy();
|
||||
m_eState = STATE_XLSP_NEXT_DC;
|
||||
}
|
||||
break;
|
||||
|
||||
#elif !defined( NO_STEAM )
|
||||
|
||||
case STATE_STEAM_REQUESTING_SERVERS:
|
||||
if ( Plat_FloatTime() > m_flTimeout )
|
||||
{
|
||||
DevWarning( "Steam search for dedicated servers timed out.\n" );
|
||||
Steam_OnDedicatedServerListFetched();
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_STEAM_NEXT_SEARCH_PASS:
|
||||
Steam_SearchPass();
|
||||
break;
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void CDsSearcher::OnEvent( KeyValues *pEvent )
|
||||
{
|
||||
char const *szEvent = pEvent->GetName();
|
||||
|
||||
#ifdef _X360
|
||||
if ( m_eState == STATE_XLSP_REQUESTING_SERVERS &&
|
||||
!Q_stricmp( "M2A_SERVER_BATCH", szEvent ) )
|
||||
{
|
||||
void const *pData = pEvent->GetPtr( "ptr" );
|
||||
int numBytes = pEvent->GetInt( "size" );
|
||||
|
||||
Xlsp_OnDcServerBatch( pData, numBytes );
|
||||
}
|
||||
#elif !defined( NO_STEAM )
|
||||
szEvent;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CDsSearcher::Destroy()
|
||||
{
|
||||
if ( m_pAsyncOperation )
|
||||
{
|
||||
m_pAsyncOperation->Release();
|
||||
m_pAsyncOperation = NULL;
|
||||
}
|
||||
|
||||
#ifdef _X360
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_XLSP_ENUMERATE_DCS:
|
||||
if ( m_pTitleServers )
|
||||
m_pTitleServers->Destroy();
|
||||
m_pTitleServers = NULL;
|
||||
break;
|
||||
case STATE_XLSP_REQUESTING_SERVERS:
|
||||
case STATE_RESERVING:
|
||||
m_dc.Destroy();
|
||||
break;
|
||||
}
|
||||
#elif !defined( NO_STEAM )
|
||||
if ( m_pServerListListener )
|
||||
{
|
||||
m_pServerListListener->Destroy();
|
||||
m_pServerListListener = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool CDsSearcher::IsFinished()
|
||||
{
|
||||
return m_eState == STATE_FINISHED;
|
||||
}
|
||||
|
||||
CDsSearcher::DsResult_t const & CDsSearcher::GetResult()
|
||||
{
|
||||
return m_Result;
|
||||
}
|
||||
|
||||
void CDsSearcher::DsResult_t::CopyToServerKey( KeyValues *pKvServer, uint64 ullCrypt ) const
|
||||
{
|
||||
Assert( m_bDedicated );
|
||||
pKvServer->SetString( "server", "dedicated" );
|
||||
|
||||
#ifdef _X360
|
||||
pKvServer->SetString( "adrInsecure", m_szInsecureSendableServerAddress );
|
||||
#elif !defined( NO_STEAM )
|
||||
if ( char const *szEncrypted = MatchSession_EncryptAddressString( m_szPublicConnectionString, ullCrypt ) )
|
||||
pKvServer->SetString( "adronline", szEncrypted );
|
||||
else
|
||||
pKvServer->SetString( "adronline", m_szPublicConnectionString );
|
||||
|
||||
if ( m_szPrivateConnectionString[0] )
|
||||
{
|
||||
if ( char const *szEncrypted = MatchSession_EncryptAddressString( m_szPrivateConnectionString, ullCrypt ) )
|
||||
pKvServer->SetString( "adrlocal", szEncrypted );
|
||||
else
|
||||
pKvServer->SetString( "adrlocal", m_szPrivateConnectionString );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
void CDsSearcher::InitDedicatedSearch()
|
||||
{
|
||||
if ( mm_dedicated_fake.GetBool() )
|
||||
{
|
||||
// Special fake of the search - it just spins for some time and
|
||||
// pretends like it was aborted
|
||||
m_flTimeout = Plat_FloatTime() + mm_dedicated_timeout_request.GetFloat();
|
||||
m_eState = STATE_WAITING;
|
||||
m_Result.m_bAborted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _X360
|
||||
Xlsp_EnumerateDcs();
|
||||
#elif !defined( NO_STEAM )
|
||||
m_flTimeout = Plat_FloatTime() + mm_dedicated_timeout_request.GetFloat();
|
||||
Steam_SearchPass();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CDsSearcher::InitWithKnownServer()
|
||||
{
|
||||
#ifdef _X360
|
||||
Assert( 0 );
|
||||
m_eState = STATE_FINISHED;
|
||||
return;
|
||||
#elif !defined( NO_STEAM )
|
||||
if ( m_pSettings->GetInt( "server/reserved" ) )
|
||||
{
|
||||
m_Result.m_bDedicated = true;
|
||||
|
||||
char const *szAdrOnline = m_pSettings->GetString( "server/adronline", "" );
|
||||
if ( char const *szDecrypted = MatchSession_DecryptAddressString( szAdrOnline, m_ullCrypt ) )
|
||||
szAdrOnline = szDecrypted;
|
||||
Q_strncpy( m_Result.m_szConnectionString, szAdrOnline, ARRAYSIZE( m_Result.m_szConnectionString ) );
|
||||
Q_strncpy( m_Result.m_szPublicConnectionString, szAdrOnline, ARRAYSIZE( m_Result.m_szPublicConnectionString ) );
|
||||
|
||||
char const *szAdrLocal = m_pSettings->GetString( "server/adrlocal", "" );
|
||||
if ( char const *szDecrypted = MatchSession_DecryptAddressString( szAdrLocal, m_ullCrypt ) )
|
||||
szAdrLocal = szDecrypted;
|
||||
Q_strncpy( m_Result.m_szPrivateConnectionString, szAdrLocal, ARRAYSIZE( m_Result.m_szPrivateConnectionString ) );
|
||||
|
||||
m_eState = STATE_FINISHED;
|
||||
}
|
||||
else
|
||||
{
|
||||
DsServer_t dsResult(
|
||||
m_pSettings->GetString( "server/adronline", "" ),
|
||||
m_pSettings->GetString( "server/adrlocal", "" ),
|
||||
0
|
||||
);
|
||||
if ( char const *szDecrypted = MatchSession_DecryptAddressString( dsResult.m_szConnectionString, m_ullCrypt ) )
|
||||
Q_strncpy( dsResult.m_szConnectionString, szDecrypted, ARRAYSIZE( dsResult.m_szConnectionString ) );
|
||||
if ( char const *szDecrypted = MatchSession_DecryptAddressString( dsResult.m_szPrivateConnectionString, m_ullCrypt ) )
|
||||
Q_strncpy( dsResult.m_szPrivateConnectionString, szDecrypted, ARRAYSIZE( dsResult.m_szPrivateConnectionString ) );
|
||||
|
||||
m_arrServerList.AddToTail( dsResult );
|
||||
|
||||
ReserveNextServer();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _X360
|
||||
|
||||
void CDsSearcher::Xlsp_EnumerateDcs()
|
||||
{
|
||||
m_eState = STATE_XLSP_ENUMERATE_DCS;
|
||||
m_pTitleServers = new CXlspTitleServers( mm_dedicated_search_maxping.GetInt(), false );
|
||||
}
|
||||
|
||||
void CDsSearcher::Xlsp_OnEnumerateDcsCompleted()
|
||||
{
|
||||
DevMsg( "Xlsp_OnEnumerateDcsCompleted - analyzing QOS results...\n" );
|
||||
|
||||
CUtlVector< CXlspDatacenter > &arrDcs = m_pTitleServers->GetDatacenters();
|
||||
m_arrDatacenters.AddMultipleToTail( arrDcs.Count(), arrDcs.Base() );
|
||||
|
||||
m_pTitleServers->Destroy();
|
||||
m_pTitleServers = NULL;
|
||||
|
||||
//
|
||||
// Sort and randomize the accepted results
|
||||
//
|
||||
m_arrDatacenters.Sort( CXlspDatacenter::Compare );
|
||||
for ( int k = 0; k < m_arrDatacenters.Count() - 1; ++ k )
|
||||
{
|
||||
CXlspDatacenter &dc1 = m_arrDatacenters[ k ];
|
||||
CXlspDatacenter &dc2 = m_arrDatacenters[ k + 1 ];
|
||||
if ( dc1.m_nPingBucket == dc2.m_nPingBucket && RandomInt( 0, 1 ) )
|
||||
{
|
||||
CXlspDatacenter dcSwap = dc1;
|
||||
dc1 = dc2;
|
||||
dc2 = dcSwap;
|
||||
}
|
||||
}
|
||||
|
||||
DevMsg( "Xlsp_OnEnumerateDcsCompleted - accepted %d datacenters.\n", m_arrDatacenters.Count() );
|
||||
for ( int k = 0; k < m_arrDatacenters.Count(); ++ k )
|
||||
{
|
||||
DevMsg( " %d. `%s`\n", k, m_arrDatacenters[k].m_szGatewayName );
|
||||
}
|
||||
|
||||
// Prepare the datacenter query
|
||||
Xlsp_PrepareDatacenterQuery();
|
||||
|
||||
// Go to the next datacenter
|
||||
m_eState = STATE_XLSP_NEXT_DC;
|
||||
}
|
||||
|
||||
void CDsSearcher::Xlsp_PrepareDatacenterQuery()
|
||||
{
|
||||
// Compute CRC of primary user's gamertag
|
||||
byte bSult = RandomInt( 5, 100 );
|
||||
CRC32_t crc32 = 0;
|
||||
if ( IPlayerLocal * player = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() ) )
|
||||
{
|
||||
char const *szPlayerName = player->GetName();
|
||||
crc32 = CRC32_ProcessSingleBuffer( szPlayerName, strlen( szPlayerName ) );
|
||||
|
||||
uint32 sult32 = bSult | ( bSult << 8 ) | ( bSult << 16 ) | ( bSult << 24 );
|
||||
crc32 ^= sult32;
|
||||
}
|
||||
if ( !crc32 )
|
||||
bSult = 0;
|
||||
|
||||
// Search key
|
||||
static ConVarRef sv_search_key( "sv_search_key" );
|
||||
char const *szPrivateKey = sv_search_key.IsValid() ? sv_search_key.GetString() : "";
|
||||
if ( !*szPrivateKey )
|
||||
szPrivateKey = "default";
|
||||
|
||||
//
|
||||
// Build query
|
||||
//
|
||||
Q_snprintf( m_chDatacenterQuery, ARRAYSIZE( m_chDatacenterQuery ),
|
||||
"\\empty\\1"
|
||||
"\\private\\%s"
|
||||
"\\players\\%d"
|
||||
"\\slots\\%d"
|
||||
"\\perm\\%s"
|
||||
"\\acct\\%02x%08x",
|
||||
szPrivateKey,
|
||||
m_pSettings->GetInt( "members/numPlayers", 0 ),
|
||||
m_pSettings->GetInt( "members/numSlots", 0 ),
|
||||
m_pSettings->GetString( "system/access", "public" ),
|
||||
bSult, crc32
|
||||
);
|
||||
|
||||
DevMsg( "Datacenters query: %s\n", m_chDatacenterQuery );
|
||||
}
|
||||
|
||||
void CDsSearcher::Xlsp_StartNextDc()
|
||||
{
|
||||
if ( !m_arrDatacenters.Count() )
|
||||
{
|
||||
m_eState = STATE_FINISHED;
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Get the next datacenter off the list
|
||||
//
|
||||
m_dc = m_arrDatacenters.Head();
|
||||
m_arrDatacenters.RemoveMultipleFromHead( 1 );
|
||||
m_flTimeout = Plat_FloatTime() + mm_dedicated_xlsp_timeout.GetFloat();
|
||||
|
||||
DevMsg( "[XLSP] Requesting server batch from %s:%d (%d masters) - ping %d [<= %d]\n"
|
||||
" ProbesXmit=%3d ProbesRecv=%3d\n"
|
||||
" RttMinInMsecs=%3d RttMedInMsecs=%3d\n"
|
||||
" UpBitsPerSec=%6d DnBitsPerSec=%6d\n",
|
||||
m_dc.m_szGatewayName, m_dc.m_nMasterServerPortStart, m_dc.m_numMasterServers, m_dc.m_qos.wRttMedInMsecs, m_dc.m_nPingBucket,
|
||||
m_dc.m_qos.cProbesXmit, m_dc.m_qos.cProbesRecv,
|
||||
m_dc.m_qos.wRttMinInMsecs, m_dc.m_qos.wRttMedInMsecs,
|
||||
m_dc.m_qos.dwUpBitsPerSec, m_dc.m_qos.dwDnBitsPerSec );
|
||||
|
||||
if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) )
|
||||
{
|
||||
m_dc.m_adrSecure = m_dc.m_xsi.inaServer;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Resolve the secure address
|
||||
//
|
||||
DWORD ret = g_pMatchExtensions->GetIXOnline()->XNetServerToInAddr( m_dc.m_xsi.inaServer, g_pMatchFramework->GetMatchTitle()->GetTitleServiceID(), &m_dc.m_adrSecure );
|
||||
if ( ret != ERROR_SUCCESS )
|
||||
{
|
||||
DevWarning( "Failed to resolve XLSP secure address (code = 0x%08X)!\n", ret );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to netadr_t on a random master port
|
||||
netadr_t inetAddr;
|
||||
inetAddr.SetType( NA_IP );
|
||||
inetAddr.SetIPAndPort( m_dc.m_adrSecure.s_addr,
|
||||
m_dc.m_nMasterServerPortStart + RandomInt( 0, m_dc.m_numMasterServers - 1 ) );
|
||||
|
||||
//
|
||||
// Prepare the request payload
|
||||
//
|
||||
char msg_buffer[ INetSupport::NC_MAX_ROUTABLE_PAYLOAD ];
|
||||
bf_write msg( msg_buffer, sizeof( msg_buffer ) );
|
||||
|
||||
msg.WriteByte( A2M_GET_SERVERS_BATCH2 );
|
||||
msg.WriteByte( '\n' );
|
||||
msg.WriteLong( 0 ); // batch starts at 0
|
||||
msg.WriteLong( m_dc.m_adrSecure.s_addr ); // datacenter's challenge
|
||||
msg.WriteString( m_chDatacenterQuery ); // datacenter query
|
||||
msg.WriteByte( '\n' );
|
||||
|
||||
g_pMatchExtensions->GetINetSupport()->SendPacket( NULL, INetSupport::NS_SOCK_CLIENT,
|
||||
inetAddr, msg.GetData(), msg.GetNumBytesWritten() );
|
||||
|
||||
m_eState = STATE_XLSP_REQUESTING_SERVERS;
|
||||
}
|
||||
|
||||
void CDsSearcher::Xlsp_OnDcServerBatch( void const *pData, int numBytes )
|
||||
{
|
||||
if ( numBytes < 8 )
|
||||
return;
|
||||
|
||||
bf_read msg( pData, numBytes );
|
||||
|
||||
int nNextId = msg.ReadLong();
|
||||
nNextId;
|
||||
|
||||
uint nChallenge = msg.ReadLong();
|
||||
if ( nChallenge != m_dc.m_adrSecure.s_addr )
|
||||
return;
|
||||
|
||||
//
|
||||
// Get master server reply message or Secure Gateway name (must match request)
|
||||
//
|
||||
char szReply[ MAX_PATH ] = {0};
|
||||
msg.ReadString( szReply, ARRAYSIZE( szReply ), true );
|
||||
|
||||
if ( !szReply[0] )
|
||||
{
|
||||
DevWarning( "XLSP master server: empty response.\n" );
|
||||
m_dc.Destroy();
|
||||
m_eState = STATE_XLSP_NEXT_DC;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !Q_stricmp( "##full", szReply ) )
|
||||
{
|
||||
DevWarning( "XLSP master server: full.\n" );
|
||||
m_dc.Destroy();
|
||||
m_eState = STATE_XLSP_NEXT_DC;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !Q_stricmp( "##local", szReply ) )
|
||||
{
|
||||
DevWarning( "XLSP master server: game is not eligible for dedicated server.\n" );
|
||||
m_dc.Destroy();
|
||||
m_eState = STATE_FINISHED;
|
||||
return;
|
||||
}
|
||||
|
||||
// Bypass the gateway name check if we're faking it.
|
||||
if ( !CommandLine()->FindParm( "-xlsp_fake_gateway" ) )
|
||||
{
|
||||
if ( Q_stricmp( m_dc.m_szGatewayName, szReply ) )
|
||||
{
|
||||
DevWarning( "XLSP master server: wrong reply `%s`, expected gateway `%s`.\n", szReply, m_dc.m_szGatewayName );
|
||||
m_dc.Destroy();
|
||||
m_eState = STATE_XLSP_NEXT_DC;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Process all the servers in the batch
|
||||
//
|
||||
m_arrServerPorts.RemoveAll();
|
||||
for ( ; ; )
|
||||
{
|
||||
uint16 nPort = msg.ReadWord();
|
||||
if ( !nPort || nPort == 0xFFFF )
|
||||
{
|
||||
// end of list
|
||||
break;
|
||||
}
|
||||
|
||||
m_arrServerPorts.AddToTail( nPort );
|
||||
}
|
||||
DevWarning( "XLSP master server: returned %d servers in batch.\n", m_arrServerPorts.Count() );
|
||||
|
||||
// Go ahead and start reserving
|
||||
ReserveNextServer();
|
||||
}
|
||||
|
||||
#elif !defined( NO_STEAM )
|
||||
|
||||
void CDsSearcher::Steam_SearchPass()
|
||||
{
|
||||
if ( mm_dedicated_force_servers.GetString()[0] )
|
||||
{
|
||||
// if convar is on to force dedicated server choices, pretend we got search results of just those servers
|
||||
CSplitString serverList( mm_dedicated_force_servers.GetString(), "," );
|
||||
|
||||
for ( int i = 0; i < serverList.Count(); i++ )
|
||||
{
|
||||
// Check if the specification has a private IP address
|
||||
char const * adrsStrings[2] = { serverList[i], "" };
|
||||
if ( char *pchDelim = strchr( serverList[i], '|' ) )
|
||||
{
|
||||
*( pchDelim ++ ) = 0;
|
||||
adrsStrings[1] = pchDelim;
|
||||
}
|
||||
|
||||
netadr_t adrsForced[2];
|
||||
adrsForced[0].SetFromString( adrsStrings[0] );
|
||||
adrsForced[1].SetFromString( adrsStrings[1] );
|
||||
|
||||
// Check if a locally discovered server is known with
|
||||
// either public or private address that is being forced
|
||||
int numServers = g_pServerManager->GetNumServers();
|
||||
for ( int iServer = 0; iServer < numServers; ++ iServer )
|
||||
{
|
||||
IMatchServer *pMatchServer = g_pServerManager->GetServerByIndex( iServer );
|
||||
if ( !pMatchServer )
|
||||
continue;
|
||||
|
||||
KeyValues *pServerDetails = pMatchServer->GetGameDetails();
|
||||
netadr_t adrsKnown[2];
|
||||
char const * adrsStringsKnown[2] = { pServerDetails->GetString( "server/adronline" ), pServerDetails->GetString( "server/adrlocal" ) };
|
||||
adrsKnown[0].SetFromString( adrsStringsKnown[0] );
|
||||
adrsKnown[1].SetFromString( adrsStringsKnown[1] );
|
||||
|
||||
for ( int iAdrForced = 0; iAdrForced < ARRAYSIZE( adrsForced ); ++ iAdrForced )
|
||||
{
|
||||
for ( int iAdrKnown = 0; iAdrKnown < ARRAYSIZE( adrsKnown ); ++ iAdrKnown )
|
||||
{
|
||||
if ( adrsForced[iAdrForced].GetIPHostByteOrder() && adrsKnown[iAdrKnown].GetIPHostByteOrder() &&
|
||||
adrsForced[iAdrForced].CompareAdr( adrsKnown[iAdrKnown] ) )
|
||||
{
|
||||
if ( !adrsForced[!iAdrForced].GetIPHostByteOrder() ) // user not forcing other address, but we know it
|
||||
adrsStrings[!iAdrForced] = adrsStringsKnown[!iAdrKnown];
|
||||
goto finished_server_lookup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finished_server_lookup:
|
||||
|
||||
m_arrServerList.AddToTail( CDsSearcher::DsServer_t(
|
||||
adrsStrings[0], adrsStrings[1],
|
||||
0 ) ); // you get no accurate ping to forced servers
|
||||
}
|
||||
|
||||
Steam_OnDedicatedServerListFetched();
|
||||
return;
|
||||
}
|
||||
|
||||
CUtlVector< MatchMakingKeyValuePair_t > filters;
|
||||
filters.EnsureCapacity( 10 );
|
||||
|
||||
// filter by game and require empty server
|
||||
filters.AddToTail( MatchMakingKeyValuePair_t( "gamedir", COM_GetModDirectory() ) );
|
||||
filters.AddToTail( MatchMakingKeyValuePair_t( "noplayers", "1" ) );
|
||||
|
||||
// Official servers
|
||||
|
||||
bool bNeedOfficialServer = false;
|
||||
char const *szServerType = m_pSettings->GetString( "options/server", NULL );
|
||||
|
||||
if ( szServerType && !Q_stricmp( szServerType, "official" ) )
|
||||
{
|
||||
bNeedOfficialServer = true;
|
||||
filters.AddToTail( MatchMakingKeyValuePair_t( "white", "1" ) );
|
||||
}
|
||||
|
||||
// Allow the game to extend the filters
|
||||
if ( KeyValues *pExtra = g_pMMF->GetMatchTitleGameSettingsMgr()->DefineDedicatedSearchKeys( m_pSettings, bNeedOfficialServer, m_nSearchPass ) )
|
||||
{
|
||||
if ( !mm_dedicated_ip.GetString()[0] )
|
||||
{
|
||||
for ( KeyValues *val = pExtra->GetFirstValue(); val; val = val->GetNextValue() )
|
||||
{
|
||||
char const *szValue = val->GetString();
|
||||
if ( !szValue || !*szValue )
|
||||
continue;
|
||||
filters.AddToTail( MatchMakingKeyValuePair_t( val->GetName(), szValue ) );
|
||||
}
|
||||
}
|
||||
pExtra->deleteThis();
|
||||
}
|
||||
|
||||
// Load test
|
||||
if (m_bLoadTest && m_nSearchPass == 0)
|
||||
{
|
||||
// Add to the "gametype" filter
|
||||
for ( int i=0; i < filters.Count(); i++ )
|
||||
{
|
||||
MatchMakingKeyValuePair_t *pKV = &(filters[i]);
|
||||
if ( !Q_stricmp( pKV->m_szKey, "gametype" ) )
|
||||
{
|
||||
Q_strncat( pKV->m_szValue, ",sv_load_test", sizeof(pKV->m_szValue) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// request the server list. We will get called back at ServerResponded, ServerFailedToRespond, and RefreshComplete
|
||||
m_eState = STATE_STEAM_REQUESTING_SERVERS;
|
||||
m_pServerListListener = new CServerListListener( this, filters );
|
||||
++m_nSearchPass;
|
||||
}
|
||||
|
||||
CDsSearcher::CServerListListener::CServerListListener( CDsSearcher *pDsSearcher, CUtlVector< MatchMakingKeyValuePair_t > &filters ) :
|
||||
m_pOuter( pDsSearcher ),
|
||||
m_hRequest( NULL )
|
||||
{
|
||||
MatchMakingKeyValuePair_t *pFilter = filters.Base();
|
||||
DevMsg( 1, "Requesting dedicated server list...\n" );
|
||||
for (int i = 0; i < filters.Count(); i++)
|
||||
{
|
||||
DevMsg("Filter %d: %s=%s\n", i, filters.Element(i).m_szKey, filters.Element(i).m_szValue);
|
||||
}
|
||||
m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestInternetServerList(
|
||||
( AppId_t ) g_pMatchFramework->GetMatchTitle()->GetTitleID(), &pFilter,
|
||||
filters.Count(), this );
|
||||
}
|
||||
|
||||
void CDsSearcher::CServerListListener::Destroy()
|
||||
{
|
||||
m_pOuter = NULL;
|
||||
|
||||
if ( m_hRequest )
|
||||
steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
|
||||
m_hRequest = NULL;
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CDsSearcher::CServerListListener::ServerResponded( HServerListRequest hReq, int iServer )
|
||||
{
|
||||
gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()
|
||||
->GetServerDetails( hReq, iServer );
|
||||
|
||||
Msg( "[MM] Server responded '%s', dist %d\n",
|
||||
pServer->m_NetAdr.GetConnectionAddressString(), pServer->m_nPing );
|
||||
|
||||
// Check if the server IP address matches
|
||||
char const *szDedicatedIp = mm_dedicated_ip.GetString();
|
||||
if ( szDedicatedIp && szDedicatedIp[0] )
|
||||
{
|
||||
char const *szServerConnString = pServer->m_NetAdr.GetConnectionAddressString();
|
||||
szServerConnString = StringAfterPrefix( szServerConnString, szDedicatedIp );
|
||||
if ( !szServerConnString ||
|
||||
( szServerConnString[0] &&
|
||||
szServerConnString[0] != ':' ) )
|
||||
{
|
||||
DevMsg( " rejected dedicated server '%s' due to ip filter '%s'\n",
|
||||
pServer->m_NetAdr.GetConnectionAddressString(), szDedicatedIp );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Register the dedicated server as acceptable
|
||||
// netadr_t adrPublic, adrPrivate;
|
||||
// adrPublic.SetFromString( pServer->m_NetAdr.GetConnectionAddressString() );
|
||||
// TODO: bool bHasPrivate = FindLANServerPrivateIPByPublicIP( adrPublic, adrPrivate );
|
||||
|
||||
// Don't reserve servers with human players playing
|
||||
int nHumanPlayers = pServer->m_nPlayers - pServer->m_nBotPlayers;
|
||||
if ( nHumanPlayers > 0 )
|
||||
return;
|
||||
|
||||
// Don't reserve servers with too high ping
|
||||
if ( ( mm_dedicated_search_maxping.GetInt() > 0 ) && ( pServer->m_nPing > mm_dedicated_search_maxping.GetInt() ) )
|
||||
return;
|
||||
|
||||
// Register the result
|
||||
if ( m_pOuter )
|
||||
{
|
||||
// See if maybe we know about a private address for this server
|
||||
char const *szPrivateAddress = "";
|
||||
netadr_t adrPublic;
|
||||
adrPublic.SetFromString( pServer->m_NetAdr.GetConnectionAddressString() );
|
||||
|
||||
XUID xuidOnline = uint64( adrPublic.GetIPNetworkByteOrder() ) | ( uint64( adrPublic.GetPort() ) << 32ull );
|
||||
if ( IMatchServer *pMatchServer = g_pServerManager->GetServerByOnlineId( xuidOnline ) )
|
||||
{
|
||||
szPrivateAddress = pMatchServer->GetGameDetails()->GetString( "server/adrlocal" );
|
||||
}
|
||||
|
||||
m_pOuter->m_arrServerList.AddToTail( CDsSearcher::DsServer_t(
|
||||
pServer->m_NetAdr.GetConnectionAddressString(),
|
||||
szPrivateAddress,
|
||||
pServer->m_nPing ) );
|
||||
|
||||
if ( m_pOuter->m_arrServerList.Count() > mm_dedicated_search_maxresults.GetInt() ||
|
||||
Plat_FloatTime() > m_pOuter->m_flTimeout )
|
||||
{
|
||||
steamapicontext->SteamMatchmakingServers()->CancelQuery( hReq );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDsSearcher::CServerListListener::RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response )
|
||||
{
|
||||
if ( m_pOuter )
|
||||
{
|
||||
m_pOuter->Steam_OnDedicatedServerListFetched();
|
||||
}
|
||||
}
|
||||
|
||||
void CDsSearcher::Steam_OnDedicatedServerListFetched()
|
||||
{
|
||||
if ( m_bLoadTest && m_nSearchPass < 2 )
|
||||
{
|
||||
m_eState = STATE_INIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( m_pServerListListener )
|
||||
{
|
||||
m_pServerListListener->Destroy();
|
||||
m_pServerListListener = NULL;
|
||||
}
|
||||
|
||||
DevMsg( 1, "Dedicated server list fetched %d servers.\n", m_arrServerList.Count() );
|
||||
|
||||
ReserveNextServer();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void CDsSearcher::ReserveNextServer()
|
||||
{
|
||||
m_eState = STATE_RESERVING;
|
||||
|
||||
#ifdef _X360
|
||||
|
||||
if ( !m_arrServerPorts.Count() )
|
||||
{
|
||||
m_dc.Destroy();
|
||||
m_eState = STATE_XLSP_NEXT_DC;
|
||||
return;
|
||||
}
|
||||
|
||||
uint16 nPort = m_arrServerPorts.Head();
|
||||
m_arrServerPorts.RemoveMultipleFromHead( 1 );
|
||||
|
||||
netadr_t inetAddrSecure;
|
||||
inetAddrSecure.SetType( NA_IP );
|
||||
inetAddrSecure.SetIPAndPort( m_dc.m_adrSecure.s_addr, nPort );
|
||||
|
||||
netadr_t inetAddrInsecureSendable;
|
||||
inetAddrInsecureSendable.SetType( NA_IP );
|
||||
inetAddrInsecureSendable.SetIPAndPort( m_dc.m_xsi.inaServer.s_addr, nPort );
|
||||
|
||||
Q_strncpy( m_Result.m_szConnectionString, inetAddrSecure.ToString(), ARRAYSIZE( m_Result.m_szConnectionString ) );
|
||||
Q_strncpy( m_Result.m_szInsecureSendableServerAddress, inetAddrInsecureSendable.ToString(), ARRAYSIZE( m_Result.m_szInsecureSendableServerAddress ) );
|
||||
|
||||
netadr_t addrPublic, addrPrivate;
|
||||
addrPrivate.SetType( NA_NULL );
|
||||
addrPublic = inetAddrSecure;
|
||||
|
||||
#elif !defined( NO_STEAM )
|
||||
|
||||
if ( !m_arrServerList.Count() )
|
||||
{
|
||||
if ( Plat_FloatTime() < m_flTimeout )
|
||||
{
|
||||
m_eState = STATE_STEAM_NEXT_SEARCH_PASS;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eState = STATE_FINISHED;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DsServer_t dss = m_arrServerList.Head();
|
||||
m_arrServerList.RemoveMultipleFromHead( 1 );
|
||||
|
||||
Q_strncpy( m_Result.m_szPublicConnectionString, dss.m_szConnectionString, ARRAYSIZE( m_Result.m_szPublicConnectionString ) );
|
||||
Q_strncpy( m_Result.m_szPrivateConnectionString, dss.m_szPrivateConnectionString, ARRAYSIZE( m_Result.m_szPrivateConnectionString ) );
|
||||
|
||||
netadr_t addrPublic, addrPrivate;
|
||||
|
||||
addrPublic.SetFromString( dss.m_szConnectionString );
|
||||
|
||||
if ( dss.m_szPrivateConnectionString[0] )
|
||||
addrPrivate.SetFromString( dss.m_szPrivateConnectionString );
|
||||
else
|
||||
addrPrivate.SetType( NA_NULL );
|
||||
|
||||
#else
|
||||
|
||||
netadr_t addrPublic, addrPrivate;
|
||||
|
||||
#endif
|
||||
|
||||
g_pMatchExtensions->GetINetSupport()->ReserveServer( addrPublic, addrPrivate,
|
||||
m_uiReserveCookie, m_pReserveSettings,
|
||||
this, &m_pAsyncOperation );
|
||||
}
|
||||
|
||||
void CDsSearcher::OnOperationFinished( IMatchAsyncOperation *pOperation )
|
||||
{
|
||||
Assert( pOperation == m_pAsyncOperation );
|
||||
|
||||
uint64 uiResult = m_pAsyncOperation->GetResult();
|
||||
if ( m_pAsyncOperation->GetState() == AOS_FAILED || !uiResult )
|
||||
{
|
||||
ReserveNextServer();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Result.m_bDedicated = true;
|
||||
char const *szReservedAddr = ( char const * )( int )uiResult;
|
||||
Q_strncpy( m_Result.m_szConnectionString, szReservedAddr, ARRAYSIZE( m_Result.m_szConnectionString ) );
|
||||
|
||||
// If this server reservation reported number of game slots then
|
||||
// force that setting into session data
|
||||
uint32 numGameSlots = uint32( pOperation->GetResultExtraInfo() & 0xFF );
|
||||
if ( m_pMatchSession && ( numGameSlots > 0 ) )
|
||||
{
|
||||
KeyValues *kvUpdate = new KeyValues( "update" );
|
||||
KeyValues::AutoDelete autodelete( kvUpdate );
|
||||
kvUpdate->SetInt( "update/members/numSlots", numGameSlots );
|
||||
|
||||
m_pMatchSession->UpdateSessionSettings( kvUpdate );
|
||||
}
|
||||
|
||||
m_eState = STATE_FINISHED;
|
||||
}
|
||||
}
|
||||
|
||||
167
matchmaking/ds_searcher.h
Normal file
167
matchmaking/ds_searcher.h
Normal file
@@ -0,0 +1,167 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef DS_SEARCHER_H
|
||||
#define DS_SEARCHER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
class CDsSearcher;
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
class CDsSearcher : public IMatchAsyncOperationCallback, IMatchEventsSink
|
||||
{
|
||||
public:
|
||||
explicit CDsSearcher( KeyValues *pSettings, uint64 uiReserveCookie, IMatchSession *pMatchSession, uint64 ullCrypt = 0ull );
|
||||
virtual ~CDsSearcher();
|
||||
|
||||
// IMatchEventsSink
|
||||
public:
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
public:
|
||||
virtual void Update();
|
||||
virtual void Destroy();
|
||||
|
||||
virtual bool IsFinished();
|
||||
|
||||
struct DsResult_t
|
||||
{
|
||||
bool m_bAborted;
|
||||
bool m_bDedicated;
|
||||
|
||||
char m_szConnectionString[256];
|
||||
#ifdef _X360
|
||||
char m_szInsecureSendableServerAddress[256];
|
||||
#elif !defined( NO_STEAM )
|
||||
char m_szPublicConnectionString[256];
|
||||
char m_szPrivateConnectionString[256];
|
||||
#endif
|
||||
|
||||
void CopyToServerKey( KeyValues *pKvServer, uint64 ullCrypt = 0ull ) const;
|
||||
};
|
||||
virtual DsResult_t const& GetResult();
|
||||
|
||||
protected:
|
||||
void InitDedicatedSearch();
|
||||
void InitWithKnownServer();
|
||||
void ReserveNextServer();
|
||||
|
||||
virtual void OnOperationFinished( IMatchAsyncOperation *pOperation );
|
||||
|
||||
float m_flTimeout;
|
||||
IMatchAsyncOperation *m_pAsyncOperation;
|
||||
|
||||
protected:
|
||||
#ifdef _X360
|
||||
|
||||
CXlspTitleServers *m_pTitleServers;
|
||||
|
||||
void Xlsp_EnumerateDcs();
|
||||
void Xlsp_OnEnumerateDcsCompleted();
|
||||
|
||||
CUtlVector< CXlspDatacenter > m_arrDatacenters;
|
||||
char m_chDatacenterQuery[ MAX_PATH ];
|
||||
|
||||
void Xlsp_PrepareDatacenterQuery();
|
||||
void Xlsp_StartNextDc();
|
||||
|
||||
CXlspDatacenter m_dc;
|
||||
|
||||
void Xlsp_OnDcServerBatch( void const *pData, int numBytes );
|
||||
|
||||
CUtlVector< uint16 > m_arrServerPorts;
|
||||
|
||||
#elif !defined( NO_STEAM )
|
||||
|
||||
int m_nSearchPass;
|
||||
void Steam_SearchPass();
|
||||
|
||||
class CServerListListener : public ISteamMatchmakingServerListResponse
|
||||
{
|
||||
public:
|
||||
explicit CServerListListener( CDsSearcher *pDsSearcher, CUtlVector< MatchMakingKeyValuePair_t > &filters );
|
||||
void Destroy();
|
||||
|
||||
public:
|
||||
// Server has responded ok with updated data
|
||||
virtual void ServerResponded( HServerListRequest hReq, int iServer );
|
||||
// Server has failed to respond
|
||||
virtual void ServerFailedToRespond( HServerListRequest hReq, int iServer )
|
||||
{
|
||||
gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()
|
||||
->GetServerDetails( hReq, iServer );
|
||||
|
||||
DevMsg( " server failed to respond '%s'\n",
|
||||
pServer->m_NetAdr.GetConnectionAddressString() );
|
||||
|
||||
}
|
||||
// A list refresh you had initiated is now 100% completed
|
||||
virtual void RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response );
|
||||
|
||||
protected:
|
||||
CDsSearcher *m_pOuter;
|
||||
HServerListRequest m_hRequest;
|
||||
};
|
||||
friend class CServerListListener;
|
||||
CServerListListener *m_pServerListListener;
|
||||
|
||||
struct DsServer_t
|
||||
{
|
||||
DsServer_t( char const *szConnectionString, char const *szPrivateConnectionString, int nPing ) : m_nPing( nPing )
|
||||
{
|
||||
Q_strncpy( m_szConnectionString, szConnectionString, ARRAYSIZE( m_szConnectionString ) );
|
||||
Q_strncpy( m_szPrivateConnectionString, szPrivateConnectionString, ARRAYSIZE( m_szPrivateConnectionString ) );
|
||||
}
|
||||
char m_szConnectionString[256];
|
||||
char m_szPrivateConnectionString[256];
|
||||
int m_nPing;
|
||||
};
|
||||
CUtlVector< DsServer_t > m_arrServerList;
|
||||
|
||||
void Steam_OnDedicatedServerListFetched();
|
||||
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
IMatchSession *m_pMatchSession;
|
||||
KeyValues *m_pSettings;
|
||||
|
||||
uint64 m_uiReserveCookie;
|
||||
KeyValues *m_pReserveSettings;
|
||||
KeyValues::AutoDelete m_autodelete_pReserveSettings;
|
||||
|
||||
enum State_t
|
||||
{
|
||||
STATE_INIT,
|
||||
STATE_WAITING,
|
||||
#ifdef _X360
|
||||
STATE_XLSP_ENUMERATE_DCS,
|
||||
STATE_XLSP_NEXT_DC,
|
||||
STATE_XLSP_REQUESTING_SERVERS,
|
||||
#elif !defined( NO_STEAM )
|
||||
STATE_STEAM_REQUESTING_SERVERS,
|
||||
STATE_STEAM_NEXT_SEARCH_PASS,
|
||||
#endif
|
||||
STATE_RESERVING,
|
||||
STATE_FINISHED,
|
||||
};
|
||||
State_t m_eState;
|
||||
|
||||
DsResult_t m_Result;
|
||||
|
||||
// If we are loadtesting, do a 2-pass search
|
||||
// 1st pass, look for empty ds with sv_load_test = 1
|
||||
// 2 pass, don't include sv_load_test flag in search
|
||||
bool m_bLoadTest;
|
||||
|
||||
uint64 m_ullCrypt;
|
||||
};
|
||||
|
||||
#endif
|
||||
351
matchmaking/episodic/mm_title.cpp
Normal file
351
matchmaking/episodic/mm_title.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "mm_title_richpresence.h"
|
||||
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
CMatchTitle::CMatchTitle()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
CMatchTitle::~CMatchTitle()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Init / shutdown
|
||||
//
|
||||
|
||||
InitReturnVal_t CMatchTitle::Init()
|
||||
{
|
||||
if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
|
||||
{
|
||||
mgr->AddListener( this, "server_pre_shutdown", false );
|
||||
mgr->AddListener( this, "game_newmap", false );
|
||||
mgr->AddListener( this, "finale_start", false );
|
||||
mgr->AddListener( this, "round_start", false );
|
||||
mgr->AddListener( this, "round_end", false );
|
||||
mgr->AddListener( this, "difficulty_changed", false );
|
||||
}
|
||||
return INIT_OK;
|
||||
}
|
||||
|
||||
void CMatchTitle::Shutdown()
|
||||
{
|
||||
if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
|
||||
{
|
||||
mgr->RemoveListener( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
uint64 CMatchTitle::GetTitleID()
|
||||
{
|
||||
#ifdef _X360
|
||||
return 0xFFFFFFFFFFFFFFFF;
|
||||
#elif !defined( SWDS )
|
||||
static uint64 uiAppID = 0ull;
|
||||
if ( !uiAppID && steamapicontext && steamapicontext->SteamUtils() )
|
||||
{
|
||||
uiAppID = steamapicontext->SteamUtils()->GetAppID();
|
||||
}
|
||||
return uiAppID;
|
||||
#else
|
||||
return 0ull;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64 CMatchTitle::GetTitleServiceID()
|
||||
{
|
||||
#ifdef _X360
|
||||
return 0x45410880ull; // Left 4 Dead 1 Service ID
|
||||
#else
|
||||
return 0ull;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CMatchTitle::IsMultiplayer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMatchTitle::PrepareNetStartupParams( void *pNetStartupParams )
|
||||
{
|
||||
#ifdef _X360
|
||||
XNetStartupParams &xnsp = *( XNetStartupParams * ) pNetStartupParams;
|
||||
|
||||
xnsp.cfgQosDataLimitDiv4 = 64; // 256 bytes
|
||||
xnsp.cfgSockDefaultRecvBufsizeInK = 64; // Increase receive size for UDP to 64k
|
||||
xnsp.cfgSockDefaultSendBufsizeInK = 64; // Keep send size at 64k too
|
||||
|
||||
int numGamePlayersMax = GetTotalNumPlayersSupported();
|
||||
|
||||
int numConnections = 4 * ( numGamePlayersMax - 1 );
|
||||
// - the max number of connections to members of your game party
|
||||
// - the max number of connections to members of your social party
|
||||
// - the max number of connections to a pending game party (if you are joining a new one ).
|
||||
// - matchmakings client info structure also creates a connection per client for the lobby.
|
||||
|
||||
// 1 - the main game session
|
||||
int numTotalConnections = 1 + numConnections;
|
||||
|
||||
// 29 - total Connections (XNADDR/XNKID pairs) ,using 5 sessions (XNKID/XNKEY pairs).
|
||||
|
||||
xnsp.cfgKeyRegMax = 16; //adding some extra room because of lazy dealocation of these pairs.
|
||||
xnsp.cfgSecRegMax = MAX( 64, numTotalConnections ); //adding some extra room because of lazy dealocation of these pairs.
|
||||
|
||||
xnsp.cfgSockMaxDgramSockets = xnsp.cfgSecRegMax;
|
||||
xnsp.cfgSockMaxStreamSockets = xnsp.cfgSecRegMax;
|
||||
#endif
|
||||
}
|
||||
|
||||
int CMatchTitle::GetTotalNumPlayersSupported()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get a guest player name
|
||||
char const * CMatchTitle::GetGuestPlayerName( int iUserIndex )
|
||||
{
|
||||
if ( vgui::ILocalize *pLocalize = g_pMatchExtensions->GetILocalize() )
|
||||
{
|
||||
if ( wchar_t* wStringTableEntry = pLocalize->Find( "#L4D360UI_Character_Guest" ) )
|
||||
{
|
||||
static char szName[ MAX_PLAYER_NAME_LENGTH ] = {0};
|
||||
pLocalize->ConvertUnicodeToANSI( wStringTableEntry, szName, ARRAYSIZE( szName ) );
|
||||
return szName;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
// Sets up all necessary client-side convars and user info before
|
||||
// connecting to server
|
||||
void CMatchTitle::PrepareClientForConnect( KeyValues *pSettings )
|
||||
{
|
||||
#ifndef SWDS
|
||||
int numPlayers = 1;
|
||||
#ifdef _X360
|
||||
numPlayers = XBX_GetNumGameUsers();
|
||||
#endif
|
||||
|
||||
//
|
||||
// Now we set the convars
|
||||
//
|
||||
|
||||
for ( int k = 0; k < numPlayers; ++ k )
|
||||
{
|
||||
int iController = k;
|
||||
#ifdef _X360
|
||||
iController = XBX_GetUserId( k );
|
||||
#endif
|
||||
IPlayerLocal *pPlayerLocal = g_pPlayerManager->GetLocalPlayer( iController );
|
||||
if ( !pPlayerLocal )
|
||||
continue;
|
||||
|
||||
// Set "name"
|
||||
static SplitScreenConVarRef s_cl_name( "name" );
|
||||
char const *szName = pPlayerLocal->GetName();
|
||||
s_cl_name.SetValue( k, szName );
|
||||
|
||||
// Set "networkid_force"
|
||||
if ( IsX360() )
|
||||
{
|
||||
static SplitScreenConVarRef s_networkid_force( "networkid_force" );
|
||||
uint64 xid = pPlayerLocal->GetXUID();
|
||||
s_networkid_force.SetValue( k, CFmtStr( "%08X:%08X:", uint32( xid >> 32 ), uint32( xid ) ) );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CMatchTitle::StartServerMap( KeyValues *pSettings )
|
||||
{
|
||||
|
||||
int numPlayers = 1;
|
||||
#ifdef _X360
|
||||
numPlayers = XBX_GetNumGameUsers();
|
||||
#endif
|
||||
|
||||
char const *szMap = pSettings->GetString( "game/bspname", NULL );
|
||||
if ( !szMap )
|
||||
return false;
|
||||
|
||||
// Check that we have the server interface and that the map is valid
|
||||
if ( !g_pMatchExtensions->GetIVEngineServer() )
|
||||
return false;
|
||||
if ( !g_pMatchExtensions->GetIVEngineServer()->IsMapValid( szMap ) )
|
||||
return false;
|
||||
|
||||
//
|
||||
// Prepare game dll reservation package
|
||||
//
|
||||
KeyValues *pGameDllReserve = g_pMatchFramework->GetMatchNetworkMsgController()->PackageGameDetailsForReservation( pSettings );
|
||||
KeyValues::AutoDelete autodelete( pGameDllReserve );
|
||||
|
||||
pGameDllReserve->SetString( "map/mapcommand", ( numPlayers <= 1 ) ? "map" : "ss_map" );
|
||||
|
||||
if ( !Q_stricmp( "commentary", pSettings->GetString( "options/play", "" ) ) )
|
||||
pGameDllReserve->SetString( "map/mapcommand", "map_commentary" );
|
||||
|
||||
// Run map based off the faked reservation packet
|
||||
g_pMatchExtensions->GetIServerGameDLL()->ApplyGameSettings( pGameDllReserve );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static KeyValues * GetCurrentMatchSessionSettings()
|
||||
{
|
||||
IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
return pIMatchSession ? pIMatchSession->GetSessionSettings() : NULL;
|
||||
}
|
||||
|
||||
void CMatchTitle::OnEvent( KeyValues *pEvent )
|
||||
{
|
||||
char const *szEvent = pEvent->GetName();
|
||||
|
||||
if ( !Q_stricmp( "OnPlayerRemoved", szEvent ) ||
|
||||
!Q_stricmp( "OnPlayerUpdated", szEvent ) )
|
||||
{
|
||||
MM_Title_RichPresence_PlayersChanged( GetCurrentMatchSessionSettings() );
|
||||
}
|
||||
else if ( !Q_stricmp( "OnMatchSessionUpdate", szEvent ) )
|
||||
{
|
||||
if ( !Q_stricmp( pEvent->GetString( "state" ), "updated" ) )
|
||||
{
|
||||
if ( KeyValues *kvUpdate = pEvent->FindKey( "update" ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), kvUpdate );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( pEvent->GetString( "state" ), "created" ) ||
|
||||
!Q_stricmp( pEvent->GetString( "state" ), "ready" ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), NULL );
|
||||
}
|
||||
else if ( !Q_stricmp( pEvent->GetString( "state" ), "closed" ) )
|
||||
{
|
||||
MM_Title_RichPresence_Update( NULL, NULL );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
int CMatchTitle::GetEventDebugID( void )
|
||||
{
|
||||
return EVENT_DEBUG_ID_INIT;
|
||||
}
|
||||
|
||||
void CMatchTitle::FireGameEvent( IGameEvent *pIGameEvent )
|
||||
{
|
||||
// Check if the current match session is on an active game server
|
||||
IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pMatchSession )
|
||||
return;
|
||||
KeyValues *pSessionSettings = pMatchSession->GetSessionSettings();
|
||||
char const *szGameServer = pSessionSettings->GetString( "server/server", "" );
|
||||
char const *szSystemLock = pSessionSettings->GetString( "system/lock", "" );
|
||||
if ( ( !szGameServer || !*szGameServer ) &&
|
||||
( !szSystemLock || !*szSystemLock ) )
|
||||
return;
|
||||
|
||||
// Also don't run on the client when there's a host
|
||||
char const *szSessionType = pMatchSession->GetSessionSystemData()->GetString( "type", NULL );
|
||||
if ( szSessionType && !Q_stricmp( szSessionType, "client" ) )
|
||||
return;
|
||||
|
||||
// Parse the game event
|
||||
char const *szGameEvent = pIGameEvent->GetName();
|
||||
if ( !szGameEvent || !*szGameEvent )
|
||||
return;
|
||||
|
||||
if ( !Q_stricmp( "round_start", szGameEvent ) )
|
||||
{
|
||||
pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" state game "
|
||||
" } "
|
||||
" } "
|
||||
) ) );
|
||||
}
|
||||
else if ( !Q_stricmp( "finale_start", szGameEvent ) )
|
||||
{
|
||||
pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" state finale "
|
||||
" } "
|
||||
" } "
|
||||
) ) );
|
||||
}
|
||||
else if ( !Q_stricmp( "game_newmap", szGameEvent ) )
|
||||
{
|
||||
KeyValues *kvUpdate = KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" state game "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
KeyValues::AutoDelete autodelete( kvUpdate );
|
||||
|
||||
pMatchSession->UpdateSessionSettings( kvUpdate );
|
||||
}
|
||||
else if ( !Q_stricmp( "difficulty_changed", szGameEvent ) )
|
||||
{
|
||||
char const *szDifficulty = pIGameEvent->GetString( "strDifficulty", "normal" );
|
||||
|
||||
KeyValues *kvUpdate = KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" game { "
|
||||
" difficulty = "
|
||||
" } "
|
||||
" } "
|
||||
);
|
||||
KeyValues::AutoDelete autodelete( kvUpdate );
|
||||
|
||||
kvUpdate->SetString( "update/game/difficulty", szDifficulty );
|
||||
|
||||
pMatchSession->UpdateSessionSettings( kvUpdate );
|
||||
}
|
||||
else if ( !Q_stricmp( "server_pre_shutdown", szGameEvent ) )
|
||||
{
|
||||
char const *szReason = pIGameEvent->GetString( "reason", "quit" );
|
||||
if ( !Q_stricmp( szReason, "quit" ) )
|
||||
{
|
||||
DevMsg( "Received server_pre_shutdown notification - server is shutting down...\n" );
|
||||
|
||||
// Transform the server shutdown event into game end event
|
||||
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
||||
"OnEngineDisconnectReason", "reason", "Server shutting down"
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
83
matchmaking/episodic/mm_title.h
Normal file
83
matchmaking/episodic/mm_title.h
Normal file
@@ -0,0 +1,83 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_TITLE_H
|
||||
#define MM_TITLE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "../mm_framework.h"
|
||||
|
||||
class CMatchTitle :
|
||||
public IMatchTitle,
|
||||
public IMatchEventsSink,
|
||||
public IGameEventListener2
|
||||
{
|
||||
// Methods exposed for the framework
|
||||
public:
|
||||
virtual InitReturnVal_t Init();
|
||||
virtual void Shutdown();
|
||||
|
||||
// Methods of IMatchTitle
|
||||
public:
|
||||
// Title ID
|
||||
virtual uint64 GetTitleID();
|
||||
|
||||
// Service ID for XLSP
|
||||
virtual uint64 GetTitleServiceID();
|
||||
|
||||
// Whether we are a single-player title or multi-player title
|
||||
virtual bool IsMultiplayer();
|
||||
|
||||
// Prepare network startup params for the title
|
||||
virtual void PrepareNetStartupParams( void *pNetStartupParams );
|
||||
|
||||
// Get total number of players supported by the title
|
||||
virtual int GetTotalNumPlayersSupported();
|
||||
|
||||
// Get a guest player name
|
||||
virtual char const * GetGuestPlayerName( int iUserIndex );
|
||||
|
||||
// Decipher title data fields
|
||||
virtual TitleDataFieldsDescription_t const * DescribeTitleDataStorage();
|
||||
|
||||
// Title achievements
|
||||
virtual TitleAchievementsDescription_t const * DescribeTitleAchievements();
|
||||
|
||||
// Title avatar awards
|
||||
virtual TitleAvatarAwardsDescription_t const * DescribeTitleAvatarAwards();
|
||||
|
||||
// Title leaderboards
|
||||
virtual KeyValues * DescribeTitleLeaderboard( char const *szLeaderboardView );
|
||||
|
||||
// Sets up all necessary client-side convars and user info before
|
||||
// connecting to server
|
||||
virtual void PrepareClientForConnect( KeyValues *pSettings );
|
||||
|
||||
// Start up a listen server with the given settings
|
||||
virtual bool StartServerMap( KeyValues *pSettings );
|
||||
|
||||
// Methods of IMatchEventsSink
|
||||
public:
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
// Methods of IGameEventListener2
|
||||
public:
|
||||
// FireEvent is called by EventManager if event just occured
|
||||
// KeyValue memory will be freed by manager if not needed anymore
|
||||
virtual void FireGameEvent( IGameEvent *event );
|
||||
virtual int GetEventDebugID( void );
|
||||
|
||||
public:
|
||||
CMatchTitle();
|
||||
~CMatchTitle();
|
||||
};
|
||||
|
||||
// Match title singleton
|
||||
extern CMatchTitle *g_pMatchTitle;
|
||||
|
||||
#endif // MM_TITLE_H
|
||||
312
matchmaking/episodic/mm_title_gamesettingsmgr.cpp
Normal file
312
matchmaking/episodic/mm_title_gamesettingsmgr.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "mm_title_richpresence.h"
|
||||
|
||||
#include "vstdlib/random.h"
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
class CMatchTitleGameSettingsMgr : public IMatchTitleGameSettingsMgr
|
||||
{
|
||||
public:
|
||||
// Extends server game details
|
||||
virtual void ExtendServerDetails( KeyValues *pDetails, KeyValues *pRequest );
|
||||
|
||||
// Adds the essential part of game details to be broadcast
|
||||
virtual void ExtendLobbyDetailsTemplate( KeyValues *pDetails, char const *szReason, KeyValues *pFullSettings );
|
||||
|
||||
// Extends game settings update packet for lobby transition,
|
||||
// either due to a migration or due to an endgame condition
|
||||
virtual void ExtendGameSettingsForLobbyTransition( KeyValues *pSettings, KeyValues *pSettingsUpdate, bool bEndGame );
|
||||
|
||||
|
||||
// Rolls up game details for matches grouping
|
||||
virtual KeyValues * RollupGameDetails( KeyValues *pDetails, KeyValues *pRollup, KeyValues *pQuery );
|
||||
|
||||
|
||||
// Defines session search keys for matchmaking
|
||||
virtual KeyValues * DefineSessionSearchKeys( KeyValues *pSettings );
|
||||
|
||||
// Defines dedicated server search key
|
||||
virtual KeyValues * DefineDedicatedSearchKeys( KeyValues *pSettings );
|
||||
|
||||
|
||||
// Initializes full game settings from potentially abbreviated game settings
|
||||
virtual void InitializeGameSettings( KeyValues *pSettings );
|
||||
|
||||
// Extends game settings update packet before it gets merged with
|
||||
// session settings and networked to remote clients
|
||||
virtual void ExtendGameSettingsUpdateKeys( KeyValues *pSettings, KeyValues *pUpdateDeleteKeys );
|
||||
|
||||
// Prepares system for session creation
|
||||
virtual KeyValues * PrepareForSessionCreate( KeyValues *pSettings );
|
||||
|
||||
|
||||
// Executes the command on the session settings, this function on host
|
||||
// is allowed to modify Members/Game subkeys and has to fill in modified players KeyValues
|
||||
// When running on a remote client "ppPlayersUpdated" is NULL and players cannot
|
||||
// be modified
|
||||
virtual void ExecuteCommand( KeyValues *pCommand, KeyValues *pSessionSystemData, KeyValues *pSettings, KeyValues **ppPlayersUpdated );
|
||||
|
||||
// Prepares the lobby for game or adjust settings of new players who
|
||||
// join a game in progress, this function is allowed to modify
|
||||
// Members/Game subkeys and has to fill in modified players KeyValues
|
||||
virtual void PrepareLobbyForGame( KeyValues *pSettings, KeyValues **ppPlayersUpdated );
|
||||
|
||||
// Prepares the host team lobby for game adjusting the game settings
|
||||
// this function is allowed to prepare modification package to update
|
||||
// Game subkeys.
|
||||
// Returns the update/delete package to be applied to session settings
|
||||
// and pushed to dependent two sesssion of the two teams.
|
||||
virtual KeyValues * PrepareTeamLinkForGame( KeyValues *pSettingsLocal, KeyValues *pSettingsRemote );
|
||||
};
|
||||
|
||||
CMatchTitleGameSettingsMgr g_MatchTitleGameSettingsMgr;
|
||||
IMatchTitleGameSettingsMgr *g_pIMatchTitleGameSettingsMgr = &g_MatchTitleGameSettingsMgr;
|
||||
|
||||
|
||||
//
|
||||
// Implementation of CMatchTitleGameSettingsMgr
|
||||
//
|
||||
|
||||
// Extends server game details
|
||||
void CMatchTitleGameSettingsMgr::ExtendServerDetails( KeyValues *pDetails, KeyValues *pRequest )
|
||||
{
|
||||
// Query server info
|
||||
INetSupport::ServerInfo_t si;
|
||||
g_pMatchExtensions->GetINetSupport()->GetServerInfo( &si );
|
||||
|
||||
// Server is always in game
|
||||
pDetails->SetString( "game/state", "game" );
|
||||
|
||||
//
|
||||
// Determine map info
|
||||
//
|
||||
{
|
||||
pDetails->SetString( "game/bspname", CFmtStr( "%s", si.m_szMapName ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the essential part of game details to be broadcast
|
||||
void CMatchTitleGameSettingsMgr::ExtendLobbyDetailsTemplate( KeyValues *pDetails, char const *szReason, KeyValues *pFullSettings )
|
||||
{
|
||||
static KeyValues *pkvExt = KeyValues::FromString(
|
||||
"settings",
|
||||
" game { "
|
||||
" bspname #empty# "
|
||||
" } "
|
||||
);
|
||||
|
||||
pDetails->MergeFrom( pkvExt, KeyValues::MERGE_KV_UPDATE );
|
||||
}
|
||||
|
||||
// Extends game settings update packet for lobby transition,
|
||||
// either due to a migration or due to an endgame condition
|
||||
void CMatchTitleGameSettingsMgr::ExtendGameSettingsForLobbyTransition( KeyValues *pSettings, KeyValues *pSettingsUpdate, bool bEndGame )
|
||||
{
|
||||
pSettingsUpdate->SetString( "game/state", "lobby" );
|
||||
}
|
||||
|
||||
// Rolls up game details for matches grouping
|
||||
KeyValues * CMatchTitleGameSettingsMgr::RollupGameDetails( KeyValues *pDetails, KeyValues *pRollup, KeyValues *pQuery )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Defines dedicated server search key
|
||||
KeyValues * CMatchTitleGameSettingsMgr::DefineDedicatedSearchKeys( KeyValues *pSettings )
|
||||
{
|
||||
if ( IsPC() )
|
||||
{
|
||||
static ConVarRef sv_search_key( "sv_search_key" );
|
||||
|
||||
KeyValues *pKeys = new KeyValues( "SearchKeys" );
|
||||
pKeys->SetString( "gametype", CFmtStr( "%s,sv_search_key_%s%d",
|
||||
"empty",
|
||||
sv_search_key.GetString(),
|
||||
g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() ) );
|
||||
return pKeys;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Defines session search keys for matchmaking
|
||||
KeyValues * CMatchTitleGameSettingsMgr::DefineSessionSearchKeys( KeyValues *pSettings )
|
||||
{
|
||||
MEM_ALLOC_CREDIT();
|
||||
KeyValues *pResult = new KeyValues( "SessionSearch" );
|
||||
|
||||
pResult->SetInt( "numPlayers", pSettings->GetInt( "members/numPlayers", XBX_GetNumGameUsers() ) );
|
||||
|
||||
/*
|
||||
char const *szGameMode = pSettings->GetString( "game/mode", "" );
|
||||
if ( IsX360() )
|
||||
{
|
||||
if ( char const *szValue = pSettings->GetString( "game/mode", NULL ) )
|
||||
{
|
||||
static ContextValue_t values[] = {
|
||||
{ "versus", SESSION_MATCH_QUERY_PUBLIC_STATE_C___SORT___CHAPTER },
|
||||
{ "teamversus", SESSION_MATCH_QUERY_TEAM_STATE_C_CHAPTER },
|
||||
{ "scavenge", SESSION_MATCH_QUERY_PUBLIC_STATE_C_CHAPTER___SORT___ROUNDS },
|
||||
{ "teamscavenge", SESSION_MATCH_QUERY_TEAM_STATE_C_CHAPTER_ROUNDS },
|
||||
{ "survival", SESSION_MATCH_QUERY_PUBLIC_STATE_C_CHAPTER },
|
||||
{ "coop", SESSION_MATCH_QUERY_PUBLIC_STATE_C_DIFF___SORT___CHAPTER },
|
||||
{ "realism", SESSION_MATCH_QUERY_PUBLIC_STATE_C_DIFF___SORT___CHAPTER },
|
||||
{ NULL, 0xFFFF },
|
||||
};
|
||||
|
||||
pResult->SetInt( "rule", values->ScanValues( szValue ) );
|
||||
}
|
||||
|
||||
// Set the matchmaking version
|
||||
pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_MMVERSION ), mm_matchmaking_version.GetInt() );
|
||||
|
||||
#ifdef _X360
|
||||
// Set the installed DLCs masks
|
||||
uint64 uiDlcsMask = MatchSession_GetDlcInstalledMask();
|
||||
for ( int k = 1; k <= mm_matchmaking_dlcsquery.GetInt(); ++ k )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_MMVERSION + k ), !!( uiDlcsMask & ( 1ull << k ) ) );
|
||||
}
|
||||
pResult->SetInt( "dlc1", PROPERTY_MMVERSION + 1 );
|
||||
pResult->SetInt( "dlcN", PROPERTY_MMVERSION + mm_matchmaking_dlcsquery.GetInt() );
|
||||
#endif
|
||||
|
||||
// X_CONTEXT_GAME_TYPE
|
||||
pResult->SetInt( CFmtStr( "Contexts/%d", X_CONTEXT_GAME_TYPE ), X_CONTEXT_GAME_TYPE_STANDARD );
|
||||
|
||||
// X_CONTEXT_GAME_MODE
|
||||
if ( char const *szValue = pSettings->GetString( "game/mode", NULL ) )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Contexts/%d", X_CONTEXT_GAME_MODE ), g_pcv_CONTEXT_GAME_MODE->ScanValues( szValue ) );
|
||||
}
|
||||
|
||||
if ( char const *szValue = pSettings->GetString( "game/state", NULL ) )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Contexts/%d", CONTEXT_STATE ), g_pcv_CONTEXT_STATE->ScanValues( szValue ) );
|
||||
}
|
||||
|
||||
if ( char const *szValue = pSettings->GetString( "game/difficulty", NULL ) )
|
||||
{
|
||||
if ( !Q_stricmp( "coop", szGameMode ) || !Q_stricmp( "realism", szGameMode ) )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Contexts/%d", CONTEXT_DIFFICULTY ), g_pcv_CONTEXT_DIFFICULTY->ScanValues( szValue ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( int val = pSettings->GetInt( "game/maxrounds" ) )
|
||||
{
|
||||
if ( !Q_stricmp( "scavenge", szGameMode ) || !Q_stricmp( "teamscavenge", szGameMode ) )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_MAXROUNDS ), val );
|
||||
}
|
||||
}
|
||||
|
||||
char const *szCampaign = pSettings->GetString( "game/campaign" );
|
||||
if ( *szCampaign )
|
||||
{
|
||||
DWORD dwContext = CONTEXT_CAMPAIGN_UNKNOWN;
|
||||
if ( KeyValues *pAllMissions = g_pMatchExtL4D->GetAllMissions() )
|
||||
{
|
||||
if ( KeyValues *pMission = pAllMissions->FindKey( szCampaign ) )
|
||||
{
|
||||
dwContext = pMission->GetInt( "x360ctx", dwContext );
|
||||
}
|
||||
}
|
||||
if ( dwContext != CONTEXT_CAMPAIGN_UNKNOWN )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Contexts/%d", CONTEXT_CAMPAIGN ), dwContext );
|
||||
}
|
||||
}
|
||||
|
||||
if ( int val = pSettings->GetInt( "game/chapter" ) )
|
||||
{
|
||||
pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_CHAPTER ), val );
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
*/
|
||||
{
|
||||
if ( char const *szValue = pSettings->GetString( "game/bspname", NULL ) )
|
||||
{
|
||||
pResult->SetString( "Filter=/game:bspname", szValue );
|
||||
}
|
||||
}
|
||||
|
||||
return pResult;
|
||||
}
|
||||
|
||||
// Initializes full game settings from potentially abbreviated game settings
|
||||
void CMatchTitleGameSettingsMgr::InitializeGameSettings( KeyValues *pSettings )
|
||||
{
|
||||
char const *szNetwork = pSettings->GetString( "system/network", "LIVE" );
|
||||
|
||||
if ( KeyValues *kv = pSettings->FindKey( "game", true ) )
|
||||
{
|
||||
kv->SetString( "state", "lobby" );
|
||||
}
|
||||
|
||||
|
||||
// Offline games don't need slots and player setup
|
||||
if ( !Q_stricmp( "offline", szNetwork ) )
|
||||
return;
|
||||
|
||||
//
|
||||
// Set the number of slots
|
||||
//
|
||||
int numSlots = 2;
|
||||
pSettings->SetInt( "members/numSlots", numSlots );
|
||||
}
|
||||
|
||||
// Extends game settings update packet before it gets merged with
|
||||
// session settings and networked to remote clients
|
||||
void CMatchTitleGameSettingsMgr::ExtendGameSettingsUpdateKeys( KeyValues *pSettings, KeyValues *pUpdateDeleteKeys )
|
||||
{
|
||||
}
|
||||
|
||||
// Prepares system for session creation
|
||||
KeyValues * CMatchTitleGameSettingsMgr::PrepareForSessionCreate( KeyValues *pSettings )
|
||||
{
|
||||
return MM_Title_RichPresence_PrepareForSessionCreate( pSettings );
|
||||
}
|
||||
|
||||
// Prepares the lobby for game or adjust settings of new players who
|
||||
// join a game in progress, this function is allowed to modify
|
||||
// Members/Game subkeys and has to write modified players XUIDs
|
||||
void CMatchTitleGameSettingsMgr::PrepareLobbyForGame( KeyValues *pSettings, KeyValues **ppPlayersUpdated )
|
||||
{
|
||||
// set player avatar/teams, etc
|
||||
}
|
||||
|
||||
// Prepares the host team lobby for game adjusting the game settings
|
||||
// this function is allowed to prepare modification package to update
|
||||
// Game subkeys.
|
||||
// Returns the update/delete package to be applied to session settings
|
||||
// and pushed to dependent two sesssion of the two teams.
|
||||
KeyValues * CMatchTitleGameSettingsMgr::PrepareTeamLinkForGame( KeyValues *pSettingsLocal, KeyValues *pSettingsRemote )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Executes the command on the session settings, this function on host
|
||||
// is allowed to modify Members/Game subkeys and has to fill in modified players KeyValues
|
||||
// When running on a remote client "ppPlayersUpdated" is NULL and players cannot
|
||||
// be modified
|
||||
void CMatchTitleGameSettingsMgr::ExecuteCommand( KeyValues *pCommand, KeyValues *pSessionSystemData, KeyValues *pSettings, KeyValues **ppPlayersUpdated )
|
||||
{
|
||||
//char const *szCommand = pCommand->GetName();
|
||||
}
|
||||
|
||||
|
||||
39
matchmaking/episodic/mm_title_main.cpp
Normal file
39
matchmaking/episodic/mm_title_main.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
#include "../mm_title_main.h"
|
||||
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
LINK_MATCHMAKING_LIB();
|
||||
|
||||
static CMatchTitle g_MatchTitle;
|
||||
CMatchTitle *g_pMatchTitle = &g_MatchTitle;
|
||||
|
||||
IMatchTitle *g_pIMatchTitle = g_pMatchTitle;
|
||||
IMatchEventsSink *g_pIMatchTitleEventsSink = g_pMatchTitle;
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Init / shutdown
|
||||
//
|
||||
|
||||
InitReturnVal_t MM_Title_Init()
|
||||
{
|
||||
return g_pMatchTitle->Init();
|
||||
}
|
||||
|
||||
void MM_Title_Shutdown()
|
||||
{
|
||||
if ( g_pMatchTitle )
|
||||
g_pMatchTitle->Shutdown();
|
||||
}
|
||||
|
||||
52
matchmaking/episodic/mm_title_richpresence.cpp
Normal file
52
matchmaking/episodic/mm_title_richpresence.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title_richpresence.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
static void SetAllUsersContext( DWORD dwContextId, DWORD dwValue, bool bAsync = true )
|
||||
{
|
||||
#ifdef _X360
|
||||
for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
|
||||
{
|
||||
if ( XBX_GetUserIsGuest( k ) )
|
||||
continue;
|
||||
int iCtrlr = XBX_GetUserId( k );
|
||||
if ( bAsync )
|
||||
XUserSetContextEx( iCtrlr, dwContextId, dwValue, MMX360_NewOverlappedDormant() );
|
||||
else
|
||||
XUserSetContext( iCtrlr, dwContextId, dwValue );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void SetAllUsersProperty( DWORD dwPropertyId, DWORD cbValue, void const *pvValue )
|
||||
{
|
||||
#ifdef _X360
|
||||
for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
|
||||
{
|
||||
if ( XBX_GetUserIsGuest( k ) )
|
||||
continue;
|
||||
int iCtrlr = XBX_GetUserId( k );
|
||||
XUserSetPropertyEx( iCtrlr, dwPropertyId, cbValue, pvValue, MMX360_NewOverlappedDormant() );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
KeyValues * MM_Title_RichPresence_PrepareForSessionCreate( KeyValues *pSettings )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void MM_Title_RichPresence_Update( KeyValues *pFullSettings, KeyValues *pUpdatedSettings )
|
||||
{
|
||||
}
|
||||
|
||||
void MM_Title_RichPresence_PlayersChanged( KeyValues *pFullSettings )
|
||||
{
|
||||
}
|
||||
20
matchmaking/episodic/mm_title_richpresence.h
Normal file
20
matchmaking/episodic/mm_title_richpresence.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_TITLE_RICHPRESENCE_H
|
||||
#define MM_TITLE_RICHPRESENCE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "mm_title.h"
|
||||
|
||||
void MM_Title_RichPresence_Update( KeyValues *pFullSettings, KeyValues *pUpdatedSettings );
|
||||
void MM_Title_RichPresence_PlayersChanged( KeyValues *pFullSettings );
|
||||
|
||||
KeyValues * MM_Title_RichPresence_PrepareForSessionCreate( KeyValues *pSettings );
|
||||
|
||||
#endif // MM_TITLE_H
|
||||
113
matchmaking/episodic/mm_title_titledata.cpp
Normal file
113
matchmaking/episodic/mm_title_titledata.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_title.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
TitleDataFieldsDescription_t const * CMatchTitle::DescribeTitleDataStorage()
|
||||
{
|
||||
#define TD_ENTRY( szName, nTD, eDataType, numBytesOffset ) \
|
||||
{ szName, TitleDataFieldsDescription_t::nTD, TitleDataFieldsDescription_t::eDataType, numBytesOffset }
|
||||
|
||||
static TitleDataFieldsDescription_t tdfd[] =
|
||||
{
|
||||
#if 0
|
||||
TD_ENTRY( "TD1.Easy.Games.Total", DB_TD1, DT_U64, offsetof( TitleData1, mGames[0] ) ),
|
||||
TD_ENTRY( "TD1.Normal.Games.Total", DB_TD1, DT_U64, offsetof( TitleData1, mGames[1] ) ),
|
||||
TD_ENTRY( "TD1.Advanced.Games.Total", DB_TD1, DT_U64, offsetof( TitleData1, mGames[2] ) ),
|
||||
TD_ENTRY( "TD1.Expert.Games.Total", DB_TD1, DT_U64, offsetof( TitleData1, mGames[3] ) ),
|
||||
#endif
|
||||
TD_ENTRY( NULL, DB_TD1, DT_U8, 0 )
|
||||
};
|
||||
|
||||
#undef TD_ENTRY
|
||||
|
||||
return tdfd;
|
||||
}
|
||||
|
||||
TitleAchievementsDescription_t const * CMatchTitle::DescribeTitleAchievements()
|
||||
{
|
||||
static TitleAchievementsDescription_t tad[] =
|
||||
{
|
||||
//#include "left4dead2.xhelp.achtitledesc.txt"
|
||||
// END MARKER
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
return tad;
|
||||
}
|
||||
|
||||
TitleAvatarAwardsDescription_t const * CMatchTitle::DescribeTitleAvatarAwards()
|
||||
{
|
||||
static TitleAvatarAwardsDescription_t taad[] =
|
||||
{
|
||||
//#include "left4dead2.xhelp.avawtitledesc.txt"
|
||||
// END MARKER
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
return taad;
|
||||
}
|
||||
|
||||
// Title leaderboards
|
||||
KeyValues * CMatchTitle::DescribeTitleLeaderboard( char const *szLeaderboardView )
|
||||
{
|
||||
/*
|
||||
// Check if this is a survival leaderboard
|
||||
if ( char const *szSurvivalMap = StringAfterPrefix( szLeaderboardView, "survival_" ) )
|
||||
{
|
||||
if ( IsX360() )
|
||||
{
|
||||
// Find the corresponding record in the mission script
|
||||
KeyValues *pSettings = new KeyValues( "settings" );
|
||||
KeyValues::AutoDelete autodelete_pSettings( pSettings );
|
||||
pSettings->SetString( "game/mode", "survival" );
|
||||
|
||||
KeyValues *pMissionInfo = NULL;
|
||||
KeyValues *pMapInfo = g_pMatchExtL4D->GetMapInfoByBspName( pSettings, szSurvivalMap, &pMissionInfo );
|
||||
if ( !pMapInfo || !pMissionInfo )
|
||||
return NULL;
|
||||
|
||||
// Find the leaderboard description in the map info
|
||||
KeyValues *pLbDesc = pMapInfo->FindKey( "x360leaderboard" );
|
||||
if ( !pLbDesc )
|
||||
return NULL;
|
||||
|
||||
// Insert the required keys
|
||||
pLbDesc = pLbDesc->MakeCopy();
|
||||
|
||||
static KeyValues *s_pRatingKey = KeyValues::FromString( ":rating", // X360 leaderboards are rated
|
||||
" name besttime " // game name of the rating field is "besttime"
|
||||
" type uint64 " // type is uint64
|
||||
" rule max" // rated field must be greater than cached value so that it can be written
|
||||
);
|
||||
pLbDesc->AddSubKey( s_pRatingKey->MakeCopy() );
|
||||
pLbDesc->SetString( "besttime/type", "uint64" );
|
||||
|
||||
return pLbDesc;
|
||||
}
|
||||
|
||||
if ( IsPC() )
|
||||
{
|
||||
KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
|
||||
" :score besttime " // :score is the leaderboard value mapped to game name "besttime"
|
||||
);
|
||||
|
||||
pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
|
||||
pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeTimeMilliSeconds ); // Note: this is actually 1/100th seconds type, Steam change pending
|
||||
pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodKeepBest ); // Upload method when writing to leaderboard
|
||||
|
||||
return pSettings;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
53
matchmaking/extkeyvalues.cpp
Normal file
53
matchmaking/extkeyvalues.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "extkeyvalues.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Rule evaluation
|
||||
//
|
||||
|
||||
static class CPropertyRule_Max : public IPropertyRule
|
||||
{
|
||||
public:
|
||||
virtual bool ApplyRuleUint64( uint64 uiBase, uint64 &uiNew )
|
||||
{
|
||||
bool bResult = ( uiNew > uiBase );
|
||||
uiNew = MAX( uiNew, uiBase );
|
||||
return bResult;
|
||||
}
|
||||
virtual bool ApplyRuleFloat( float flBase, float &flNew )
|
||||
{
|
||||
bool bResult = ( flNew > flBase );
|
||||
flNew = MAX( flNew, flBase );
|
||||
return bResult;
|
||||
}
|
||||
}
|
||||
g_PropertyRule_Max;
|
||||
|
||||
static class CPropertyRule_None : public IPropertyRule
|
||||
{
|
||||
public:
|
||||
virtual bool ApplyRuleUint64( uint64 uiBase, uint64 &uiNew ) { return true; }
|
||||
virtual bool ApplyRuleFloat( float flBase, float &flNew ) { return true; }
|
||||
}
|
||||
g_PropertyRule_None;
|
||||
|
||||
IPropertyRule * GetRuleByName( char const *szRuleName )
|
||||
{
|
||||
if ( !Q_stricmp( szRuleName, "max" ) )
|
||||
return &g_PropertyRule_Max;
|
||||
|
||||
return &g_PropertyRule_None;
|
||||
}
|
||||
68
matchmaking/extkeyvalues.h
Normal file
68
matchmaking/extkeyvalues.h
Normal file
@@ -0,0 +1,68 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef EXT_KEYVALUES_H
|
||||
#define EXT_KEYVALUES_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
#include "matchmaking/mm_helpers.h"
|
||||
|
||||
#define KeyValuesAddDefaultValue( kv, szKeyName, defValue, fnSet ) \
|
||||
( (kv)->FindKey( szKeyName ) ? (1) : ( (kv)->fnSet( szKeyName, defValue ), 1 ) )
|
||||
|
||||
#define KeyValuesAddDefaultString( kv, szKeyName, defValue ) \
|
||||
KeyValuesAddDefaultValue( kv, szKeyName, defValue, SetString )
|
||||
|
||||
|
||||
//
|
||||
// ContextValue_t allows for quick mapping from strings to title-defined values
|
||||
// in title specific code.
|
||||
//
|
||||
// static ContextValue_t values[] = {
|
||||
// { "=INGAME", CONTEXT_GAME_STATE_INGAME },
|
||||
// { "=INFINALE", CONTEXT_GAME_STATE_INFINALE },
|
||||
// { NULL, CONTEXT_GAME_STATE_INLOBBY },
|
||||
// };
|
||||
// SetAllUsersContext( CONTEXT_GAME_STATE, values->ScanValues( szValue ) );
|
||||
|
||||
struct ContextValue_t
|
||||
{
|
||||
char const *m_szValue;
|
||||
unsigned int m_dwValue;
|
||||
|
||||
inline unsigned int ScanValues( char const *szValue )
|
||||
{
|
||||
ContextValue_t const *p = this;
|
||||
for ( ; p->m_szValue; ++ p )
|
||||
{
|
||||
if ( !Q_stricmp( p->m_szValue, szValue ) )
|
||||
break;
|
||||
}
|
||||
return p->m_dwValue;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Rule evaluation
|
||||
//
|
||||
|
||||
class IPropertyRule
|
||||
{
|
||||
public:
|
||||
virtual bool ApplyRuleUint64( uint64 uiBase, uint64 &uiNew ) = 0;
|
||||
virtual bool ApplyRuleFloat( float flBase, float &flNew ) = 0;
|
||||
};
|
||||
|
||||
IPropertyRule * GetRuleByName( char const *szRuleName );
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
169
matchmaking/game.cpp
Normal file
169
matchmaking/game.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
|
||||
#include "game.h"
|
||||
#include "gamemanager.h"
|
||||
#include "matchsystem.h"
|
||||
#include "playermanager.h"
|
||||
#include "matchmaking.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
CMatchGame::CMatchGame()
|
||||
{
|
||||
}
|
||||
|
||||
CMatchGame::~CMatchGame()
|
||||
{
|
||||
}
|
||||
|
||||
void CMatchGame::Reset()
|
||||
{
|
||||
RemoveAllPlayers();
|
||||
}
|
||||
|
||||
XNKID CMatchGame::GetXNKID()
|
||||
{
|
||||
return (XNKID) g_pMatchmaking->GetSessionXNKID();
|
||||
}
|
||||
|
||||
int CMatchGame::GetNumPlayers()
|
||||
{
|
||||
return m_PlayerList.Count();
|
||||
}
|
||||
|
||||
IPlayer * CMatchGame::GetPlayer( int index )
|
||||
{
|
||||
if ( m_PlayerList.IsValidIndex( index ) )
|
||||
return m_PlayerList[index];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CMatchGame::AddPlayer( XUID xuid, int pExperience[ MATCH_MAX_DIFFICULTIES ] /*= NULL*/, int pSkills[ MATCH_MAX_DIFFICULTIES ][ MATCH_MAX_SKILL_FIELDS ] /*= NULL*/ )
|
||||
{
|
||||
if ( MM_IsDebug() )
|
||||
{
|
||||
Msg( "[L4DMM] CMatchGame::AddPlayer( %llx )\n", xuid );
|
||||
}
|
||||
|
||||
IPlayer * player = g_pPlayerManager->FindPlayer( xuid );
|
||||
if ( !player )
|
||||
{
|
||||
Warning( "[L4DMM] CMatchGame::AddPlayer - Cannot find player by %llx!\n", xuid );
|
||||
return;
|
||||
}
|
||||
|
||||
// Find a player first
|
||||
for ( int k = 0; k < m_PlayerList.Count(); ++ k )
|
||||
{
|
||||
if( m_PlayerList[k]->GetXUID() == xuid )
|
||||
{
|
||||
Warning( "[L4DMM] CMatchGame::AddPlayer - player %llx already added!\n", xuid );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_PlayerList.AddToTail( player );
|
||||
}
|
||||
|
||||
void CMatchGame::RemovePlayer( XUID xuid )
|
||||
{
|
||||
if ( MM_IsDebug() )
|
||||
{
|
||||
Msg( "[L4DMM] CMatchGame::RemovePlayer( %llx )\n", xuid );
|
||||
}
|
||||
|
||||
int numRemoved = 0;
|
||||
for ( int k = 0; k < m_PlayerList.Count(); ++ k )
|
||||
{
|
||||
if( m_PlayerList[k]->GetXUID() == xuid )
|
||||
{
|
||||
m_PlayerList.Remove( k );
|
||||
++ numRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !numRemoved )
|
||||
{
|
||||
Warning( "[L4DMM] CMatchGame::RemovePlayer - Cannot find player by %llx!\n", xuid );
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchGame::RemoveAllPlayers( )
|
||||
{
|
||||
if ( MM_IsDebug() )
|
||||
{
|
||||
Msg( "[L4DMM] CMatchGame::RemoveAllPlayers\n" );
|
||||
}
|
||||
|
||||
m_PlayerList.RemoveAll();
|
||||
}
|
||||
|
||||
int CMatchGame::GetAggregateExperience( )
|
||||
{
|
||||
int difficulty = 1; // CMatchSystem::GetMatchSystem()->GetManager()->GameGetDifficulty();
|
||||
|
||||
double exp = 0;
|
||||
int numExp = 0;
|
||||
|
||||
int numPlayers = m_PlayerList.Count();
|
||||
|
||||
for( int index = 0; index < numPlayers; ++index )
|
||||
{
|
||||
IPlayer * player = GetPlayer( index );
|
||||
if( player )
|
||||
{
|
||||
// TODO: exp += player->GetExperience( difficulty );
|
||||
++ numExp;
|
||||
}
|
||||
}
|
||||
|
||||
if( numExp )
|
||||
exp /= numExp;
|
||||
|
||||
if ( MM_IsDebug() >= 2 )
|
||||
{
|
||||
Msg( "[L4DMM] Game aggregate experience on difficulty%d = %f out of %d exp players.\n", difficulty, exp, numExp );
|
||||
}
|
||||
|
||||
return exp;
|
||||
}
|
||||
|
||||
int CMatchGame::GetAggregateSkill( int iSkillType )
|
||||
{
|
||||
int difficulty = 1; // CMatchSystem::GetMatchSystem()->GetManager()->GameGetDifficulty();
|
||||
|
||||
double skill = 0;
|
||||
int numSkills = 0;
|
||||
|
||||
int numPlayers = m_PlayerList.Count();
|
||||
|
||||
for( int index = 0; index < numPlayers; ++index )
|
||||
{
|
||||
IPlayer * player = GetPlayer( index );
|
||||
if( player )
|
||||
{
|
||||
// TODO: skill += player->GetSkill( iSkillType, difficulty );
|
||||
++ numSkills;
|
||||
}
|
||||
}
|
||||
|
||||
if( numSkills )
|
||||
skill /= numSkills;
|
||||
|
||||
if ( MM_IsDebug() >= 2 )
|
||||
{
|
||||
Msg( "[L4DMM] Game aggregate skill%d on difficulty%d = %f out of %d skill players.\n", iSkillType, difficulty, skill, numSkills );
|
||||
}
|
||||
|
||||
return skill;
|
||||
}
|
||||
|
||||
|
||||
50
matchmaking/game.h
Normal file
50
matchmaking/game.h
Normal file
@@ -0,0 +1,50 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
|
||||
#ifndef _GAME_H_
|
||||
#define _GAME_H_
|
||||
|
||||
#include "mm_framework.h"
|
||||
#include "matchmaking/igame.h"
|
||||
#include "utlvector.h"
|
||||
|
||||
//In an idea world game is manageing all of the aspects of the game session, however in our case
|
||||
//it will serve as an interface into the engine and matchmaking to get the information we need.
|
||||
class CMatchGame : public IMatchGame
|
||||
{
|
||||
public:
|
||||
CMatchGame();
|
||||
|
||||
~CMatchGame();
|
||||
|
||||
//IMatchGame
|
||||
|
||||
virtual void Reset();
|
||||
|
||||
virtual XNKID GetXNKID();
|
||||
|
||||
virtual int GetNumPlayers();
|
||||
|
||||
virtual IPlayer * GetPlayer(int index);
|
||||
|
||||
virtual void AddPlayer( XUID xuid, int pExperience[ MATCH_MAX_DIFFICULTIES ] = NULL, int pSkills[ MATCH_MAX_DIFFICULTIES ][ MATCH_MAX_SKILL_FIELDS ] = NULL );
|
||||
|
||||
virtual void RemovePlayer( XUID xuid );
|
||||
|
||||
virtual void RemoveAllPlayers( );
|
||||
|
||||
virtual int GetAggregateExperience( );
|
||||
|
||||
virtual int GetAggregateSkill( int iSkillType );
|
||||
|
||||
private:
|
||||
|
||||
typedef CUtlVector< IPlayer * > IPlayerList_t;
|
||||
IPlayerList_t m_PlayerList;
|
||||
};
|
||||
|
||||
#endif
|
||||
540
matchmaking/leaderboards.cpp
Normal file
540
matchmaking/leaderboards.cpp
Normal file
@@ -0,0 +1,540 @@
|
||||
//========= Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
#include "leaderboards.h"
|
||||
|
||||
#include "fmtstr.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//
|
||||
// Definition of leaderboard request queue class
|
||||
//
|
||||
|
||||
class CLeaderboardRequestQueue : public ILeaderboardRequestQueue
|
||||
{
|
||||
public:
|
||||
CLeaderboardRequestQueue();
|
||||
~CLeaderboardRequestQueue();
|
||||
|
||||
// ILeaderboardRequestQueue
|
||||
public:
|
||||
virtual void Request( KeyValues *pRequest );
|
||||
virtual void Update();
|
||||
|
||||
public:
|
||||
KeyValues * GetFinishedRequest();
|
||||
|
||||
protected:
|
||||
CUtlVector< KeyValues * > m_arrRequests;
|
||||
bool m_bQueryRunning;
|
||||
KeyValues *m_pFinishedRequest;
|
||||
|
||||
void OnStartNewQuery();
|
||||
void OnSubmitQuery();
|
||||
void OnQueryFinished();
|
||||
void Cleanup();
|
||||
|
||||
protected:
|
||||
#ifdef _X360
|
||||
|
||||
// Leaderboard query data
|
||||
CUtlVector< XUID > m_arrXuids;
|
||||
CUtlVector< XUSER_STATS_SPEC > m_arrSpecs;
|
||||
CUtlBuffer m_bufResults;
|
||||
XOVERLAPPED m_xOverlapped;
|
||||
|
||||
void ProcessResults( XUSER_STATS_READ_RESULTS const *pResults );
|
||||
|
||||
// Leaderboard description data
|
||||
CUtlVector< KeyValues * > m_arrViewDescriptions;
|
||||
|
||||
KeyValues * FindViewDescription( DWORD dwViewId );
|
||||
|
||||
#elif !defined( NO_STEAM )
|
||||
|
||||
KeyValues *m_pViewDescription;
|
||||
|
||||
CCallResult< CLeaderboardRequestQueue, LeaderboardFindResult_t > m_CallbackOnLeaderboardFindResult;
|
||||
void Steam_OnLeaderboardFindResult( LeaderboardFindResult_t *p, bool bError );
|
||||
|
||||
CCallResult< CLeaderboardRequestQueue, LeaderboardScoresDownloaded_t > m_CallbackOnLeaderboardScoresDownloaded;
|
||||
void Steam_OnLeaderboardScoresDownloaded( LeaderboardScoresDownloaded_t *p, bool bError );
|
||||
|
||||
void ProcessResults( LeaderboardEntry_t const &lbe );
|
||||
|
||||
#endif
|
||||
};
|
||||
CLeaderboardRequestQueue g_LeaderboardRequestQueue;
|
||||
ILeaderboardRequestQueue *g_pLeaderboardRequestQueue = &g_LeaderboardRequestQueue;
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Implementation of leaderboard request queue class
|
||||
//
|
||||
|
||||
CLeaderboardRequestQueue::CLeaderboardRequestQueue() :
|
||||
m_bQueryRunning( false ),
|
||||
#if !defined( _X360 ) && !defined( NO_STEAM )
|
||||
m_pViewDescription( NULL ),
|
||||
#endif
|
||||
m_pFinishedRequest( NULL )
|
||||
{
|
||||
}
|
||||
|
||||
CLeaderboardRequestQueue::~CLeaderboardRequestQueue()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
void CLeaderboardRequestQueue::Request( KeyValues *pRequest )
|
||||
{
|
||||
if ( !pRequest )
|
||||
return;
|
||||
|
||||
DevMsg( "CLeaderboardRequestQueue::Request\n" );
|
||||
KeyValuesDumpAsDevMsg( pRequest, 1 );
|
||||
|
||||
m_arrRequests.AddToTail( pRequest->MakeCopy() );
|
||||
}
|
||||
|
||||
void CLeaderboardRequestQueue::Update()
|
||||
{
|
||||
if ( m_bQueryRunning )
|
||||
{
|
||||
#ifdef _X360
|
||||
if ( XHasOverlappedIoCompleted( &m_xOverlapped ) )
|
||||
{
|
||||
OnQueryFinished();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if ( m_arrRequests.Count() )
|
||||
{
|
||||
OnStartNewQuery();
|
||||
}
|
||||
else if ( m_arrRequests.NumAllocated() )
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
KeyValues * CLeaderboardRequestQueue::GetFinishedRequest()
|
||||
{
|
||||
return m_pFinishedRequest;
|
||||
}
|
||||
|
||||
void CLeaderboardRequestQueue::OnQueryFinished()
|
||||
{
|
||||
#ifdef _X360
|
||||
// Submit results for processing
|
||||
XUSER_STATS_READ_RESULTS const *pResults = ( XUSER_STATS_READ_RESULTS const * ) m_bufResults.Base();
|
||||
if ( pResults && pResults->dwNumViews > 0 && pResults->pViews )
|
||||
ProcessResults( pResults );
|
||||
#endif
|
||||
|
||||
// Query is no longer running
|
||||
m_bQueryRunning = false;
|
||||
|
||||
DevMsg( "CLeaderboardRequestQueue::OnQueryFinished\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pFinishedRequest, 1 );
|
||||
|
||||
// Stuff the data into the players
|
||||
#ifdef _X360
|
||||
for ( int iCtrlr = 0; iCtrlr < XUSER_MAX_COUNT; ++ iCtrlr )
|
||||
{
|
||||
XUID xuid = 0;
|
||||
XUSER_SIGNIN_INFO xsi;
|
||||
if ( XUserGetSigninState( iCtrlr ) != eXUserSigninState_NotSignedIn &&
|
||||
ERROR_SUCCESS == XUserGetSigninInfo( iCtrlr, XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) &&
|
||||
!(xsi.dwInfoFlags & XUSER_INFO_FLAG_GUEST) )
|
||||
xuid = xsi.xuid;
|
||||
#else
|
||||
int iCtrlr = XBX_GetPrimaryUserId();
|
||||
{
|
||||
XUID xuid = g_pPlayerManager->GetLocalPlayer( iCtrlr )->GetXUID();
|
||||
#endif
|
||||
|
||||
KeyValues *pUserViews = NULL;
|
||||
if ( xuid )
|
||||
pUserViews = m_pFinishedRequest->FindKey( CFmtStr( "%llx", xuid ) );
|
||||
|
||||
if ( pUserViews )
|
||||
{
|
||||
IPlayerLocal *pPlayer = g_pPlayerManager->GetLocalPlayer( iCtrlr );
|
||||
if ( pPlayer )
|
||||
(( PlayerLocal * ) pPlayer)->OnLeaderboardRequestFinished( pUserViews );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void CLeaderboardRequestQueue::Cleanup()
|
||||
{
|
||||
#ifdef _X360
|
||||
// Clear view descriptions
|
||||
while ( m_arrViewDescriptions.Count() )
|
||||
{
|
||||
m_arrViewDescriptions.Head()->deleteThis();
|
||||
m_arrViewDescriptions.FastRemove( 0 );
|
||||
}
|
||||
|
||||
m_arrXuids.Purge();
|
||||
m_arrSpecs.Purge();
|
||||
m_bufResults.Purge();
|
||||
m_arrViewDescriptions.Purge();
|
||||
#elif !defined( NO_STEAM )
|
||||
if ( m_pViewDescription )
|
||||
m_pViewDescription->deleteThis();
|
||||
m_pViewDescription = NULL;
|
||||
#endif
|
||||
|
||||
// Clear requests
|
||||
while ( m_arrRequests.Count() )
|
||||
{
|
||||
m_arrRequests.Head()->deleteThis();
|
||||
m_arrRequests.FastRemove( 0 );
|
||||
}
|
||||
|
||||
m_arrRequests.Purge();
|
||||
|
||||
// Clear finished result
|
||||
if ( m_pFinishedRequest )
|
||||
m_pFinishedRequest->deleteThis();
|
||||
m_pFinishedRequest = NULL;
|
||||
}
|
||||
|
||||
void CLeaderboardRequestQueue::OnStartNewQuery()
|
||||
{
|
||||
// When we are starting a new query we need to get rid of the old finished result
|
||||
if ( m_pFinishedRequest )
|
||||
m_pFinishedRequest->deleteThis();
|
||||
m_pFinishedRequest = NULL;
|
||||
|
||||
#if !defined( NO_STEAM ) && !defined( SWDS )
|
||||
extern CInterlockedInt g_numSteamLeaderboardWriters;
|
||||
if ( g_numSteamLeaderboardWriters )
|
||||
return; // yield to writers that can alter the leaderboard
|
||||
#endif
|
||||
|
||||
DevMsg( "CLeaderboardRequestQueue::OnStartNewQuery preparing request...\n" );
|
||||
|
||||
#ifdef _X360
|
||||
// Prepare the XUIDs first
|
||||
m_arrXuids.RemoveAll();
|
||||
for ( DWORD k = 0; k < XBX_GetNumGameUsers(); ++ k )
|
||||
{
|
||||
int iCtrlr = XBX_GetUserId( k );
|
||||
XUSER_SIGNIN_INFO xsi;
|
||||
if ( XUserGetSigninState( iCtrlr ) != eXUserSigninState_NotSignedIn &&
|
||||
ERROR_SUCCESS == XUserGetSigninInfo( iCtrlr, XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) &&
|
||||
!(xsi.dwInfoFlags & XUSER_INFO_FLAG_GUEST) )
|
||||
{
|
||||
m_arrXuids.AddToTail( xsi.xuid );
|
||||
DevMsg( " XUID: %s (%llx)\n", xsi.szUserName, xsi.xuid );
|
||||
}
|
||||
}
|
||||
|
||||
// Clear view descriptions
|
||||
while ( m_arrViewDescriptions.Count() )
|
||||
{
|
||||
m_arrViewDescriptions.Head()->deleteThis();
|
||||
m_arrViewDescriptions.FastRemove( 0 );
|
||||
}
|
||||
|
||||
// Prepare the spec
|
||||
m_arrSpecs.RemoveAll();
|
||||
for ( int q = 0; q < m_arrRequests.Count(); ++ q )
|
||||
{
|
||||
KeyValues *pRequest = m_arrRequests[q];
|
||||
KeyValues::AutoDelete autodelete_pRequest( pRequest );
|
||||
m_arrRequests.Remove( q -- );
|
||||
|
||||
char const *szViewName = pRequest->GetName();
|
||||
|
||||
KeyValues *pDescription = g_pMMF->GetMatchTitle()->DescribeTitleLeaderboard( szViewName );
|
||||
if ( !pDescription )
|
||||
{
|
||||
DevWarning( " View %s failed to allocate description!\n", szViewName );
|
||||
}
|
||||
KeyValues::AutoDelete autodelete_pDescription( pDescription );
|
||||
|
||||
// See if we already have a request for this view
|
||||
DWORD dwViewId = pDescription->GetInt( ":id" );
|
||||
for ( int k = 0; k < m_arrSpecs.Count(); ++ k )
|
||||
{
|
||||
if ( m_arrSpecs[k].dwViewId == dwViewId )
|
||||
{
|
||||
dwViewId = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we already have a request for this view, then continue
|
||||
if ( !dwViewId )
|
||||
continue;
|
||||
|
||||
// Otherwise add this view to the spec
|
||||
XUSER_STATS_SPEC xss = {0};
|
||||
xss.dwViewId = dwViewId;
|
||||
Assert( !xss.dwNumColumnIds );
|
||||
|
||||
m_arrSpecs.AddToTail( xss );
|
||||
pDescription->SetString( ":name", szViewName );
|
||||
m_arrViewDescriptions.AddToTail( pDescription );
|
||||
autodelete_pDescription.Assign( NULL ); // don't autodelete now
|
||||
|
||||
DevMsg( " View: %s (%d)\n", szViewName, dwViewId );
|
||||
}
|
||||
#elif !defined( NO_STEAM )
|
||||
// Clear view descriptions
|
||||
if ( m_pViewDescription )
|
||||
m_pViewDescription->deleteThis();
|
||||
m_pViewDescription = NULL;
|
||||
|
||||
for ( int q = 0; q < m_arrRequests.Count(); ++ q )
|
||||
{
|
||||
KeyValues *pRequest = m_arrRequests[q];
|
||||
KeyValues::AutoDelete autodelete_pRequest( pRequest );
|
||||
m_arrRequests.Remove( q -- );
|
||||
|
||||
char const *szViewName = pRequest->GetName();
|
||||
|
||||
m_pViewDescription = g_pMMF->GetMatchTitle()->DescribeTitleLeaderboard( szViewName );
|
||||
if ( !m_pViewDescription )
|
||||
{
|
||||
DevWarning( " View %s failed to allocate description!\n", szViewName );
|
||||
continue;
|
||||
}
|
||||
|
||||
m_pViewDescription->SetString( ":name", szViewName );
|
||||
|
||||
SteamAPICall_t hCall = steamapicontext->SteamUserStats()->FindLeaderboard( szViewName );
|
||||
m_CallbackOnLeaderboardFindResult.Set( hCall, this, &CLeaderboardRequestQueue::Steam_OnLeaderboardFindResult );
|
||||
m_bQueryRunning = true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Clean up all the requests in the queue
|
||||
DevMsg( "CLeaderboardRequestQueue::OnStartNewQuery - request prepared.\n" );
|
||||
|
||||
// Run the query
|
||||
OnSubmitQuery();
|
||||
}
|
||||
|
||||
void CLeaderboardRequestQueue::OnSubmitQuery()
|
||||
{
|
||||
#ifdef _X360
|
||||
if ( m_arrXuids.Count() && m_arrSpecs.Count() )
|
||||
{
|
||||
DWORD dwBytes = 0;
|
||||
DWORD ret = XUserReadStats( 0,
|
||||
m_arrXuids.Count(), m_arrXuids.Base(),
|
||||
m_arrSpecs.Count(), m_arrSpecs.Base(),
|
||||
&dwBytes, NULL, NULL );
|
||||
|
||||
if ( ret == ERROR_INSUFFICIENT_BUFFER )
|
||||
{
|
||||
ZeroMemory( &m_xOverlapped, sizeof( m_xOverlapped ) );
|
||||
m_bufResults.EnsureCapacity( dwBytes );
|
||||
|
||||
ret = XUserReadStats( 0,
|
||||
m_arrXuids.Count(), m_arrXuids.Base(),
|
||||
m_arrSpecs.Count(), m_arrSpecs.Base(),
|
||||
&dwBytes, ( PXUSER_STATS_READ_RESULTS ) m_bufResults.Base(),
|
||||
&m_xOverlapped );
|
||||
}
|
||||
|
||||
if ( ret == ERROR_IO_PENDING )
|
||||
{
|
||||
DevMsg( "CLeaderboardRequestQueue::OnSubmitQuery - query submitted...\n" );
|
||||
m_bQueryRunning = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DevWarning( "CLeaderboardRequestQueue::OnSubmitQuery - failed [code = %d, bytes = %d]\n", ret, dwBytes );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _X360
|
||||
void CLeaderboardRequestQueue::ProcessResults( XUSER_STATS_READ_RESULTS const *pResults )
|
||||
{
|
||||
/*
|
||||
901D41D61DC61 // XUID %llx
|
||||
{
|
||||
survival_c5m2_park
|
||||
{
|
||||
:rank = 923 // uint64
|
||||
:rows = 999 // uint64
|
||||
:rating = 600 // uint64
|
||||
besttime = 600 // uint64
|
||||
}
|
||||
... more views ...
|
||||
}
|
||||
... more users ...
|
||||
*/
|
||||
DevMsg( "LeaderboardRequestQueue: ProcessResults ( %d views )\n", pResults->dwNumViews );
|
||||
for ( DWORD k = 0; k < pResults->dwNumViews; ++ k )
|
||||
{
|
||||
XUSER_STATS_VIEW const *pView = &pResults->pViews[k];
|
||||
Assert( pView );
|
||||
|
||||
KeyValues *pViewDesc = FindViewDescription( pView->dwViewId );
|
||||
Assert( pViewDesc );
|
||||
if ( !pViewDesc )
|
||||
{
|
||||
Warning( "LeaderboardRequestQueue: ProcessResults has no view description for view %d!\n", pView->dwViewId );
|
||||
continue;
|
||||
}
|
||||
|
||||
char const *szViewName = pViewDesc->GetString( ":name" );
|
||||
DevMsg( " Processing view %d ( %s ), total rows = %d\n", pView->dwViewId, szViewName, pView->dwTotalViewRows );
|
||||
|
||||
for ( DWORD r = 0; r < pView->dwNumRows; ++ r )
|
||||
{
|
||||
XUSER_STATS_ROW const *pRow = &pView->pRows[r];
|
||||
if ( !pRow->dwRank && !pRow->i64Rating )
|
||||
{
|
||||
DevMsg( " Gamer %s (%llx) not in view\n", pRow->szGamertag, pRow->xuid );
|
||||
continue; // gamer is not present in the leaderboard
|
||||
}
|
||||
DevMsg( " Gamer %s (%llx) data loaded: rank=%d, rating=%lld\n",
|
||||
pRow->szGamertag, pRow->xuid, pRow->dwRank, pRow->i64Rating );
|
||||
|
||||
// Gamer is present in the leaderboard and should be included in the results
|
||||
if ( !m_pFinishedRequest )
|
||||
m_pFinishedRequest = new KeyValues( "Leaderboard" );
|
||||
|
||||
// Find or create the view for this gamer
|
||||
KeyValues *pUserInfo = m_pFinishedRequest->FindKey( CFmtStr( "%llx/%s", pRow->xuid, szViewName ), true );
|
||||
|
||||
// Set his rank and rating
|
||||
pUserInfo->SetUint64( ":rank", pRow->dwRank );
|
||||
pUserInfo->SetUint64( ":rows", pView->dwTotalViewRows );
|
||||
pUserInfo->SetUint64( ":rating", pRow->i64Rating );
|
||||
|
||||
if ( char const *szName = pViewDesc->GetString( ":rating/name", NULL ) )
|
||||
{
|
||||
if ( szName[0] )
|
||||
pUserInfo->SetUint64( szName, pRow->i64Rating );
|
||||
}
|
||||
|
||||
// Process additional columns that were retrieved
|
||||
Assert( !pRow->dwNumColumns );
|
||||
// for ( DWORD c = 0; c < pRow->dwNumColumns; ++ c )
|
||||
// {
|
||||
// XUSER_STATS_COLUMN const *pCol = pRow->pColumns[ c ];
|
||||
// KeyValues *pColDesc = FindViewColumnDesc( pViewDesc, pCol->wColumnId )
|
||||
// }
|
||||
}
|
||||
}
|
||||
DevMsg( "LeaderboardRequestQueue: ProcessResults finished.\n" );
|
||||
}
|
||||
#elif !defined( NO_STEAM )
|
||||
void CLeaderboardRequestQueue::ProcessResults( LeaderboardEntry_t const &lbe )
|
||||
{
|
||||
/*
|
||||
901D41D61DC61 // XUID %llx
|
||||
{
|
||||
survival_c5m2_park
|
||||
{
|
||||
:rank = 923 // uint64
|
||||
:rows = 999 // uint64
|
||||
:rating = 600 // uint64
|
||||
besttime = 600 // uint64
|
||||
}
|
||||
... more views ...
|
||||
}
|
||||
... more users ...
|
||||
*/
|
||||
KeyValues *pViewDesc = m_pViewDescription;
|
||||
Assert( pViewDesc );
|
||||
if ( !pViewDesc )
|
||||
{
|
||||
Warning( "LeaderboardRequestQueue: ProcessResults has no view description for view!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
char const *szViewName = pViewDesc->GetString( ":name" );
|
||||
DevMsg( " Processing view %s\n", szViewName );
|
||||
|
||||
DevMsg( " Gamer data loaded: rank=%d, score=%d\n",
|
||||
lbe.m_nGlobalRank, lbe.m_nScore );
|
||||
|
||||
// Gamer is present in the leaderboard and should be included in the results
|
||||
if ( !m_pFinishedRequest )
|
||||
m_pFinishedRequest = new KeyValues( "Leaderboard" );
|
||||
|
||||
// Find or create the view for this gamer
|
||||
KeyValues *pUserInfo = m_pFinishedRequest->FindKey( CFmtStr( "%llx/%s",
|
||||
g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID(), szViewName ), true );
|
||||
|
||||
// Set user score
|
||||
pUserInfo->SetUint64( pViewDesc->GetString( ":score" ), lbe.m_nScore );
|
||||
|
||||
DevMsg( "LeaderboardRequestQueue: ProcessResults finished.\n" );
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _X360
|
||||
|
||||
KeyValues * CLeaderboardRequestQueue::FindViewDescription( DWORD dwViewId )
|
||||
{
|
||||
if ( !dwViewId )
|
||||
return NULL;
|
||||
|
||||
for ( int k = 0; k < m_arrViewDescriptions.Count(); ++ k )
|
||||
{
|
||||
KeyValues *pDesc = m_arrViewDescriptions[k];
|
||||
if ( pDesc->GetInt( ":id" ) == ( int ) dwViewId )
|
||||
return pDesc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#elif !defined( NO_STEAM )
|
||||
|
||||
void CLeaderboardRequestQueue::Steam_OnLeaderboardFindResult( LeaderboardFindResult_t *p, bool bError )
|
||||
{
|
||||
if ( bError || !p->m_bLeaderboardFound )
|
||||
{
|
||||
DevMsg( "Steam leaderboard was not found.\n" );
|
||||
OnQueryFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
// Download the data
|
||||
SteamAPICall_t hCall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( p->m_hSteamLeaderboard,
|
||||
k_ELeaderboardDataRequestGlobalAroundUser, 0, 0 );
|
||||
m_CallbackOnLeaderboardScoresDownloaded.Set( hCall, this, &CLeaderboardRequestQueue::Steam_OnLeaderboardScoresDownloaded );
|
||||
}
|
||||
|
||||
void CLeaderboardRequestQueue::Steam_OnLeaderboardScoresDownloaded( LeaderboardScoresDownloaded_t *p, bool bError )
|
||||
{
|
||||
// Fetch the data if found and no error
|
||||
LeaderboardEntry_t lbe;
|
||||
if ( !bError &&
|
||||
p->m_cEntryCount == 1 &&
|
||||
steamapicontext->SteamUserStats()->GetDownloadedLeaderboardEntry( p->m_hSteamLeaderboardEntries, 0, &lbe, NULL, 0 ) )
|
||||
{
|
||||
ProcessResults( lbe );
|
||||
}
|
||||
|
||||
OnQueryFinished();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
21
matchmaking/leaderboards.h
Normal file
21
matchmaking/leaderboards.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//========= Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
|
||||
#ifndef _LEADERBOARDS_H_
|
||||
#define _LEADERBOARDS_H_
|
||||
|
||||
class ILeaderboardRequestQueue;
|
||||
extern class ILeaderboardRequestQueue *g_pLeaderboardRequestQueue;
|
||||
|
||||
class ILeaderboardRequestQueue
|
||||
{
|
||||
public:
|
||||
virtual void Request( KeyValues *pRequest ) = 0;
|
||||
virtual void Update() = 0;
|
||||
};
|
||||
|
||||
#endif // _LEADERBOARDS_H_
|
||||
194
matchmaking/main.cpp
Normal file
194
matchmaking/main.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
//====== Copyright <20> 1996-2007, Valve Corporation, All rights reserved. =======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef POSIX
|
||||
#include <conio.h>
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
// GCSDK uses the console log channel
|
||||
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_CONSOLE, "Console" );
|
||||
|
||||
void LinkMatchmakingLib()
|
||||
{
|
||||
// This function is required for the linker to include CMatchFramework
|
||||
}
|
||||
|
||||
static CMatchFramework g_MatchFramework;
|
||||
CMatchFramework *g_pMMF = &g_MatchFramework;
|
||||
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CMatchFramework, IMatchFramework,
|
||||
IMATCHFRAMEWORK_VERSION_STRING, g_MatchFramework );
|
||||
|
||||
//
|
||||
// IAppSystem implementation
|
||||
//
|
||||
|
||||
static CreateInterfaceFn s_pfnDelegateFactory;
|
||||
static void * InternalFactory( const char *pName, int *pReturnCode )
|
||||
{
|
||||
if ( pReturnCode )
|
||||
{
|
||||
*pReturnCode = IFACE_OK;
|
||||
}
|
||||
|
||||
// Try to get interface via delegate
|
||||
if ( void *pInterface = s_pfnDelegateFactory ? s_pfnDelegateFactory( pName, pReturnCode ) : NULL )
|
||||
{
|
||||
return pInterface;
|
||||
}
|
||||
|
||||
// Try to get internal interface
|
||||
if ( void *pInterface = Sys_GetFactoryThis()( pName, pReturnCode ) )
|
||||
{
|
||||
return pInterface;
|
||||
}
|
||||
|
||||
// Failed
|
||||
if ( pReturnCode )
|
||||
{
|
||||
*pReturnCode = IFACE_FAILED;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
typedef void * (CMatchExtensions::* ExtFn_t)();
|
||||
|
||||
struct MatchExtInterface_t
|
||||
{
|
||||
char const *m_szName;
|
||||
ExtFn_t m_pfnGetInterface;
|
||||
bool m_bConnected;
|
||||
};
|
||||
|
||||
static MatchExtInterface_t s_table[] =
|
||||
{
|
||||
{ LOCALIZE_INTERFACE_VERSION, (ExtFn_t) &CMatchExtensions::GetILocalize, false },
|
||||
{ INETSUPPORT_VERSION_STRING, (ExtFn_t) &CMatchExtensions::GetINetSupport, false },
|
||||
{ IENGINEVOICE_INTERFACE_VERSION, (ExtFn_t) &CMatchExtensions::GetIEngineVoice, false },
|
||||
{ VENGINE_CLIENT_INTERFACE_VERSION, (ExtFn_t) &CMatchExtensions::GetIVEngineClient, false },
|
||||
{ INTERFACEVERSION_VENGINESERVER, (ExtFn_t) &CMatchExtensions::GetIVEngineServer, false },
|
||||
{ INTERFACEVERSION_GAMEEVENTSMANAGER2, (ExtFn_t) &CMatchExtensions::GetIGameEventManager2, false },
|
||||
#ifdef _X360
|
||||
{ XBOXSYSTEM_INTERFACE_VERSION, (ExtFn_t) &CMatchExtensions::GetIXboxSystem, false },
|
||||
{ XONLINE_INTERFACE_VERSION, (ExtFn_t) &CMatchExtensions::GetIXOnline, false },
|
||||
#endif
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
bool CMatchFramework::Connect( CreateInterfaceFn factory )
|
||||
{
|
||||
Assert( !s_pfnDelegateFactory );
|
||||
|
||||
s_pfnDelegateFactory = factory;
|
||||
|
||||
CreateInterfaceFn ourFactory = InternalFactory;
|
||||
ConnectTier1Libraries( &ourFactory, 1 );
|
||||
ConnectTier2Libraries( &ourFactory, 1 );
|
||||
ConVar_Register();
|
||||
|
||||
// Get our extension interfaces
|
||||
for ( MatchExtInterface_t *ptr = s_table; ptr->m_szName; ++ ptr )
|
||||
{
|
||||
if ( !ptr->m_bConnected
|
||||
// && !(g_pMatchExtensions->*(ptr->m_pfnGetInterface))()
|
||||
)
|
||||
{
|
||||
void *pvInterface = ourFactory( ptr->m_szName, NULL );
|
||||
if ( pvInterface )
|
||||
{
|
||||
g_pMatchExtensions->RegisterExtensionInterface( ptr->m_szName, pvInterface );
|
||||
ptr->m_bConnected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s_pfnDelegateFactory = NULL;
|
||||
|
||||
MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
|
||||
|
||||
SteamApiContext_Init();
|
||||
|
||||
#if !defined( _GAMECONSOLE ) && !defined( SWDS )
|
||||
// Trigger intialization from Steam users
|
||||
if ( g_pPlayerManager )
|
||||
g_pPlayerManager->OnGameUsersChanged();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMatchFramework::Disconnect()
|
||||
{
|
||||
SteamApiContext_Shutdown();
|
||||
|
||||
for ( MatchExtInterface_t *ptr = s_table; ptr->m_szName; ++ ptr )
|
||||
{
|
||||
if ( ptr->m_bConnected )
|
||||
{
|
||||
void *pvInterface = (g_pMatchExtensions->*(ptr->m_pfnGetInterface))();
|
||||
Assert( pvInterface );
|
||||
g_pMatchExtensions->UnregisterExtensionInterface( ptr->m_szName, pvInterface );
|
||||
ptr->m_bConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
DisconnectTier2Libraries();
|
||||
ConVar_Unregister();
|
||||
DisconnectTier1Libraries();
|
||||
}
|
||||
|
||||
void * CMatchFramework::QueryInterface( const char *pInterfaceName )
|
||||
{
|
||||
if ( !Q_stricmp( pInterfaceName, IMATCHFRAMEWORK_VERSION_STRING ) )
|
||||
return static_cast< IMatchFramework* >( this );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *COM_GetModDirectory()
|
||||
{
|
||||
static char modDir[MAX_PATH];
|
||||
if ( Q_strlen( modDir ) == 0 )
|
||||
{
|
||||
const char *gamedir = CommandLine()->ParmValue("-game", CommandLine()->ParmValue( "-defaultgamedir", "hl2" ) );
|
||||
Q_strncpy( modDir, gamedir, sizeof(modDir) );
|
||||
if ( strchr( modDir, '/' ) || strchr( modDir, '\\' ) )
|
||||
{
|
||||
Q_StripLastDir( modDir, sizeof(modDir) );
|
||||
int dirlen = Q_strlen( modDir );
|
||||
Q_strncpy( modDir, gamedir + dirlen, sizeof(modDir) - dirlen );
|
||||
}
|
||||
}
|
||||
|
||||
return modDir;
|
||||
}
|
||||
|
||||
bool IsLocalClientConnectedToServer()
|
||||
{
|
||||
return
|
||||
( g_pMatchExtensions &&
|
||||
g_pMatchExtensions->GetIVEngineClient() &&
|
||||
( g_pMatchExtensions->GetIVEngineClient()->IsConnected() ||
|
||||
g_pMatchExtensions->GetIVEngineClient()->IsDrawingLoadingImage() ||
|
||||
g_pMatchExtensions->GetIVEngineClient()->IsTransitioningToLoad() ) );
|
||||
}
|
||||
794
matchmaking/match_searcher.cpp
Normal file
794
matchmaking/match_searcher.cpp
Normal file
@@ -0,0 +1,794 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
#include "vstdlib/random.h"
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
static ConVar mm_session_search_num_results( "mm_session_search_num_results", "10", FCVAR_DEVELOPMENTONLY );
|
||||
static ConVar mm_session_search_distance( "mm_session_search_distance", "1", FCVAR_DEVELOPMENTONLY );
|
||||
ConVar mm_session_search_qos_timeout( "mm_session_search_qos_timeout", "15.0", FCVAR_RELEASE );
|
||||
// static ConVar mm_session_search_ping_limit( "mm_session_search_ping_limit", "200", FCVAR_RELEASE ); -- removed, using mm_dedicated_search_maxping instead
|
||||
static ConVar mm_session_search_ping_buckets( "mm_session_search_ping_buckets", "4", FCVAR_RELEASE );
|
||||
|
||||
CMatchSearcher::CMatchSearcher( KeyValues *pSettings ) :
|
||||
m_pSettings( pSettings ), // takes ownership
|
||||
m_autodelete_pSettings( m_pSettings ),
|
||||
m_pSessionSearchTree( NULL ),
|
||||
m_autodelete_pSessionSearchTree( m_pSessionSearchTree ),
|
||||
m_pSearchPass( NULL ),
|
||||
m_eState( STATE_INIT )
|
||||
{
|
||||
#ifdef _X360
|
||||
ZeroMemory( &m_xOverlapped, sizeof( m_xOverlapped ) );
|
||||
m_pQosResults = NULL;
|
||||
m_pCancelOverlappedJob = NULL;
|
||||
#endif
|
||||
|
||||
DevMsg( "Created CMatchSearcher:\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
|
||||
InitializeSettings();
|
||||
}
|
||||
|
||||
CMatchSearcher::~CMatchSearcher()
|
||||
{
|
||||
DevMsg( "Destroying CMatchSearcher:\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
|
||||
// Free all retrieved game details
|
||||
CUtlVector< SearchResult_t > *arrResults[] = { &m_arrSearchResults, &m_arrSearchResultsAggregate };
|
||||
for ( int ia = 0; ia < ARRAYSIZE( arrResults ); ++ ia )
|
||||
{
|
||||
for ( int k = 0; k < arrResults[ia]->Count(); ++ k )
|
||||
{
|
||||
SearchResult_t &sr = arrResults[ia]->Element( k );
|
||||
if ( sr.m_pGameDetails )
|
||||
{
|
||||
sr.m_pGameDetails->deleteThis();
|
||||
sr.m_pGameDetails = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSearcher::InitializeSettings()
|
||||
{
|
||||
// Initialize only the settings required to connect...
|
||||
|
||||
if ( KeyValues *kv = m_pSettings->FindKey( "system", true ) )
|
||||
{
|
||||
KeyValuesAddDefaultString( kv, "network", "LIVE" );
|
||||
KeyValuesAddDefaultString( kv, "access", "public" );
|
||||
}
|
||||
|
||||
if ( KeyValues *pMembers = m_pSettings->FindKey( "members", true ) )
|
||||
{
|
||||
int numMachines = pMembers->GetInt( "numMachines", -1 );
|
||||
if ( numMachines == -1 )
|
||||
{
|
||||
numMachines = 1;
|
||||
pMembers->SetInt( "numMachines", numMachines );
|
||||
}
|
||||
|
||||
int numPlayers = pMembers->GetInt( "numPlayers", -1 );
|
||||
if ( numPlayers == -1 )
|
||||
{
|
||||
numPlayers = 1;
|
||||
#ifdef _GAMECONSOLE
|
||||
numPlayers = XBX_GetNumGameUsers();
|
||||
#endif
|
||||
pMembers->SetInt( "numPlayers", numPlayers );
|
||||
}
|
||||
|
||||
pMembers->SetInt( "numSlots", numPlayers );
|
||||
|
||||
KeyValues *pMachine = pMembers->FindKey( "machine0");
|
||||
|
||||
if ( !pMachine )
|
||||
{
|
||||
pMachine = pMembers->CreateNewKey();
|
||||
pMachine->SetName( "machine0" );
|
||||
|
||||
XUID machineid = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
|
||||
|
||||
pMachine->SetUint64( "id", machineid );
|
||||
pMachine->SetUint64( "flags", MatchSession_GetMachineFlags() );
|
||||
pMachine->SetInt( "numPlayers", numPlayers );
|
||||
pMachine->SetUint64( "dlcmask", g_pMatchFramework->GetMatchSystem()->GetDlcManager()->GetDataInfo()->GetUint64( "@info/installed" ) );
|
||||
pMachine->SetString( "tuver", MatchSession_GetTuInstalledString() );
|
||||
pMachine->SetInt( "ping", 0 );
|
||||
|
||||
for ( int k = 0; k < numPlayers; ++ k )
|
||||
{
|
||||
if ( KeyValues *pPlayer = pMachine->FindKey( CFmtStr( "player%d", k ), true ) )
|
||||
{
|
||||
int iController = 0;
|
||||
#ifdef _GAMECONSOLE
|
||||
iController = XBX_GetUserId( k );
|
||||
#endif
|
||||
IPlayerLocal *player = g_pPlayerManager->GetLocalPlayer( iController );
|
||||
|
||||
pPlayer->SetUint64( "xuid", player->GetXUID() );
|
||||
pPlayer->SetString( "name", player->GetName() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DevMsg( "CMatchSearcher::InitializeGameSettings adjusted settings:\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
}
|
||||
|
||||
KeyValues * CMatchSearcher::GetSearchSettings()
|
||||
{
|
||||
return m_pSettings;
|
||||
}
|
||||
|
||||
void CMatchSearcher::Destroy()
|
||||
{
|
||||
// Stop the search
|
||||
if ( m_eState == STATE_SEARCHING )
|
||||
{
|
||||
#ifdef _X360
|
||||
m_pCancelOverlappedJob = ThreadExecute( MMX360_CancelOverlapped, &m_xOverlapped ); // UpdateDormantOperations will clean the rest
|
||||
MMX360_RegisterDormant( this );
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _X360
|
||||
if ( m_eState == STATE_CHECK_QOS )
|
||||
{
|
||||
g_pMatchExtensions->GetIXOnline()->XNetQosRelease( m_pQosResults );
|
||||
m_pQosResults = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined( NO_STEAM )
|
||||
while ( m_arrOutstandingAsyncOperation.Count() > 0 )
|
||||
{
|
||||
IMatchAsyncOperation *pAsyncOperation = m_arrOutstandingAsyncOperation.Tail();
|
||||
m_arrOutstandingAsyncOperation.RemoveMultipleFromTail( 1 );
|
||||
if ( pAsyncOperation )
|
||||
pAsyncOperation->Release();
|
||||
}
|
||||
#endif
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CMatchSearcher::OnSearchEvent( KeyValues *pNotify )
|
||||
{
|
||||
g_pMatchEventsSubscription->BroadcastEvent( pNotify );
|
||||
}
|
||||
|
||||
#ifdef _X360
|
||||
bool CMatchSearcher::UpdateDormantOperation()
|
||||
{
|
||||
if ( !m_pCancelOverlappedJob->IsFinished() )
|
||||
return true; // keep running dormant
|
||||
|
||||
m_pCancelOverlappedJob->Release();
|
||||
m_pCancelOverlappedJob = NULL;
|
||||
|
||||
delete this;
|
||||
return false; // destroyed object, remove from dormant list
|
||||
}
|
||||
#endif
|
||||
|
||||
void CMatchSearcher::Update()
|
||||
{
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_INIT:
|
||||
m_eState = STATE_SEARCHING;
|
||||
|
||||
// Session is searching
|
||||
OnSearchEvent( new KeyValues(
|
||||
"OnMatchSessionUpdate",
|
||||
"state", "progress",
|
||||
"progress", "searching"
|
||||
) );
|
||||
|
||||
// Kick off the search
|
||||
StartSearch();
|
||||
break;
|
||||
|
||||
case STATE_SEARCHING:
|
||||
// Waiting for session search to complete
|
||||
#ifdef _X360
|
||||
if ( XHasOverlappedIoCompleted( &m_xOverlapped ) )
|
||||
Live_OnSessionSearchCompleted();
|
||||
#endif
|
||||
break;
|
||||
|
||||
#ifdef _X360
|
||||
case STATE_CHECK_QOS:
|
||||
// Keep checking for results or until the wait time expires
|
||||
if ( Plat_FloatTime() > m_flQosTimeout ||
|
||||
!m_pQosResults->cxnqosPending )
|
||||
Live_OnQosCheckCompleted();
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined (NO_STEAM)
|
||||
case STATE_WAITING_LOBBY_DATA_AND_PING:
|
||||
{
|
||||
bool bWaitLonger = ( ( Plat_MSTime() - m_uiQosTimeoutStartMS ) < ( mm_session_search_qos_timeout.GetFloat() * 1000.f ) );
|
||||
if ( bWaitLonger )
|
||||
{
|
||||
bool bHasPendingLobbyData = false;
|
||||
for ( int j = 0; j < m_arrSearchResults.Count(); ++ j )
|
||||
{
|
||||
SearchResult_t &sr = m_arrSearchResults[j];
|
||||
if ( !sr.m_numPlayers )
|
||||
{ // didn't even receive lobby data from Steam yet
|
||||
bHasPendingLobbyData = true;
|
||||
}
|
||||
else if ( sr.m_svAdr.GetIPHostByteOrder() && ( sr.m_svPing <= 0 ) )
|
||||
{ // received valid IP for lobby, still pinging
|
||||
bHasPendingLobbyData = true;
|
||||
}
|
||||
}
|
||||
if ( !bHasPendingLobbyData )
|
||||
bWaitLonger = false;
|
||||
}
|
||||
if ( !bWaitLonger )
|
||||
{
|
||||
m_CallbackOnLobbyDataReceived.Unregister();
|
||||
|
||||
// Go ahead and start joining the results
|
||||
AggregateSearchPassResults();
|
||||
OnSearchPassDone( m_pSearchPass );
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined (NO_STEAM)
|
||||
void CMatchSearcher::Steam_OnLobbyDataReceived( LobbyDataUpdate_t *pLobbyDataUpdate )
|
||||
{
|
||||
int iResultIndex = 0;
|
||||
for ( ; iResultIndex < m_arrSearchResults.Count(); ++ iResultIndex )
|
||||
{
|
||||
if ( m_arrSearchResults[ iResultIndex ].m_uiLobbyId == pLobbyDataUpdate->m_ulSteamIDLobby )
|
||||
break;
|
||||
}
|
||||
if ( !m_arrSearchResults.IsValidIndex( iResultIndex ) )
|
||||
return;
|
||||
|
||||
SearchResult_t *pRes = &m_arrSearchResults[ iResultIndex ];
|
||||
|
||||
if ( !pLobbyDataUpdate->m_bSuccess )
|
||||
{
|
||||
DevMsg( "[MM] Could not get lobby data for lobby %llu (%llx)\n",
|
||||
pRes->m_uiLobbyId, pRes->m_uiLobbyId );
|
||||
pRes->m_numPlayers = -1; // set numPlayers to negative number to indicate a failure here
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get num players
|
||||
const char *numPlayers = steamapicontext->SteamMatchmaking()->GetLobbyData(
|
||||
pRes->m_uiLobbyId, "members:numPlayers" );
|
||||
if ( !numPlayers )
|
||||
{
|
||||
DevMsg( "[MM] Unable to get num players for lobby (%llx)\n",
|
||||
pRes->m_uiLobbyId );
|
||||
pRes->m_numPlayers = -1; // set numPlayers to negative number to indicate a failure here
|
||||
}
|
||||
else
|
||||
{
|
||||
pRes->m_numPlayers = V_atoi( numPlayers );
|
||||
if ( !pRes->m_numPlayers )
|
||||
pRes->m_numPlayers = -1; // set numPlayers to negative number to indicate a failure here
|
||||
}
|
||||
|
||||
// Get the address of the server
|
||||
const char* pServerAdr = steamapicontext->SteamMatchmaking()->GetLobbyData(
|
||||
pRes->m_uiLobbyId, "server:adronline" );
|
||||
|
||||
if ( !pServerAdr )
|
||||
{
|
||||
// DevMsg( "[MM] Unable to get server address from lobby (%llx)\n",
|
||||
// pRes->m_uiLobbyId );
|
||||
}
|
||||
else
|
||||
{
|
||||
pRes->m_svAdr.SetFromString( pServerAdr );
|
||||
|
||||
DevMsg ( "[MM] Lobby %d: id %llu (%llx), num Players %d, sv ip %s, pinging dist %d\n",
|
||||
iResultIndex, pRes->m_uiLobbyId, pRes->m_uiLobbyId, pRes->m_numPlayers,
|
||||
pRes->m_svAdr.ToString(), pRes->m_svPing );
|
||||
|
||||
// Ping server
|
||||
IMatchAsyncOperation *pAsyncOperationPing = NULL;
|
||||
g_pMatchExtensions->GetINetSupport()->ServerPing( pRes->m_svAdr, this, &pAsyncOperationPing );
|
||||
m_arrOutstandingAsyncOperation.AddToTail( pAsyncOperationPing );
|
||||
pRes->m_pAsyncOperationPingWeakRef = pAsyncOperationPing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Callback for server reservation check
|
||||
void CMatchSearcher::OnOperationFinished( IMatchAsyncOperation *pOperation )
|
||||
{
|
||||
if ( !pOperation )
|
||||
return;
|
||||
|
||||
if ( m_eState != STATE_WAITING_LOBBY_DATA_AND_PING )
|
||||
return;
|
||||
|
||||
for ( int i = 0; i < m_arrSearchResults.Count(); ++ i )
|
||||
{
|
||||
if ( m_arrSearchResults[i].m_pAsyncOperationPingWeakRef != pOperation )
|
||||
continue;
|
||||
|
||||
int result = pOperation->GetResult();
|
||||
bool failed = ( pOperation->GetState() == AOS_FAILED );
|
||||
SearchResult_t *pRes = &m_arrSearchResults[i];
|
||||
|
||||
pRes->m_svPing = ( failed ? -1 : ( result ? result : -1 ) );
|
||||
if ( pRes->m_svPing < 0 )
|
||||
{
|
||||
DevMsg( "[MM] Failed pinging server %s for lobby#%d %llu (%llx)\n",
|
||||
pRes->m_svAdr.ToString(), i, pRes->m_uiLobbyId, pRes->m_uiLobbyId );
|
||||
}
|
||||
else
|
||||
{
|
||||
DevMsg( "[MM] Successfully pinged server %s for lobby#%d %llu (%llx) = %d ms\n",
|
||||
pRes->m_svAdr.ToString(), i, pRes->m_uiLobbyId, pRes->m_uiLobbyId, pRes->m_svPing );
|
||||
}
|
||||
|
||||
m_arrSearchResults[i].m_pAsyncOperationPingWeakRef = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CMatchSearcher::OnSearchDone()
|
||||
{
|
||||
m_eState = STATE_DONE;
|
||||
}
|
||||
|
||||
void CMatchSearcher::AggregateSearchPassResults()
|
||||
{
|
||||
#ifndef NO_STEAM
|
||||
|
||||
extern ConVar mm_dedicated_search_maxping;
|
||||
int nPingLimitMax = mm_dedicated_search_maxping.GetInt();
|
||||
if ( nPingLimitMax <= 0 )
|
||||
nPingLimitMax = 5000;
|
||||
for ( int k = 0; k < mm_session_search_ping_buckets.GetInt(); ++ k )
|
||||
{
|
||||
int iPingLow = ( nPingLimitMax * k ) / mm_session_search_ping_buckets.GetInt();
|
||||
int iPingHigh = ( nPingLimitMax * ( k + 1 ) ) / mm_session_search_ping_buckets.GetInt();
|
||||
for ( int iResult = 0; iResult < m_arrSearchResults.Count(); ++ iResult )
|
||||
{
|
||||
SearchResult_t *pRes = &m_arrSearchResults[iResult];
|
||||
if ( ( pRes->m_svPing > iPingLow ) && ( pRes->m_svPing <= iPingHigh ) )
|
||||
{
|
||||
m_arrSearchResultsAggregate.AddToTail( *pRes );
|
||||
|
||||
DevMsg ( "[MM] Search aggregated result%d / %d: id %llu (%llx), num Players %d, sv ip %s, dist %d\n",
|
||||
m_arrSearchResultsAggregate.Count(), iResult, pRes->m_uiLobbyId, pRes->m_uiLobbyId, pRes->m_numPlayers,
|
||||
pRes->m_svAdr.ToString(), pRes->m_svPing );
|
||||
}
|
||||
}
|
||||
}
|
||||
DevMsg ( "[MM] Search aggregated %d results\n", m_arrSearchResultsAggregate.Count() );
|
||||
|
||||
#endif
|
||||
|
||||
m_arrSearchResults.Purge();
|
||||
|
||||
}
|
||||
|
||||
void CMatchSearcher::OnSearchPassDone( KeyValues *pSearchPass )
|
||||
{
|
||||
// If we have enough results, then call it done
|
||||
if ( m_arrSearchResultsAggregate.Count() >= mm_session_search_num_results.GetInt() )
|
||||
{
|
||||
OnSearchDone();
|
||||
return;
|
||||
}
|
||||
|
||||
// Evaluate if there is a nextpass condition
|
||||
char const *szNextPassKeyName = "nextpass";
|
||||
if ( KeyValues *pConditions = pSearchPass->FindKey( "nextpass?" ) )
|
||||
{
|
||||
// Inspect conditions and select which next pass will happen
|
||||
}
|
||||
|
||||
if ( KeyValues *pNextPass = pSearchPass->FindKey( szNextPassKeyName ) )
|
||||
{
|
||||
StartSearchPass( pNextPass );
|
||||
}
|
||||
else
|
||||
{
|
||||
OnSearchDone();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef _X360
|
||||
|
||||
void CMatchSearcher::Live_OnSessionSearchCompleted()
|
||||
{
|
||||
DevMsg( "Received %d search results from Xbox LIVE.\n", GetXSearchResult()->dwSearchResults );
|
||||
|
||||
for( unsigned int i = 0; i < GetXSearchResult()->dwSearchResults; ++ i )
|
||||
{
|
||||
XSESSION_SEARCHRESULT const &xsr = GetXSearchResult()->pResults[i];
|
||||
|
||||
SearchResult_t sr = { xsr.info, NULL };
|
||||
m_arrSearchResults.AddToTail( sr );
|
||||
|
||||
DevMsg( 2, "Result #%02d: %llx\n", i + 1, ( const uint64& ) xsr.info.sessionID );
|
||||
}
|
||||
|
||||
if ( !m_arrSearchResults.Count() )
|
||||
{
|
||||
OnSearchPassDone( m_pSearchPass );
|
||||
}
|
||||
else
|
||||
{
|
||||
DevMsg( "Checking QOS with %d search results.\n", m_arrSearchResults.Count() );
|
||||
Live_CheckSearchResultsQos();
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSearcher::Live_CheckSearchResultsQos()
|
||||
{
|
||||
m_eState = STATE_CHECK_QOS;
|
||||
|
||||
int nResults = m_arrSearchResults.Count();
|
||||
CUtlVector< const void * > memQosData;
|
||||
memQosData.SetCount( 3 * nResults );
|
||||
|
||||
const void ** bufQosData[3];
|
||||
for ( int k = 0; k < ARRAYSIZE( bufQosData ); ++ k )
|
||||
bufQosData[k] = &memQosData[ k * nResults ];
|
||||
|
||||
for ( int k = 0; k < m_arrSearchResults.Count(); ++ k )
|
||||
{
|
||||
SearchResult_t const &sr = m_arrSearchResults[k];
|
||||
|
||||
bufQosData[0][k] = &sr.m_info.hostAddress;
|
||||
bufQosData[1][k] = &sr.m_info.sessionID;
|
||||
bufQosData[2][k] = &sr.m_info.keyExchangeKey;
|
||||
}
|
||||
|
||||
//
|
||||
// Note: XNetQosLookup requires only 2 successful probes to be received from the host.
|
||||
// This is much less than the recommended 8 probes because on a 10% data loss profile
|
||||
// it is impossible to find the host when requiring 8 probes to be received.
|
||||
m_flQosTimeout = Plat_FloatTime() + mm_session_search_qos_timeout.GetFloat();
|
||||
int res = g_pMatchExtensions->GetIXOnline()->XNetQosLookup(
|
||||
nResults,
|
||||
reinterpret_cast< XNADDR const ** >( bufQosData[0] ),
|
||||
reinterpret_cast< XNKID const ** >( bufQosData[1] ),
|
||||
reinterpret_cast< XNKEY const ** >( bufQosData[2] ),
|
||||
0, // number of security gateways to probe
|
||||
NULL, // gateway ip addresses
|
||||
NULL, // gateway service ids
|
||||
2, // number of probes
|
||||
0, // upstream bandwith to use (0 = default)
|
||||
0, // flags - not supported
|
||||
NULL, // signal event
|
||||
&m_pQosResults );// results
|
||||
|
||||
if ( res != 0 )
|
||||
{
|
||||
DevWarning( "OnlineSearch::Live_CheckSearchResultsQos - XNetQosLookup failed (code = 0x%08X)!\n", res );
|
||||
m_arrSearchResults.Purge();
|
||||
OnSearchPassDone( m_pSearchPass );
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSearcher::Live_OnQosCheckCompleted()
|
||||
{
|
||||
for ( uint k = m_pQosResults->cxnqos; k --> 0; )
|
||||
{
|
||||
XNQOSINFO &xqi = m_pQosResults->axnqosinfo[k];
|
||||
|
||||
BYTE uNeedFlags = XNET_XNQOSINFO_TARGET_CONTACTED | XNET_XNQOSINFO_DATA_RECEIVED;
|
||||
if ( ( ( xqi.bFlags & uNeedFlags ) != uNeedFlags) ||
|
||||
( xqi.bFlags & XNET_XNQOSINFO_TARGET_DISABLED ) )
|
||||
{
|
||||
m_arrSearchResults.Remove( k );
|
||||
continue;
|
||||
}
|
||||
|
||||
extern ConVar mm_dedicated_search_maxping;
|
||||
if ( mm_dedicated_search_maxping.GetInt() > 0 &&
|
||||
xqi.wRttMedInMsecs > mm_dedicated_search_maxping.GetInt() )
|
||||
{
|
||||
m_arrSearchResults.Remove( k );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( xqi.cbData && xqi.pbData )
|
||||
{
|
||||
MM_GameDetails_QOS_t gd = { xqi.pbData, xqi.cbData, xqi.wRttMedInMsecs };
|
||||
Assert( !m_arrSearchResults[k].m_pGameDetails );
|
||||
m_arrSearchResults[k].m_pGameDetails = g_pMatchFramework->GetMatchNetworkMsgController()->UnpackGameDetailsFromQOS( &gd );
|
||||
}
|
||||
}
|
||||
|
||||
g_pMatchExtensions->GetIXOnline()->XNetQosRelease( m_pQosResults );
|
||||
m_pQosResults = NULL;
|
||||
|
||||
// Go ahead and start joining the results
|
||||
DevMsg( "Qos completed with %d search results.\n", m_arrSearchResults.Count() );
|
||||
AggregateSearchPassResults();
|
||||
OnSearchPassDone( m_pSearchPass );
|
||||
}
|
||||
|
||||
#elif !defined( NO_STEAM )
|
||||
|
||||
void CMatchSearcher::Steam_OnLobbyMatchListReceived( LobbyMatchList_t *pLobbyMatchList, bool bError )
|
||||
{
|
||||
Msg( "[MM] Received %d search results.\n", bError ? 0 : pLobbyMatchList->m_nLobbiesMatching );
|
||||
|
||||
m_CallbackOnLobbyDataReceived.Register( this, &CMatchSearcher::Steam_OnLobbyDataReceived );
|
||||
|
||||
// Walk through search results and request lobby data
|
||||
for ( int iLobby = 0; iLobby < (int) ( bError ? 0 : pLobbyMatchList->m_nLobbiesMatching ); ++ iLobby )
|
||||
{
|
||||
uint64 uiLobbyId = steamapicontext->SteamMatchmaking()->GetLobbyByIndex( iLobby ).ConvertToUint64();
|
||||
|
||||
SearchResult_t sr = { uiLobbyId };
|
||||
sr.m_pGameDetails = NULL;
|
||||
sr.m_svAdr.SetFromString( "0.0.0.0" );
|
||||
sr.m_svPing = 0;
|
||||
sr.m_numPlayers = 0;
|
||||
sr.m_pAsyncOperationPingWeakRef = NULL;
|
||||
m_arrSearchResults.AddToTail( sr );
|
||||
|
||||
steamapicontext->SteamMatchmaking()->RequestLobbyData( sr.m_uiLobbyId );
|
||||
}
|
||||
|
||||
m_eState = STATE_WAITING_LOBBY_DATA_AND_PING; // Waiting for lobby data
|
||||
m_uiQosPingLastMS = m_uiQosTimeoutStartMS = Plat_MSTime();
|
||||
}
|
||||
|
||||
KeyValues * CMatchSearcher::SearchResult_t::GetGameDetails() const
|
||||
{
|
||||
if ( !m_pGameDetails )
|
||||
{
|
||||
m_pGameDetails = g_pMatchFramework->GetMatchNetworkMsgController()->UnpackGameDetailsFromSteamLobby( m_uiLobbyId );
|
||||
}
|
||||
|
||||
return m_pGameDetails;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void CMatchSearcher::StartSearch()
|
||||
{
|
||||
Assert( !m_pSessionSearchTree );
|
||||
m_pSessionSearchTree = g_pMMF->GetMatchTitleGameSettingsMgr()->DefineSessionSearchKeys( m_pSettings );
|
||||
m_autodelete_pSessionSearchTree.Assign( m_pSessionSearchTree );
|
||||
|
||||
Assert( m_pSessionSearchTree );
|
||||
if ( !m_pSessionSearchTree )
|
||||
{
|
||||
DevWarning( "OnlineSearch::StartSearch failed to build filter list!\n" );
|
||||
OnSearchDone();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartSearchPass( m_pSessionSearchTree );
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSearcher::StartSearchPass( KeyValues *pSearchPass )
|
||||
{
|
||||
KeyValues *pSearchParams = pSearchPass;
|
||||
m_pSearchPass = pSearchPass;
|
||||
|
||||
// Make sure we have fresh buffer for search results
|
||||
m_arrSearchResults.Purge();
|
||||
m_eState = STATE_SEARCHING;
|
||||
|
||||
DevMsg( "OnlineSearch::StartSearchPass:\n" );
|
||||
KeyValuesDumpAsDevMsg( pSearchParams, 1 );
|
||||
|
||||
#ifdef _X360
|
||||
|
||||
DWORD dwSearchRule = pSearchParams->GetInt( "rule" );
|
||||
|
||||
m_arrContexts.RemoveAll();
|
||||
if ( KeyValues *pContexts = pSearchParams->FindKey( "Contexts" ) )
|
||||
{
|
||||
for ( KeyValues *val = pContexts->GetFirstValue(); val; val = val->GetNextValue() )
|
||||
{
|
||||
XUSER_CONTEXT ctx = { 0 };
|
||||
ctx.dwContextId = atoi( val->GetName() );
|
||||
|
||||
if ( val->GetDataType() == KeyValues::TYPE_INT )
|
||||
{
|
||||
ctx.dwValue = val->GetInt();
|
||||
m_arrContexts.AddToTail( ctx );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_arrProperties.RemoveAll();
|
||||
if ( KeyValues *pContexts = pSearchParams->FindKey( "Properties" ) )
|
||||
{
|
||||
for ( KeyValues *val = pContexts->GetFirstValue(); val; val = val->GetNextValue() )
|
||||
{
|
||||
XUSER_PROPERTY prop = { 0 };
|
||||
prop.dwPropertyId = atoi( val->GetName() );
|
||||
|
||||
if ( val->GetDataType() == KeyValues::TYPE_INT )
|
||||
{
|
||||
prop.value.type = XUSER_DATA_TYPE_INT32;
|
||||
prop.value.nData = val->GetInt();
|
||||
m_arrProperties.AddToTail( prop );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DWORD ret = ERROR_SUCCESS;
|
||||
DWORD numBytes = 0;
|
||||
|
||||
DWORD dwNumSlotsRequired = pSearchParams->GetInt( "numPlayers" );
|
||||
|
||||
//
|
||||
// Issue the asynchrounous session search request
|
||||
//
|
||||
ret = g_pMatchExtensions->GetIXOnline()->XSessionSearchEx(
|
||||
dwSearchRule, XBX_GetPrimaryUserId(), mm_session_search_num_results.GetInt(),
|
||||
dwNumSlotsRequired,
|
||||
m_arrProperties.Count(), m_arrContexts.Count(),
|
||||
m_arrProperties.Base(), m_arrContexts.Base(),
|
||||
&numBytes, NULL, NULL
|
||||
);
|
||||
|
||||
// Log the search request to read X360 queries easier
|
||||
DevMsg( "XSessionSearchEx by rule %d for slots %d\n", dwSearchRule, dwNumSlotsRequired );
|
||||
for ( int k = 0; k < m_arrContexts.Count(); ++ k )
|
||||
DevMsg( " CTX %u/0x%08X = 0x%X/%u\n", m_arrContexts[k].dwContextId, m_arrContexts[k].dwContextId, m_arrContexts[k].dwValue, m_arrContexts[k].dwValue );
|
||||
for ( int k = 0; k < m_arrProperties.Count(); ++ k )
|
||||
DevMsg( " PRP %u/0x%08X = 0x%X/%u\n", m_arrProperties[k].dwPropertyId, m_arrProperties[k].dwPropertyId, m_arrProperties[k].value.nData, m_arrProperties[k].value.nData );
|
||||
DevMsg( "will use %u bytes buffer.\n", numBytes );
|
||||
|
||||
if ( ERROR_INSUFFICIENT_BUFFER == ret && numBytes > 0 )
|
||||
{
|
||||
m_bufSearchResultHeader.EnsureCapacity( numBytes );
|
||||
ZeroMemory( GetXSearchResult(), numBytes );
|
||||
ZeroMemory( &m_xOverlapped, sizeof( m_xOverlapped ) );
|
||||
|
||||
DevMsg( "Searching...\n" );
|
||||
ret = g_pMatchExtensions->GetIXOnline()->XSessionSearchEx(
|
||||
dwSearchRule, XBX_GetPrimaryUserId(), mm_session_search_num_results.GetInt(),
|
||||
dwNumSlotsRequired,
|
||||
m_arrProperties.Count(), m_arrContexts.Count(),
|
||||
m_arrProperties.Base(), m_arrContexts.Base(),
|
||||
&numBytes, GetXSearchResult(), &m_xOverlapped
|
||||
);
|
||||
|
||||
if ( ret == ERROR_IO_PENDING )
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise search failed
|
||||
DevWarning( "XSessionSearchEx failed (code = 0x%08X)\n", ret );
|
||||
ZeroMemory( &m_xOverlapped, sizeof( m_xOverlapped ) );
|
||||
OnSearchPassDone( m_pSearchPass );
|
||||
|
||||
#elif !defined( NO_STEAM )
|
||||
|
||||
ISteamMatchmaking *mm = steamapicontext->SteamMatchmaking();
|
||||
|
||||
// Configure filters
|
||||
DWORD dwNumSlotsRequired = pSearchParams->GetInt( "numPlayers" );
|
||||
mm->AddRequestLobbyListFilterSlotsAvailable( dwNumSlotsRequired );
|
||||
|
||||
// Set filters
|
||||
char const * arrKeys[] = { "Filter<", "Filter<=", "Filter=", "Filter<>", "Filter>=", "Filter>" };
|
||||
ELobbyComparison nValueCmp[] = { k_ELobbyComparisonLessThan, k_ELobbyComparisonEqualToOrLessThan, k_ELobbyComparisonEqual, k_ELobbyComparisonNotEqual, k_ELobbyComparisonEqualToOrGreaterThan, k_ELobbyComparisonGreaterThan };
|
||||
for ( int k = 0; k < ARRAYSIZE( arrKeys ); ++ k )
|
||||
{
|
||||
if ( KeyValues *kv = pSearchParams->FindKey( arrKeys[k] ) )
|
||||
{
|
||||
for ( KeyValues *val = kv->GetFirstValue(); val; val = val->GetNextValue() )
|
||||
{
|
||||
if ( val->GetDataType() == KeyValues::TYPE_STRING )
|
||||
{
|
||||
mm->AddRequestLobbyListStringFilter( val->GetName(), val->GetString(), nValueCmp[k] );
|
||||
}
|
||||
else if ( val->GetDataType() == KeyValues::TYPE_INT )
|
||||
{
|
||||
mm->AddRequestLobbyListNumericalFilter( val->GetName(), val->GetInt(), nValueCmp[k] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set ordering near values
|
||||
if ( KeyValues *kv = pSearchParams->FindKey( "Near" ) )
|
||||
{
|
||||
for ( KeyValues *val = kv->GetFirstValue(); val; val = val->GetNextValue() )
|
||||
{
|
||||
if ( val->GetDataType() == KeyValues::TYPE_INT )
|
||||
{
|
||||
mm->AddRequestLobbyListNearValueFilter( val->GetName(), val->GetInt() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: need to set near value filter for strings
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ELobbyDistanceFilter eFilter = k_ELobbyDistanceFilterDefault;
|
||||
switch ( mm_session_search_distance.GetInt() )
|
||||
{
|
||||
case k_ELobbyDistanceFilterClose:
|
||||
case k_ELobbyDistanceFilterDefault:
|
||||
case k_ELobbyDistanceFilterFar:
|
||||
case k_ELobbyDistanceFilterWorldwide:
|
||||
eFilter = ( ELobbyDistanceFilter ) mm_session_search_distance.GetInt();
|
||||
break;
|
||||
}
|
||||
// Set distance filter to Near
|
||||
mm->AddRequestLobbyListDistanceFilter( eFilter );
|
||||
|
||||
// Set dependent lobby
|
||||
if ( uint64 uiDependentLobbyId = pSearchParams->GetUint64( "DependentLobby", 0ull ) )
|
||||
{
|
||||
mm->AddRequestLobbyListCompatibleMembersFilter( uiDependentLobbyId );
|
||||
}
|
||||
|
||||
// RequestLobbyList - will get called back at Steam_OnLobbyMatchListReceived
|
||||
DevMsg( "Searching...\n" );
|
||||
|
||||
SteamAPICall_t hCall = mm->RequestLobbyList();
|
||||
|
||||
m_CallbackOnLobbyMatchListReceived.Set( hCall, this, &CMatchSearcher::Steam_OnLobbyMatchListReceived );
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Results retrieval overrides
|
||||
//
|
||||
|
||||
bool CMatchSearcher::IsSearchFinished() const
|
||||
{
|
||||
return m_eState == STATE_DONE;
|
||||
}
|
||||
|
||||
int CMatchSearcher::GetNumSearchResults() const
|
||||
{
|
||||
return IsSearchFinished() ? m_arrSearchResultsAggregate.Count() : 0;
|
||||
}
|
||||
|
||||
CMatchSearcher::SearchResult_t const & CMatchSearcher::GetSearchResult( int idx ) const
|
||||
{
|
||||
if ( !IsSearchFinished() || !m_arrSearchResultsAggregate.IsValidIndex( idx ) )
|
||||
{
|
||||
Assert( false );
|
||||
static SearchResult_t s_empty;
|
||||
return s_empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_arrSearchResultsAggregate[ idx ];
|
||||
}
|
||||
}
|
||||
179
matchmaking/match_searcher.h
Normal file
179
matchmaking/match_searcher.h
Normal file
@@ -0,0 +1,179 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MATCH_SEARCHER_H
|
||||
#define MATCH_SEARCHER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#if !defined( NO_STEAM )
|
||||
#include "steam/steam_api.h"
|
||||
#endif // _X360
|
||||
|
||||
//
|
||||
// CMatchSearcher
|
||||
//
|
||||
|
||||
class CMatchSearcher
|
||||
#ifdef _X360
|
||||
: public IDormantOperation
|
||||
#endif
|
||||
#if !defined (NO_STEAM)
|
||||
: public IMatchAsyncOperationCallback
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
explicit CMatchSearcher( KeyValues *pSettings );
|
||||
virtual ~CMatchSearcher();
|
||||
|
||||
public:
|
||||
// Run a frame update
|
||||
virtual void Update();
|
||||
|
||||
// Destroy the object
|
||||
virtual void Destroy();
|
||||
|
||||
//
|
||||
// Overrides
|
||||
//
|
||||
public:
|
||||
// Obtain adjusted match search settings
|
||||
virtual KeyValues * GetSearchSettings();
|
||||
|
||||
// Event broadcasting
|
||||
virtual void OnSearchEvent( KeyValues *pNotify );
|
||||
|
||||
protected:
|
||||
|
||||
#ifdef _X360
|
||||
|
||||
public:
|
||||
struct SearchResult_t
|
||||
{
|
||||
inline XNKID GetXNKID() const { return m_info.sessionID; }
|
||||
KeyValues * GetGameDetails() const { return m_pGameDetails; }
|
||||
|
||||
XSESSION_INFO m_info;
|
||||
KeyValues *m_pGameDetails;
|
||||
};
|
||||
|
||||
protected:
|
||||
CUtlVector< XUSER_CONTEXT > m_arrContexts;
|
||||
CUtlVector< XUSER_PROPERTY > m_arrProperties;
|
||||
|
||||
float m_flQosTimeout;
|
||||
XNQOS *m_pQosResults;
|
||||
|
||||
CUtlBuffer m_bufSearchResultHeader;
|
||||
XSESSION_SEARCHRESULT_HEADER * GetXSearchResult() { return ( XSESSION_SEARCHRESULT_HEADER * ) m_bufSearchResultHeader.Base(); }
|
||||
XOVERLAPPED m_xOverlapped;
|
||||
CJob *m_pCancelOverlappedJob;
|
||||
|
||||
void Live_OnSessionSearchCompleted();
|
||||
|
||||
void Live_CheckSearchResultsQos();
|
||||
void Live_OnQosCheckCompleted();
|
||||
|
||||
virtual bool UpdateDormantOperation();
|
||||
|
||||
#elif !defined( NO_STEAM )
|
||||
|
||||
public:
|
||||
struct SearchResult_t
|
||||
{
|
||||
inline XNKID GetXNKID() const { return ( const XNKID & ) m_uiLobbyId; }
|
||||
KeyValues * GetGameDetails() const;
|
||||
|
||||
uint64 m_uiLobbyId;
|
||||
mutable KeyValues *m_pGameDetails;
|
||||
netadr_t m_svAdr;
|
||||
int m_svPing;
|
||||
int m_numPlayers;
|
||||
IMatchAsyncOperation *m_pAsyncOperationPingWeakRef;
|
||||
};
|
||||
|
||||
protected:
|
||||
CCallResult< CMatchSearcher, LobbyMatchList_t > m_CallbackOnLobbyMatchListReceived;
|
||||
void Steam_OnLobbyMatchListReceived( LobbyMatchList_t *p, bool bError );
|
||||
|
||||
#else
|
||||
|
||||
public:
|
||||
struct SearchResult_t
|
||||
{
|
||||
inline XNKID GetXNKID() const { return ( const XNKID & ) m_uiLobbyId; }
|
||||
KeyValues * GetGameDetails() const { return m_pGameDetails; }
|
||||
|
||||
uint64 m_uiLobbyId;
|
||||
KeyValues *m_pGameDetails;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
protected:
|
||||
KeyValues *m_pSettings;
|
||||
KeyValues::AutoDelete m_autodelete_pSettings;
|
||||
|
||||
KeyValues *m_pSessionSearchTree;
|
||||
KeyValues::AutoDelete m_autodelete_pSessionSearchTree;
|
||||
|
||||
KeyValues *m_pSearchPass;
|
||||
|
||||
#if !defined( NO_STEAM )
|
||||
|
||||
uint32 m_uiQosTimeoutStartMS;
|
||||
uint32 m_uiQosPingLastMS;
|
||||
CUtlVector< IMatchAsyncOperation * > m_arrOutstandingAsyncOperation;
|
||||
|
||||
STEAM_CALLBACK_MANUAL( CMatchSearcher, Steam_OnLobbyDataReceived, LobbyDataUpdate_t, m_CallbackOnLobbyDataReceived );
|
||||
|
||||
// Callback for server reservation check
|
||||
virtual void OnOperationFinished( IMatchAsyncOperation *pOperation );
|
||||
|
||||
#endif
|
||||
|
||||
enum State_t
|
||||
{
|
||||
STATE_INIT,
|
||||
STATE_SEARCHING,
|
||||
#ifdef _X360
|
||||
STATE_CHECK_QOS,
|
||||
#endif
|
||||
|
||||
#if !defined( NO_STEAM )
|
||||
STATE_WAITING_LOBBY_DATA_AND_PING,
|
||||
#endif
|
||||
|
||||
STATE_DONE
|
||||
};
|
||||
State_t m_eState;
|
||||
|
||||
CUtlVector< SearchResult_t > m_arrSearchResults;
|
||||
CUtlVector< SearchResult_t > m_arrSearchResultsAggregate;
|
||||
|
||||
protected:
|
||||
void InitializeSettings();
|
||||
|
||||
void StartSearch();
|
||||
virtual void StartSearchPass( KeyValues *pSearchPass );
|
||||
void AggregateSearchPassResults();
|
||||
|
||||
virtual void OnSearchPassDone( KeyValues *pSearchPass );
|
||||
virtual void OnSearchDone();
|
||||
|
||||
//
|
||||
// Results retrieval overrides
|
||||
//
|
||||
public:
|
||||
virtual bool IsSearchFinished() const;
|
||||
virtual int GetNumSearchResults() const;
|
||||
virtual SearchResult_t const & GetSearchResult( int idx ) const;
|
||||
};
|
||||
|
||||
|
||||
#endif // MATCH_SEARCHER_H
|
||||
|
||||
42
matchmaking/matchmaking_base.vpc
Normal file
42
matchmaking/matchmaking_base.vpc
Normal file
@@ -0,0 +1,42 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_base.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTLIBNAME "matchmakingbase"
|
||||
|
||||
$include "matchmaking_base_inc.vpc"
|
||||
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DLL"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$IntermediateDirectory ".\Debug_matchmakingbase$PLATSUBDIR" [$WINDOWS||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_matchmakingbase_360" [$X360]
|
||||
$IntermediateDirectory ".\Debug_matchmakingbase_PS3" [$PS3]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$IntermediateDirectory ".\Release_matchmakingbase$PLATSUBDIR" [$WINDOWS||$POSIX]
|
||||
$IntermediateDirectory ".\Release_matchmakingbase_360" [$X360]
|
||||
$IntermediateDirectory ".\Release_matchmakingbase_PS3" [$PS3]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "matchmakingbase"
|
||||
{
|
||||
}
|
||||
13
matchmaking/matchmaking_base.vpc.vpc_cache
Normal file
13
matchmaking/matchmaking_base.vpc.vpc_cache
Normal file
@@ -0,0 +1,13 @@
|
||||
"vpc_cache"
|
||||
{
|
||||
"CacheVersion" "1"
|
||||
"win32"
|
||||
{
|
||||
"CRCFile" "matchmakingbase.vcxproj.vpc_crc"
|
||||
"OutputFiles"
|
||||
{
|
||||
"0" "matchmakingbase.vcxproj"
|
||||
"1" "matchmakingbase.vcxproj.filters"
|
||||
}
|
||||
}
|
||||
}
|
||||
98
matchmaking/matchmaking_base_ds.vpc
Normal file
98
matchmaking/matchmaking_base_ds.vpc
Normal file
@@ -0,0 +1,98 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_base_ds.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTLIBNAME "matchmakingbase_ds"
|
||||
|
||||
$include "matchmaking_base_inc.vpc"
|
||||
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DS_DLL;SWDS"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$IntermediateDirectory ".\Debug_matchmakingbase_ds$PLATSUBDIR" [$WINDOWS||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_matchmakingbase_ds_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$IntermediateDirectory ".\Release_matchmakingbase_ds$PLATSUBDIR" [$WINDOWS||$POSIX]
|
||||
$IntermediateDirectory ".\Release_matchmakingbase_ds_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "matchmakingbase_ds"
|
||||
{
|
||||
$Folder "Matchmaking"
|
||||
{
|
||||
-$File "mm_session.cpp"
|
||||
}
|
||||
|
||||
$Folder "Sessions"
|
||||
{
|
||||
-$File "ds_searcher.h"
|
||||
-$File "ds_searcher.cpp"
|
||||
-$File "match_searcher.h"
|
||||
-$File "match_searcher.cpp"
|
||||
-$File "mm_session_offline_custom.h"
|
||||
-$File "mm_session_offline_custom.cpp"
|
||||
-$File "mm_session_online_client.h"
|
||||
-$File "mm_session_online_client.cpp"
|
||||
-$File "mm_session_online_host.h"
|
||||
-$File "mm_session_online_host.cpp"
|
||||
-$File "mm_session_online_search.h"
|
||||
-$File "mm_session_online_search.cpp"
|
||||
-$File "mm_session_online_teamsearch.h"
|
||||
-$File "mm_session_online_teamsearch.cpp"
|
||||
-$File "sys_session.h"
|
||||
-$File "sys_session.cpp"
|
||||
}
|
||||
|
||||
$Folder "Platform - Steam"
|
||||
{
|
||||
}
|
||||
|
||||
$Folder "Platform - Xbox 360"
|
||||
{
|
||||
}
|
||||
|
||||
$Folder "Systems"
|
||||
{
|
||||
$Folder "Headers"
|
||||
{
|
||||
-$File "leaderboards.h"
|
||||
-$File "player.h"
|
||||
-$File "searchmanager.h"
|
||||
}
|
||||
-$File "leaderboards.cpp"
|
||||
-$File "player.cpp"
|
||||
-$File "searchmanager.cpp"
|
||||
}
|
||||
|
||||
$Folder "Utils"
|
||||
{
|
||||
}
|
||||
|
||||
$Folder "Public Headers"
|
||||
{
|
||||
}
|
||||
|
||||
$Folder "Source Files"
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
13
matchmaking/matchmaking_base_ds.vpc.vpc_cache
Normal file
13
matchmaking/matchmaking_base_ds.vpc.vpc_cache
Normal file
@@ -0,0 +1,13 @@
|
||||
"vpc_cache"
|
||||
{
|
||||
"CacheVersion" "1"
|
||||
"win32"
|
||||
{
|
||||
"CRCFile" "matchmakingbase_ds.vcxproj.vpc_crc"
|
||||
"OutputFiles"
|
||||
{
|
||||
"0" "matchmakingbase_ds.vcxproj"
|
||||
"1" "matchmakingbase_ds.vcxproj.filters"
|
||||
}
|
||||
}
|
||||
}
|
||||
149
matchmaking/matchmaking_base_inc.vpc
Normal file
149
matchmaking/matchmaking_base_inc.vpc
Normal file
@@ -0,0 +1,149 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_base_inc.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR ".."
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE,..\common,$SRCDIR\gcsdk\steamextra,$SRCDIR\game\shared,$SRCDIR\thirdparty\protobuf-2.5.0\src"
|
||||
$PreprocessorDefinitions "$BASE;NO_STRING_T;VECTOR;VERSION_SAFE_STEAM_API_INTERFACES;PROTECTED_THINGS_ENABLE;NO_STEAM_GAMECOORDINATOR"
|
||||
$PreprocessorDefinitions "$BASE;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead" [!$PS3]
|
||||
}
|
||||
}
|
||||
|
||||
$Project
|
||||
{
|
||||
$Folder "Matchmaking"
|
||||
{
|
||||
$Folder "Headers"
|
||||
{
|
||||
$File "matchmakingqos.h"
|
||||
$File "mm_framework.h"
|
||||
$File "mm_netmsgcontroller.h"
|
||||
$File "mm_events.h"
|
||||
$File "mm_extensions.h"
|
||||
$File "mm_session.h"
|
||||
$File "mm_title_main.h"
|
||||
$File "mm_voice.h"
|
||||
}
|
||||
$File "matchmakingqos.cpp"
|
||||
$File "mm_events.cpp"
|
||||
$File "mm_extensions.cpp"
|
||||
$File "mm_framework.cpp"
|
||||
$File "mm_netmsgcontroller.cpp"
|
||||
$File "mm_session.cpp"
|
||||
$File "mm_voice.cpp"
|
||||
}
|
||||
|
||||
$Folder "Sessions"
|
||||
{
|
||||
$File "ds_searcher.h"
|
||||
$File "ds_searcher.cpp"
|
||||
$File "match_searcher.h"
|
||||
$File "match_searcher.cpp"
|
||||
$File "mm_netmgr.h"
|
||||
$File "mm_netmgr.cpp"
|
||||
$File "mm_session_offline_custom.h"
|
||||
$File "mm_session_offline_custom.cpp"
|
||||
$File "mm_session_online_client.h"
|
||||
$File "mm_session_online_client.cpp"
|
||||
$File "mm_session_online_host.h"
|
||||
$File "mm_session_online_host.cpp"
|
||||
$File "mm_session_online_search.h"
|
||||
$File "mm_session_online_search.cpp"
|
||||
$File "mm_session_online_teamsearch.h"
|
||||
$File "mm_session_online_teamsearch.cpp"
|
||||
$File "sys_session.h"
|
||||
$File "sys_session.cpp"
|
||||
}
|
||||
|
||||
$Folder "Platform - Steam"
|
||||
{
|
||||
$File "steam_apihook.cpp"
|
||||
$File "steam_apihook.h"
|
||||
$File "steam_datacenterjobs.h"
|
||||
$File "steam_datacenterjobs.cpp" [!$X360]
|
||||
$File "steam_lobbyapi.cpp" [!$NO_STEAM && !$DEDICATED && !$X360]
|
||||
$File "steam_lobbyapi.h"
|
||||
}
|
||||
|
||||
$Folder "Platform - Xbox 360"
|
||||
{
|
||||
$File "x360_lobbyapi.cpp" [$X360]
|
||||
$File "x360_lobbyapi.h"
|
||||
$File "x360_netmgr.cpp" [$X360]
|
||||
$File "x360_netmgr.h"
|
||||
$File "x360_xlsp_cmd.cpp"
|
||||
$File "x360_xlsp_cmd.h"
|
||||
}
|
||||
|
||||
$Folder "Systems"
|
||||
{
|
||||
$Folder "Headers"
|
||||
{
|
||||
$File "datacenter.h"
|
||||
$File "leaderboards.h"
|
||||
$File "matchsystem.h"
|
||||
$File "mm_dlc.h"
|
||||
$File "player.h"
|
||||
$File "playermanager.h"
|
||||
$File "searchmanager.h"
|
||||
$File "servermanager.h"
|
||||
$File "playerrankingdata.h"
|
||||
}
|
||||
$File "datacenter.cpp"
|
||||
$File "mm_dlc.cpp"
|
||||
$File "leaderboards.cpp"
|
||||
$File "matchsystem.cpp"
|
||||
$File "player.cpp"
|
||||
$File "playermanager.cpp"
|
||||
$File "searchmanager.cpp"
|
||||
$File "servermanager.cpp"
|
||||
$File "playerrankingdata.cpp"
|
||||
}
|
||||
|
||||
$Folder "Utils"
|
||||
{
|
||||
$File "extkeyvalues.h"
|
||||
$File "extkeyvalues.cpp"
|
||||
}
|
||||
|
||||
$Folder "Public Headers"
|
||||
{
|
||||
$File "$SRCDIR\public\matchmaking\idatacenter.h"
|
||||
$File "$SRCDIR\public\matchmaking\imatchasync.h"
|
||||
$File "$SRCDIR\public\matchmaking\imatchextensions.h"
|
||||
$File "$SRCDIR\public\matchmaking\imatchevents.h"
|
||||
$File "$SRCDIR\public\matchmaking\imatchframework.h"
|
||||
$File "$SRCDIR\public\matchmaking\imatchtitle.h"
|
||||
$File "$SRCDIR\public\matchmaking\imatchnetworkmsg.h"
|
||||
$File "$SRCDIR\public\matchmaking\imatchsystem.h"
|
||||
$File "$SRCDIR\public\matchmaking\imatchvoice.h"
|
||||
$File "$SRCDIR\public\matchmaking\iplayer.h"
|
||||
$File "$SRCDIR\public\matchmaking\iplayermanager.h"
|
||||
$File "$SRCDIR\public\matchmaking\isearchmanager.h"
|
||||
$File "$SRCDIR\public\matchmaking\iservermanager.h"
|
||||
$File "$SRCDIR\public\matchmaking\iplayerrankingdata.h"
|
||||
}
|
||||
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$File "$SRCDIR\public\filesystem_helpers.cpp"
|
||||
$File "main.cpp"
|
||||
}
|
||||
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib gcsdk [!$X360 && !$PS3]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
55
matchmaking/matchmaking_cstrike15.vpc
Normal file
55
matchmaking/matchmaking_cstrike15.vpc
Normal file
@@ -0,0 +1,55 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_cstrike15.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTBINNAME "matchmaking"
|
||||
|
||||
$Include "matchmaking_cstrike15_inc.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DLL"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_$GAMENAME$PLATSUBDIR" [$WINDOWS||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME$PLATSUBDIR" [$WINDOWS||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_360" [$X360]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_360" [$X360]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_PS3" [$PS3]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_PS3" [$PS3]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_$GAMENAME$PLATSUBDIR" [$WINDOWS||$POSIX]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME$PLATSUBDIR" [$WINDOWS||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_360" [$X360]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_360" [$X360]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_PS3" [$PS3]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_PS3" [$PS3]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Matchmaking (CSGO)"
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib matchmakingbase
|
||||
}
|
||||
}
|
||||
13
matchmaking/matchmaking_cstrike15.vpc.vpc_cache
Normal file
13
matchmaking/matchmaking_cstrike15.vpc.vpc_cache
Normal file
@@ -0,0 +1,13 @@
|
||||
"vpc_cache"
|
||||
{
|
||||
"CacheVersion" "1"
|
||||
"win32"
|
||||
{
|
||||
"CRCFile" "matchmaking_csgo.vcxproj.vpc_crc"
|
||||
"OutputFiles"
|
||||
{
|
||||
"0" "matchmaking_csgo.vcxproj"
|
||||
"1" "matchmaking_csgo.vcxproj.filters"
|
||||
}
|
||||
}
|
||||
}
|
||||
49
matchmaking/matchmaking_cstrike15_ds.vpc
Normal file
49
matchmaking/matchmaking_cstrike15_ds.vpc
Normal file
@@ -0,0 +1,49 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_cstrike15_ds.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTBINNAME "matchmaking_ds"
|
||||
|
||||
$Include "matchmaking_cstrike15_inc.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DS_DLL;SWDS"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_$GAMENAME_DS$PLATSUBDIR" [$WINDOWS||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_DS$PLATSUBDIR" [$WINDOWS||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_DS_360" [$X360]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_DS_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_$GAMENAME_DS$PLATSUBDIR" [$WINDOWS||$POSIX]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_DS$PLATSUBDIR" [$WINDOWS||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_DS_360" [$X360]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_DS_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Matchmaking_DS (CSGO)"
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib matchmakingbase_ds
|
||||
}
|
||||
}
|
||||
13
matchmaking/matchmaking_cstrike15_ds.vpc.vpc_cache
Normal file
13
matchmaking/matchmaking_cstrike15_ds.vpc.vpc_cache
Normal file
@@ -0,0 +1,13 @@
|
||||
"vpc_cache"
|
||||
{
|
||||
"CacheVersion" "1"
|
||||
"win32"
|
||||
{
|
||||
"CRCFile" "matchmaking_ds_csgo.vcxproj.vpc_crc"
|
||||
"OutputFiles"
|
||||
{
|
||||
"0" "matchmaking_ds_csgo.vcxproj"
|
||||
"1" "matchmaking_ds_csgo.vcxproj.filters"
|
||||
}
|
||||
}
|
||||
}
|
||||
40
matchmaking/matchmaking_cstrike15_inc.vpc
Normal file
40
matchmaking/matchmaking_cstrike15_inc.vpc
Normal file
@@ -0,0 +1,40 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_cstrike15_inc.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR ".."
|
||||
$Macro GAMENAME "csgo"
|
||||
$Macro GAMESUBDIR "cstrike15"
|
||||
$Macro GENERATED_PROTO_DIR "generated_proto"
|
||||
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\$GAMENAME\bin"
|
||||
$Macro DEVKITBINDIR "$GAMENAME\bin" [$X360]
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
|
||||
$Include "matchmaking_inc.vpc"
|
||||
|
||||
$Project
|
||||
{
|
||||
$Folder "cstrike15"
|
||||
{
|
||||
$File "$GAMESUBDIR/mm_title.h"
|
||||
$File "$GAMESUBDIR/mm_title.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_gamesettingsmgr.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_main.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_richpresence.h"
|
||||
$File "$GAMESUBDIR/mm_title_richpresence.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_titledata.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_contextvalues.h"
|
||||
}
|
||||
|
||||
$Folder "Shared"
|
||||
{
|
||||
$File "$SRCDIR/game/shared/cstrike15/gametypes.h"
|
||||
$File "$SRCDIR/game/shared/cstrike15/gametypes.cpp"
|
||||
$File "$SRCDIR/public/gametypes/igametypes.h"
|
||||
$File "$SRCDIR/common/platforminputdevice.h"
|
||||
$File "$SRCDIR/common/platforminputdevice.cpp"
|
||||
}
|
||||
}
|
||||
49
matchmaking/matchmaking_dota.vpc
Normal file
49
matchmaking/matchmaking_dota.vpc
Normal file
@@ -0,0 +1,49 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_dota.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTBINNAME "matchmaking"
|
||||
|
||||
$Include "matchmaking_dota_inc.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DLL"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_$GAMENAME" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_360" [$X360]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_$GAMENAME" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_360" [$X360]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Matchmaking (DOTA)"
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib matchmakingbase
|
||||
}
|
||||
}
|
||||
49
matchmaking/matchmaking_dota_ds.vpc
Normal file
49
matchmaking/matchmaking_dota_ds.vpc
Normal file
@@ -0,0 +1,49 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_dota_ds.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTBINNAME "matchmaking_ds"
|
||||
|
||||
$Include "matchmaking_dota_inc.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DS_DLL;SWDS"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_DS_360" [$X360]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_DS_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_DS_360" [$X360]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_DS_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Matchmaking_DS (DOTA)"
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib matchmakingbase_ds
|
||||
}
|
||||
}
|
||||
29
matchmaking/matchmaking_dota_inc.vpc
Normal file
29
matchmaking/matchmaking_dota_inc.vpc
Normal file
@@ -0,0 +1,29 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_dota_inc.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR ".."
|
||||
$Macro GAMENAME "dota"
|
||||
$Macro GAMESUBDIR "dota"
|
||||
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\$GAMENAME\bin"
|
||||
$Macro DEVKITBINDIR "$GAMENAME\bin" [$X360]
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
|
||||
$Include "matchmaking_inc.vpc"
|
||||
|
||||
$Project
|
||||
{
|
||||
$Folder "DOTA"
|
||||
{
|
||||
$File "$GAMESUBDIR/mm_title.h"
|
||||
$File "$GAMESUBDIR/mm_title.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_gamesettingsmgr.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_main.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_richpresence.h"
|
||||
$File "$GAMESUBDIR/mm_title_richpresence.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_titledata.cpp"
|
||||
}
|
||||
}
|
||||
49
matchmaking/matchmaking_episodic.vpc
Normal file
49
matchmaking/matchmaking_episodic.vpc
Normal file
@@ -0,0 +1,49 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_episodic.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTBINNAME "matchmaking"
|
||||
|
||||
$Include "matchmaking_episodic_inc.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DLL"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_$GAMENAME" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_360" [$X360]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_$GAMENAME" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_360" [$X360]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Matchmaking (episodic)"
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib matchmakingbase
|
||||
}
|
||||
}
|
||||
49
matchmaking/matchmaking_episodic_ds.vpc
Normal file
49
matchmaking/matchmaking_episodic_ds.vpc
Normal file
@@ -0,0 +1,49 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_episodic_ds.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTBINNAME "matchmaking_ds"
|
||||
|
||||
$Include "matchmaking_episodic_inc.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DS_DLL;SWDS"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_DS_360" [$X360]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_DS_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_DS_360" [$X360]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_DS_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Matchmaking_DS (episodic)"
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib matchmakingbase_ds
|
||||
}
|
||||
}
|
||||
29
matchmaking/matchmaking_episodic_inc.vpc
Normal file
29
matchmaking/matchmaking_episodic_inc.vpc
Normal file
@@ -0,0 +1,29 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_episodic_inc.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR ".."
|
||||
$Macro GAMENAME "episodic"
|
||||
$Macro GAMESUBDIR "episodic"
|
||||
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\$GAMENAME\bin"
|
||||
$Macro DEVKITBINDIR "$GAMENAME\bin" [$X360]
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
|
||||
$Include "matchmaking_inc.vpc"
|
||||
|
||||
$Project
|
||||
{
|
||||
$Folder "episodic"
|
||||
{
|
||||
$File "$GAMESUBDIR/mm_title.h"
|
||||
$File "$GAMESUBDIR/mm_title.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_gamesettingsmgr.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_main.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_richpresence.h"
|
||||
$File "$GAMESUBDIR/mm_title_richpresence.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_titledata.cpp"
|
||||
}
|
||||
}
|
||||
71
matchmaking/matchmaking_inc.vpc
Normal file
71
matchmaking/matchmaking_inc.vpc
Normal file
@@ -0,0 +1,71 @@
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE,$SRCDIR\gcsdk\steamextra,..\common,$SRCDIR\thirdparty\protobuf-2.5.0\src"
|
||||
$AdditionalIncludeDirectories "$BASE,..\common\xlast_$GAMENAME" [!$DEMO]
|
||||
$AdditionalIncludeDirectories "$BASE,..\common\xlast_$GAMENAME_demo" [$DEMO]
|
||||
$PreprocessorDefinitions "$BASE;NO_STRING_T;VECTOR;VERSION_SAFE_STEAM_API_INTERFACES;PROTECTED_THINGS_ENABLE;NO_STEAM_GAMECOORDINATOR"
|
||||
$PreprocessorDefinitions "$BASE;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead" [!$PS3]
|
||||
}
|
||||
|
||||
$Linker
|
||||
{
|
||||
$AdditionalLibraryDirectories "$BASE;$(XEDK)\lib\win32\vs2010" [$XDKINSTALLED]
|
||||
$SystemLibraries "iconv" [$OSXALL]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Linker [$WINDOWS]
|
||||
{
|
||||
$AdditionalDependencies "$BASE wsock32.lib ws2_32.lib wininet.lib"
|
||||
}
|
||||
|
||||
$Linker [$PS3]
|
||||
{
|
||||
$AdditionalDependencies "$BASE libnet_stub.a libnetctl_stub.a"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$Linker [$X360]
|
||||
{
|
||||
$AdditionalDependencies "$BASE xaudiod2.lib Xhvd2.lib xmpd.lib"
|
||||
}
|
||||
|
||||
$Linker [$WINDOWS]
|
||||
{
|
||||
$AdditionalDependencies "$BASE wsock32.lib ws2_32.lib wininet.lib"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$Linker [$X360]
|
||||
{
|
||||
$AdditionalDependencies "$BASE xaudio2.lib Xhv2.lib xmp.lib"
|
||||
}
|
||||
|
||||
$Linker [$WINDOWS]
|
||||
{
|
||||
$AdditionalDependencies "$BASE wsock32.lib ws2_32.lib wininet.lib"
|
||||
}
|
||||
}
|
||||
|
||||
$Project
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib mathlib
|
||||
$Lib tier2
|
||||
$Lib tier3
|
||||
$ImplibExternal steam_api [ ( $WIN32 || $POSIX || $PS3 ) && !$NO_STEAM ]
|
||||
$ImplibExternal steam_api64 [ $WIN64 && !$NO_STEAM ]
|
||||
$Lib gcsdk [!$X360 && !$PS3]
|
||||
}
|
||||
}
|
||||
|
||||
55
matchmaking/matchmaking_portal2.vpc
Normal file
55
matchmaking/matchmaking_portal2.vpc
Normal file
@@ -0,0 +1,55 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_portal2.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTBINNAME "matchmaking"
|
||||
|
||||
$Include "matchmaking_portal2_inc.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DLL"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_$GAMENAME" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_360" [$X360]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_360" [$X360]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_PS3" [$PS3]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_PS3" [$PS3]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_$GAMENAME" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_360" [$X360]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_360" [$X360]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_PS3" [$PS3]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_PS3" [$PS3]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Matchmaking (portal2)"
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib matchmakingbase
|
||||
}
|
||||
}
|
||||
49
matchmaking/matchmaking_portal2_ds.vpc
Normal file
49
matchmaking/matchmaking_portal2_ds.vpc
Normal file
@@ -0,0 +1,49 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_portal2_ds.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTBINNAME "matchmaking_ds"
|
||||
|
||||
$Include "matchmaking_portal2_inc.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DS_DLL;SWDS"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_DS_360" [$X360]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_DS_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_DS_360" [$X360]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_DS_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Matchmaking_DS (portal2)"
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib matchmakingbase_ds
|
||||
}
|
||||
}
|
||||
29
matchmaking/matchmaking_portal2_inc.vpc
Normal file
29
matchmaking/matchmaking_portal2_inc.vpc
Normal file
@@ -0,0 +1,29 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_portal2_inc.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR ".."
|
||||
$Macro GAMENAME "portal2"
|
||||
$Macro GAMESUBDIR "portal2"
|
||||
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\$GAMENAME\bin"
|
||||
$Macro DEVKITBINDIR "$GAMENAME\bin" [$X360]
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
|
||||
$Include "matchmaking_inc.vpc"
|
||||
|
||||
$Project
|
||||
{
|
||||
$Folder "portal2"
|
||||
{
|
||||
$File "$GAMESUBDIR/mm_title.h"
|
||||
$File "$GAMESUBDIR/mm_title.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_gamesettingsmgr.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_main.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_richpresence.h"
|
||||
$File "$GAMESUBDIR/mm_title_richpresence.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_titledata.cpp"
|
||||
}
|
||||
}
|
||||
49
matchmaking/matchmaking_swarm.vpc
Normal file
49
matchmaking/matchmaking_swarm.vpc
Normal file
@@ -0,0 +1,49 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_swarm.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTBINNAME "matchmaking"
|
||||
|
||||
$Include "matchmaking_swarm_inc.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DLL"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_$GAMENAME" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_360" [$X360]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_$GAMENAME" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_360" [$X360]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Matchmaking (Swarm)"
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib matchmakingbase
|
||||
}
|
||||
}
|
||||
49
matchmaking/matchmaking_swarm_ds.vpc
Normal file
49
matchmaking/matchmaking_swarm_ds.vpc
Normal file
@@ -0,0 +1,49 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_swarm_ds.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTBINNAME "matchmaking_ds"
|
||||
|
||||
$Include "matchmaking_swarm_inc.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DS_DLL;SWDS"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_DS_360" [$X360]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_DS_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_DS_360" [$X360]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_DS_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Matchmaking_DS (Swarm)"
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib matchmakingbase_ds
|
||||
}
|
||||
}
|
||||
31
matchmaking/matchmaking_swarm_inc.vpc
Normal file
31
matchmaking/matchmaking_swarm_inc.vpc
Normal file
@@ -0,0 +1,31 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_swarm_inc.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR ".."
|
||||
$Macro GAMENAME "swarm"
|
||||
$Macro GAMESUBDIR "swarm"
|
||||
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\$GAMENAME\bin"
|
||||
$Macro DEVKITBINDIR "$GAMENAME\bin" [$X360]
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
|
||||
$Include "matchmaking_inc.vpc"
|
||||
|
||||
$Project
|
||||
{
|
||||
$Folder "Swarm"
|
||||
{
|
||||
$File "$GAMESUBDIR/matchext_swarm.h"
|
||||
$File "$GAMESUBDIR/matchext_swarm.cpp"
|
||||
$File "$GAMESUBDIR/mm_title.h"
|
||||
$File "$GAMESUBDIR/mm_title.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_gamesettingsmgr.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_main.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_richpresence.h"
|
||||
$File "$GAMESUBDIR/mm_title_richpresence.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_titledata.cpp"
|
||||
}
|
||||
}
|
||||
49
matchmaking/matchmaking_tf.vpc
Normal file
49
matchmaking/matchmaking_tf.vpc
Normal file
@@ -0,0 +1,49 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_tf.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTBINNAME "matchmaking"
|
||||
|
||||
$Include "matchmaking_tf_inc.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DLL"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_$GAMENAME" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_360" [$X360]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_$GAMENAME" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_360" [$X360]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Matchmaking (tf)"
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib matchmakingbase
|
||||
}
|
||||
}
|
||||
49
matchmaking/matchmaking_tf_ds.vpc
Normal file
49
matchmaking/matchmaking_tf_ds.vpc
Normal file
@@ -0,0 +1,49 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_tf_ds.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro OUTBINNAME "matchmaking_ds"
|
||||
|
||||
$Include "matchmaking_tf_inc.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;MATCHMAKING_DS_DLL;SWDS"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Debug_$GAMENAME_DS_360" [$X360]
|
||||
$IntermediateDirectory ".\Debug_$GAMENAME_DS_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_DS" [$WIN32||$POSIX]
|
||||
|
||||
$OutputDirectory ".\Release_$GAMENAME_DS_360" [$X360]
|
||||
$IntermediateDirectory ".\Release_$GAMENAME_DS_360" [$X360]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Matchmaking_DS (tf)"
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib matchmakingbase_ds
|
||||
}
|
||||
}
|
||||
29
matchmaking/matchmaking_tf_inc.vpc
Normal file
29
matchmaking/matchmaking_tf_inc.vpc
Normal file
@@ -0,0 +1,29 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// matchmaking_tf_inc.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR ".."
|
||||
$Macro GAMENAME "tf"
|
||||
$Macro GAMESUBDIR "tf"
|
||||
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\$GAMENAME\bin"
|
||||
$Macro DEVKITBINDIR "$GAMENAME\bin" [$X360]
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
|
||||
$Include "matchmaking_inc.vpc"
|
||||
|
||||
$Project
|
||||
{
|
||||
$Folder "tf"
|
||||
{
|
||||
$File "$GAMESUBDIR/mm_title.h"
|
||||
$File "$GAMESUBDIR/mm_title.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_gamesettingsmgr.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_main.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_richpresence.h"
|
||||
$File "$GAMESUBDIR/mm_title_richpresence.cpp"
|
||||
$File "$GAMESUBDIR/mm_title_titledata.cpp"
|
||||
}
|
||||
}
|
||||
262
matchmaking/matchmakingqos.cpp
Normal file
262
matchmaking/matchmakingqos.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
//====== Copyright c 1996-2007, Valve Corporation, All rights reserved. =======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "matchmakingqos.h"
|
||||
#include "threadtools.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
#if defined( _X360 )
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Quality of Service detection routines
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CQosDetector
|
||||
{
|
||||
public:
|
||||
CQosDetector();
|
||||
~CQosDetector();
|
||||
|
||||
public:
|
||||
bool InitiateLookup();
|
||||
bool WaitForResults();
|
||||
|
||||
public:
|
||||
MM_QOS_t GetResults() const { return m_Results; }
|
||||
|
||||
private:
|
||||
void Release();
|
||||
XNQOS *m_pQos;
|
||||
|
||||
void ProcessResults();
|
||||
MM_QOS_t m_Results;
|
||||
};
|
||||
|
||||
CQosDetector::CQosDetector() : m_pQos( NULL )
|
||||
{
|
||||
m_Results.nPingMsMin = 50; // 50 ms default ping
|
||||
m_Results.nPingMsMed = 50; // 50 ms default ping
|
||||
m_Results.flBwUpKbs = 32.0f; // 32 KB/s = 256 kbps
|
||||
m_Results.flBwDnKbs = 32.0f; // 32 KB/s = 256 kbps
|
||||
m_Results.flLoss = 0.0f; // 0% packet loss
|
||||
}
|
||||
|
||||
CQosDetector::~CQosDetector()
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
void CQosDetector::Release()
|
||||
{
|
||||
if ( m_pQos )
|
||||
{
|
||||
g_pMatchExtensions->GetIXOnline()->XNetQosRelease( m_pQos );
|
||||
m_pQos = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool CQosDetector::InitiateLookup()
|
||||
{
|
||||
Release();
|
||||
|
||||
//
|
||||
// Issue the asynchronous QOS service lookup query
|
||||
//
|
||||
XNQOS *pQos = NULL;
|
||||
INT err = g_pMatchExtensions->GetIXOnline()->XNetQosServiceLookup( NULL, NULL, &pQos );
|
||||
if ( err )
|
||||
{
|
||||
Msg( "CQosDetector::InitiateLookup failed, err = %d\n", err );
|
||||
return false;
|
||||
}
|
||||
if ( !pQos )
|
||||
{
|
||||
Msg( "CQosDetector::InitiateLookup failed, qos is null.\n" );
|
||||
return false;
|
||||
}
|
||||
m_pQos = pQos;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CQosDetector::WaitForResults()
|
||||
{
|
||||
if ( !m_pQos )
|
||||
return false;
|
||||
|
||||
//
|
||||
// Spin-sleep here while waiting for the probes to come back
|
||||
//
|
||||
const int numSecQosTimeout = 30; // Consider QOS query timed out if 30 seconds elapsed
|
||||
float flTimeStart = Plat_FloatTime(), flTimeEndTimeout = flTimeStart + numSecQosTimeout;
|
||||
for ( ; ; )
|
||||
{
|
||||
if ( Plat_FloatTime() > flTimeEndTimeout )
|
||||
{
|
||||
Msg( "CQosDetector::WaitForResults timed out after %d sec.\n", numSecQosTimeout );
|
||||
Release();
|
||||
return false; // Timed out
|
||||
}
|
||||
|
||||
if ( m_pQos->axnqosinfo->bFlags & XNET_XNQOSINFO_COMPLETE )
|
||||
break; // QOS query has completed
|
||||
|
||||
ThreadSleep( 10 );
|
||||
}
|
||||
|
||||
if ( ! ( m_pQos->axnqosinfo->bFlags & XNET_XNQOSINFO_TARGET_CONTACTED ) ||
|
||||
( m_pQos->axnqosinfo->bFlags & XNET_XNQOSINFO_TARGET_DISABLED ) )
|
||||
{
|
||||
// Failed to contact host or target disabled
|
||||
Msg( "CQosDetector::WaitForResults host unavailable.\n" );
|
||||
Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
ProcessResults();
|
||||
Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CQosDetector::ProcessResults()
|
||||
{
|
||||
MM_QOS_t Results;
|
||||
|
||||
Results.nPingMsMin = m_pQos->axnqosinfo->wRttMinInMsecs;
|
||||
Results.nPingMsMed = m_pQos->axnqosinfo->wRttMedInMsecs;
|
||||
|
||||
Results.flBwUpKbs = m_pQos->axnqosinfo->dwUpBitsPerSec / 8192.0f;
|
||||
if ( Results.flBwUpKbs < 1.f )
|
||||
{
|
||||
Results.flBwUpKbs = 1.f;
|
||||
}
|
||||
Results.flBwDnKbs = m_pQos->axnqosinfo->dwDnBitsPerSec / 8192.0f;
|
||||
if ( Results.flBwDnKbs < 1.f )
|
||||
{
|
||||
Results.flBwDnKbs = 1.f;
|
||||
}
|
||||
|
||||
Results.flLoss = m_pQos->axnqosinfo->cProbesXmit ? ( float( m_pQos->axnqosinfo->cProbesXmit - m_pQos->axnqosinfo->cProbesRecv ) * 100.f / m_pQos->axnqosinfo->cProbesXmit ) : 0.f;
|
||||
|
||||
m_Results = Results;
|
||||
|
||||
// Dump the results
|
||||
Msg(
|
||||
"[QOS] Fresh QOS results available:\n"
|
||||
"[QOS] ping %d min, %d med\n"
|
||||
"[QOS] bandwidth %.1f kB/s upstream, %.1f kB/s downstream\n"
|
||||
"[QOS] avg packet loss %.0f percents\n",
|
||||
Results.nPingMsMin, Results.nPingMsMed,
|
||||
Results.flBwUpKbs, Results.flBwDnKbs,
|
||||
Results.flLoss
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Global QOS detector thread
|
||||
//
|
||||
|
||||
static class CQosThread
|
||||
{
|
||||
public:
|
||||
CQosThread();
|
||||
|
||||
MM_QOS_t GetResults();
|
||||
|
||||
static unsigned ThreadProc( void *pThis ) { return ( (CQosThread *) pThis )->Run(), 0; }
|
||||
void Run();
|
||||
|
||||
private:
|
||||
ThreadHandle_t m_hHandle;
|
||||
CThreadEvent m_hRequestResultsEvent; // Auto reset event to trigger next query
|
||||
CQosDetector m_QosDetector;
|
||||
}
|
||||
s_QosThread;
|
||||
|
||||
CQosThread::CQosThread() : m_hHandle( NULL )
|
||||
{
|
||||
}
|
||||
|
||||
void CQosThread::Run()
|
||||
{
|
||||
//
|
||||
// Sit here and fetch fresh QOS results whenever somebody needs them.
|
||||
// Every request of QOS data is instantaneous and returns the last successful QOS query result.
|
||||
//
|
||||
|
||||
for ( unsigned int numQosRequestsMade = 0; ; ++ numQosRequestsMade )
|
||||
{
|
||||
m_QosDetector.InitiateLookup();
|
||||
m_QosDetector.WaitForResults();
|
||||
|
||||
if ( numQosRequestsMade )
|
||||
{
|
||||
m_hRequestResultsEvent.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseThreadHandle( m_hHandle );
|
||||
m_hHandle = NULL;
|
||||
}
|
||||
|
||||
MM_QOS_t CQosThread::GetResults()
|
||||
{
|
||||
// Launch the thread if this is the first time QOS is needed
|
||||
if ( !m_hHandle )
|
||||
{
|
||||
m_hHandle = CreateSimpleThread( ThreadProc, this );
|
||||
|
||||
if( m_hHandle )
|
||||
{
|
||||
ThreadSetAffinity( m_hHandle, XBOX_PROCESSOR_3 );
|
||||
}
|
||||
}
|
||||
|
||||
// Signal the event that we can make a fresh QOS query
|
||||
m_hRequestResultsEvent.Set();
|
||||
return m_QosDetector.GetResults();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Function to retrieve the result of the last QOS query
|
||||
//
|
||||
|
||||
MM_QOS_t MM_GetQos()
|
||||
{
|
||||
return s_QosThread.GetResults();
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
//
|
||||
// Default implementation of QOS
|
||||
//
|
||||
|
||||
static struct Default_MM_QOS_t : public MM_QOS_t
|
||||
{
|
||||
Default_MM_QOS_t()
|
||||
{
|
||||
nPingMsMin = 50; // 50 ms default ping
|
||||
nPingMsMed = 50; // 50 ms default ping
|
||||
flBwUpKbs = 32.0f; // 32 KB/s = 256 kbps
|
||||
flBwDnKbs = 32.0f; // 32 KB/s = 256 kbps
|
||||
flLoss = 0.0f; // 0% packet loss
|
||||
}
|
||||
}
|
||||
s_DefaultQos;
|
||||
|
||||
MM_QOS_t MM_GetQos()
|
||||
{
|
||||
return s_DefaultQos;
|
||||
}
|
||||
|
||||
#endif
|
||||
19
matchmaking/matchmakingqos.h
Normal file
19
matchmaking/matchmakingqos.h
Normal file
@@ -0,0 +1,19 @@
|
||||
//====== Copyright c 1996-2007, Valve Corporation, All rights reserved. =======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MATCHMAKING_QOS_H
|
||||
#define MATCHMAKING_QOS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
MM_QOS_t MM_GetQos();
|
||||
|
||||
#endif // #ifndef MATCHMAKING_QOS_H
|
||||
106
matchmaking/matchsystem.cpp
Normal file
106
matchmaking/matchsystem.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
//========= Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
#ifdef _X360
|
||||
#include "xonline.h"
|
||||
#else
|
||||
#include "xbox/xboxstubs.h"
|
||||
#endif
|
||||
|
||||
#include "matchsystem.h"
|
||||
#include "playermanager.h"
|
||||
#include "servermanager.h"
|
||||
#include "datacenter.h"
|
||||
|
||||
#ifndef SWDS
|
||||
#include "searchmanager.h"
|
||||
#include "leaderboards.h"
|
||||
#endif
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
static CMatchSystem s_MatchSystem;
|
||||
CMatchSystem *g_pMatchSystem = &s_MatchSystem;
|
||||
|
||||
bool IsValidXNKID( XNKID xnkid )
|
||||
{
|
||||
for(int i = 0; i < 8; ++i)
|
||||
{
|
||||
if(xnkid.ab[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CMatchSystem::CMatchSystem()
|
||||
{
|
||||
}
|
||||
|
||||
CMatchSystem::~CMatchSystem()
|
||||
{
|
||||
}
|
||||
|
||||
IPlayerManager * CMatchSystem::GetPlayerManager()
|
||||
{
|
||||
return g_pPlayerManager;
|
||||
}
|
||||
|
||||
IMatchVoice * CMatchSystem::GetMatchVoice()
|
||||
{
|
||||
return g_pMatchVoice;
|
||||
}
|
||||
|
||||
|
||||
IServerManager * CMatchSystem::GetUserGroupsServerManager()
|
||||
{
|
||||
return g_pServerManager;
|
||||
}
|
||||
|
||||
ISearchManager * CMatchSystem::CreateGameSearchManager( KeyValues *pParams )
|
||||
{
|
||||
#ifndef SWDS
|
||||
return new CSearchManager( pParams );
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
IDatacenter * CMatchSystem::GetDatacenter()
|
||||
{
|
||||
return g_pDatacenter;
|
||||
}
|
||||
|
||||
IDlcManager * CMatchSystem::GetDlcManager()
|
||||
{
|
||||
return g_pDlcManager;
|
||||
}
|
||||
|
||||
void CMatchSystem::Update()
|
||||
{
|
||||
if ( g_pPlayerManager )
|
||||
g_pPlayerManager->Update();
|
||||
|
||||
if ( g_pServerManager )
|
||||
g_pServerManager->Update();
|
||||
|
||||
#ifndef SWDS
|
||||
CSearchManager::UpdateAll();
|
||||
|
||||
if ( g_pLeaderboardRequestQueue )
|
||||
g_pLeaderboardRequestQueue->Update();
|
||||
#endif
|
||||
|
||||
if ( g_pDatacenter )
|
||||
g_pDatacenter->Update();
|
||||
|
||||
if ( g_pDlcManager )
|
||||
g_pDlcManager->Update();
|
||||
}
|
||||
|
||||
49
matchmaking/matchsystem.h
Normal file
49
matchmaking/matchsystem.h
Normal file
@@ -0,0 +1,49 @@
|
||||
//========= Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
#ifndef _MATCHSYSTEM_H_
|
||||
#define _MATCHSYSTEM_H_
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
#include "utlvector.h"
|
||||
|
||||
enum PacketTargetType
|
||||
{
|
||||
PTT_PLAYER = 0,
|
||||
PTT_GAME,
|
||||
};
|
||||
|
||||
bool IsValidXNKID( XNKID xnkid );
|
||||
|
||||
class CMatchSystem : public IMatchSystem
|
||||
{
|
||||
public:
|
||||
CMatchSystem();
|
||||
~CMatchSystem();
|
||||
|
||||
public:
|
||||
void Update();
|
||||
|
||||
//IMatchSystem
|
||||
public:
|
||||
virtual IPlayerManager * GetPlayerManager();
|
||||
|
||||
virtual IMatchVoice * GetMatchVoice();
|
||||
|
||||
virtual IServerManager * GetUserGroupsServerManager();
|
||||
|
||||
virtual ISearchManager * CreateGameSearchManager( KeyValues *pParams );
|
||||
|
||||
virtual IDatacenter * GetDatacenter();
|
||||
|
||||
virtual IDlcManager * GetDlcManager();
|
||||
};
|
||||
|
||||
extern CMatchSystem *g_pMatchSystem;
|
||||
|
||||
#endif
|
||||
|
||||
340
matchmaking/mm_dlc.cpp
Normal file
340
matchmaking/mm_dlc.cpp
Normal file
@@ -0,0 +1,340 @@
|
||||
//========= Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
#ifndef _X360
|
||||
#include "xbox/xboxstubs.h"
|
||||
#endif
|
||||
|
||||
#include "mm_framework.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
|
||||
static CDlcManager g_DlcManager;
|
||||
CDlcManager *g_pDlcManager = &g_DlcManager;
|
||||
|
||||
CON_COMMAND( mm_dlc_debugprint, "Shows information about dlc" )
|
||||
{
|
||||
KeyValuesDumpAsDevMsg( g_pDlcManager->GetDataInfo(), 1 );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CDlcManager::CDlcManager() :
|
||||
m_pDataInfo( NULL ),
|
||||
m_eState( STATE_IDLE ),
|
||||
m_flTimestamp( 0.0f ),
|
||||
m_bNeedToDiscoverAllDlcs( true ),
|
||||
m_bNeedToUpdateFileSystem( false )
|
||||
{
|
||||
#ifdef _X360
|
||||
m_hEnumerator = NULL;
|
||||
memset( &m_xOverlapped, 0, sizeof( m_xOverlapped ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
CDlcManager::~CDlcManager()
|
||||
{
|
||||
if ( m_pDataInfo )
|
||||
m_pDataInfo->deleteThis();
|
||||
m_pDataInfo = NULL;
|
||||
}
|
||||
|
||||
void CDlcManager::Update()
|
||||
{
|
||||
#ifdef _X360
|
||||
// Per TCR-126 we don't want to open DLC for users who haven't unlocked the game yet
|
||||
IXboxSystem *pXboxSystem = g_pMatchExtensions->GetIXboxSystem();
|
||||
if ( !pXboxSystem || !pXboxSystem->IsArcadeTitleUnlocked() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD ret = 0;
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_XENUMERATE:
|
||||
if ( !XHasOverlappedIoCompleted( &m_xOverlapped ) )
|
||||
return;
|
||||
|
||||
ret = XGetOverlappedResult( &m_xOverlapped, ( DWORD * ) &m_dwNumItems, false );
|
||||
if ( ret != ERROR_SUCCESS )
|
||||
{
|
||||
Warning( "DLCMANAGER: XContentCreateEnumerator/XEnumerate async failed with error %d!\n", ret );
|
||||
m_dwNumItems = 0;
|
||||
m_bNeedToDiscoverAllDlcs = true;
|
||||
}
|
||||
|
||||
CloseHandle( m_hEnumerator );
|
||||
m_hEnumerator = NULL;
|
||||
|
||||
CreateNextContent();
|
||||
return;
|
||||
|
||||
case STATE_XCONTENT_CREATE:
|
||||
if ( !XHasOverlappedIoCompleted( &m_xOverlapped ) )
|
||||
return;
|
||||
|
||||
ProcessNextContent();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Once we are idle check if we need to update the file systems list of DLC
|
||||
if ( m_eState == STATE_IDLE )
|
||||
{
|
||||
if ( m_bNeedToUpdateFileSystem )
|
||||
{
|
||||
m_bNeedToUpdateFileSystem = false;
|
||||
g_pFullFileSystem->DiscoverDLC( XBX_GetPrimaryUserId() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDlcManager::RequestDlcUpdate()
|
||||
{
|
||||
if ( m_eState > STATE_IDLE )
|
||||
return;
|
||||
|
||||
if ( m_eState == STATE_IDLE && !m_bNeedToDiscoverAllDlcs )
|
||||
{
|
||||
Msg( "DLCMANAGER: RequestDlcUpdate has no new content.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// If we specified dlc via the command line we can skip the actual enumeration
|
||||
const char *pCmdLine = CommandLine()->GetCmdLine();
|
||||
if ( V_stristr( pCmdLine, "-dlc" ) )
|
||||
{
|
||||
m_eState = STATE_IDLE;
|
||||
m_bNeedToUpdateFileSystem = true;
|
||||
m_bNeedToDiscoverAllDlcs = false;
|
||||
return;
|
||||
}
|
||||
|
||||
#if !defined( NO_STEAM ) && !defined( SWDS )
|
||||
// Client is requesting a DLC update
|
||||
m_CallbackOnDLCInstalled.Register( this, &CDlcManager::Steam_OnDLCInstalled );
|
||||
Steam_OnDLCInstalled( NULL );
|
||||
#endif
|
||||
|
||||
#ifdef _X360
|
||||
if ( XBX_GetPrimaryUserIsGuest() )
|
||||
{
|
||||
Msg( "DLCMANAGER: RequestDlcUpdate will not update for guests.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD nBufferSize = 0;
|
||||
DWORD ret = XContentCreateEnumerator( XBX_GetPrimaryUserId(), XCONTENTDEVICE_ANY,
|
||||
XCONTENTTYPE_MARKETPLACE, 0, 100, &nBufferSize, &m_hEnumerator );
|
||||
if ( ret )
|
||||
{
|
||||
Warning( "DLCMANAGER: XContentCreateEnumerator failed with error %d!\n", ret );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( nBufferSize )
|
||||
{
|
||||
m_dwNumItems = 0;
|
||||
m_arrContentData.EnsureCapacity( nBufferSize/sizeof( XCONTENT_DATA ) + 1 );
|
||||
m_arrContentData.RemoveAll();
|
||||
ret = XEnumerate( m_hEnumerator, m_arrContentData.Base(), nBufferSize, NULL, &m_xOverlapped );
|
||||
if ( ret && ret != ERROR_IO_PENDING )
|
||||
{
|
||||
Warning( "DLCMANAGER: XContentCreateEnumerator/XEnumerate failed with error %d!\n", ret );
|
||||
}
|
||||
Msg( "DLCMANAGER: XContentCreateEnumerator/XEnumerate initiated.\n" );
|
||||
m_eState = STATE_XENUMERATE;
|
||||
m_flTimestamp = Plat_FloatTime();
|
||||
m_bNeedToDiscoverAllDlcs = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Warning( "DLCMANAGER: XContentCreateEnumerator not starting enumeration!\n" );
|
||||
::CloseHandle( m_hEnumerator );
|
||||
m_hEnumerator = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _X360
|
||||
void CDlcManager::CreateNextContent()
|
||||
{
|
||||
Msg( "DLCMANAGER: enumeration checkpoint after %.3f sec\n", Plat_FloatTime() - m_flTimestamp );
|
||||
while ( m_dwNumItems -- > 0 )
|
||||
{
|
||||
XCONTENT_DATA *pContentData = m_arrContentData.Base() + m_dwNumItems;
|
||||
char chKey[ XCONTENT_MAX_FILENAME_LENGTH + 1 ];
|
||||
memcpy( chKey, pContentData->szFileName, XCONTENT_MAX_FILENAME_LENGTH );
|
||||
chKey[ XCONTENT_MAX_FILENAME_LENGTH ] = 0;
|
||||
|
||||
if ( m_pDataInfo->FindKey( chKey ) )
|
||||
continue; // Already processed that DLC
|
||||
|
||||
// Kick off DLC processing
|
||||
m_dwLicenseMask = 0;
|
||||
DWORD ret = XContentCreate( XBX_GetPrimaryUserId(), "PKG", pContentData, XCONTENTFLAG_OPENEXISTING, NULL, &m_dwLicenseMask, &m_xOverlapped );
|
||||
if ( ret && ( ret != ERROR_IO_PENDING ) )
|
||||
{
|
||||
Warning( "DLCMANAGER: [%.*s] is corrupt\n", ARRAYSIZE( pContentData->szFileName ), pContentData->szFileName );
|
||||
continue; // assume corrupt
|
||||
}
|
||||
|
||||
m_eState = STATE_XCONTENT_CREATE;
|
||||
return;
|
||||
}
|
||||
|
||||
// All done
|
||||
m_eState = STATE_IDLE;
|
||||
float flTime = Plat_FloatTime() - m_flTimestamp;
|
||||
Msg( "DLCMANAGER: Full update finished after %.3f sec\n", flTime );
|
||||
KeyValuesDumpAsDevMsg( m_pDataInfo, 1 );
|
||||
}
|
||||
|
||||
void CDlcManager::ProcessNextContent()
|
||||
{
|
||||
DWORD dwResult = 0;
|
||||
DWORD ret = XGetOverlappedResult( &m_xOverlapped, &dwResult, false );
|
||||
if( ret == ERROR_SUCCESS )
|
||||
{
|
||||
XCONTENT_DATA *pContentData = m_arrContentData.Base() + m_dwNumItems;
|
||||
char chKey[ XCONTENT_MAX_FILENAME_LENGTH + 1 ];
|
||||
memcpy( chKey, pContentData->szFileName, XCONTENT_MAX_FILENAME_LENGTH );
|
||||
chKey[ XCONTENT_MAX_FILENAME_LENGTH ] = 0;
|
||||
|
||||
Msg( "DLCMANAGER: [%.*s] has license mask = 0x%08X\n", ARRAYSIZE( pContentData->szFileName ), pContentData->szFileName, m_dwLicenseMask );
|
||||
|
||||
if ( !m_pDataInfo )
|
||||
{
|
||||
m_pDataInfo = new KeyValues( "DlcManager" );
|
||||
m_pDataInfo->SetUint64( "@info/installed", 0 );
|
||||
}
|
||||
|
||||
if ( KeyValues *pDlc = m_pDataInfo->FindKey( chKey, true ) )
|
||||
{
|
||||
pDlc->SetInt( "licensemask", m_dwLicenseMask );
|
||||
pDlc->SetWString( "displayname", pContentData->szDisplayName );
|
||||
pDlc->SetInt( "deviceid", pContentData->DeviceID );
|
||||
}
|
||||
|
||||
m_pDataInfo->SetUint64( "@info/installed", m_pDataInfo->GetUint64( "@info/installed" ) | ( 1ull << DLC_LICENSE_ID( m_dwLicenseMask ) ) );
|
||||
m_bNeedToUpdateFileSystem = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
XCONTENT_DATA *pContentData = m_arrContentData.Base() + m_dwNumItems;
|
||||
Warning( "DLCMANAGER: [%.*s] async open failed with error %d\n", ARRAYSIZE( pContentData->szFileName ), pContentData->szFileName, ret );
|
||||
}
|
||||
XContentClose( "PKG", NULL );
|
||||
CreateNextContent();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool CDlcManager::IsDlcUpdateFinished( bool bWaitForFinish )
|
||||
{
|
||||
if ( m_eState == STATE_IDLE )
|
||||
return true;
|
||||
if ( !bWaitForFinish )
|
||||
return false;
|
||||
|
||||
float flTimestamp = Plat_FloatTime();
|
||||
while ( m_eState != STATE_IDLE )
|
||||
{
|
||||
Update();
|
||||
ThreadSleep( 1 );
|
||||
}
|
||||
float flEndTimestamp = Plat_FloatTime();
|
||||
|
||||
Warning( "DLCMANAGER: Forcing wait for update to finish stalled for %.3f sec\n", flEndTimestamp - flTimestamp );
|
||||
return true;
|
||||
}
|
||||
|
||||
KeyValues * CDlcManager::GetDataInfo()
|
||||
{
|
||||
return m_pDataInfo;
|
||||
}
|
||||
|
||||
void CDlcManager::OnEvent( KeyValues *kvEvent )
|
||||
{
|
||||
#ifdef _X360
|
||||
char const *szEvent = kvEvent->GetName();
|
||||
|
||||
if ( !Q_stricmp( "OnDowloadableContentInstalled", szEvent ) )
|
||||
{
|
||||
m_bNeedToDiscoverAllDlcs = true;
|
||||
}
|
||||
else if ( !Q_stricmp( "OnLiveMembershipPurchased", szEvent ) )
|
||||
{
|
||||
m_bNeedToDiscoverAllDlcs = true;
|
||||
}
|
||||
else if ( !Q_stricmp( "OnSysSigninChange", szEvent ) )
|
||||
{
|
||||
m_bNeedToDiscoverAllDlcs = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined( NO_STEAM ) && !defined( SWDS )
|
||||
void CDlcManager::Steam_OnDLCInstalled( DlcInstalled_t *pParam )
|
||||
{
|
||||
m_bNeedToDiscoverAllDlcs = false;
|
||||
|
||||
TitleDlcDescription_t const *dlcs = g_pMatchFramework->GetMatchTitle()->DescribeTitleDlcs();
|
||||
if ( !dlcs )
|
||||
return;
|
||||
|
||||
TitleDataFieldsDescription_t const *fields = g_pMatchFramework->GetMatchTitle()->DescribeTitleDataStorage();
|
||||
|
||||
uint64 uiOldDlcMask = m_pDataInfo->GetUint64( "@info/installed" );
|
||||
if ( !m_pDataInfo )
|
||||
{
|
||||
m_pDataInfo = new KeyValues( "DlcManager" );
|
||||
m_pDataInfo->SetUint64( "@info/installed", 0 );
|
||||
}
|
||||
IPlayerLocal *pPlayerLocal = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( XBX_GetPrimaryUserId() );
|
||||
for ( ; dlcs->m_uiLicenseMaskId; ++ dlcs )
|
||||
{
|
||||
// Check if DLC already detected
|
||||
if ( ( uiOldDlcMask & dlcs->m_uiLicenseMaskId ) == dlcs->m_uiLicenseMaskId )
|
||||
continue;
|
||||
|
||||
// Check player profile first
|
||||
TitleDataFieldsDescription_t const *pDlcField = dlcs->m_szTitleDataBitfieldStatName ?
|
||||
TitleDataFieldsDescriptionFindByString( fields, dlcs->m_szTitleDataBitfieldStatName ) : NULL;
|
||||
if ( pDlcField && pPlayerLocal &&
|
||||
TitleDataFieldsDescriptionGetBit( pDlcField, pPlayerLocal ) )
|
||||
{
|
||||
m_pDataInfo->SetUint64( "@info/installed", m_pDataInfo->GetUint64( "@info/installed" ) | dlcs->m_uiLicenseMaskId );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check Steam subscription
|
||||
if ( steamapicontext->SteamApps()->BIsSubscribedApp( dlcs->m_idDlcAppId ) )
|
||||
{
|
||||
m_pDataInfo->SetUint64( "@info/installed", m_pDataInfo->GetUint64( "@info/installed" ) | dlcs->m_uiLicenseMaskId );
|
||||
|
||||
// Set player profile bit
|
||||
if ( pDlcField && pPlayerLocal )
|
||||
TitleDataFieldsDescriptionSetBit( pDlcField, pPlayerLocal, true );
|
||||
}
|
||||
}
|
||||
|
||||
// Send the event in case detected DLC installed changes
|
||||
uint64 uiNewDlcMask = m_pDataInfo->GetUint64( "@info/installed" );
|
||||
if ( uiNewDlcMask != uiOldDlcMask )
|
||||
{
|
||||
KeyValues *kvEvent = new KeyValues( "OnDowloadableContentInstalled" );
|
||||
kvEvent->SetUint64( "installed", uiNewDlcMask );
|
||||
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( kvEvent );
|
||||
m_bNeedToUpdateFileSystem = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
83
matchmaking/mm_dlc.h
Normal file
83
matchmaking/mm_dlc.h
Normal file
@@ -0,0 +1,83 @@
|
||||
//========= Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
#ifndef _MM_DLC_H_
|
||||
#define _MM_DLC_H_
|
||||
|
||||
#include "utlvector.h"
|
||||
#include "utlmap.h"
|
||||
|
||||
class CDlcManager :
|
||||
public IDlcManager,
|
||||
public IMatchEventsSink
|
||||
{
|
||||
public :
|
||||
CDlcManager();
|
||||
virtual ~CDlcManager();
|
||||
|
||||
//
|
||||
// IDlcManager implementation
|
||||
//
|
||||
public:
|
||||
//
|
||||
// RequestDlcUpdate
|
||||
// requests a background DLC update
|
||||
//
|
||||
virtual void RequestDlcUpdate();
|
||||
virtual bool IsDlcUpdateFinished( bool bWaitForFinish = false );
|
||||
|
||||
//
|
||||
// GetDataInfo
|
||||
// retrieves the last acquired dlc information
|
||||
//
|
||||
virtual KeyValues * GetDataInfo();
|
||||
|
||||
//
|
||||
// IMatchEventsSink implementation
|
||||
//
|
||||
public:
|
||||
virtual void OnEvent( KeyValues *kvEvent );
|
||||
|
||||
//
|
||||
// Interface for match system
|
||||
//
|
||||
public:
|
||||
void Update();
|
||||
|
||||
protected:
|
||||
KeyValues *m_pDataInfo;
|
||||
|
||||
#ifdef _X360
|
||||
HANDLE m_hEnumerator;
|
||||
XOVERLAPPED m_xOverlapped;
|
||||
int32 m_dwNumItems;
|
||||
DWORD m_dwLicenseMask;
|
||||
CUtlVector< XCONTENT_DATA > m_arrContentData;
|
||||
void CreateNextContent();
|
||||
void ProcessNextContent();
|
||||
#endif
|
||||
|
||||
#if !defined( NO_STEAM ) && !defined( SWDS )
|
||||
STEAM_CALLBACK_MANUAL( CDlcManager, Steam_OnDLCInstalled, DlcInstalled_t, m_CallbackOnDLCInstalled );
|
||||
#endif
|
||||
|
||||
enum State_t
|
||||
{
|
||||
STATE_IDLE,
|
||||
#ifdef _X360
|
||||
STATE_XENUMERATE,
|
||||
STATE_XCONTENT_CREATE
|
||||
#endif
|
||||
};
|
||||
State_t m_eState;
|
||||
bool m_bNeedToDiscoverAllDlcs;
|
||||
bool m_bNeedToUpdateFileSystem;
|
||||
float m_flTimestamp;
|
||||
};
|
||||
|
||||
extern class CDlcManager *g_pDlcManager;
|
||||
|
||||
#endif // _DATACENTER_H_
|
||||
221
matchmaking/mm_events.cpp
Normal file
221
matchmaking/mm_events.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_events.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
CMatchEventsSubscription::CMatchEventsSubscription() :
|
||||
m_bAllowNestedBroadcasts( false ),
|
||||
m_bBroadcasting( false )
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
CMatchEventsSubscription::~CMatchEventsSubscription()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
static CMatchEventsSubscription g_MatchEventsSubscription;
|
||||
CMatchEventsSubscription *g_pMatchEventsSubscription = &g_MatchEventsSubscription;
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
void CMatchEventsSubscription::Subscribe( IMatchEventsSink *pSink )
|
||||
{
|
||||
if ( !pSink )
|
||||
return;
|
||||
|
||||
int idx = m_arrSinks.Find( pSink );
|
||||
if ( m_arrSinks.IsValidIndex( idx ) )
|
||||
{
|
||||
Assert( m_arrRefCount.IsValidIndex( idx ) );
|
||||
Assert( m_arrRefCount[ idx ] > 0 );
|
||||
|
||||
++ m_arrRefCount[ idx ];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_arrSinks.AddToTail( pSink );
|
||||
m_arrRefCount.AddToTail( 1 );
|
||||
|
||||
Assert( m_arrSinks.Count() == m_arrRefCount.Count() );
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchEventsSubscription::Unsubscribe( IMatchEventsSink *pSink )
|
||||
{
|
||||
if ( !pSink )
|
||||
return;
|
||||
|
||||
int idx = m_arrSinks.Find( pSink );
|
||||
Assert( m_arrSinks.IsValidIndex( idx ) );
|
||||
if ( !m_arrSinks.IsValidIndex( idx ) )
|
||||
return;
|
||||
|
||||
Assert( m_arrRefCount.IsValidIndex( idx ) );
|
||||
Assert( m_arrRefCount[ idx ] > 0 );
|
||||
|
||||
-- m_arrRefCount[ idx ];
|
||||
if ( m_arrRefCount[ idx ] <= 0 )
|
||||
{
|
||||
m_arrSinks.Remove( idx );
|
||||
m_arrRefCount.Remove( idx );
|
||||
|
||||
Assert( m_arrSinks.Count() == m_arrRefCount.Count() );
|
||||
|
||||
// Update outstanding iterators that are beyond removal point
|
||||
for ( int k = 0; k < m_arrIteratorsOutstanding.Count(); ++ k )
|
||||
{
|
||||
if ( m_arrIteratorsOutstanding[k] >= idx )
|
||||
-- m_arrIteratorsOutstanding[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchEventsSubscription::Shutdown()
|
||||
{
|
||||
m_bBroadcasting = true; // Blocks all BroadcastEvent calls from being dispatched!
|
||||
}
|
||||
|
||||
void CMatchEventsSubscription::BroadcastEvent( KeyValues *pEvent )
|
||||
{
|
||||
//
|
||||
// Network raw packet decryption
|
||||
//
|
||||
if ( !Q_stricmp( "OnNetLanConnectionlessPacket", pEvent->GetName() ) )
|
||||
{
|
||||
if ( void * pRawPacket = pEvent->GetPtr( "rawpkt" ) )
|
||||
{
|
||||
netpacket_t *pkt = ( netpacket_t * ) pRawPacket;
|
||||
pEvent->deleteThis();
|
||||
|
||||
g_pConnectionlessLanMgr->ProcessConnectionlessPacket( pkt );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Broadcasting events is reliable even when subscribers get added
|
||||
// or removed during broadcasts, or even broadcast nested events.
|
||||
//
|
||||
// Nested broadcasts are not allowed because it messes up the perception
|
||||
// of event timeline by external listeners, nested broadcast is enqueued
|
||||
// instead and broadcast after the original event has been broadcast to
|
||||
// all subscribers.
|
||||
//
|
||||
|
||||
if ( m_bBroadcasting )
|
||||
{
|
||||
if ( !m_bAllowNestedBroadcasts )
|
||||
{
|
||||
m_arrQueuedEvents.AddToTail( pEvent );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_bBroadcasting = true;
|
||||
|
||||
KeyValues::AutoDelete autoDeleteEvent( pEvent );
|
||||
m_arrSentEvents.AddToHead( pEvent );
|
||||
|
||||
// Internally events are cracked before external subscribers
|
||||
g_pMMF->OnEvent( pEvent );
|
||||
|
||||
// iterate subscribers
|
||||
for ( m_arrIteratorsOutstanding.AddToTail( 0 );
|
||||
m_arrIteratorsOutstanding.Tail() < m_arrSinks.Count();
|
||||
++ m_arrIteratorsOutstanding.Tail() )
|
||||
{
|
||||
int i = m_arrIteratorsOutstanding.Tail();
|
||||
IMatchEventsSink *pSink = m_arrSinks[ i ];
|
||||
|
||||
Assert( m_arrRefCount.IsValidIndex( i ) );
|
||||
Assert( m_arrRefCount[i] > 0 );
|
||||
Assert( pSink );
|
||||
|
||||
pSink->OnEvent( pEvent );
|
||||
}
|
||||
m_arrIteratorsOutstanding.RemoveMultipleFromTail( 1 );
|
||||
|
||||
m_bBroadcasting = false;
|
||||
|
||||
//
|
||||
// Broadcast queued events
|
||||
//
|
||||
if ( m_arrQueuedEvents.Count() > 0 )
|
||||
{
|
||||
KeyValues *pQueued = m_arrQueuedEvents.Head();
|
||||
m_arrQueuedEvents.RemoveMultipleFromHead( 1 );
|
||||
BroadcastEvent( pQueued );
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// No more queued events left, clean up registered event data
|
||||
//
|
||||
for ( int k = 0; k < m_arrEventData.Count(); ++ k )
|
||||
{
|
||||
KeyValues *pRegistered = m_arrEventData[k];
|
||||
pRegistered->deleteThis();
|
||||
}
|
||||
m_arrEventData.Purge();
|
||||
m_arrSentEvents.Purge();
|
||||
}
|
||||
|
||||
void CMatchEventsSubscription::RegisterEventData( KeyValues *pEventData )
|
||||
{
|
||||
Assert( pEventData );
|
||||
if ( !pEventData )
|
||||
return;
|
||||
|
||||
Assert( m_bBroadcasting );
|
||||
|
||||
char const *szEventDataKey = pEventData->GetName();
|
||||
Assert( szEventDataKey && *szEventDataKey );
|
||||
if ( !szEventDataKey || !*szEventDataKey )
|
||||
return;
|
||||
|
||||
for ( int k = 0; k < m_arrEventData.Count(); ++ k )
|
||||
{
|
||||
KeyValues *&pRegistered = m_arrEventData[k];
|
||||
if ( !Q_stricmp( szEventDataKey, pRegistered->GetName() ) )
|
||||
{
|
||||
pRegistered->deleteThis();
|
||||
pRegistered = pEventData;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_arrEventData.AddToTail( pEventData );
|
||||
}
|
||||
|
||||
KeyValues * CMatchEventsSubscription::GetEventData( char const *szEventDataKey )
|
||||
{
|
||||
Assert( m_bBroadcasting );
|
||||
|
||||
for ( int k = 0; k < m_arrEventData.Count(); ++ k )
|
||||
{
|
||||
KeyValues *pRegistered = m_arrEventData[k];
|
||||
if ( !Q_stricmp( szEventDataKey, pRegistered->GetName() ) )
|
||||
return pRegistered;
|
||||
}
|
||||
|
||||
for ( int k = 0; k < m_arrSentEvents.Count(); ++ k )
|
||||
{
|
||||
KeyValues *pRegistered = m_arrSentEvents[k];
|
||||
if ( !Q_stricmp( szEventDataKey, pRegistered->GetName() ) )
|
||||
return pRegistered;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
51
matchmaking/mm_events.h
Normal file
51
matchmaking/mm_events.h
Normal file
@@ -0,0 +1,51 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_EVENTS_H
|
||||
#define MM_EVENTS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "mm_framework.h"
|
||||
#include "utlvector.h"
|
||||
|
||||
class CMatchEventsSubscription : public IMatchEventsSubscription
|
||||
{
|
||||
// Methods of IMatchEventsSubscription
|
||||
public:
|
||||
virtual void Subscribe( IMatchEventsSink *pSink );
|
||||
virtual void Unsubscribe( IMatchEventsSink *pSink );
|
||||
|
||||
virtual void BroadcastEvent( KeyValues *pEvent );
|
||||
|
||||
virtual void RegisterEventData( KeyValues *pEventData );
|
||||
virtual KeyValues * GetEventData( char const *szEventDataKey );
|
||||
|
||||
public:
|
||||
bool IsBroacasting() const { return m_bBroadcasting; }
|
||||
void Shutdown();
|
||||
|
||||
public:
|
||||
CMatchEventsSubscription();
|
||||
~CMatchEventsSubscription();
|
||||
|
||||
protected:
|
||||
CUtlVector< IMatchEventsSink * > m_arrSinks;
|
||||
CUtlVector< int > m_arrRefCount;
|
||||
CUtlVector< int > m_arrIteratorsOutstanding;
|
||||
|
||||
bool m_bBroadcasting;
|
||||
bool m_bAllowNestedBroadcasts;
|
||||
CUtlVector< KeyValues * > m_arrQueuedEvents;
|
||||
CUtlVector< KeyValues * > m_arrEventData;
|
||||
CUtlVector< KeyValues * > m_arrSentEvents;
|
||||
};
|
||||
|
||||
// Match events subscription singleton
|
||||
extern CMatchEventsSubscription *g_pMatchEventsSubscription;
|
||||
|
||||
#endif // MM_EVENTS_H
|
||||
128
matchmaking/mm_extensions.cpp
Normal file
128
matchmaking/mm_extensions.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_extensions.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
CMatchExtensions::CMatchExtensions()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
CMatchExtensions::~CMatchExtensions()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
static CMatchExtensions g_MatchExtensions;
|
||||
CMatchExtensions *g_pMatchExtensions = &g_MatchExtensions;
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
void CMatchExtensions::RegisterExtensionInterface( char const *szInterfaceString, void *pvInterface )
|
||||
{
|
||||
Assert( szInterfaceString );
|
||||
Assert( pvInterface );
|
||||
if ( !szInterfaceString || !pvInterface )
|
||||
return;
|
||||
|
||||
RegisteredInterface_t &rInfo = m_mapRegisteredInterfaces[ szInterfaceString ];
|
||||
Assert( rInfo.m_nRefCount >= 0 );
|
||||
|
||||
if ( rInfo.m_nRefCount > 0 )
|
||||
{
|
||||
// Interface already registered
|
||||
Assert( pvInterface == rInfo.m_pvInterface );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fresh registration of the interface
|
||||
Assert( !rInfo.m_pvInterface );
|
||||
rInfo.m_pvInterface = pvInterface;
|
||||
}
|
||||
|
||||
// Increment refcount
|
||||
++ rInfo.m_nRefCount;
|
||||
|
||||
// Fire a callback for interface registration
|
||||
OnExtensionInterfaceUpdated( szInterfaceString, pvInterface );
|
||||
}
|
||||
|
||||
void CMatchExtensions::UnregisterExtensionInterface( char const *szInterfaceString, void *pvInterface )
|
||||
{
|
||||
Assert( szInterfaceString );
|
||||
Assert( pvInterface );
|
||||
if ( !szInterfaceString || !pvInterface )
|
||||
return;
|
||||
|
||||
RegisteredInterface_t &rInfo = m_mapRegisteredInterfaces[ szInterfaceString ];
|
||||
Assert( rInfo.m_nRefCount > 0 );
|
||||
if ( rInfo.m_nRefCount <= 0 )
|
||||
return;
|
||||
|
||||
Assert( pvInterface == rInfo.m_pvInterface );
|
||||
|
||||
-- rInfo.m_nRefCount;
|
||||
if ( 0 == rInfo.m_nRefCount )
|
||||
{
|
||||
rInfo.m_pvInterface = NULL;
|
||||
OnExtensionInterfaceUpdated( szInterfaceString, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
void * CMatchExtensions::GetRegisteredExtensionInterface( char const *szInterfaceString )
|
||||
{
|
||||
RegisteredInterface_t &rInfo = m_mapRegisteredInterfaces[ szInterfaceString ];
|
||||
|
||||
if ( rInfo.m_nRefCount > 0 && rInfo.m_pvInterface )
|
||||
return rInfo.m_pvInterface;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// Known interfaces recognition
|
||||
//
|
||||
|
||||
void CMatchExtensions::OnExtensionInterfaceUpdated( char const *szInterfaceString, void *pvInterface )
|
||||
{
|
||||
typedef void * Exts_t::* Ext_t;
|
||||
|
||||
struct CachedInterfacePtr_t
|
||||
{
|
||||
char const *m_szName;
|
||||
Ext_t m_ppInterface;
|
||||
};
|
||||
|
||||
static CachedInterfacePtr_t s_table[] =
|
||||
{
|
||||
{ LOCALIZE_INTERFACE_VERSION, (Ext_t) &Exts_t::m_pILocalize },
|
||||
{ INETSUPPORT_VERSION_STRING, (Ext_t) &Exts_t::m_pINetSupport },
|
||||
{ IENGINEVOICE_INTERFACE_VERSION, (Ext_t) &Exts_t::m_pIEngineVoice },
|
||||
{ VENGINE_CLIENT_INTERFACE_VERSION, (Ext_t) &Exts_t::m_pIVEngineClient },
|
||||
{ INTERFACEVERSION_VENGINESERVER, (Ext_t) &Exts_t::m_pIVEngineServer },
|
||||
{ INTERFACEVERSION_SERVERGAMEDLL, (Ext_t) &Exts_t::m_pIServerGameDLL },
|
||||
{ INTERFACEVERSION_GAMEEVENTSMANAGER2, (Ext_t) &Exts_t::m_pIGameEventManager2 },
|
||||
{ CLIENT_DLL_INTERFACE_VERSION, ( Ext_t ) &Exts_t::m_pIBaseClientDLL },
|
||||
#ifdef _X360
|
||||
{ XBOXSYSTEM_INTERFACE_VERSION, (Ext_t) &Exts_t::m_pIXboxSystem },
|
||||
{ XONLINE_INTERFACE_VERSION, (Ext_t) &Exts_t::m_pIXOnline },
|
||||
#endif
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
for ( CachedInterfacePtr_t *ptr = s_table; ptr->m_szName; ++ ptr )
|
||||
{
|
||||
if ( !Q_stricmp( ptr->m_szName, szInterfaceString ) )
|
||||
{
|
||||
m_exts.*(ptr->m_ppInterface) = pvInterface;
|
||||
}
|
||||
}
|
||||
}
|
||||
99
matchmaking/mm_extensions.h
Normal file
99
matchmaking/mm_extensions.h
Normal file
@@ -0,0 +1,99 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_EXTENSIONS_H
|
||||
#define MM_EXTENSIONS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "UtlStringMap.h"
|
||||
|
||||
#include "vgui/ILocalize.h"
|
||||
#include "engine/inetsupport.h"
|
||||
#include "cdll_int.h"
|
||||
#include "eiface.h"
|
||||
#include "igameevents.h"
|
||||
#ifdef _X360
|
||||
#include "ixboxsystem.h"
|
||||
#endif
|
||||
#include "eiface.h"
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
#include "engine/ienginevoice.h"
|
||||
|
||||
class CMatchExtensions : public IMatchExtensions
|
||||
{
|
||||
// Methods of IMatchExtensions
|
||||
public:
|
||||
// Registers an extension interface
|
||||
virtual void RegisterExtensionInterface( char const *szInterfaceString, void *pvInterface );
|
||||
|
||||
// Unregisters an extension interface
|
||||
virtual void UnregisterExtensionInterface( char const *szInterfaceString, void *pvInterface );
|
||||
|
||||
// Gets a pointer to a registered extension interface
|
||||
virtual void * GetRegisteredExtensionInterface( char const *szInterfaceString );
|
||||
|
||||
public:
|
||||
CMatchExtensions();
|
||||
~CMatchExtensions();
|
||||
|
||||
protected:
|
||||
struct RegisteredInterface_t
|
||||
{
|
||||
void *m_pvInterface;
|
||||
int m_nRefCount;
|
||||
|
||||
RegisteredInterface_t() : m_nRefCount( 0 ), m_pvInterface( 0 ) {}
|
||||
};
|
||||
typedef CUtlStringMap< RegisteredInterface_t > InterfaceMap_t;
|
||||
InterfaceMap_t m_mapRegisteredInterfaces;
|
||||
|
||||
protected:
|
||||
void OnExtensionInterfaceUpdated( char const *szInterfaceString, void *pvInterface );
|
||||
|
||||
public:
|
||||
vgui::ILocalize * GetILocalize() { return m_exts.m_pILocalize; }
|
||||
INetSupport * GetINetSupport() { return m_exts.m_pINetSupport; }
|
||||
IEngineVoice * GetIEngineVoice() { return m_exts.m_pIEngineVoice; }
|
||||
IVEngineClient * GetIVEngineClient() { return m_exts.m_pIVEngineClient; }
|
||||
IVEngineServer * GetIVEngineServer() { return m_exts.m_pIVEngineServer; }
|
||||
IServerGameDLL * GetIServerGameDLL() { return m_exts.m_pIServerGameDLL; }
|
||||
IGameEventManager2 * GetIGameEventManager2() { return m_exts.m_pIGameEventManager2; }
|
||||
IBaseClientDLL * GetIBaseClientDLL() { return m_exts.m_pIBaseClientDLL; }
|
||||
#ifdef _X360
|
||||
IXboxSystem * GetIXboxSystem() { return m_exts.m_pIXboxSystem; }
|
||||
IXOnline * GetIXOnline() { return m_exts.m_pIXOnline; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Known extension interfaces
|
||||
struct Exts_t
|
||||
{
|
||||
inline Exts_t() { memset( this, 0, sizeof( *this ) ); }
|
||||
|
||||
vgui::ILocalize *m_pILocalize;
|
||||
INetSupport *m_pINetSupport;
|
||||
IEngineVoice *m_pIEngineVoice;
|
||||
IVEngineClient *m_pIVEngineClient;
|
||||
IVEngineServer *m_pIVEngineServer;
|
||||
IServerGameDLL *m_pIServerGameDLL;
|
||||
IGameEventManager2 *m_pIGameEventManager2;
|
||||
IBaseClientDLL *m_pIBaseClientDLL;
|
||||
#ifdef _X360
|
||||
IXboxSystem *m_pIXboxSystem;
|
||||
IXOnline *m_pIXOnline;
|
||||
#endif
|
||||
}
|
||||
m_exts;
|
||||
};
|
||||
|
||||
// Match title singleton
|
||||
extern CMatchExtensions *g_pMatchExtensions;
|
||||
|
||||
#endif // MM_EXTENSIONS_H
|
||||
960
matchmaking/mm_framework.cpp
Normal file
960
matchmaking/mm_framework.cpp
Normal file
@@ -0,0 +1,960 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_framework.h"
|
||||
#include "mm_netmsgcontroller.h"
|
||||
|
||||
#include "matchsystem.h"
|
||||
|
||||
#include "mm_title_main.h"
|
||||
#include "x360_lobbyapi.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
// Stress testing events listeners every frame
|
||||
ConVar mm_events_listeners_validation( "mm_events_listeners_validation", "0", FCVAR_DEVELOPMENTONLY );
|
||||
|
||||
|
||||
//
|
||||
// Implementation of Steam invite listener
|
||||
//
|
||||
uint64 g_uiLastInviteFlags = 0ull;
|
||||
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
||||
class CMatchSteamInviteListener
|
||||
{
|
||||
public:
|
||||
void RunFrame();
|
||||
void Register();
|
||||
STEAM_CALLBACK_MANUAL( CMatchSteamInviteListener, Steam_OnGameLobbyJoinRequested, GameLobbyJoinRequested_t, m_CallbackOnGameLobbyJoinRequested );
|
||||
#ifdef _PS3
|
||||
STEAM_CALLBACK_MANUAL( CMatchSteamInviteListener, Steam_OnPSNGameBootInviteResult, PSNGameBootInviteResult_t, m_CallbackOnPSNGameBootInviteResult );
|
||||
#endif
|
||||
|
||||
protected:
|
||||
GameLobbyJoinRequested_t m_msgPending;
|
||||
}
|
||||
g_MatchSteamInviteListener;
|
||||
|
||||
void CMatchSteamInviteListener::Register()
|
||||
{
|
||||
m_CallbackOnGameLobbyJoinRequested.Register( this, &CMatchSteamInviteListener::Steam_OnGameLobbyJoinRequested );
|
||||
#ifdef _PS3
|
||||
m_CallbackOnPSNGameBootInviteResult.Register( this, &CMatchSteamInviteListener::Steam_OnPSNGameBootInviteResult );
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
class CMatchSteamInviteListener
|
||||
{
|
||||
public:
|
||||
void RunFrame() {}
|
||||
void Register() {}
|
||||
}
|
||||
g_MatchSteamInviteListener;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
CMatchFramework::CMatchFramework() :
|
||||
m_pMatchSession( NULL ),
|
||||
m_bJoinTeamSession( false ),
|
||||
m_pTeamSessionSettings( NULL )
|
||||
{
|
||||
}
|
||||
|
||||
CMatchFramework::~CMatchFramework()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
InitReturnVal_t CMatchFramework::Init()
|
||||
{
|
||||
InitReturnVal_t ret = INIT_OK;
|
||||
|
||||
ret = MM_Title_Init();
|
||||
if ( ret != INIT_OK )
|
||||
return ret;
|
||||
|
||||
g_MatchSteamInviteListener.Register();
|
||||
|
||||
return INIT_OK;
|
||||
}
|
||||
|
||||
void CMatchFramework::Shutdown()
|
||||
{
|
||||
// Shutdown event system
|
||||
g_pMatchEventsSubscription->Shutdown();
|
||||
|
||||
// Shutdown the title
|
||||
MM_Title_Shutdown();
|
||||
|
||||
// Cancel any pending server updates before shutdown
|
||||
g_pServerManager->EnableServersUpdate( false );
|
||||
|
||||
// Cancel any pending datacenter queries
|
||||
g_pDatacenter->EnableUpdate( false );
|
||||
}
|
||||
|
||||
void CMatchFramework::RunFrame()
|
||||
{
|
||||
// Run frame listeners validation if requested
|
||||
if ( mm_events_listeners_validation.GetBool() )
|
||||
{
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "mm_events_listeners_validation" ) );
|
||||
}
|
||||
|
||||
#ifdef _X360
|
||||
if ( IXOnline *pIXOnline = g_pMatchExtensions->GetIXOnline() )
|
||||
pIXOnline->RunFrame();
|
||||
|
||||
MMX360_UpdateDormantOperations();
|
||||
|
||||
SysSession360_UpdatePending();
|
||||
#endif
|
||||
|
||||
RunFrame_Invite();
|
||||
g_MatchSteamInviteListener.RunFrame();
|
||||
|
||||
#ifdef _X360
|
||||
// Pump rate adjustments
|
||||
MatchSession_RateAdjustmentUpdate();
|
||||
#endif
|
||||
|
||||
// Let the network mgr run
|
||||
g_pConnectionlessLanMgr->Update();
|
||||
|
||||
// Let the matchsystem run
|
||||
g_pMatchSystem->Update();
|
||||
|
||||
// Let the match session run
|
||||
if ( m_pMatchSession )
|
||||
m_pMatchSession->Update();
|
||||
|
||||
// Let the match title run frame
|
||||
g_pIMatchTitle->RunFrame();
|
||||
|
||||
if ( m_bJoinTeamSession )
|
||||
{
|
||||
m_bJoinTeamSession = false;
|
||||
MatchSession( m_pTeamSessionSettings );
|
||||
m_pTeamSessionSettings->deleteThis();
|
||||
m_pTeamSessionSettings = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
IMatchExtensions * CMatchFramework::GetMatchExtensions()
|
||||
{
|
||||
return g_pMatchExtensions;
|
||||
}
|
||||
|
||||
IMatchEventsSubscription * CMatchFramework::GetEventsSubscription()
|
||||
{
|
||||
return g_pMatchEventsSubscription;
|
||||
}
|
||||
|
||||
IMatchTitle * CMatchFramework::GetMatchTitle()
|
||||
{
|
||||
return g_pIMatchTitle;
|
||||
}
|
||||
|
||||
IMatchTitleGameSettingsMgr * CMatchFramework::GetMatchTitleGameSettingsMgr()
|
||||
{
|
||||
return g_pIMatchTitleGameSettingsMgr;
|
||||
}
|
||||
|
||||
IMatchNetworkMsgController * CMatchFramework::GetMatchNetworkMsgController()
|
||||
{
|
||||
return g_pMatchNetMsgControllerBase;
|
||||
}
|
||||
|
||||
IMatchSystem * CMatchFramework::GetMatchSystem()
|
||||
{
|
||||
return g_pMatchSystem;
|
||||
}
|
||||
|
||||
void CMatchFramework::ApplySettings( KeyValues* keyValues )
|
||||
{
|
||||
g_pMatchExtensions->GetIServerGameDLL()->ApplyGameSettings( keyValues );
|
||||
}
|
||||
|
||||
|
||||
#ifdef _X360
|
||||
static XINVITE_INFO s_InviteInfo;
|
||||
#else
|
||||
static uint64 s_InviteInfo;
|
||||
#endif
|
||||
static bool s_bInviteSessionDelayedJoin;
|
||||
static int s_nInviteConfirmed;
|
||||
|
||||
template < int datasize >
|
||||
static bool IsZeroData( void const *pvData )
|
||||
{
|
||||
static char s_zerodata[ datasize ];
|
||||
return !memcmp( s_zerodata, pvData, datasize );
|
||||
}
|
||||
|
||||
static bool ValidateInviteController( int iController )
|
||||
{
|
||||
#ifdef _X360
|
||||
XUSER_SIGNIN_STATE eSignInState = XUserGetSigninState( iController );
|
||||
XUSER_SIGNIN_INFO xsi = {0};
|
||||
if ( ( eSignInState != eXUserSigninState_SignedInToLive ) ||
|
||||
( ERROR_SUCCESS != XUserGetSigninInfo( iController, XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) ) ||
|
||||
! ( xsi.dwInfoFlags & XUSER_INFO_FLAG_LIVE_ENABLED ) )
|
||||
{
|
||||
DevWarning( "ValidateInviteController: ctrl%d check1 failed (state=%d, flags=0x%X, xuid=%llx)!\n",
|
||||
eSignInState, xsi.dwInfoFlags, xsi.xuid );
|
||||
|
||||
if ( KeyValues *notify = new KeyValues(
|
||||
"OnInvite", "action", "error", "error", "NotOnline" ) )
|
||||
{
|
||||
notify->SetInt( "user", iController );
|
||||
g_pMatchEventsSubscription->BroadcastEvent( notify );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOL bMultiplayer = FALSE;
|
||||
if ( ( ERROR_SUCCESS != XUserCheckPrivilege( iController, XPRIVILEGE_MULTIPLAYER_SESSIONS, &bMultiplayer ) ) ||
|
||||
( !bMultiplayer ) )
|
||||
{
|
||||
DevWarning( "ValidateInviteController: ctrl%d check2 failed (state=%d, flags=0x%X, xuid=%llx) - on multiplayer priv!\n",
|
||||
eSignInState, xsi.dwInfoFlags, xsi.xuid );
|
||||
|
||||
if ( KeyValues *notify = new KeyValues(
|
||||
"OnInvite", "action", "error", "error", "NoMultiplayer" ) )
|
||||
{
|
||||
notify->SetInt( "user", iController );
|
||||
g_pMatchEventsSubscription->BroadcastEvent( notify );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ValidateInviteControllers()
|
||||
{
|
||||
for ( DWORD k = 0; k < XBX_GetNumGameUsers(); ++ k )
|
||||
{
|
||||
if ( !ValidateInviteController( XBX_GetUserId( k ) ) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool VerifyInviteEligibility()
|
||||
{
|
||||
#ifdef _X360
|
||||
// Make sure that the inviter is not signed in
|
||||
for ( int k = 0; k < XUSER_MAX_COUNT; ++ k )
|
||||
{
|
||||
XUID xuid;
|
||||
if ( ERROR_SUCCESS == XUserGetXUID( k, &xuid ) &&
|
||||
xuid == s_InviteInfo.xuidInviter )
|
||||
{
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues(
|
||||
"OnInvite", "action", "error", "error", "SameConsole" ) );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the user is currently inactive
|
||||
bool bExistingUser = false;
|
||||
for ( DWORD k = 0; k < XBX_GetNumGameUsers(); ++ k )
|
||||
{
|
||||
if ( XBX_GetInvitedUserId() == (DWORD) XBX_GetUserId( k ) &&
|
||||
!XBX_GetUserIsGuest( k ) )
|
||||
{
|
||||
bExistingUser = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this is the existing user that the invite is for a different session
|
||||
// than the session they are currently in (e.g. they are in a lobby and do
|
||||
// "Join Party and Game" with another user who is in the same lobby)
|
||||
char chInviteSessionInfo[ XSESSION_INFO_STRING_LENGTH ] = {0};
|
||||
MMX360_SessionInfoToString( s_InviteInfo.hostInfo, chInviteSessionInfo );
|
||||
if ( IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession() )
|
||||
{
|
||||
bool bJoinable = ( ( IMatchSessionInternal * ) pIMatchSession )->IsAnotherSessionJoinable( chInviteSessionInfo );
|
||||
if ( !bJoinable && bExistingUser )
|
||||
{
|
||||
Warning( "VerifyInviteEligibility: declined invite due to local session!\n" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// New user is eligible since otherwise he shouldn't be able to accept an invite
|
||||
if ( !bExistingUser || ( XBX_GetNumGameUsers() < 2 ) ||
|
||||
( g_pMMF->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_INVITE_ONLY_SINGLE_USER ) )
|
||||
{
|
||||
if ( !ValidateInviteController( XBX_GetInvitedUserId() ) )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check that every user is valid
|
||||
return ValidateInviteControllers();
|
||||
}
|
||||
|
||||
static void JoinInviteSession()
|
||||
{
|
||||
s_bInviteSessionDelayedJoin = false;
|
||||
|
||||
#ifdef _X360
|
||||
if ( 0ull == ( uint64 const & ) s_InviteInfo.hostInfo.sessionID )
|
||||
#else
|
||||
if ( !s_InviteInfo )
|
||||
#endif
|
||||
return;
|
||||
|
||||
if ( g_pMatchExtensions->GetIVEngineClient()->IsDrawingLoadingImage() )
|
||||
{
|
||||
s_bInviteSessionDelayedJoin = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Invites cannot be accepted from inside an event broadcast
|
||||
// internally used events must be top-level events since they
|
||||
// operate on signed in / active users, trigger playermanager,
|
||||
// account access and other events
|
||||
// Wait until next frame in such case
|
||||
if ( g_pMatchEventsSubscription && g_pMatchEventsSubscription->IsBroacasting() )
|
||||
{
|
||||
s_bInviteSessionDelayedJoin = true;
|
||||
return;
|
||||
}
|
||||
|
||||
#if !defined( NO_STEAM ) && !defined( _GAMECONSOLE ) && !defined( SWDS )
|
||||
extern bool g_bSteamStatsReceived;
|
||||
if ( !g_bSteamStatsReceived && ( g_uiLastInviteFlags & MM_INVITE_FLAG_PCBOOT ) )
|
||||
{
|
||||
s_bInviteSessionDelayedJoin = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _X360
|
||||
DevMsg( "JoinInviteSession: sessionid = %llx, xuid = %llx\n", ( uint64 const & ) s_InviteInfo.hostInfo.sessionID, s_InviteInfo.xuidInvitee );
|
||||
#else
|
||||
DevMsg( "JoinInviteSession: sessionid = %llx\n", s_InviteInfo );
|
||||
#endif
|
||||
|
||||
//
|
||||
// Validate the user accepting the invite
|
||||
//
|
||||
#ifdef _GAMECONSOLE
|
||||
if ( XBX_GetInvitedUserId() == INVALID_USER_ID )
|
||||
{
|
||||
DevWarning( "JoinInviteSession: no invited user!\n" );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef _X360
|
||||
XUSER_SIGNIN_STATE eSignInState = XUserGetSigninState( XBX_GetInvitedUserId() );
|
||||
XUSER_SIGNIN_INFO xsi = {0};
|
||||
if ( ( eSignInState != eXUserSigninState_SignedInToLive ) ||
|
||||
( ERROR_SUCCESS != XUserGetSigninInfo( XBX_GetInvitedUserId(), XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) ) ||
|
||||
! ( xsi.dwInfoFlags & XUSER_INFO_FLAG_LIVE_ENABLED ) ||
|
||||
( xsi.dwInfoFlags & XUSER_INFO_FLAG_GUEST ) ||
|
||||
!IsEqualXUID( xsi.xuid, s_InviteInfo.xuidInvitee ) )
|
||||
{
|
||||
DevWarning( "JoinInviteSession: invited user signin information validation failed (state=%d, flags=0x%X, xuid=%llx)!\n",
|
||||
eSignInState, xsi.dwInfoFlags, xsi.xuid );
|
||||
return;
|
||||
}
|
||||
BOOL bMultiplayer = FALSE;
|
||||
if ( ( ERROR_SUCCESS != XUserCheckPrivilege( XBX_GetInvitedUserId(), XPRIVILEGE_MULTIPLAYER_SESSIONS, &bMultiplayer ) ) ||
|
||||
( !bMultiplayer ) )
|
||||
{
|
||||
DevWarning( "JoinInviteSession: no multiplayer priv!\n" );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Check if the currently-involved user is accepting the invite
|
||||
//
|
||||
#ifdef _GAMECONSOLE
|
||||
bool bExistingUser = false;
|
||||
for ( DWORD k = 0; k < XBX_GetNumGameUsers(); ++ k )
|
||||
{
|
||||
if ( XBX_GetInvitedUserId() == (DWORD) XBX_GetUserId( k ) &&
|
||||
!XBX_GetUserIsGuest( k ) )
|
||||
{
|
||||
bExistingUser = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !bExistingUser ||
|
||||
( ( XBX_GetNumGameUsers() > 1 ) && ( g_pMMF->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_INVITE_ONLY_SINGLE_USER ) ) )
|
||||
{
|
||||
// Another controller is accepting the invite or guest status
|
||||
// has changed.
|
||||
// then we need to reset all our XBX core state:
|
||||
|
||||
DevMsg( "JoinInviteSession: activating inactive controller%d\n", XBX_GetInvitedUserId() );
|
||||
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnProfilesWriteOpportunity", "reason", "deactivation" ) );
|
||||
|
||||
XBX_ClearUserIdSlots();
|
||||
|
||||
XBX_SetPrimaryUserId( XBX_GetInvitedUserId() );
|
||||
XBX_SetPrimaryUserIsGuest( 0 );
|
||||
|
||||
XBX_SetUserId( 0, XBX_GetInvitedUserId() );
|
||||
XBX_SetUserIsGuest( 0, 0 );
|
||||
|
||||
XBX_SetNumGameUsers( 1 );
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnProfilesChanged", "numProfiles", int(1) ) );
|
||||
|
||||
IPlayerLocal *pPlayer = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() );
|
||||
if ( !pPlayer )
|
||||
{
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues(
|
||||
"OnInvite", "action", "error", "error", "" ) );
|
||||
return;
|
||||
}
|
||||
( ( PlayerLocal * ) pPlayer )->SetFlag_AwaitingTitleData();
|
||||
|
||||
// Since we have activated a new profile, we need to wait until title data gets loaded
|
||||
DevMsg( "JoinInviteSession: activated inactive controller%d, waiting for title data...\n", XBX_GetInvitedUserId() );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Validate storage device
|
||||
s_nInviteConfirmed = -1;
|
||||
if ( KeyValues *notify = new KeyValues( "OnInvite" ) )
|
||||
{
|
||||
#ifdef _X360
|
||||
char chSessionInfo[ XSESSION_INFO_STRING_LENGTH ] = {0};
|
||||
MMX360_SessionInfoToString( s_InviteInfo.hostInfo, chSessionInfo );
|
||||
notify->SetInt( "user", XBX_GetInvitedUserId() );
|
||||
notify->SetString( "sessioninfo", chSessionInfo );
|
||||
#else
|
||||
notify->SetUint64( "sessionid", s_InviteInfo );
|
||||
#endif
|
||||
notify->SetString( "action", "storage" );
|
||||
notify->SetPtr( "confirmed", &s_nInviteConfirmed );
|
||||
|
||||
g_pMatchEventsSubscription->BroadcastEvent( notify );
|
||||
|
||||
// If handlers decided they need to confirm storage devices, etc.
|
||||
if ( s_nInviteConfirmed != -1 )
|
||||
{
|
||||
DevMsg( "JoinInviteSession: waiting for storage device selection...\n" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify eligibility
|
||||
DevMsg( "JoinInviteSession: verifying eligibility...\n" );
|
||||
if ( !VerifyInviteEligibility() )
|
||||
return;
|
||||
DevMsg( "JoinInviteSession: connecting...\n" );
|
||||
|
||||
//
|
||||
// Argument validation
|
||||
//
|
||||
#ifdef _GAMECONSOLE
|
||||
Assert( XBX_GetInvitedUserId() >= 0 );
|
||||
Assert( XBX_GetInvitedUserId() < XUSER_MAX_COUNT );
|
||||
Assert( XBX_GetSlotByUserId( XBX_GetInvitedUserId() ) < ( int ) XBX_GetNumGameUsers() );
|
||||
Assert( XBX_GetNumGameUsers() < MAX_SPLITSCREEN_CLIENTS );
|
||||
#endif
|
||||
|
||||
// Requesting to join the stored off session
|
||||
KeyValues *pSettings = KeyValues::FromString(
|
||||
"settings",
|
||||
" system { "
|
||||
" network LIVE "
|
||||
" } "
|
||||
" options { "
|
||||
" action joinsession "
|
||||
" } "
|
||||
);
|
||||
|
||||
#ifdef _X360
|
||||
pSettings->SetUint64( "options/sessionid", ( const uint64 & ) s_InviteInfo.hostInfo.sessionID );
|
||||
|
||||
if ( !IsZeroData< sizeof( s_InviteInfo.hostInfo.keyExchangeKey ) >( &s_InviteInfo.hostInfo.keyExchangeKey ) )
|
||||
{
|
||||
// Missing sessioninfo will cause the session info to be discovered during session
|
||||
// creation time
|
||||
char chSessionInfoBuffer[ XSESSION_INFO_STRING_LENGTH ] = {0};
|
||||
MMX360_SessionInfoToString( s_InviteInfo.hostInfo, chSessionInfoBuffer );
|
||||
pSettings->SetString( "options/sessioninfo", chSessionInfoBuffer );
|
||||
}
|
||||
#else
|
||||
pSettings->SetUint64( "options/sessionid", s_InviteInfo );
|
||||
#endif
|
||||
|
||||
KeyValues::AutoDelete autodelete( pSettings );
|
||||
Q_memset( &s_InviteInfo, 0, sizeof( s_InviteInfo ) );
|
||||
|
||||
g_pMatchFramework->MatchSession( pSettings );
|
||||
}
|
||||
|
||||
static void OnInviteAccepted()
|
||||
{
|
||||
// Verify eligibility
|
||||
DevMsg( "OnInviteAccepted: verifying eligibility...\n" );
|
||||
if ( !VerifyInviteEligibility() )
|
||||
return;
|
||||
DevMsg( "OnInviteAccepted: confirming...\n" );
|
||||
|
||||
// Make sure the user confirms the invite
|
||||
s_nInviteConfirmed = -1;
|
||||
if ( KeyValues *notify = new KeyValues( "OnInvite" ) )
|
||||
{
|
||||
#ifdef _X360
|
||||
char chSessionInfo[ XSESSION_INFO_STRING_LENGTH ] = {0};
|
||||
MMX360_SessionInfoToString( s_InviteInfo.hostInfo, chSessionInfo );
|
||||
notify->SetInt( "user", XBX_GetInvitedUserId() );
|
||||
notify->SetString( "sessioninfo", chSessionInfo );
|
||||
#else
|
||||
notify->SetUint64( "sessionid", s_InviteInfo );
|
||||
#endif
|
||||
notify->SetString( "action", "accepted" );
|
||||
notify->SetPtr( "confirmed", &s_nInviteConfirmed );
|
||||
|
||||
g_pMatchEventsSubscription->BroadcastEvent( notify );
|
||||
|
||||
// If handlers decided they need to confirm destructive actions or
|
||||
// select storage devices, etc.
|
||||
if ( s_nInviteConfirmed != -1 )
|
||||
{
|
||||
DevMsg( "OnInviteAccepted: waiting for confirmation...\n" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
DevMsg( "OnInviteAccepted: accepting...\n" );
|
||||
|
||||
// Otherwise, launch depending on our current MOD
|
||||
// if ( !Q_stricmp( GetCurrentMod(), "left4dead2" ) ) <-- for multi-game package
|
||||
{
|
||||
// Kick off our join
|
||||
JoinInviteSession();
|
||||
}
|
||||
// else <-- for multi-game package supporting cross-game invites
|
||||
// {
|
||||
// // Save off our session ID for later retrieval
|
||||
// // NOTE: We may need to actually save off the inviter's XID and search for them later on if we took too long or the
|
||||
// // session they were a part of went away
|
||||
//
|
||||
// XBX_SetInviteSessionId( inviteInfo.hostInfo.sessionID );
|
||||
//
|
||||
// // Quit via the menu path "QuitNoConfirm"
|
||||
// EngineVGui()->SystemNotification( SYSTEMNOTIFY_INVITE_SHUTDOWN, NULL );
|
||||
// }
|
||||
}
|
||||
|
||||
void CMatchFramework::RunFrame_Invite()
|
||||
{
|
||||
if ( s_bInviteSessionDelayedJoin )
|
||||
JoinInviteSession();
|
||||
}
|
||||
|
||||
void CMatchFramework::AcceptInvite( int iController )
|
||||
{
|
||||
#ifdef _X360
|
||||
s_bInviteSessionDelayedJoin = false;
|
||||
|
||||
// Grab our invite info
|
||||
DWORD dwError = g_pMatchExtensions->GetIXOnline()->XInviteGetAcceptedInfo( iController, &s_InviteInfo );
|
||||
if ( dwError != ERROR_SUCCESS )
|
||||
{
|
||||
ZeroMemory( &s_InviteInfo, sizeof( s_InviteInfo ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// We only care if we're asked to join this title's session
|
||||
if ( s_InviteInfo.dwTitleID != GetMatchTitle()->GetTitleID() )
|
||||
{
|
||||
ZeroMemory( &s_InviteInfo, sizeof( s_InviteInfo ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// We just mark the invited user and let the matchmaking handle profile changes
|
||||
XBX_SetInvitedUserId( iController );
|
||||
|
||||
// Invite accepted logic after globals have been setup
|
||||
OnInviteAccepted();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
||||
void CMatchSteamInviteListener::Steam_OnGameLobbyJoinRequested( GameLobbyJoinRequested_t *pJoinInvite )
|
||||
{
|
||||
#ifdef _PS3
|
||||
if ( pJoinInvite->m_steamIDFriend.ConvertToUint64() != ~0ull )
|
||||
{
|
||||
g_uiLastInviteFlags = ( pJoinInvite->m_steamIDFriend.BConsoleUserAccount() ? MM_INVITE_FLAG_CONSOLE : 0 );
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined( _GAMECONSOLE )
|
||||
g_uiLastInviteFlags = ( pJoinInvite->m_steamIDFriend.ConvertToUint64() == ~0ull ) ? MM_INVITE_FLAG_PCBOOT : 0;
|
||||
#endif
|
||||
|
||||
m_msgPending = GameLobbyJoinRequested_t();
|
||||
s_bInviteSessionDelayedJoin = false;
|
||||
s_InviteInfo = pJoinInvite->m_steamIDLobby.ConvertToUint64();
|
||||
if ( !s_InviteInfo )
|
||||
return;
|
||||
|
||||
#ifdef _GAMECONSOLE
|
||||
// We just mark the invited user and let the matchmaking handle profile changes
|
||||
XBX_SetInvitedUserId( XBX_GetPrimaryUserId() );
|
||||
#endif
|
||||
|
||||
// Whether we have to make invite go pending
|
||||
char chBuffer[2] = {};
|
||||
if ( g_pMatchExtensions->GetIVEngineClient()->IsDrawingLoadingImage() ||
|
||||
( g_pMatchEventsSubscription && g_pMatchEventsSubscription->IsBroacasting() ) ||
|
||||
( g_pMatchExtensions->GetIBaseClientDLL()->GetStatus( chBuffer, 2 ), ( chBuffer[0] != '+' ) ) )
|
||||
{
|
||||
m_msgPending = *pJoinInvite;
|
||||
return;
|
||||
}
|
||||
|
||||
// Invite accepted logic after globals have been setup
|
||||
OnInviteAccepted();
|
||||
}
|
||||
|
||||
#ifdef _PS3
|
||||
void CMatchSteamInviteListener::Steam_OnPSNGameBootInviteResult( PSNGameBootInviteResult_t *pParam )
|
||||
{
|
||||
if ( pParam->m_bGameBootInviteExists && pParam->m_steamIDLobby.IsValid() )
|
||||
{
|
||||
g_uiLastInviteFlags = MM_INVITE_FLAG_CONSOLE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CMatchSteamInviteListener::RunFrame()
|
||||
{
|
||||
if ( m_msgPending.m_steamIDLobby.IsValid() )
|
||||
{
|
||||
GameLobbyJoinRequested_t msgRequest = m_msgPending;
|
||||
Steam_OnGameLobbyJoinRequested( &msgRequest );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
IMatchSession *CMatchFramework::GetMatchSession()
|
||||
{
|
||||
return m_pMatchSession;
|
||||
}
|
||||
|
||||
void CMatchFramework::CreateSession( KeyValues *pSettings )
|
||||
{
|
||||
DevMsg( "CreateSession: \n");
|
||||
KeyValuesDumpAsDevMsg( pSettings );
|
||||
|
||||
#ifndef SWDS
|
||||
if ( !pSettings )
|
||||
return;
|
||||
|
||||
IMatchSessionInternal *pMatchSessionNew = NULL;
|
||||
|
||||
//
|
||||
// Analyze the type of session requested to create
|
||||
//
|
||||
|
||||
char const *szNetwork = pSettings->GetString( "system/network", "offline" );
|
||||
|
||||
#ifdef _X360
|
||||
if ( !Q_stricmp( "LIVE", szNetwork ) && !ValidateInviteControllers() )
|
||||
return;
|
||||
#endif
|
||||
|
||||
// Recompute XUIDs for the session type that we are creating
|
||||
g_pPlayerManager->RecomputePlayerXUIDs( szNetwork );
|
||||
|
||||
//
|
||||
// Process create session request
|
||||
//
|
||||
if ( !Q_stricmp( "offline", szNetwork ) )
|
||||
{
|
||||
CMatchSessionOfflineCustom *pSession = new CMatchSessionOfflineCustom( pSettings );
|
||||
pMatchSessionNew = pSession;
|
||||
}
|
||||
else
|
||||
{
|
||||
CMatchSessionOnlineHost *pSession = new CMatchSessionOnlineHost( pSettings );
|
||||
pMatchSessionNew = pSession;
|
||||
}
|
||||
|
||||
if ( pMatchSessionNew )
|
||||
{
|
||||
CloseSession();
|
||||
m_pMatchSession = pMatchSessionNew;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CMatchFramework::MatchSession( KeyValues *pSettings )
|
||||
{
|
||||
#ifndef SWDS
|
||||
if ( !pSettings )
|
||||
return;
|
||||
|
||||
DevMsg( "MatchSession: \n");
|
||||
KeyValuesDumpAsDevMsg( pSettings );
|
||||
|
||||
IMatchSessionInternal *pMatchSessionNew = NULL;
|
||||
|
||||
//
|
||||
// Analyze what kind of client-side matchmaking
|
||||
// needs to happen.
|
||||
//
|
||||
|
||||
char const *szNetwork = pSettings->GetString( "system/network", "LIVE" );
|
||||
char const *szAction = pSettings->GetString( "options/action", "" );
|
||||
|
||||
// Recompute XUIDs for the session type that we are creating
|
||||
g_pPlayerManager->RecomputePlayerXUIDs( szNetwork );
|
||||
|
||||
//
|
||||
// Process match session request
|
||||
//
|
||||
if ( !Q_stricmp( "joinsession", szAction ) )
|
||||
{
|
||||
#ifdef _X360
|
||||
// For LIVE sessions we need to be eligible
|
||||
if ( !Q_stricmp( "LIVE", szNetwork ) && !ValidateInviteControllers() )
|
||||
return;
|
||||
#endif
|
||||
|
||||
// We have an explicit session to join
|
||||
CMatchSessionOnlineClient *pSession = new CMatchSessionOnlineClient( pSettings );
|
||||
pMatchSessionNew = pSession;
|
||||
}
|
||||
else if ( !Q_stricmp( "joininvitesession", szAction ) )
|
||||
{
|
||||
#ifdef _X360
|
||||
ZeroMemory( &s_InviteInfo, sizeof( s_InviteInfo ) );
|
||||
XUSER_SIGNIN_INFO xsi;
|
||||
if ( ERROR_SUCCESS == XUserGetSigninInfo( XBX_GetInvitedUserId(), XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) )
|
||||
s_InviteInfo.xuidInvitee = xsi.xuid;
|
||||
|
||||
uint64 uiSessionID = pSettings->GetUint64( "options/sessionid", 0ull );
|
||||
s_InviteInfo.hostInfo.sessionID = ( XNKID & ) uiSessionID;
|
||||
|
||||
OnInviteAccepted();
|
||||
#endif
|
||||
}
|
||||
else // "quickmatch" or "custommatch"
|
||||
{
|
||||
#ifdef _X360
|
||||
// For LIVE sessions we need to be eligible
|
||||
if ( !Q_stricmp( "LIVE", szNetwork ) && !ValidateInviteControllers() )
|
||||
return;
|
||||
#endif
|
||||
|
||||
CMatchSessionOnlineSearch *pSession = new CMatchSessionOnlineSearch( pSettings );
|
||||
pMatchSessionNew = pSession;
|
||||
}
|
||||
|
||||
if ( pMatchSessionNew )
|
||||
{
|
||||
CloseSession();
|
||||
m_pMatchSession = pMatchSessionNew;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CMatchFramework::CloseSession()
|
||||
{
|
||||
// Destroy the session
|
||||
if ( m_pMatchSession )
|
||||
{
|
||||
IMatchSessionInternal *pMatchSession = m_pMatchSession;
|
||||
m_pMatchSession = NULL;
|
||||
pMatchSession->Destroy();
|
||||
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "closed" ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool CMatchFramework::IsOnlineGame( void )
|
||||
{
|
||||
IMatchSession *pMatchSession = GetMatchSession();
|
||||
|
||||
if ( pMatchSession )
|
||||
{
|
||||
KeyValues* kv = pMatchSession->GetSessionSettings();
|
||||
if ( kv )
|
||||
{
|
||||
char const *szMode = kv->GetString( "system/network", NULL );
|
||||
if ( szMode && !V_stricmp( "LIVE", szMode ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CMatchFramework::UpdateTeamProperties( KeyValues *pTeamProperties )
|
||||
{
|
||||
IMatchSession *pMatchSession = GetMatchSession();
|
||||
IMatchTitleGameSettingsMgr *pMatchTitleGameSettingsMgr = GetMatchTitleGameSettingsMgr();
|
||||
|
||||
if ( pMatchSession && pMatchTitleGameSettingsMgr )
|
||||
{
|
||||
pMatchSession->UpdateTeamProperties( pTeamProperties );
|
||||
KeyValues *pCurrentSettings = pMatchSession->GetSessionSettings();
|
||||
pMatchTitleGameSettingsMgr->UpdateTeamProperties( pCurrentSettings, pTeamProperties );
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchFramework::OnEvent( KeyValues *pEvent )
|
||||
{
|
||||
char const *szEvent = pEvent->GetName();
|
||||
|
||||
if ( !Q_stricmp( "mmF->CloseSession", szEvent ) )
|
||||
{
|
||||
CloseSession();
|
||||
return;
|
||||
}
|
||||
else if ( !Q_stricmp( "OnInvite", szEvent ) )
|
||||
{
|
||||
if ( !Q_stricmp( "join", pEvent->GetString( "action" ) ) )
|
||||
{
|
||||
s_bInviteSessionDelayedJoin = true;
|
||||
}
|
||||
else if ( !Q_stricmp( "deny", pEvent->GetString( "action" ) ) )
|
||||
{
|
||||
Q_memset( &s_InviteInfo, 0, sizeof( s_InviteInfo ) );
|
||||
s_bInviteSessionDelayedJoin = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if ( !Q_stricmp( "OnSteamOverlayCall::LobbyJoin", szEvent ) )
|
||||
{
|
||||
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
||||
GameLobbyJoinRequested_t msg;
|
||||
msg.m_steamIDLobby.SetFromUint64( pEvent->GetUint64( "sessionid" ) );
|
||||
msg.m_steamIDFriend.SetFromUint64( ~0ull );
|
||||
g_MatchSteamInviteListener.Steam_OnGameLobbyJoinRequested( &msg );
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
else if ( !Q_stricmp( "OnMatchSessionUpdate", szEvent ) )
|
||||
{
|
||||
KeyValues *pUpdate = pEvent->FindKey( "update" );
|
||||
|
||||
if ( pUpdate )
|
||||
{
|
||||
const char *pAction = pUpdate->GetString( "options/action", "" );
|
||||
if ( !Q_stricmp( "joinsession", pAction ) )
|
||||
{
|
||||
KeyValues *pTeamMembers = pUpdate->FindKey( "teamMembers" );
|
||||
|
||||
if ( pTeamMembers )
|
||||
{
|
||||
// Received console team match settings from host
|
||||
// Find what team we are on
|
||||
|
||||
int numPlayers = pTeamMembers->GetInt( "numPlayers" );
|
||||
int playerTeam = -1;
|
||||
|
||||
int activeUer = XBX_GetPrimaryUserId();
|
||||
IPlayerLocal *player = g_pPlayerManager->GetLocalPlayer( activeUer );
|
||||
uint64 localPlayerId = player->GetXUID();
|
||||
|
||||
for ( int i = 0; i < numPlayers; i++ )
|
||||
{
|
||||
KeyValues *pTeamPlayer = pTeamMembers->FindKey( CFmtStr( "player%d", i ) );
|
||||
uint64 playerId = pTeamPlayer->GetUint64( "xuid" );
|
||||
|
||||
if ( playerId == localPlayerId )
|
||||
{
|
||||
int team = pTeamPlayer->GetInt( "team" );
|
||||
DevMsg( "Adding player %llu to team %d\n", playerId, team );
|
||||
playerTeam = team;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_pTeamSessionSettings = pUpdate->MakeCopy();
|
||||
m_pTeamSessionSettings->SetName( "settings ");
|
||||
|
||||
// Delete the "teamMembers" key
|
||||
m_pTeamSessionSettings->RemoveSubKey( m_pTeamSessionSettings->FindKey( "teamMembers" ) );
|
||||
|
||||
// Add "conteam" value
|
||||
m_pTeamSessionSettings->SetInt( "conteam", playerTeam );
|
||||
|
||||
// Add the "sessionHostDataUnpacked" key
|
||||
KeyValues *pSessionHostDataSrc = pUpdate->FindKey( "sessionHostDataUnpacked" );
|
||||
if ( pSessionHostDataSrc )
|
||||
{
|
||||
KeyValues *pSessionHostDataDst = m_pTeamSessionSettings->CreateNewKey();
|
||||
pSessionHostDataDst->SetName( "sessionHostDataUnpacked" );
|
||||
|
||||
pSessionHostDataSrc->CopySubkeys( pSessionHostDataDst );
|
||||
}
|
||||
|
||||
m_bJoinTeamSession = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Delegate to the managers
|
||||
//
|
||||
if ( g_pPlayerManager )
|
||||
g_pPlayerManager->OnEvent( pEvent );
|
||||
if ( g_pServerManager )
|
||||
g_pServerManager->OnEvent( pEvent );
|
||||
if ( g_pDatacenter )
|
||||
g_pDatacenter->OnEvent( pEvent );
|
||||
if ( g_pDlcManager )
|
||||
g_pDlcManager->OnEvent( pEvent );
|
||||
|
||||
//
|
||||
// Delegate to the title
|
||||
//
|
||||
if ( g_pIMatchTitleEventsSink )
|
||||
g_pIMatchTitleEventsSink->OnEvent( pEvent );
|
||||
|
||||
//
|
||||
// Delegate to the session
|
||||
//
|
||||
if ( m_pMatchSession )
|
||||
m_pMatchSession->OnEvent( pEvent );
|
||||
}
|
||||
|
||||
void CMatchFramework::SetCurrentMatchSession( IMatchSessionInternal *pNewMatchSession )
|
||||
{
|
||||
m_pMatchSession = pNewMatchSession;
|
||||
}
|
||||
|
||||
uint64 CMatchFramework::GetLastInviteFlags()
|
||||
{
|
||||
return g_uiLastInviteFlags;
|
||||
}
|
||||
|
||||
|
||||
155
matchmaking/mm_framework.h
Normal file
155
matchmaking/mm_framework.h
Normal file
@@ -0,0 +1,155 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_FRAMEWORK_H
|
||||
#define MM_FRAMEWORK_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/icommandline.h"
|
||||
|
||||
#include "tier1/strtools.h"
|
||||
#include "tier1/checksum_crc.h"
|
||||
#include "tier1/keyvalues.h"
|
||||
#include "tier1/utlbuffer.h"
|
||||
#include "tier1/fmtstr.h"
|
||||
|
||||
#include "mathlib/mathlib.h"
|
||||
|
||||
#include "const.h"
|
||||
|
||||
#include "inetmsghandler.h"
|
||||
#include "appframework/IAppSystemGroup.h"
|
||||
#include "matchmaking/imatchframework.h"
|
||||
#include "igameevents.h"
|
||||
|
||||
#include "tier2/tier2.h"
|
||||
#include "vstdlib/jobthread.h"
|
||||
|
||||
#include "extkeyvalues.h"
|
||||
#include "steam_apihook.h"
|
||||
|
||||
|
||||
class CMatchFramework;
|
||||
|
||||
|
||||
#include "mm_extensions.h"
|
||||
#include "mm_events.h"
|
||||
#include "mm_voice.h"
|
||||
#include "mm_session.h"
|
||||
#include "mm_netmgr.h"
|
||||
|
||||
#include "matchsystem.h"
|
||||
#include "playermanager.h"
|
||||
#include "servermanager.h"
|
||||
#include "searchmanager.h"
|
||||
#include "datacenter.h"
|
||||
#include "mm_dlc.h"
|
||||
|
||||
enum MatchFrameworkInviteFlags_t
|
||||
{
|
||||
// Indicates that invite was received using console mechanisms (XMB/boot/etc.)
|
||||
MM_INVITE_FLAG_CONSOLE = ( 1 << 0 ),
|
||||
// Indicates that the game was booted through a Steam invite
|
||||
MM_INVITE_FLAG_PCBOOT = ( 1 << 1 ),
|
||||
};
|
||||
|
||||
|
||||
class CMatchFramework :
|
||||
public CTier2AppSystem< IMatchFramework >,
|
||||
public IMatchEventsSink
|
||||
{
|
||||
// Methods of IAppSystem
|
||||
public:
|
||||
virtual bool Connect( CreateInterfaceFn factory );
|
||||
virtual void Disconnect();
|
||||
virtual void *QueryInterface( const char *pInterfaceName );
|
||||
virtual InitReturnVal_t Init();
|
||||
virtual void Shutdown();
|
||||
|
||||
// Methods of IMatchFramework
|
||||
public:
|
||||
// Run frame of the matchmaking framework
|
||||
virtual void RunFrame();
|
||||
|
||||
|
||||
// Get matchmaking extensions
|
||||
virtual IMatchExtensions * GetMatchExtensions();
|
||||
|
||||
// Get events container
|
||||
virtual IMatchEventsSubscription * GetEventsSubscription();
|
||||
|
||||
// Get the matchmaking title interface
|
||||
virtual IMatchTitle * GetMatchTitle();
|
||||
|
||||
// Get the match session interface of the current match framework type
|
||||
virtual IMatchSession * GetMatchSession();
|
||||
|
||||
// Get the network msg encode/decode factory
|
||||
virtual IMatchNetworkMsgController * GetMatchNetworkMsgController();
|
||||
|
||||
// Get the match system
|
||||
virtual IMatchSystem * GetMatchSystem();
|
||||
|
||||
// Send the key values back to the server
|
||||
virtual void ApplySettings( KeyValues* keyValues );
|
||||
|
||||
// Entry point to create session
|
||||
virtual void CreateSession( KeyValues *pSettings );
|
||||
|
||||
// Entry point to match into a session
|
||||
virtual void MatchSession( KeyValues *pSettings );
|
||||
|
||||
// Accept invite
|
||||
virtual void AcceptInvite( int iController );
|
||||
|
||||
// Close the session
|
||||
virtual void CloseSession();
|
||||
|
||||
// Checks to see if the current game is being played online ( as opposed to locally against bots )
|
||||
virtual bool IsOnlineGame( void );
|
||||
|
||||
// Called by the client to notify matchmaking that it should update matchmaking properties based
|
||||
// on player distribution among the teams.
|
||||
virtual void UpdateTeamProperties( KeyValues *pTeamProperties );
|
||||
|
||||
//
|
||||
// IMatchEventsSink
|
||||
//
|
||||
public:
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
// Additional matchmaking title-defined interface
|
||||
public:
|
||||
virtual IMatchTitleGameSettingsMgr * GetMatchTitleGameSettingsMgr();
|
||||
|
||||
public:
|
||||
void SetCurrentMatchSession( IMatchSessionInternal *pNewMatchSession );
|
||||
uint64 GetLastInviteFlags();
|
||||
|
||||
protected:
|
||||
void RunFrame_Invite();
|
||||
|
||||
public:
|
||||
CMatchFramework();
|
||||
~CMatchFramework();
|
||||
|
||||
protected:
|
||||
IMatchSessionInternal *m_pMatchSession;
|
||||
|
||||
bool m_bJoinTeamSession;
|
||||
KeyValues *m_pTeamSessionSettings;
|
||||
};
|
||||
|
||||
extern CMatchFramework *g_pMMF;
|
||||
|
||||
extern const char *COM_GetModDirectory();
|
||||
extern bool IsLocalClientConnectedToServer();
|
||||
|
||||
#endif // MM_FRAMEWORK_H
|
||||
189
matchmaking/mm_netmgr.cpp
Normal file
189
matchmaking/mm_netmgr.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_netmgr.h"
|
||||
|
||||
#include "proto_oob.h"
|
||||
#include "protocol.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//
|
||||
// Convars
|
||||
//
|
||||
|
||||
ConVar net_allow_multicast( "net_allow_multicast", "1", FCVAR_RELEASE | FCVAR_ARCHIVE );
|
||||
ConVar net_allow_syslink( "net_allow_syslink", "1", FCVAR_DEVELOPMENTONLY );
|
||||
|
||||
|
||||
//
|
||||
// CConnectionlessLanMgr
|
||||
//
|
||||
|
||||
CConnectionlessLanMgr::CConnectionlessLanMgr()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
CConnectionlessLanMgr::~CConnectionlessLanMgr()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
static CConnectionlessLanMgr g_ConnectionlessLanMgr;
|
||||
CConnectionlessLanMgr *g_pConnectionlessLanMgr = &g_ConnectionlessLanMgr;
|
||||
|
||||
//
|
||||
// Implementation of CConnectionlessLanMgr
|
||||
//
|
||||
|
||||
KeyValues * CConnectionlessLanMgr::UnpackPacket( netpacket_t *packet )
|
||||
{
|
||||
if ( !packet )
|
||||
return NULL;
|
||||
|
||||
if ( !packet->size || !packet->data )
|
||||
return NULL;
|
||||
|
||||
// Try to unpack the data
|
||||
if ( packet->message.ReadLong() != 0 )
|
||||
return NULL;
|
||||
if ( packet->message.ReadLong() != g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() )
|
||||
return NULL;
|
||||
|
||||
MEM_ALLOC_CREDIT();
|
||||
int nDataLen = packet->message.ReadLong();
|
||||
|
||||
m_buffer.Clear();
|
||||
m_buffer.EnsureCapacity( nDataLen );
|
||||
packet->message.ReadBytes( m_buffer.Base(), nDataLen );
|
||||
m_buffer.SeekPut( CUtlBuffer::SEEK_HEAD, nDataLen );
|
||||
m_buffer.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
|
||||
|
||||
// Unpack key values
|
||||
KeyValues *pMsg = new KeyValues( "" );
|
||||
if ( pMsg->ReadAsBinary( m_buffer ) )
|
||||
{
|
||||
// Read binary data as well
|
||||
if ( int nBinarySize = packet->message.ReadLong() )
|
||||
{
|
||||
m_buffer.Clear();
|
||||
m_buffer.EnsureCapacity( nBinarySize );
|
||||
packet->message.ReadBytes( m_buffer.Base(), nBinarySize );
|
||||
m_buffer.SeekPut( CUtlBuffer::SEEK_HEAD, nBinarySize );
|
||||
|
||||
// set the ptr to point to our buffer
|
||||
pMsg->SetPtr( "binary/ptr", m_buffer.Base() );
|
||||
}
|
||||
|
||||
return pMsg; // "pMsg" is deleted by caller
|
||||
}
|
||||
|
||||
pMsg->deleteThis();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CConnectionlessLanMgr::ProcessConnectionlessPacket( netpacket_t *packet )
|
||||
{
|
||||
// Unpack key values
|
||||
KeyValues *pMsg = UnpackPacket( packet );
|
||||
if ( !pMsg )
|
||||
return false;
|
||||
|
||||
MEM_ALLOC_CREDIT();
|
||||
KeyValues *notify = new KeyValues( "OnNetLanConnectionlessPacket" );
|
||||
notify->SetString( "from", ns_address_render( packet->from ).String() );
|
||||
notify->AddSubKey( pMsg );
|
||||
|
||||
g_pMatchEventsSubscription->BroadcastEvent( notify );
|
||||
return true; // "pMsg" is deleted as child of "notify"
|
||||
}
|
||||
|
||||
void CConnectionlessLanMgr::Update()
|
||||
{
|
||||
#ifdef _X360
|
||||
if ( !net_allow_syslink.GetBool() )
|
||||
return;
|
||||
|
||||
g_pMatchExtensions->GetINetSupport()->ProcessSocket( INetSupport::NS_SOCK_SYSTEMLINK, this );
|
||||
#endif
|
||||
}
|
||||
|
||||
void CConnectionlessLanMgr::SendPacket( KeyValues *pMsg, char const *szAddress /*= NULL*/, INetSupport::NetworkSocket_t eSock )
|
||||
{
|
||||
char buf[ INetSupport::NC_MAX_ROUTABLE_PAYLOAD ];
|
||||
bf_write msg( buf, sizeof( buf ) );
|
||||
|
||||
msg.WriteLong( CONNECTIONLESS_HEADER );
|
||||
msg.WriteLong( 0 );
|
||||
msg.WriteLong( g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() );
|
||||
|
||||
CUtlBuffer data;
|
||||
data.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
|
||||
pMsg->WriteAsBinary( data );
|
||||
|
||||
msg.WriteLong( data.TellMaxPut() );
|
||||
msg.WriteBytes( data.Base(), data.TellMaxPut() );
|
||||
Assert( !msg.IsOverflowed() );
|
||||
|
||||
// Special case when encoding binary data
|
||||
KeyValues *kvPtr = pMsg->FindKey( "binary/ptr" );
|
||||
KeyValues *kvSize = pMsg->FindKey( "binary/size" );
|
||||
if ( kvPtr && kvSize )
|
||||
{
|
||||
void *pvData = kvPtr->GetPtr();
|
||||
int nSize = kvSize->GetInt();
|
||||
|
||||
if ( pvData && nSize )
|
||||
{
|
||||
msg.WriteLong( nSize );
|
||||
if ( !msg.WriteBytes( pvData, nSize ) )
|
||||
{
|
||||
Assert( 0 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.WriteLong( 0 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.WriteLong( 0 );
|
||||
}
|
||||
Assert( !msg.IsOverflowed() );
|
||||
|
||||
// Prepare the address
|
||||
netadr_t inetAddr;
|
||||
if ( szAddress )
|
||||
{
|
||||
if ( szAddress[0] == '*' && szAddress[1] == ':' )
|
||||
{
|
||||
inetAddr.SetType( NA_BROADCAST );
|
||||
inetAddr.SetPort( atoi( szAddress + 2 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
inetAddr.SetFromString( szAddress );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inetAddr.SetType( NA_BROADCAST );
|
||||
inetAddr.SetPort( 0 );
|
||||
}
|
||||
|
||||
// Check if broadcasts are allowed
|
||||
if ( inetAddr.GetType() == NA_BROADCAST &&
|
||||
!net_allow_multicast.GetBool() )
|
||||
return;
|
||||
|
||||
// Sending the connectionless packet
|
||||
g_pMatchExtensions->GetINetSupport()->SendPacket( NULL, eSock,
|
||||
inetAddr, msg.GetData(), msg.GetNumBytesWritten() );
|
||||
}
|
||||
47
matchmaking/mm_netmgr.h
Normal file
47
matchmaking/mm_netmgr.h
Normal file
@@ -0,0 +1,47 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_NETMGR_H
|
||||
#define MM_NETMGR_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
class CConnectionlessLanMgr;
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
class CConnectionlessLanMgr : public IConnectionlessPacketHandler
|
||||
{
|
||||
//
|
||||
// IConnectionlessPacketHandler
|
||||
//
|
||||
public:
|
||||
virtual bool ProcessConnectionlessPacket( netpacket_t *packet );
|
||||
|
||||
public:
|
||||
void Update();
|
||||
void SendPacket( KeyValues *msg, char const *szAddress = NULL, INetSupport::NetworkSocket_t eSock
|
||||
#ifdef _X360
|
||||
= INetSupport::NS_SOCK_SYSTEMLINK
|
||||
#else
|
||||
= INetSupport::NS_SOCK_CLIENT
|
||||
#endif
|
||||
);
|
||||
KeyValues * UnpackPacket( netpacket_t *packet );
|
||||
|
||||
public:
|
||||
CConnectionlessLanMgr();
|
||||
~CConnectionlessLanMgr();
|
||||
|
||||
protected:
|
||||
CUtlBuffer m_buffer;
|
||||
};
|
||||
|
||||
// Match events subscription singleton
|
||||
extern CConnectionlessLanMgr *g_pConnectionlessLanMgr;
|
||||
|
||||
#endif // MM_EVENTS_H
|
||||
306
matchmaking/mm_netmsgcontroller.cpp
Normal file
306
matchmaking/mm_netmsgcontroller.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_netmsgcontroller.h"
|
||||
|
||||
#include "matchmakingqos.h"
|
||||
|
||||
#include "proto_oob.h"
|
||||
#include "bitbuf.h"
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
CMatchNetworkMsgControllerBase::CMatchNetworkMsgControllerBase()
|
||||
{
|
||||
}
|
||||
|
||||
CMatchNetworkMsgControllerBase::~CMatchNetworkMsgControllerBase()
|
||||
{
|
||||
}
|
||||
|
||||
static CMatchNetworkMsgControllerBase g_MatchNetMsgControllerBase;
|
||||
CMatchNetworkMsgControllerBase *g_pMatchNetMsgControllerBase = &g_MatchNetMsgControllerBase;
|
||||
|
||||
//
|
||||
// A way to copy just value
|
||||
//
|
||||
|
||||
static void CopyValue( KeyValues *pTo, KeyValues *pFrom )
|
||||
{
|
||||
switch ( pFrom->GetDataType() )
|
||||
{
|
||||
case KeyValues::TYPE_INT:
|
||||
pTo->SetInt( "", pFrom->GetInt() );
|
||||
break;
|
||||
case KeyValues::TYPE_UINT64:
|
||||
pTo->SetUint64( "", pFrom->GetUint64() );
|
||||
break;
|
||||
case KeyValues::TYPE_STRING:
|
||||
pTo->SetString( "", pFrom->GetString() );
|
||||
break;
|
||||
default:
|
||||
DevWarning( "NetMsgCtrlr::CopyValue using unknown type!\n" );
|
||||
Assert( 0 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
MM_QOS_t CMatchNetworkMsgControllerBase::GetQOS()
|
||||
{
|
||||
return MM_GetQos();
|
||||
}
|
||||
|
||||
KeyValues * CMatchNetworkMsgControllerBase::GetActiveServerGameDetails( KeyValues *pRequest )
|
||||
{
|
||||
// Query server info
|
||||
INetSupport::ServerInfo_t si;
|
||||
g_pMatchExtensions->GetINetSupport()->GetServerInfo( &si );
|
||||
|
||||
KeyValues *pDetails = NULL;
|
||||
|
||||
if ( si.m_bActive )
|
||||
{
|
||||
MEM_ALLOC_CREDIT();
|
||||
//
|
||||
// Parse the game details from the values
|
||||
//
|
||||
pDetails = KeyValues::FromString(
|
||||
"GameDetailsServer",
|
||||
" system { "
|
||||
" network LIVE "
|
||||
" access public "
|
||||
" } "
|
||||
" server { "
|
||||
" name = "
|
||||
" server = "
|
||||
" adronline = "
|
||||
" adrlocal = "
|
||||
" } "
|
||||
" members { "
|
||||
" numSlots #int#0 "
|
||||
" numPlayers #int#0 "
|
||||
" } "
|
||||
);
|
||||
|
||||
//
|
||||
// For a listen server and other MM session overlay the session settings
|
||||
//
|
||||
if ( !si.m_bDedicated && g_pMatchFramework->GetMatchSession() )
|
||||
{
|
||||
pDetails->MergeFrom( g_pMatchFramework->GetMatchSession()->GetSessionSettings(), KeyValues::MERGE_KV_BORROW );
|
||||
}
|
||||
|
||||
//
|
||||
// Get server information
|
||||
//
|
||||
|
||||
pDetails->SetString( "server/name", si.m_szServerName );
|
||||
pDetails->SetString( "server/server", si.m_bDedicated ? "dedicated" : "listen" );
|
||||
pDetails->SetString( "server/adronline", si.m_netAdrOnline.ToString() );
|
||||
pDetails->SetString( "server/adrlocal", si.m_netAdr.ToString() );
|
||||
|
||||
if ( si.m_bDedicated && si.m_bLobbyExclusive && si.m_bGroupExclusive )
|
||||
pDetails->SetString( "system/access", "friends" );
|
||||
|
||||
si.m_numMaxHumanPlayers = ClampArrayBounds( si.m_numMaxHumanPlayers, g_pMMF->GetMatchTitle()->GetTotalNumPlayersSupported() );
|
||||
pDetails->SetInt( "members/numSlots", si.m_numMaxHumanPlayers );
|
||||
|
||||
si.m_numHumanPlayers = ClampArrayBounds( si.m_numHumanPlayers, si.m_numMaxHumanPlayers );
|
||||
pDetails->SetInt( "members/numPlayers", si.m_numHumanPlayers );
|
||||
|
||||
static ConVarRef host_info_show( "host_info_show" );
|
||||
if ( host_info_show.GetInt() < 2 )
|
||||
pDetails->SetString( "options/action", "crypt" );
|
||||
}
|
||||
else if ( IVEngineClient *pIVEngineClient = g_pMatchExtensions->GetIVEngineClient() )
|
||||
{
|
||||
if ( pIVEngineClient->IsLevelMainMenuBackground() )
|
||||
return NULL;
|
||||
|
||||
char const *szLevelName = pIVEngineClient->GetLevelNameShort();
|
||||
if ( !szLevelName || !*szLevelName )
|
||||
return NULL;
|
||||
|
||||
MEM_ALLOC_CREDIT();
|
||||
pDetails = new KeyValues( "GameDetailsClient" );
|
||||
}
|
||||
|
||||
if ( !pDetails )
|
||||
return NULL;
|
||||
|
||||
// Allow title to add game-specific settings
|
||||
g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendServerDetails( pDetails, pRequest );
|
||||
|
||||
return pDetails;
|
||||
}
|
||||
|
||||
static KeyValues * GetLobbyDetailsTemplate( char const *szReason = "", KeyValues *pSettings = NULL )
|
||||
{
|
||||
KeyValues *pDetails = KeyValues::FromString(
|
||||
"settings",
|
||||
" system { "
|
||||
" network #empty# "
|
||||
" access #empty# "
|
||||
" netflag #empty# "
|
||||
" lock #empty# "
|
||||
" } "
|
||||
" options { "
|
||||
" server #empty# "
|
||||
" } "
|
||||
" members { "
|
||||
" numSlots #int#0 "
|
||||
" numPlayers #int#0 "
|
||||
" } "
|
||||
);
|
||||
|
||||
g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendLobbyDetailsTemplate( pDetails, szReason, pSettings );
|
||||
|
||||
return pDetails;
|
||||
}
|
||||
|
||||
KeyValues * CMatchNetworkMsgControllerBase::UnpackGameDetailsFromQOS( MM_GameDetails_QOS_t const *pvQosReply )
|
||||
{
|
||||
//
|
||||
// Check if we have correct header
|
||||
//
|
||||
CUtlBuffer bufQos( pvQosReply->m_pvData, pvQosReply->m_numDataBytes, CUtlBuffer::READ_ONLY );
|
||||
bufQos.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
|
||||
int iProtocol = bufQos.GetInt();
|
||||
int iVersion = bufQos.GetInt();
|
||||
|
||||
if ( iProtocol != g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() )
|
||||
return NULL;
|
||||
if ( 0 != iVersion )
|
||||
return NULL;
|
||||
|
||||
//
|
||||
// Read the game details that we have received
|
||||
//
|
||||
MEM_ALLOC_CREDIT();
|
||||
KeyValues *pDetails = new KeyValues( "" );
|
||||
if ( !pDetails->ReadAsBinary( bufQos ) )
|
||||
{
|
||||
pDetails->deleteThis();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Read the terminator
|
||||
int iTerm = bufQos.GetInt();
|
||||
if ( iTerm != 0 )
|
||||
{
|
||||
DevWarning( "UnpackGameDetailsFromQOS found bad QOS block terminator!\n" );
|
||||
}
|
||||
|
||||
return pDetails;
|
||||
}
|
||||
|
||||
void CMatchNetworkMsgControllerBase::PackageGameDetailsForQOS( KeyValues *pSettings, CUtlBuffer &buf )
|
||||
{
|
||||
KeyValues *pDetails = GetLobbyDetailsTemplate( "qos", pSettings );
|
||||
KeyValues::AutoDelete autodelete( pDetails );
|
||||
|
||||
// Keep only keys specified in the template
|
||||
pDetails->MergeFrom( pSettings, KeyValues::MERGE_KV_BORROW );
|
||||
|
||||
// Write the details as binary
|
||||
buf.PutInt( g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() );
|
||||
buf.PutInt( 0 );
|
||||
pDetails->WriteAsBinary( buf );
|
||||
buf.PutInt( 0 );
|
||||
}
|
||||
|
||||
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
||||
static void UnpackGameDetailsFromSteamLobbyInKey( uint64 uiLobbyID, char const *szPath, KeyValues *pKey )
|
||||
{
|
||||
// Iterate over all the values
|
||||
for ( KeyValues *val = pKey->GetFirstValue(); val; val = val->GetNextValue() )
|
||||
{
|
||||
char const *szLobbyData = steamapicontext->SteamMatchmaking()
|
||||
->GetLobbyData( uiLobbyID, CFmtStr( "%s%s", szPath, val->GetName() ) );
|
||||
|
||||
switch ( val->GetDataType() )
|
||||
{
|
||||
case KeyValues::TYPE_INT:
|
||||
val->SetInt( "", atoi( szLobbyData ) );
|
||||
break;
|
||||
case KeyValues::TYPE_STRING:
|
||||
val->SetString( "", szLobbyData );
|
||||
break;
|
||||
default:
|
||||
DevWarning( "UnpackGameDetailsFromSteamLobby defined unknown type in schema!\n" );
|
||||
Assert( 0 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over subkeys
|
||||
for ( KeyValues *sub = pKey->GetFirstTrueSubKey(); sub; sub = sub->GetNextTrueSubKey() )
|
||||
{
|
||||
UnpackGameDetailsFromSteamLobbyInKey( uiLobbyID, CFmtStr( "%s%s:", szPath, sub->GetName() ), sub );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
KeyValues * CMatchNetworkMsgControllerBase::UnpackGameDetailsFromSteamLobby( uint64 uiLobbyID )
|
||||
{
|
||||
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
||||
// Make sure the basic metadata is set on the lobby
|
||||
char const *arrRequiredMetadata[] = { "system:network", "system:access" };
|
||||
for ( int k = 0; k < ARRAYSIZE( arrRequiredMetadata ); ++ k )
|
||||
{
|
||||
char const *szMetadata = steamapicontext->SteamMatchmaking()->GetLobbyData( uiLobbyID, arrRequiredMetadata[k] );
|
||||
if ( !szMetadata || !*szMetadata )
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Allocate details template
|
||||
KeyValues *pDetails = GetLobbyDetailsTemplate();
|
||||
|
||||
// Iterate over all the keys
|
||||
UnpackGameDetailsFromSteamLobbyInKey( uiLobbyID, "", pDetails );
|
||||
|
||||
// Get members info
|
||||
if ( KeyValues *kvMembers = pDetails->FindKey( "members", true ) )
|
||||
{
|
||||
int numSlots = steamapicontext->SteamMatchmaking()->GetLobbyMemberLimit( uiLobbyID );
|
||||
numSlots = ClampArrayBounds( numSlots, g_pMMF->GetMatchTitle()->GetTotalNumPlayersSupported() );
|
||||
kvMembers->SetInt( "numSlots", numSlots );
|
||||
|
||||
int numPlayers = steamapicontext->SteamMatchmaking()->GetNumLobbyMembers( uiLobbyID );
|
||||
numPlayers = ClampArrayBounds( numPlayers, numSlots );
|
||||
kvMembers->SetInt( "numPlayers", numPlayers );
|
||||
}
|
||||
|
||||
return pDetails;
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
KeyValues * CMatchNetworkMsgControllerBase::PackageGameDetailsForReservation( KeyValues *pSettings )
|
||||
{
|
||||
KeyValues *res = GetLobbyDetailsTemplate( "reserve", pSettings );
|
||||
res->SetName( COM_GetModDirectory() );
|
||||
|
||||
// Keep only keys specified in the template
|
||||
res->MergeFrom( pSettings, KeyValues::MERGE_KV_BORROW );
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
39
matchmaking/mm_netmsgcontroller.h
Normal file
39
matchmaking/mm_netmsgcontroller.h
Normal file
@@ -0,0 +1,39 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_NETMSGCONTROLLER_H
|
||||
#define MM_NETMSGCONTROLLER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
class CMatchNetworkMsgControllerBase : public IMatchNetworkMsgController
|
||||
{
|
||||
// Methods of IMatchNetworkMsgController
|
||||
public:
|
||||
// To determine host Quality-of-Service
|
||||
virtual MM_QOS_t GetQOS();
|
||||
|
||||
virtual KeyValues * GetActiveServerGameDetails( KeyValues *pRequest );
|
||||
|
||||
virtual KeyValues * UnpackGameDetailsFromQOS( MM_GameDetails_QOS_t const *pvQosReply );
|
||||
virtual KeyValues * UnpackGameDetailsFromSteamLobby( uint64 uiLobbyID );
|
||||
|
||||
virtual void PackageGameDetailsForQOS( KeyValues *pSettings, CUtlBuffer &buf );
|
||||
|
||||
virtual KeyValues * PackageGameDetailsForReservation( KeyValues *pSettings );
|
||||
|
||||
public:
|
||||
CMatchNetworkMsgControllerBase();
|
||||
~CMatchNetworkMsgControllerBase();
|
||||
};
|
||||
|
||||
// Match title singleton
|
||||
extern CMatchNetworkMsgControllerBase *g_pMatchNetMsgControllerBase;
|
||||
|
||||
#endif // MM_NETMSGCONTROLLER_H
|
||||
468
matchmaking/mm_session.cpp
Normal file
468
matchmaking/mm_session.cpp
Normal file
@@ -0,0 +1,468 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_framework.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#ifdef _X360
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Adjust our rate based on our quality of service
|
||||
//-----------------------------------------------------------------------------
|
||||
static ConVar mm_clientrateupdate_enabled( "mm_clientrateupdate_enabled", "1", 0, "Automatically update the client rate based on Xbox LIVE QoS" );
|
||||
static ConVar mm_clientrateupdate_adjust( "mm_clientrateupdate_adjust", "0.6", 0, "Downstream rate adjustment" );
|
||||
static ConVar mm_clientrateupdate_minimum( "mm_clientrateupdate_minimum", "20000", 0, "Minimum supported rate, Xbox TCR requires 40kbps" );
|
||||
static ConVar mm_clientrateupdate_maximum( "mm_clientrateupdate_maximum", "30000", 0, "Maximum supported rate" );
|
||||
static ConVar mm_clientrateupdate_qos_timeout( "mm_clientrateupdate_qos_timeout", "20", 0, "How long to wait for QOS to be determined" );
|
||||
|
||||
static void AdjustClientRateBasedOnQoS( DWORD dwDnBitsPerSec )
|
||||
{
|
||||
if ( !mm_clientrateupdate_enabled.GetBool() )
|
||||
return;
|
||||
|
||||
static ConVarRef cl_rate( "rate" );
|
||||
|
||||
int desiredRate = (int)( ( dwDnBitsPerSec / 8.0f ) * mm_clientrateupdate_adjust.GetFloat() );
|
||||
|
||||
desiredRate = clamp( desiredRate, mm_clientrateupdate_minimum.GetInt(), mm_clientrateupdate_maximum.GetInt() );
|
||||
|
||||
// Update the client rate
|
||||
ConColorMsg( Color( 255, 0, 255, 255 ), "[QoS] Bandwidth %d bps, Updating client rate to %d\n", dwDnBitsPerSec, desiredRate );
|
||||
|
||||
cl_rate.SetValue( desiredRate );
|
||||
}
|
||||
|
||||
struct RateAdjustmentAsyncCall
|
||||
{
|
||||
// X360 peer
|
||||
XNADDR apxna;
|
||||
XNADDR const *papxna;
|
||||
XNKID apxnkid;
|
||||
XNKID const *papxnkid;
|
||||
XNKEY apxnkey;
|
||||
XNKEY const *papxnkey;
|
||||
|
||||
// XLSP server
|
||||
IN_ADDR ina;
|
||||
DWORD dwServiceId;
|
||||
|
||||
// QOS handle
|
||||
XNQOS *pQOS;
|
||||
|
||||
// Time when QOS probe started
|
||||
float flTimeStarted;
|
||||
}
|
||||
*g_pRateAdjustmentAsyncCall = NULL;
|
||||
|
||||
void MatchSession_RateAdjustmentUpdate_Release()
|
||||
{
|
||||
if ( !g_pRateAdjustmentAsyncCall )
|
||||
return;
|
||||
|
||||
if ( g_pRateAdjustmentAsyncCall->pQOS )
|
||||
g_pMatchExtensions->GetIXOnline()->XNetQosRelease( g_pRateAdjustmentAsyncCall->pQOS );
|
||||
|
||||
delete g_pRateAdjustmentAsyncCall;
|
||||
g_pRateAdjustmentAsyncCall = NULL;
|
||||
}
|
||||
|
||||
// Keeps adjusting client side rate setting based on QOS with server
|
||||
void MatchSession_RateAdjustmentUpdate()
|
||||
{
|
||||
if ( !g_pRateAdjustmentAsyncCall )
|
||||
return;
|
||||
|
||||
if ( g_pRateAdjustmentAsyncCall->pQOS->cxnqosPending &&
|
||||
Plat_FloatTime() < g_pRateAdjustmentAsyncCall->flTimeStarted + mm_clientrateupdate_qos_timeout.GetFloat() )
|
||||
return;
|
||||
|
||||
ConColorMsg( Color( 255, 0, 255, 255 ), "[QoS] Rate adjustment query %s\n", g_pRateAdjustmentAsyncCall->pQOS->cxnqosPending ? "timed out" : "completed" );
|
||||
|
||||
// QOS finished or timed out
|
||||
XNQOSINFO &xni = g_pRateAdjustmentAsyncCall->pQOS->axnqosinfo[0];
|
||||
AdjustClientRateBasedOnQoS( xni.dwDnBitsPerSec );
|
||||
MatchSession_RateAdjustmentUpdate_Release();
|
||||
}
|
||||
|
||||
void MatchSession_RateAdjustmentUpdate_Start( IN_ADDR const &ina )
|
||||
{
|
||||
MatchSession_RateAdjustmentUpdate_Release();
|
||||
|
||||
g_pRateAdjustmentAsyncCall = new RateAdjustmentAsyncCall;
|
||||
ZeroMemory( g_pRateAdjustmentAsyncCall, sizeof( *g_pRateAdjustmentAsyncCall ) );
|
||||
|
||||
g_pRateAdjustmentAsyncCall->ina = ina;
|
||||
g_pRateAdjustmentAsyncCall->dwServiceId = g_pMatchFramework->GetMatchTitle()->GetTitleServiceID();
|
||||
g_pRateAdjustmentAsyncCall->flTimeStarted = Plat_FloatTime();
|
||||
|
||||
ConColorMsg( Color( 255, 0, 255, 255 ), "[QoS] Rate adjustment query scheduled for XLSP server: %08X\n", ina.s_addr );
|
||||
|
||||
INT ret = g_pMatchExtensions->GetIXOnline()->XNetQosLookup(
|
||||
0, NULL, NULL, NULL,
|
||||
1, &g_pRateAdjustmentAsyncCall->ina, &g_pRateAdjustmentAsyncCall->dwServiceId,
|
||||
2, 0, 0, NULL, &g_pRateAdjustmentAsyncCall->pQOS );
|
||||
if ( ret != ERROR_SUCCESS )
|
||||
{
|
||||
g_pRateAdjustmentAsyncCall->flTimeStarted = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void MatchSession_RateAdjustmentUpdate_Start( XSESSION_INFO const &xsi )
|
||||
{
|
||||
MatchSession_RateAdjustmentUpdate_Release();
|
||||
|
||||
g_pRateAdjustmentAsyncCall = new RateAdjustmentAsyncCall;
|
||||
ZeroMemory( g_pRateAdjustmentAsyncCall, sizeof( *g_pRateAdjustmentAsyncCall ) );
|
||||
|
||||
g_pRateAdjustmentAsyncCall->apxna = xsi.hostAddress;
|
||||
g_pRateAdjustmentAsyncCall->papxna = &g_pRateAdjustmentAsyncCall->apxna;
|
||||
|
||||
g_pRateAdjustmentAsyncCall->apxnkid = xsi.sessionID;
|
||||
g_pRateAdjustmentAsyncCall->papxnkid = &g_pRateAdjustmentAsyncCall->apxnkid;
|
||||
|
||||
g_pRateAdjustmentAsyncCall->apxnkey = xsi.keyExchangeKey;
|
||||
g_pRateAdjustmentAsyncCall->papxnkey = &g_pRateAdjustmentAsyncCall->apxnkey;
|
||||
|
||||
g_pRateAdjustmentAsyncCall->flTimeStarted = Plat_FloatTime();
|
||||
|
||||
ConColorMsg( Color( 255, 0, 255, 255 ), "[QoS] Rate adjustment query scheduled for Xbox 360 peer: %08X/%08X\n", xsi.hostAddress.ina.s_addr, xsi.hostAddress.inaOnline.s_addr );
|
||||
|
||||
INT ret = g_pMatchExtensions->GetIXOnline()->XNetQosLookup(
|
||||
1, &g_pRateAdjustmentAsyncCall->papxna, &g_pRateAdjustmentAsyncCall->papxnkid, &g_pRateAdjustmentAsyncCall->papxnkey,
|
||||
0, NULL, NULL,
|
||||
2, 0, 0, NULL, &g_pRateAdjustmentAsyncCall->pQOS );
|
||||
if ( ret != ERROR_SUCCESS )
|
||||
{
|
||||
g_pRateAdjustmentAsyncCall->flTimeStarted = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void MatchSession_BroadcastSessionSettingsUpdate( KeyValues *pUpdateDeletePackage )
|
||||
{
|
||||
KeyValues *notify = new KeyValues( "OnMatchSessionUpdate" );
|
||||
notify->SetString( "state", "updated" );
|
||||
|
||||
if ( KeyValues *kvUpdate = pUpdateDeletePackage->FindKey( "update" ) )
|
||||
notify->AddSubKey( kvUpdate->MakeCopy() );
|
||||
if ( KeyValues *kvDelete = pUpdateDeletePackage->FindKey( "delete" ) )
|
||||
notify->AddSubKey( kvDelete->MakeCopy() );
|
||||
|
||||
g_pMatchEventsSubscription->BroadcastEvent( notify );
|
||||
}
|
||||
|
||||
|
||||
ConVar cl_session( "cl_session", "", FCVAR_USERINFO | FCVAR_HIDDEN | FCVAR_SERVER_CAN_EXECUTE | FCVAR_DEVELOPMENTONLY );
|
||||
|
||||
void MatchSession_PrepareClientForConnect( KeyValues *pSettings, uint64 uiReservationCookieOverride )
|
||||
{
|
||||
char chSession[64];
|
||||
sprintf( chSession, "$%llx", uiReservationCookieOverride ? uiReservationCookieOverride :
|
||||
g_pMatchFramework->GetMatchSession()->GetSessionSystemData()->
|
||||
GetUint64( "xuidReserve", 0ull ) );
|
||||
cl_session.SetValue( chSession );
|
||||
|
||||
g_pMatchFramework->GetMatchTitle()->PrepareClientForConnect( pSettings );
|
||||
}
|
||||
|
||||
static bool MatchSession_ResolveServerInfo_Helper_DsResult( KeyValues *pSettings, CSysSessionBase *pSysSession,
|
||||
MatchSessionServerInfo_t &info, uint uiResolveFlags, uint64 ullCrypt )
|
||||
{
|
||||
#ifdef _X360
|
||||
// On dedicated servers host should have given us an insecure
|
||||
// address representing our Title Server
|
||||
char const *szInsecureServerAddr = pSettings->GetString( "server/adrInsecure" );
|
||||
netadr_t inetInsecure;
|
||||
inetInsecure.SetFromString( szInsecureServerAddr );
|
||||
|
||||
IN_ADDR inaddrInsecure;
|
||||
inaddrInsecure.s_addr = inetInsecure.GetIPNetworkByteOrder();
|
||||
|
||||
if ( ( uiResolveFlags & ( info.RESOLVE_DSRESULT | info.RESOLVE_QOS_RATE_PROBE ) ) == info.RESOLVE_QOS_RATE_PROBE )
|
||||
{
|
||||
// We are not required to resolve the DSRESULT, just submit the QOS rate probe
|
||||
MatchSession_RateAdjustmentUpdate_Start( inaddrInsecure );
|
||||
return true;
|
||||
}
|
||||
|
||||
char const *szServerType = pSettings->GetString( "server/server", "listen" );
|
||||
if ( !Q_stricmp( szServerType, "listen" ) )
|
||||
{
|
||||
info.m_dsResult.m_bDedicated = false;
|
||||
return true;
|
||||
}
|
||||
if ( !Q_stricmp( szServerType, "externalpeer" ) )
|
||||
{
|
||||
info.m_dsResult.m_bDedicated = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Q_strncpy( info.m_dsResult.m_szInsecureSendableServerAddress,
|
||||
szInsecureServerAddr,
|
||||
ARRAYSIZE( info.m_dsResult.m_szInsecureSendableServerAddress ) );
|
||||
|
||||
// Map it to a secure address
|
||||
IN_ADDR inaddrSecure;
|
||||
DWORD ret = ERROR_FUNCTION_FAILED;
|
||||
if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) )
|
||||
{
|
||||
inaddrSecure = inaddrInsecure;
|
||||
ret = ERROR_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = g_pMatchExtensions->GetIXOnline()->XNetServerToInAddr( inaddrInsecure, g_pMatchFramework->GetMatchTitle()->GetTitleServiceID(), &inaddrSecure );
|
||||
}
|
||||
if ( ret != ERROR_SUCCESS )
|
||||
{
|
||||
DevWarning( "Failed to resolve XLSP secure address (code = 0x%08X, insecure = %s/%s)!\n",
|
||||
ret, inetInsecure.ToString(), szInsecureServerAddr );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
netadr_t inetSecure = inetInsecure;
|
||||
inetSecure.SetIP( inaddrSecure.s_addr );
|
||||
DevMsg( "Resolved XLSP secure address %s, insecure address was %s.\n",
|
||||
inetSecure.ToString(), szInsecureServerAddr );
|
||||
|
||||
Q_strncpy( info.m_dsResult.m_szConnectionString,
|
||||
inetSecure.ToString(),
|
||||
ARRAYSIZE( info.m_dsResult.m_szConnectionString ) );
|
||||
|
||||
info.m_dsResult.m_bDedicated = true;
|
||||
|
||||
// Start QOS rate calculation for the dedicated XLSP server
|
||||
MatchSession_RateAdjustmentUpdate_Start( inaddrInsecure );
|
||||
}
|
||||
#elif !defined( NO_STEAM )
|
||||
|
||||
char const *szAddress = pSettings->GetString( "server/adronline", "0.0.0.0" );
|
||||
if ( char const *szDecrypted = MatchSession_DecryptAddressString( szAddress, ullCrypt ) )
|
||||
szAddress = szDecrypted;
|
||||
Q_strncpy( info.m_dsResult.m_szPublicConnectionString, szAddress,
|
||||
ARRAYSIZE( info.m_dsResult.m_szPublicConnectionString ) );
|
||||
|
||||
szAddress = pSettings->GetString( "server/adrlocal", "0.0.0.0" );
|
||||
if ( char const *szDecrypted = MatchSession_DecryptAddressString( szAddress, ullCrypt ) )
|
||||
szAddress = szDecrypted;
|
||||
Q_strncpy( info.m_dsResult.m_szPrivateConnectionString, szAddress,
|
||||
ARRAYSIZE( info.m_dsResult.m_szPrivateConnectionString ) );
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool MatchSession_ResolveServerInfo_Helper_ConnectString( KeyValues *pSettings, CSysSessionBase *pSysSession, MatchSessionServerInfo_t &info, uint uiResolveFlags )
|
||||
{
|
||||
//
|
||||
// Prepare the connect command
|
||||
//
|
||||
#ifdef _X360
|
||||
char const *szServerType = pSettings->GetString( "server/server", "listen" );
|
||||
if ( !Q_stricmp( "externalpeer", szServerType ) && !( uiResolveFlags & info.RESOLVE_ALLOW_EXTPEER ) )
|
||||
pSysSession = NULL;
|
||||
|
||||
char const *szConnectionString = info.m_dsResult.m_szConnectionString;
|
||||
if ( info.m_dsResult.m_bDedicated )
|
||||
{
|
||||
info.m_szSecureServerAddress = info.m_dsResult.m_szConnectionString;
|
||||
}
|
||||
else if ( CSysSessionClient *pSysSessionClient = dynamic_cast< CSysSessionClient * >( pSysSession ) )
|
||||
{
|
||||
XSESSION_INFO xsi = {0};
|
||||
szConnectionString = pSysSessionClient->GetHostNetworkAddress( xsi );
|
||||
if ( !szConnectionString )
|
||||
{
|
||||
DevWarning( "MatchSession_ResolveServerInfo_Helper_ConnectString::GetHostNetworkAddress failed!\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start QOS rate calculation for our session host X360 xnaddr
|
||||
MatchSession_RateAdjustmentUpdate_Start( xsi );
|
||||
}
|
||||
else if ( char const *szSessionInfo = pSettings->GetString( "server/sessioninfo", NULL ) )
|
||||
{
|
||||
// We don't have a dedicated server and don't allow to use external peer directly,
|
||||
// register security keys
|
||||
XSESSION_INFO xsi = {0};
|
||||
MMX360_SessionInfoFromString( xsi, szSessionInfo );
|
||||
|
||||
// Resolve XNADDR
|
||||
IN_ADDR inaddrRemote;
|
||||
g_pMatchExtensions->GetIXOnline()->XNetRegisterKey( &xsi.sessionID, &xsi.keyExchangeKey );
|
||||
if ( int err = g_pMatchExtensions->GetIXOnline()->XNetXnAddrToInAddr( &xsi.hostAddress, &xsi.sessionID, &inaddrRemote ) )
|
||||
{
|
||||
DevWarning( "MatchSession_ResolveServerInfo_Helper_ConnectString::XNetXnAddrToInAddr"
|
||||
" failed to resolve XNADDR ( code 0x%08X, sessioninfo = %s )\n",
|
||||
err, szSessionInfo );
|
||||
|
||||
g_pMatchExtensions->GetIXOnline()->XNetUnregisterKey( &xsi.sessionID );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initiate secure connection and key exchange
|
||||
if ( int err = g_pMatchExtensions->GetIXOnline()->XNetConnect( inaddrRemote ) )
|
||||
{
|
||||
DevWarning( "MatchSession_ResolveServerInfo_Helper_ConnectString::XNetConnect"
|
||||
" failed to start key exchange ( code 0x%08X, sessioninfo = %s )\n",
|
||||
err, szSessionInfo );
|
||||
|
||||
// Secure IN_ADDR associations are removed implicitly when their key gets unregistered
|
||||
g_pMatchExtensions->GetIXOnline()->XNetUnregisterKey( &xsi.sessionID );
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Prepare connection string
|
||||
//
|
||||
netadr_t inetAddr;
|
||||
inetAddr.SetType( NA_IP );
|
||||
inetAddr.SetIPAndPort( inaddrRemote.s_addr, 0 );
|
||||
|
||||
// Now we know the address for the game to connect
|
||||
Q_strncpy( info.m_dsResult.m_szConnectionString, inetAddr.ToString( true ), ARRAYSIZE( info.m_dsResult.m_szConnectionString ) );
|
||||
|
||||
//
|
||||
// Remember all the settings needed to deallocate the secure association
|
||||
//
|
||||
info.m_szSecureServerAddress = info.m_dsResult.m_szInsecureSendableServerAddress;
|
||||
Q_snprintf( info.m_dsResult.m_szInsecureSendableServerAddress,
|
||||
ARRAYSIZE( info.m_dsResult.m_szInsecureSendableServerAddress ),
|
||||
"SESSIONINFO %s", szSessionInfo );
|
||||
|
||||
// Start QOS rate calculation for opponents session host X360 remote xnaddr
|
||||
MatchSession_RateAdjustmentUpdate_Start( xsi );
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
Q_snprintf( info.m_szConnectCmd, sizeof( info.m_szConnectCmd ),
|
||||
"connect_splitscreen %s %s %d\n",
|
||||
szConnectionString,
|
||||
szConnectionString,
|
||||
XBX_GetNumGameUsers() );
|
||||
#elif !defined( NO_STEAM )
|
||||
Q_snprintf( info.m_szConnectCmd, sizeof( info.m_szConnectCmd ),
|
||||
"connect %s %s\n",
|
||||
info.m_dsResult.m_szPublicConnectionString,
|
||||
info.m_dsResult.m_szPrivateConnectionString );
|
||||
#endif
|
||||
|
||||
info.m_xuidJingle = pSettings->GetUint64( "server/xuid", 0ull );
|
||||
|
||||
if ( uint64 uiReservationCookieOverride = pSettings->GetUint64( "server/reservationid", 0ull ) )
|
||||
info.m_uiReservationCookie = uiReservationCookieOverride;
|
||||
else if ( pSysSession )
|
||||
info.m_uiReservationCookie = pSysSession->GetReservationCookie();
|
||||
else
|
||||
info.m_uiReservationCookie = 0ull;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MatchSession_ResolveServerInfo( KeyValues *pSettings, CSysSessionBase *pSysSession, MatchSessionServerInfo_t &info, uint uiResolveFlags, uint64 ullCrypt )
|
||||
{
|
||||
if ( ( uiResolveFlags & ( info.RESOLVE_DSRESULT | info.RESOLVE_QOS_RATE_PROBE ) ) &&
|
||||
!MatchSession_ResolveServerInfo_Helper_DsResult( pSettings, pSysSession, info, uiResolveFlags, ullCrypt ) )
|
||||
return false;
|
||||
|
||||
if ( ( uiResolveFlags & info.RESOLVE_CONNECTSTRING ) &&
|
||||
!MatchSession_ResolveServerInfo_Helper_ConnectString( pSettings, pSysSession, info, uiResolveFlags ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ConVar mm_tu_string( "mm_tu_string", "00000000" );
|
||||
|
||||
uint64 MatchSession_GetMachineFlags()
|
||||
{
|
||||
uint64 uiFlags = 0;
|
||||
if ( IsPS3() )
|
||||
uiFlags |= MACHINE_PLATFORM_PS3;
|
||||
return uiFlags;
|
||||
}
|
||||
|
||||
char const * MatchSession_GetTuInstalledString()
|
||||
{
|
||||
return mm_tu_string.GetString();
|
||||
}
|
||||
|
||||
char const * MatchSession_EncryptAddressString( char const *szAddress, uint64 ullCrypt )
|
||||
{
|
||||
if ( !szAddress || !*szAddress )
|
||||
return NULL;
|
||||
if ( !ullCrypt )
|
||||
return NULL;
|
||||
if ( szAddress[0] == ':' )
|
||||
return NULL;
|
||||
if ( szAddress[ 0 ] == '$' )
|
||||
return NULL;
|
||||
|
||||
static unsigned char s_chData[256];
|
||||
int nLen = Q_strlen( szAddress );
|
||||
if ( nLen >= ARRAYSIZE( s_chData )/2 - 1 )
|
||||
return NULL;
|
||||
|
||||
// Copy the address
|
||||
s_chData[0] = '$';
|
||||
for ( int j = 0; j < nLen; ++ j )
|
||||
{
|
||||
uint8 uiVal = uint8( szAddress[j] ) ^ uint8( reinterpret_cast< uint8 * >(&ullCrypt)[ j % sizeof( uint64 ) ] );
|
||||
Q_snprintf( (char*)( s_chData + 1 + 2*j ), 3, "%02X", ( uint32 ) uiVal );
|
||||
}
|
||||
return (char*) s_chData;
|
||||
}
|
||||
|
||||
char const * MatchSession_DecryptAddressString( char const *szAddress, uint64 ullCrypt )
|
||||
{
|
||||
if ( !szAddress || !*szAddress )
|
||||
return NULL;
|
||||
if ( !ullCrypt )
|
||||
return NULL;
|
||||
if ( szAddress[ 0 ] != '$' )
|
||||
return NULL;
|
||||
|
||||
static unsigned char s_chData[ 256 ];
|
||||
int nLen = Q_strlen( szAddress );
|
||||
if ( nLen*2 + 2 >= ARRAYSIZE( s_chData ) )
|
||||
return NULL;
|
||||
|
||||
// Copy the address
|
||||
for ( int j = 0; j < nLen/2; ++j )
|
||||
{
|
||||
uint32 uiVal;
|
||||
if ( !sscanf( szAddress + 1 + 2*j, "%02X", &uiVal ) )
|
||||
return NULL;
|
||||
if ( uiVal > 0xFF )
|
||||
return NULL;
|
||||
uiVal = uint8( uiVal ) ^ uint8( reinterpret_cast< uint8 * >(&ullCrypt)[ j % sizeof( uint64 ) ] );
|
||||
if ( !uiVal )
|
||||
return NULL;
|
||||
s_chData[j] = uiVal;
|
||||
}
|
||||
s_chData[nLen/2] = 0;
|
||||
return (char*) s_chData;
|
||||
}
|
||||
|
||||
|
||||
CON_COMMAND( mm_debugprint, "Show debug information about current matchmaking session" )
|
||||
{
|
||||
if ( IMatchSession *pIMatchSession = g_pMMF->GetMatchSession() )
|
||||
{
|
||||
( ( IMatchSessionInternal * ) pIMatchSession )->DebugPrint();
|
||||
}
|
||||
else
|
||||
{
|
||||
DevMsg( "No match session.\n" );
|
||||
}
|
||||
}
|
||||
96
matchmaking/mm_session.h
Normal file
96
matchmaking/mm_session.h
Normal file
@@ -0,0 +1,96 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_SESSION_H
|
||||
#define MM_SESSION_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
class IMatchSessionInternal : public IMatchSession, public IMatchEventsSink
|
||||
{
|
||||
public:
|
||||
// Run a frame update
|
||||
virtual void Update() = 0;
|
||||
|
||||
// Destroy the session object
|
||||
virtual void Destroy() = 0;
|
||||
|
||||
// Debug print a session object
|
||||
virtual void DebugPrint() = 0;
|
||||
|
||||
// Check if another session is joinable
|
||||
virtual bool IsAnotherSessionJoinable( char const *pszAnotherSessionInfo ) = 0;
|
||||
};
|
||||
|
||||
#include "protocol.h"
|
||||
|
||||
#ifndef SWDS
|
||||
|
||||
#include "sys_session.h"
|
||||
#include "x360_xlsp_cmd.h"
|
||||
#include "ds_searcher.h"
|
||||
#include "match_searcher.h"
|
||||
|
||||
#include "mm_session_offline_custom.h"
|
||||
#include "mm_session_online_host.h"
|
||||
#include "mm_session_online_client.h"
|
||||
#include "mm_session_online_search.h"
|
||||
#include "mm_session_online_teamsearch.h"
|
||||
|
||||
void MatchSession_BroadcastSessionSettingsUpdate( KeyValues *pUpdateDeletePackage );
|
||||
void MatchSession_PrepareClientForConnect( KeyValues *pSettings, uint64 uiReservationCookieOverride = 0ull );
|
||||
|
||||
struct MatchSessionServerInfo_t
|
||||
{
|
||||
CDsSearcher::DsResult_t m_dsResult;
|
||||
|
||||
char m_szConnectCmd[256];
|
||||
|
||||
char const *m_szSecureServerAddress;
|
||||
|
||||
XUID m_xuidJingle;
|
||||
uint64 m_uiReservationCookie;
|
||||
|
||||
enum ResolveFlags_t
|
||||
{
|
||||
RESOLVE_DSRESULT = 0x01,
|
||||
RESOLVE_CONNECTSTRING = 0x02,
|
||||
RESOLVE_ALLOW_EXTPEER = 0x04,
|
||||
RESOLVE_QOS_RATE_PROBE = 0x08,
|
||||
};
|
||||
|
||||
enum ResolveMasks_t
|
||||
{
|
||||
RESOLVE_DEFAULT = RESOLVE_DSRESULT | RESOLVE_CONNECTSTRING | RESOLVE_QOS_RATE_PROBE,
|
||||
};
|
||||
};
|
||||
bool MatchSession_ResolveServerInfo( KeyValues *pSettings, CSysSessionBase *pSysSession,
|
||||
MatchSessionServerInfo_t &info, uint uiResolveFlags = MatchSessionServerInfo_t::RESOLVE_DEFAULT,
|
||||
uint64 ullCrypt = 0ull );
|
||||
|
||||
uint64 MatchSession_GetMachineFlags();
|
||||
char const * MatchSession_GetTuInstalledString();
|
||||
|
||||
enum MatchSessionMachineFlags_t
|
||||
{
|
||||
MACHINE_PLATFORM_PS3 = ( 1 << 0 ), // Machine is PS3
|
||||
};
|
||||
|
||||
char const * MatchSession_EncryptAddressString( char const *szAddress, uint64 ullCrypt );
|
||||
char const * MatchSession_DecryptAddressString( char const *szAddress, uint64 ullCrypt );
|
||||
|
||||
#endif // SWDS
|
||||
|
||||
#ifdef _X360
|
||||
|
||||
// Keeps adjusting client side rate setting based on QOS with server
|
||||
void MatchSession_RateAdjustmentUpdate();
|
||||
|
||||
#endif
|
||||
|
||||
#endif // MM_SESSION_H
|
||||
332
matchmaking/mm_session_offline_custom.cpp
Normal file
332
matchmaking/mm_session_offline_custom.cpp
Normal file
@@ -0,0 +1,332 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
#include "fmtstr.h"
|
||||
#include "netmessages_signon.h"
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//
|
||||
// CMatchSessionOfflineCustom
|
||||
//
|
||||
// Implementation of an offline session
|
||||
// that allows customization before the actual
|
||||
// game commences (like playing commentary mode
|
||||
// or playing single-player)
|
||||
//
|
||||
|
||||
CMatchSessionOfflineCustom::CMatchSessionOfflineCustom( KeyValues *pSettings ) :
|
||||
m_pSettings( pSettings->MakeCopy() ),
|
||||
m_autodelete_pSettings( m_pSettings ),
|
||||
m_eState( STATE_INIT ),
|
||||
m_bExpectingServerReload( false )
|
||||
{
|
||||
DevMsg( "Created CMatchSessionOfflineCustom:\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
|
||||
InitializeGameSettings();
|
||||
}
|
||||
|
||||
CMatchSessionOfflineCustom::~CMatchSessionOfflineCustom()
|
||||
{
|
||||
DevMsg( "Destroying CMatchSessionOfflineCustom:\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
}
|
||||
|
||||
KeyValues * CMatchSessionOfflineCustom::GetSessionSettings()
|
||||
{
|
||||
return m_pSettings;
|
||||
}
|
||||
|
||||
void CMatchSessionOfflineCustom::UpdateSessionSettings( KeyValues *pSettings )
|
||||
{
|
||||
// Extend the update keys
|
||||
g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendGameSettingsUpdateKeys( m_pSettings, pSettings );
|
||||
m_pSettings->MergeFrom( pSettings );
|
||||
|
||||
// Broadcast the update to everybody interested
|
||||
MatchSession_BroadcastSessionSettingsUpdate( pSettings );
|
||||
}
|
||||
|
||||
void CMatchSessionOfflineCustom::UpdateTeamProperties( KeyValues *pTeamProperties )
|
||||
{
|
||||
}
|
||||
|
||||
void CMatchSessionOfflineCustom::Command( KeyValues *pCommand )
|
||||
{
|
||||
char const *szCommand = pCommand->GetName();
|
||||
|
||||
if ( !Q_stricmp( "Start", szCommand ) && m_eState < STATE_RUNNING )
|
||||
{
|
||||
m_eState = STATE_RUNNING;
|
||||
|
||||
OnGamePrepareLobbyForGame();
|
||||
|
||||
UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" server { "
|
||||
" server listen "
|
||||
" } "
|
||||
" } "
|
||||
) ) );
|
||||
|
||||
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
||||
"OnProfilesWriteOpportunity", "reason", "sessionstart"
|
||||
) );
|
||||
|
||||
bool bResult = g_pMatchFramework->GetMatchTitle()->StartServerMap( m_pSettings );
|
||||
if ( !bResult )
|
||||
{
|
||||
Warning( "Failed to start server map!\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
Assert( 0 );
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "error", "error", "nomap" ) );
|
||||
}
|
||||
Msg( "Succeeded in starting server map!\n" );
|
||||
return;
|
||||
}
|
||||
if ( !Q_stricmp( "QueueConnect", szCommand ) )
|
||||
{
|
||||
char const *szConnectAddress = pCommand->GetString( "adronline", "0.0.0.0" );
|
||||
uint64 uiReservationId = pCommand->GetUint64( "reservationid" );
|
||||
bool bAutoCloseSession = pCommand->GetBool( "auto_close_session" );
|
||||
Assert( bAutoCloseSession );
|
||||
if ( bAutoCloseSession )
|
||||
{
|
||||
// Switch the state
|
||||
m_eState = STATE_RUNNING;
|
||||
|
||||
MatchSession_PrepareClientForConnect( m_pSettings, uiReservationId );
|
||||
|
||||
// Close the session, potentially resetting a bunch of state
|
||||
if ( bAutoCloseSession )
|
||||
g_pMatchFramework->CloseSession();
|
||||
|
||||
// Determine reservation settings required
|
||||
g_pMatchExtensions->GetINetSupport()->UpdateClientReservation( uiReservationId, 0ull );
|
||||
|
||||
// Issue the connect command
|
||||
g_pMatchExtensions->GetIVEngineClient()->StartLoadingScreenForCommand( CFmtStr( "connect %s", szConnectAddress ) );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Let the title-specific matchmaking handle the command
|
||||
//
|
||||
CUtlVector< KeyValues * > arrPlayersUpdated;
|
||||
arrPlayersUpdated.SetCount( m_pSettings->GetInt( "members/numPlayers", 0 ) );
|
||||
memset( arrPlayersUpdated.Base(), 0, arrPlayersUpdated.Count() * sizeof( KeyValues * ) );
|
||||
|
||||
g_pMMF->GetMatchTitleGameSettingsMgr()->ExecuteCommand( pCommand, GetSessionSystemData(), m_pSettings, arrPlayersUpdated.Base() );
|
||||
|
||||
// Now notify the framework about player updated
|
||||
for ( int k = 0; k < arrPlayersUpdated.Count(); ++ k )
|
||||
{
|
||||
if ( !arrPlayersUpdated[k] )
|
||||
break;
|
||||
|
||||
// Notify the framework about player updated
|
||||
KeyValues *kvEvent = new KeyValues( "OnPlayerUpdated" );
|
||||
kvEvent->SetUint64( "xuid", arrPlayersUpdated[k]->GetUint64( "xuid" ) );
|
||||
g_pMatchEventsSubscription->BroadcastEvent( kvEvent );
|
||||
}
|
||||
|
||||
//
|
||||
// Send the command as event for handling
|
||||
//
|
||||
KeyValues *pEvent = pCommand->MakeCopy();
|
||||
pEvent->SetName( CFmtStr( "Command::%s", pCommand->GetName() ) );
|
||||
g_pMatchEventsSubscription->BroadcastEvent( pEvent );
|
||||
}
|
||||
|
||||
uint64 CMatchSessionOfflineCustom::GetSessionID()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CMatchSessionOfflineCustom::Update()
|
||||
{
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_INIT:
|
||||
m_eState = STATE_CONFIG;
|
||||
|
||||
// Let everybody know that the session is now ready
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "ready", "transition", "offlineinit" ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOfflineCustom::Destroy()
|
||||
{
|
||||
if ( m_eState == STATE_RUNNING )
|
||||
{
|
||||
g_pMatchExtensions->GetIVEngineClient()->ExecuteClientCmd( "disconnect" );
|
||||
|
||||
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
||||
"OnProfilesWriteOpportunity", "reason", "sessionend"
|
||||
) );
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CMatchSessionOfflineCustom::DebugPrint()
|
||||
{
|
||||
DevMsg( "CMatchSessionOfflineCustom [ state=%d ]\n", m_eState );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
}
|
||||
|
||||
void CMatchSessionOfflineCustom::OnEvent( KeyValues *pEvent )
|
||||
{
|
||||
char const *szEvent = pEvent->GetName();
|
||||
|
||||
if ( !Q_stricmp( "OnEngineClientSignonStateChange", szEvent ) )
|
||||
{
|
||||
int iOldState = pEvent->GetInt( "old", 0 );
|
||||
int iNewState = pEvent->GetInt( "new", 0 );
|
||||
|
||||
if ( iOldState >= SIGNONSTATE_CONNECTED &&
|
||||
iNewState < SIGNONSTATE_CONNECTED )
|
||||
{
|
||||
// Disconnecting from server
|
||||
DevMsg( "OnEngineClientSignonStateChange\n" );
|
||||
if ( m_bExpectingServerReload )
|
||||
{
|
||||
m_bExpectingServerReload = false;
|
||||
DevMsg( " session was expecting server reload...\n" );
|
||||
return;
|
||||
}
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "mmF->CloseSession" ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnEngineClientSignonStatePrepareChange", szEvent ) )
|
||||
{
|
||||
char const *szReason = pEvent->GetString( "reason" );
|
||||
if ( !Q_stricmp( "reload", szReason ) )
|
||||
{
|
||||
Assert( !m_bExpectingServerReload );
|
||||
m_bExpectingServerReload = true;
|
||||
return;
|
||||
}
|
||||
else if ( !Q_stricmp( "load", szReason ) )
|
||||
{
|
||||
char const *szLevelName = g_pMatchExtensions->GetIVEngineClient()->GetLevelName();
|
||||
if ( szLevelName && szLevelName[0] && g_pMatchExtensions->GetIVEngineClient()->IsConnected() )
|
||||
{
|
||||
Assert( !m_bExpectingServerReload );
|
||||
m_bExpectingServerReload = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnEngineEndGame", szEvent ) )
|
||||
{
|
||||
DevMsg( "OnEngineEndGame\n" );
|
||||
|
||||
// Issue the disconnect command
|
||||
g_pMatchExtensions->GetIVEngineClient()->ExecuteClientCmd( "disconnect" );
|
||||
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "mmF->CloseSession" ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOfflineCustom::InitializeGameSettings()
|
||||
{
|
||||
// Since the session can be created with a minimal amount of data available
|
||||
// the session object is responsible for initializing the missing data to defaults
|
||||
// or saved values or values from gamer progress/profile or etc...
|
||||
|
||||
if ( KeyValues *kv = m_pSettings->FindKey( "system", true ) )
|
||||
{
|
||||
kv->SetString( "network", "offline" );
|
||||
kv->SetString( "access", "public" );
|
||||
}
|
||||
|
||||
if ( KeyValues *kv = m_pSettings->FindKey( "options", true ) )
|
||||
{
|
||||
kv->SetString( "server", "listen" );
|
||||
}
|
||||
|
||||
if ( KeyValues *pMembers = m_pSettings->FindKey( "members", true ) )
|
||||
{
|
||||
pMembers->SetInt( "numMachines", 1 );
|
||||
|
||||
int numPlayers = 1;
|
||||
#ifdef _GAMECONSOLE
|
||||
numPlayers = XBX_GetNumGameUsers();
|
||||
#endif
|
||||
pMembers->SetInt( "numPlayers", numPlayers );
|
||||
pMembers->SetInt( "numSlots", numPlayers );
|
||||
|
||||
if ( KeyValues *pMachine = pMembers->FindKey( "machine0", true ) )
|
||||
{
|
||||
IPlayerLocal *pPriPlayer = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() );
|
||||
|
||||
pMachine->SetUint64( "id", ( pPriPlayer ? pPriPlayer->GetXUID() : INVALID_XUID ) );
|
||||
pMachine->SetUint64( "flags", MatchSession_GetMachineFlags() );
|
||||
pMachine->SetInt( "numPlayers", numPlayers );
|
||||
pMachine->SetUint64( "dlcmask", g_pMatchFramework->GetMatchSystem()->GetDlcManager()->GetDataInfo()->GetUint64( "@info/installed" ) );
|
||||
pMachine->SetString( "tuver", MatchSession_GetTuInstalledString() );
|
||||
pMachine->SetInt( "ping", 0 );
|
||||
|
||||
for ( int k = 0; k < numPlayers; ++ k )
|
||||
{
|
||||
if ( KeyValues *pPlayer = pMachine->FindKey( CFmtStr( "player%d", k ), true ) )
|
||||
{
|
||||
int iController = 0;
|
||||
#ifdef _GAMECONSOLE
|
||||
iController = XBX_GetUserId( k );
|
||||
#endif
|
||||
IPlayerLocal *player = g_pPlayerManager->GetLocalPlayer( iController );
|
||||
if ( player )
|
||||
{
|
||||
pPlayer->SetUint64( "xuid", player->GetXUID() );
|
||||
pPlayer->SetString( "name", player->GetName() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Let the title extend the game settings
|
||||
g_pMMF->GetMatchTitleGameSettingsMgr()->InitializeGameSettings( m_pSettings, "host" );
|
||||
|
||||
DevMsg( "CMatchSessionOfflineCustom::InitializeGameSettings adjusted settings:\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
}
|
||||
|
||||
void CMatchSessionOfflineCustom::OnGamePrepareLobbyForGame()
|
||||
{
|
||||
// Remember which players will get updated
|
||||
CUtlVector< KeyValues * > arrPlayersUpdated;
|
||||
arrPlayersUpdated.SetCount( m_pSettings->GetInt( "members/numPlayers", 0 ) );
|
||||
memset( arrPlayersUpdated.Base(), 0, arrPlayersUpdated.Count() * sizeof( KeyValues * ) );
|
||||
|
||||
g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareLobbyForGame( m_pSettings, arrPlayersUpdated.Base() );
|
||||
|
||||
// Notify the framework of the updates
|
||||
for ( int k = 0; k < arrPlayersUpdated.Count(); ++ k )
|
||||
{
|
||||
if ( !arrPlayersUpdated[k] )
|
||||
break;
|
||||
|
||||
// Notify the framework about player updated
|
||||
KeyValues *kvEvent = new KeyValues( "OnPlayerUpdated" );
|
||||
kvEvent->SetUint64( "xuid", arrPlayersUpdated[k]->GetUint64( "xuid" ) );
|
||||
g_pMatchEventsSubscription->BroadcastEvent( kvEvent );
|
||||
}
|
||||
|
||||
// Let the title prepare for connect
|
||||
MatchSession_PrepareClientForConnect( m_pSettings );
|
||||
}
|
||||
86
matchmaking/mm_session_offline_custom.h
Normal file
86
matchmaking/mm_session_offline_custom.h
Normal file
@@ -0,0 +1,86 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_SESSION_OFFLINE_CUSTOM_H
|
||||
#define MM_SESSION_OFFLINE_CUSTOM_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
//
|
||||
// CMatchSessionOfflineCustom
|
||||
//
|
||||
// Implementation of an offline session
|
||||
// that allows customization before the actual
|
||||
// game commences (like playing commentary mode
|
||||
// or playing single-player)
|
||||
//
|
||||
|
||||
class CMatchSessionOfflineCustom : public IMatchSessionInternal
|
||||
{
|
||||
// Methods of IMatchSession
|
||||
public:
|
||||
// Get an internal pointer to session system-specific data
|
||||
virtual KeyValues * GetSessionSystemData() { return NULL; }
|
||||
|
||||
// Get an internal pointer to session settings
|
||||
virtual KeyValues * GetSessionSettings();
|
||||
|
||||
// Update session settings, only changing keys and values need
|
||||
// to be passed and they will be updated
|
||||
virtual void UpdateSessionSettings( KeyValues *pSettings );
|
||||
|
||||
virtual void UpdateTeamProperties( KeyValues *pTeamProperties );
|
||||
|
||||
virtual uint64 GetSessionID();
|
||||
|
||||
// Issue a session command
|
||||
virtual void Command( KeyValues *pCommand );
|
||||
|
||||
// Run a frame update
|
||||
virtual void Update();
|
||||
|
||||
// Destroy the session object
|
||||
virtual void Destroy();
|
||||
|
||||
// Debug print a session object
|
||||
virtual void DebugPrint();
|
||||
|
||||
// Check if another session is joinable
|
||||
virtual bool IsAnotherSessionJoinable( char const *pszAnotherSessionInfo ) { return true; }
|
||||
|
||||
// Process event
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
public:
|
||||
explicit CMatchSessionOfflineCustom( KeyValues *pSettings );
|
||||
~CMatchSessionOfflineCustom();
|
||||
|
||||
protected:
|
||||
void InitializeGameSettings();
|
||||
|
||||
//
|
||||
// Overrides
|
||||
//
|
||||
protected:
|
||||
void OnGamePrepareLobbyForGame();
|
||||
|
||||
protected:
|
||||
KeyValues *m_pSettings;
|
||||
KeyValues::AutoDelete m_autodelete_pSettings;
|
||||
|
||||
enum State_t
|
||||
{
|
||||
STATE_INIT,
|
||||
STATE_CONFIG,
|
||||
STATE_RUNNING
|
||||
};
|
||||
State_t m_eState;
|
||||
bool m_bExpectingServerReload;
|
||||
};
|
||||
|
||||
#endif
|
||||
639
matchmaking/mm_session_online_client.cpp
Normal file
639
matchmaking/mm_session_online_client.cpp
Normal file
@@ -0,0 +1,639 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
#include "vstdlib/random.h"
|
||||
#include "fmtstr.h"
|
||||
|
||||
#include "netmessages_signon.h"
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
//
|
||||
// CMatchSessionOnlineClient
|
||||
//
|
||||
// Implementation of an online session of a host machine
|
||||
//
|
||||
|
||||
void CMatchSessionOnlineClient::Init()
|
||||
{
|
||||
KeyValues *psessionHostDataUnpacked = m_pSettings->FindKey( "sessionHostDataUnpacked" );
|
||||
if ( psessionHostDataUnpacked )
|
||||
{
|
||||
m_pSettings->SetPtr( "options/sessionHostData", psessionHostDataUnpacked );
|
||||
}
|
||||
}
|
||||
|
||||
CMatchSessionOnlineClient::CMatchSessionOnlineClient( KeyValues *pSettings ) :
|
||||
m_pSettings( pSettings->MakeCopy() ),
|
||||
m_autodelete_pSettings( m_pSettings ),
|
||||
m_pSysData( new KeyValues( "SysSessionData", "type", "client" ) ),
|
||||
m_autodelete_pSysData( m_pSysData ),
|
||||
m_eState( STATE_INIT ),
|
||||
m_pSysSession( NULL )
|
||||
{
|
||||
DevMsg( "Created CMatchSessionOnlineClient:\n" );
|
||||
Init();
|
||||
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
|
||||
InitializeGameSettings();
|
||||
}
|
||||
|
||||
CMatchSessionOnlineClient::CMatchSessionOnlineClient( CSysSessionClient *pSysSession, KeyValues *pSettings ) :
|
||||
m_pSettings( pSettings ),
|
||||
m_autodelete_pSettings( m_pSettings ),
|
||||
m_pSysData( new KeyValues( "SysSessionData", "type", "client" ) ),
|
||||
m_autodelete_pSysData( m_pSysData ),
|
||||
m_eState( STATE_LOBBY ),
|
||||
m_pSysSession( pSysSession )
|
||||
{
|
||||
DevMsg( "Converted sys session into CMatchSessionOnlineClient:\n" );
|
||||
Init();
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
}
|
||||
|
||||
CMatchSessionOnlineClient::CMatchSessionOnlineClient( CSysSessionHost *pSysSession, KeyValues *pExtendedSettings ) :
|
||||
m_pSettings( pExtendedSettings->FindKey( "settings" ) ),
|
||||
m_autodelete_pSettings( m_pSettings ),
|
||||
m_pSysData( new KeyValues( "SysSessionData", "type", "client" ) ),
|
||||
m_autodelete_pSysData( m_pSysData ),
|
||||
m_eState( STATE_LOBBY ), // it's at least lobby, we'll figure out later
|
||||
m_pSysSession( NULL )
|
||||
{
|
||||
DevMsg( "Migrating into CMatchSessionOnlineClient...\n" );
|
||||
Init();
|
||||
Assert( m_pSettings );
|
||||
|
||||
KeyValues::AutoDelete autodelete( pExtendedSettings );
|
||||
pExtendedSettings->RemoveSubKey( m_pSettings ); // it's now our settings
|
||||
|
||||
// Install our session
|
||||
g_pMMF->SetCurrentMatchSession( this );
|
||||
|
||||
// Check if the game is in a non-lobby state
|
||||
char const *szState = pExtendedSettings->GetString( "state", "" );
|
||||
if ( !Q_stricmp( szState, "game" ) )
|
||||
m_eState = STATE_GAME;
|
||||
else if ( !Q_stricmp( szState, "ending" ) )
|
||||
m_eState = STATE_ENDING;
|
||||
|
||||
// Now we need to create the system session to reflect the client session passed
|
||||
m_pSysSession = new CSysSessionClient( pSysSession, m_pSettings );
|
||||
pSysSession->Destroy();
|
||||
|
||||
// Show the state
|
||||
DevMsg( "Migrated into CMatchSessionOnlineClient:\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
}
|
||||
|
||||
CMatchSessionOnlineClient::~CMatchSessionOnlineClient()
|
||||
{
|
||||
DevMsg( "Destroying CMatchSessionOnlineClient:\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
}
|
||||
|
||||
KeyValues * CMatchSessionOnlineClient::GetSessionSystemData()
|
||||
{
|
||||
// Setup our sys data
|
||||
m_pSysData->SetUint64( "xuidReserve", m_pSysSession ? m_pSysSession->GetReservationCookie() : 0ull );
|
||||
m_pSysData->SetUint64( "xuidHost", m_pSysSession ? m_pSysSession->GetHostXuid() : 0ull );
|
||||
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_LOBBY:
|
||||
m_pSysData->SetString( "state", "lobby" );
|
||||
break;
|
||||
case STATE_GAME:
|
||||
m_pSysData->SetString( "state", "game" );
|
||||
break;
|
||||
default:
|
||||
m_pSysData->SetString( "state", "" );
|
||||
break;
|
||||
}
|
||||
|
||||
return m_pSysData;
|
||||
}
|
||||
|
||||
KeyValues * CMatchSessionOnlineClient::GetSessionSettings()
|
||||
{
|
||||
return m_pSettings;
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineClient::UpdateSessionSettings( KeyValues *pSettings )
|
||||
{
|
||||
// Avoid a warning and assert for queue state manipulation
|
||||
if ( pSettings->GetFirstSubKey()->GetString( "game/mmqueue", NULL ) )
|
||||
return;
|
||||
|
||||
// Otherwise warn
|
||||
Warning( "CMatchSessionOnlineClient::UpdateSessionSettings is unavailable in state %d!\n", m_eState );
|
||||
Assert( !"CMatchSessionOnlineClient::UpdateSessionSettings is unavailable!\n" );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineClient::UpdateTeamProperties( KeyValues *pSettings )
|
||||
{
|
||||
m_pSysSession->UpdateTeamProperties( pSettings );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineClient::Command( KeyValues *pCommand )
|
||||
{
|
||||
char const *szCommand, *szRun;
|
||||
szCommand = pCommand->GetName();
|
||||
szRun = pCommand->GetString( "run", "" );
|
||||
|
||||
if ( !Q_stricmp( szRun, "host" ) || !Q_stricmp( szRun, "all" ) || !Q_stricmp( szRun, "xuid" ) )
|
||||
{
|
||||
if ( m_pSysSession )
|
||||
{
|
||||
m_pSysSession->Command( pCommand );
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ( !*szRun || !Q_stricmp( szRun, "local" ) )
|
||||
{
|
||||
OnRunCommand( pCommand );
|
||||
return;
|
||||
}
|
||||
|
||||
Warning( "CMatchSessionOnlineClient::Command( %s ) unhandled!\n", szCommand );
|
||||
Assert( !"CMatchSessionOnlineClient::Command" );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineClient::OnRunCommand( KeyValues *pCommand )
|
||||
{
|
||||
char const *szCommand = pCommand->GetName();
|
||||
if ( !Q_stricmp( "Migrate", szCommand ) )
|
||||
{
|
||||
if ( m_pSysSession ) // TODO: research who sends the "Migrate" command and how secure it is?
|
||||
{
|
||||
m_pSysSession->Migrate( pCommand );
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( !Q_stricmp( "QueueConnect", szCommand ) )
|
||||
{
|
||||
if ( ( m_eState == STATE_LOBBY ) && !pCommand->GetUint64( "_remote_xuidsrc" ) )
|
||||
{
|
||||
OnRunCommand_QueueConnect( pCommand );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Client-side command cannot update the players
|
||||
g_pMMF->GetMatchTitleGameSettingsMgr()->ExecuteCommand( pCommand, GetSessionSystemData(), m_pSettings, NULL );
|
||||
|
||||
// Send the command as event for handling
|
||||
KeyValues *pEvent = pCommand->MakeCopy();
|
||||
pEvent->SetName( CFmtStr( "Command::%s", pCommand->GetName() ) );
|
||||
g_pMatchEventsSubscription->BroadcastEvent( pEvent );
|
||||
}
|
||||
|
||||
uint64 CMatchSessionOnlineClient::GetSessionID()
|
||||
{
|
||||
if( m_pSysSession )
|
||||
{
|
||||
return m_pSysSession->GetSessionID();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineClient::Update()
|
||||
{
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_INIT:
|
||||
m_eState = STATE_CREATING;
|
||||
|
||||
// Adjust invite-based settings
|
||||
uint64 uiInviteFlags = g_pMMF->GetLastInviteFlags();
|
||||
m_pSettings->SetUint64( "members/joinflags", uiInviteFlags );
|
||||
|
||||
// Session is creating
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues(
|
||||
"OnMatchSessionUpdate",
|
||||
"state", "progress",
|
||||
"progress", "joining"
|
||||
) );
|
||||
|
||||
// Trigger session creation
|
||||
m_pSysSession = new CSysSessionClient( m_pSettings );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( m_pSysSession )
|
||||
{
|
||||
m_pSysSession->Update();
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineClient::Destroy()
|
||||
{
|
||||
if ( m_eState != STATE_MIGRATE )
|
||||
{
|
||||
g_pMatchExtensions->GetINetSupport()->UpdateClientReservation( 0ull, 0ull );
|
||||
}
|
||||
|
||||
if ( m_eState == STATE_GAME || m_eState == STATE_ENDING )
|
||||
{
|
||||
m_eState = STATE_INIT; // the object is being deleted, but prevent signon state change handlers
|
||||
g_pMatchExtensions->GetIVEngineClient()->ExecuteClientCmd( "disconnect" );
|
||||
}
|
||||
|
||||
if ( m_pSysSession )
|
||||
{
|
||||
m_pSysSession->Destroy();
|
||||
m_pSysSession = NULL;
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineClient::DebugPrint()
|
||||
{
|
||||
DevMsg( "CMatchSessionOnlineClient [ state=%d ]\n", m_eState );
|
||||
|
||||
DevMsg( "System data:\n" );
|
||||
KeyValuesDumpAsDevMsg( GetSessionSystemData(), 1 );
|
||||
|
||||
DevMsg( "Settings data:\n" );
|
||||
KeyValuesDumpAsDevMsg( GetSessionSettings(), 1 );
|
||||
|
||||
if ( m_pSysSession )
|
||||
m_pSysSession->DebugPrint();
|
||||
else
|
||||
DevMsg( "SysSession is NULL\n" );
|
||||
}
|
||||
|
||||
bool CMatchSessionOnlineClient::IsAnotherSessionJoinable( const char *pszAnotherSessionInfo )
|
||||
{
|
||||
#ifdef _X360
|
||||
if ( m_pSysSession )
|
||||
{
|
||||
XSESSION_INFO xsi;
|
||||
if ( m_pSysSession->GetHostNetworkAddress( xsi ) )
|
||||
{
|
||||
XSESSION_INFO xsiAnother;
|
||||
MMX360_SessionInfoFromString( xsiAnother, pszAnotherSessionInfo );
|
||||
if ( !memcmp( &xsiAnother.sessionID, &xsi.sessionID, sizeof( xsi.sessionID ) ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineClient::OnEvent( KeyValues *pEvent )
|
||||
{
|
||||
char const *szEvent = pEvent->GetName();
|
||||
|
||||
if ( !Q_stricmp( "OnEngineClientSignonStateChange", szEvent ) )
|
||||
{
|
||||
int iOldState = pEvent->GetInt( "old", 0 );
|
||||
int iNewState = pEvent->GetInt( "new", 0 );
|
||||
|
||||
if ( iOldState >= SIGNONSTATE_CONNECTED &&
|
||||
iNewState < SIGNONSTATE_CONNECTED )
|
||||
{
|
||||
if ( m_eState == STATE_GAME )
|
||||
{
|
||||
// Lost connection from server or explicit disconnect
|
||||
DevMsg( "OnEngineClientSignonStateChange\n" );
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "mmF->CloseSession" ) );
|
||||
}
|
||||
else if ( m_eState == STATE_ENDING )
|
||||
{
|
||||
m_eState = STATE_LOBBY;
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "ready", "transition", "clientendgame" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnEngineDisconnectReason", szEvent ) )
|
||||
{
|
||||
if ( m_eState == STATE_GAME )
|
||||
{
|
||||
// Lost connection from server or explicit disconnect
|
||||
char const *szReason = pEvent->GetString( "reason", "" );
|
||||
DevMsg( "OnEngineDisconnectReason %s\n", szReason );
|
||||
|
||||
bool bLobbySalvagable =
|
||||
StringHasPrefix( szReason, "Connection to server timed out" ) ||
|
||||
StringHasPrefix( szReason, "Server shutting down" );
|
||||
|
||||
if ( KeyValues *pDisconnectHdlr = g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareClientLobbyForGameDisconnect( m_pSettings, pEvent ) )
|
||||
{
|
||||
KeyValues::AutoDelete autodelete( pDisconnectHdlr );
|
||||
char const *szDisconnectHdlr = pDisconnectHdlr->GetString( "disconnecthdlr", "" );
|
||||
if ( !Q_stricmp( szDisconnectHdlr, "destroy" ) )
|
||||
bLobbySalvagable = false;
|
||||
else if ( !Q_stricmp( szDisconnectHdlr, "lobby" ) )
|
||||
bLobbySalvagable = true;
|
||||
}
|
||||
|
||||
if ( !bLobbySalvagable )
|
||||
{
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "mmF->CloseSession" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Server shutting down, try to retain the lobby
|
||||
pEvent->SetString( "disconnecthdlr", "lobby" );
|
||||
g_pMatchEventsSubscription->RegisterEventData( pEvent->MakeCopy() );
|
||||
OnEndGameToLobby();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnEngineEndGame", szEvent ) )
|
||||
{
|
||||
if ( m_eState == STATE_GAME )
|
||||
{
|
||||
OnEndGameToLobby();
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnMatchSessionUpdate", szEvent ) )
|
||||
{
|
||||
char const *szState = pEvent->GetString( "state", "" );
|
||||
if ( !Q_stricmp( "updated", szState ) )
|
||||
{
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_LOBBY:
|
||||
if ( KeyValues *pServer = pEvent->FindKey( "update/server" ) )
|
||||
{
|
||||
// If we are in a con team match we only connect to game servers
|
||||
// in response to an invite from the host so don't do anything
|
||||
KeyValues *pConTeamMatch = m_pSettings->FindKey("options/conteammatch");
|
||||
if ( !pConTeamMatch ||
|
||||
( pEvent->GetUint64( "update/server/xuid" ) && pEvent->GetUint64( "update/server/reservationid" ) ) )
|
||||
{
|
||||
// Game server has become available - connect to it!
|
||||
ConnectGameServer();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_GAME:
|
||||
if ( KeyValues *pServer = pEvent->FindKey( "delete/server" ) )
|
||||
{
|
||||
// Lobby leader has disconnected from game server, disconnect too
|
||||
OnEndGameToLobby();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnNetLanConnectionlessPacket", szEvent ) )
|
||||
{
|
||||
char const *szPacketType = pEvent->GetFirstTrueSubKey()->GetName();
|
||||
if ( m_pSysSession && m_eState > STATE_CREATING &&
|
||||
!Q_stricmp( szPacketType, "LanSearch" ) )
|
||||
{
|
||||
m_pSysSession->ReplyLanSearch( pEvent );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnSysMuteListChanged", szEvent ) )
|
||||
{
|
||||
if ( m_pSysSession )
|
||||
m_pSysSession->Voice_UpdateMutelist();
|
||||
}
|
||||
else if ( !Q_stricmp( "mmF->SysSessionUpdate", szEvent ) )
|
||||
{
|
||||
if ( m_pSysSession && pEvent->GetPtr( "syssession", NULL ) == m_pSysSession )
|
||||
{
|
||||
// This is our session
|
||||
if ( char const *szError = pEvent->GetString( "error", NULL ) )
|
||||
{
|
||||
// Destroy the session
|
||||
m_pSysSession->Destroy();
|
||||
m_pSysSession = NULL;
|
||||
m_eState = STATE_CREATING;
|
||||
|
||||
// Handle error
|
||||
KeyValues *kvUpdateError = pEvent->MakeCopy();
|
||||
kvUpdateError->SetName( "OnMatchSessionUpdate" );
|
||||
kvUpdateError->SetString( "state", "error" );
|
||||
g_pMatchEventsSubscription->BroadcastEvent( kvUpdateError );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( uint64 ullCrypt = pEvent->GetUint64( "crypt" ) )
|
||||
m_pSysData->SetUint64( "crypt", ullCrypt );
|
||||
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_CREATING:
|
||||
// Session was creating
|
||||
// Now we are at least in the lobby
|
||||
m_eState = STATE_LOBBY;
|
||||
OnClientFullyConnectedToSession();
|
||||
return;
|
||||
|
||||
default:
|
||||
if ( char const *szAction = pEvent->GetString( "action", NULL ) )
|
||||
{
|
||||
if ( !Q_stricmp( "host", szAction ) )
|
||||
{
|
||||
KeyValues *pExtendedSettings = new KeyValues( "ExtendedSettings" );
|
||||
char const *szMigrateState = "lobby";
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_GAME:
|
||||
szMigrateState = "game";
|
||||
break;
|
||||
case STATE_ENDING:
|
||||
szMigrateState = "ending";
|
||||
break;
|
||||
}
|
||||
pExtendedSettings->SetString( "state", szMigrateState );
|
||||
if ( uint64 ullCrypt = m_pSysData->GetUint64( "crypt" ) )
|
||||
pExtendedSettings->SetUint64( "crypt", ullCrypt );
|
||||
pExtendedSettings->AddSubKey( m_pSettings );
|
||||
|
||||
// Release ownership of the resources since new match session now owns them
|
||||
m_pSettings = NULL;
|
||||
m_autodelete_pSettings.Assign( NULL );
|
||||
|
||||
CSysSessionClient *pSysSession = m_pSysSession;
|
||||
m_pSysSession = NULL;
|
||||
|
||||
// Destroy our instance and create the new match interface
|
||||
m_eState = STATE_MIGRATE;
|
||||
g_pMMF->SetCurrentMatchSession( NULL );
|
||||
this->Destroy();
|
||||
|
||||
// Now we need to create the new host session that will install itself
|
||||
IMatchSession *pNewHost = new CMatchSessionOnlineHost( pSysSession, pExtendedSettings );
|
||||
Assert( g_pMMF->GetMatchSession() == pNewHost );
|
||||
pNewHost;
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
DevWarning( "Unhandled mmF->SysSessionUpdate!\n" );
|
||||
Assert( !"Unhandled mmF->SysSessionUpdate!\n" );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "mmF->SysSessionCommand", szEvent ) )
|
||||
{
|
||||
if ( m_pSysSession && pEvent->GetPtr( "syssession", NULL ) == m_pSysSession )
|
||||
{
|
||||
KeyValues *pCommand = pEvent->GetFirstTrueSubKey();
|
||||
if ( pCommand )
|
||||
{
|
||||
OnRunCommand( pCommand );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineClient::OnClientFullyConnectedToSession()
|
||||
{
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "created" ) );
|
||||
|
||||
// Check whether the game server is available (i.e. game in progress)
|
||||
if ( KeyValues *pServer = m_pSettings->FindKey( "server" ) )
|
||||
{
|
||||
ConnectGameServer();
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "ready", "transition", "clientconnect" ) );
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineClient::OnEndGameToLobby()
|
||||
{
|
||||
m_eState = STATE_ENDING;
|
||||
|
||||
// Issue the disconnect command
|
||||
g_pMatchExtensions->GetIVEngineClient()->ExecuteClientCmd( "disconnect" );
|
||||
|
||||
// Mark gameplay state as inactive
|
||||
m_pSysSession->SetSessionActiveGameplayState( false, NULL );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineClient::InitializeGameSettings()
|
||||
{
|
||||
// Initialize only the settings required to connect...
|
||||
|
||||
if ( KeyValues *kv = m_pSettings->FindKey( "system", true ) )
|
||||
{
|
||||
KeyValuesAddDefaultString( kv, "network", "LIVE" );
|
||||
KeyValuesAddDefaultString( kv, "access", "public" );
|
||||
}
|
||||
|
||||
if ( KeyValues *pMembers = m_pSettings->FindKey( "members", true ) )
|
||||
{
|
||||
pMembers->SetInt( "numMachines", 1 );
|
||||
|
||||
int numPlayers = 1;
|
||||
#ifdef _GAMECONSOLE
|
||||
numPlayers = XBX_GetNumGameUsers();
|
||||
#endif
|
||||
pMembers->SetInt( "numPlayers", numPlayers );
|
||||
pMembers->SetInt( "numSlots", numPlayers );
|
||||
|
||||
if ( KeyValues *pMachine = pMembers->FindKey( "machine0", true ) )
|
||||
{
|
||||
XUID machineid = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
|
||||
|
||||
pMachine->SetUint64( "id", machineid );
|
||||
#if defined( _PS3 ) && !defined( NO_STEAM )
|
||||
pMachine->SetUint64( "psnid", steamapicontext->SteamUser()->GetConsoleSteamID().ConvertToUint64() );
|
||||
#endif
|
||||
pMachine->SetUint64( "flags", MatchSession_GetMachineFlags() );
|
||||
pMachine->SetInt( "numPlayers", numPlayers );
|
||||
pMachine->SetUint64( "dlcmask", g_pMatchFramework->GetMatchSystem()->GetDlcManager()->GetDataInfo()->GetUint64( "@info/installed" ) );
|
||||
pMachine->SetString( "tuver", MatchSession_GetTuInstalledString() );
|
||||
pMachine->SetInt( "ping", 0 );
|
||||
|
||||
for ( int k = 0; k < numPlayers; ++ k )
|
||||
{
|
||||
if ( KeyValues *pPlayer = pMachine->FindKey( CFmtStr( "player%d", k ), true ) )
|
||||
{
|
||||
int iController = 0;
|
||||
#ifdef _GAMECONSOLE
|
||||
iController = XBX_GetUserId( k );
|
||||
#endif
|
||||
IPlayerLocal *player = g_pPlayerManager->GetLocalPlayer( iController );
|
||||
|
||||
pPlayer->SetUint64( "xuid", player->GetXUID() );
|
||||
pPlayer->SetString( "name", player->GetName() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Let the title extend the game settings
|
||||
g_pMMF->GetMatchTitleGameSettingsMgr()->InitializeGameSettings( m_pSettings, "client" );
|
||||
|
||||
DevMsg( "CMatchSessionOnlineClient::InitializeGameSettings adjusted settings:\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineClient::OnRunCommand_QueueConnect( KeyValues *pCommand )
|
||||
{
|
||||
char const *szConnectAddress = pCommand->GetString( "adronline", "0.0.0.0" );
|
||||
uint64 uiReservationId = pCommand->GetUint64( "reservationid" );
|
||||
bool bAutoCloseSession = pCommand->GetBool( "auto_close_session" );
|
||||
|
||||
// Switch the state
|
||||
m_eState = STATE_GAME;
|
||||
|
||||
MatchSession_PrepareClientForConnect( m_pSettings, uiReservationId );
|
||||
|
||||
// Mark gameplay state as active
|
||||
m_pSysSession->SetSessionActiveGameplayState( true, szConnectAddress );
|
||||
|
||||
// Close the session, potentially resetting a bunch of state
|
||||
if ( bAutoCloseSession )
|
||||
g_pMatchFramework->CloseSession();
|
||||
|
||||
// Determine reservation settings required
|
||||
g_pMatchExtensions->GetINetSupport()->UpdateClientReservation( uiReservationId, 0ull );
|
||||
|
||||
// Issue the connect command
|
||||
g_pMatchExtensions->GetIVEngineClient()->StartLoadingScreenForCommand( CFmtStr( "connect %s", szConnectAddress ) );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineClient::ConnectGameServer()
|
||||
{
|
||||
// Switch the state
|
||||
m_eState = STATE_GAME;
|
||||
|
||||
MatchSession_PrepareClientForConnect( m_pSettings );
|
||||
|
||||
//
|
||||
// Resolve server information
|
||||
//
|
||||
MatchSessionServerInfo_t msInfo = {0};
|
||||
if ( !MatchSession_ResolveServerInfo( m_pSettings, m_pSysSession, msInfo, msInfo.RESOLVE_DEFAULT, m_pSysData->GetUint64( "crypt" ) ) )
|
||||
{
|
||||
// Destroy the session
|
||||
m_pSysSession->Destroy();
|
||||
m_pSysSession = NULL;
|
||||
|
||||
// Handle error
|
||||
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate",
|
||||
"state", "error", "error", "connect" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine reservation settings required
|
||||
g_pMatchExtensions->GetINetSupport()->UpdateClientReservation( msInfo.m_uiReservationCookie, msInfo.m_xuidJingle );
|
||||
|
||||
// Mark gameplay state as active
|
||||
m_pSysSession->SetSessionActiveGameplayState( true, msInfo.m_szSecureServerAddress );
|
||||
|
||||
// Issue the connect command
|
||||
g_pMatchExtensions->GetIVEngineClient()->ClientCmd_Unrestricted( msInfo.m_szConnectCmd );
|
||||
}
|
||||
|
||||
96
matchmaking/mm_session_online_client.h
Normal file
96
matchmaking/mm_session_online_client.h
Normal file
@@ -0,0 +1,96 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_SESSION_ONLINE_CLIENT_H
|
||||
#define MM_SESSION_ONLINE_CLIENT_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
//
|
||||
// CMatchSessionOnlineClient
|
||||
//
|
||||
// Implementation of an online session of a client machine
|
||||
//
|
||||
|
||||
class CMatchSessionOnlineClient : public IMatchSessionInternal
|
||||
{
|
||||
// Methods of IMatchSession
|
||||
public:
|
||||
// Get an internal pointer to session system-specific data
|
||||
virtual KeyValues * GetSessionSystemData();
|
||||
|
||||
// Get an internal pointer to session settings
|
||||
virtual KeyValues * GetSessionSettings();
|
||||
|
||||
// Update session settings, only changing keys and values need
|
||||
// to be passed and they will be updated
|
||||
virtual void UpdateSessionSettings( KeyValues *pSettings );
|
||||
|
||||
virtual void UpdateTeamProperties( KeyValues *pTeamProperties );
|
||||
|
||||
// Issue a session command
|
||||
virtual void Command( KeyValues *pCommand );
|
||||
|
||||
virtual uint64 GetSessionID();
|
||||
|
||||
// Run a frame update
|
||||
virtual void Update();
|
||||
|
||||
// Destroy the session object
|
||||
virtual void Destroy();
|
||||
|
||||
// Debug print a session object
|
||||
virtual void DebugPrint();
|
||||
|
||||
// Check if another session is joinable
|
||||
virtual bool IsAnotherSessionJoinable( char const *pszAnotherSessionInfo );
|
||||
|
||||
// Process event
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
void Init();
|
||||
|
||||
public:
|
||||
explicit CMatchSessionOnlineClient( KeyValues *pSettings );
|
||||
explicit CMatchSessionOnlineClient( CSysSessionClient *pSysSession, KeyValues *pSettings );
|
||||
explicit CMatchSessionOnlineClient( CSysSessionHost *pSysSession, KeyValues *pExtendedSettings );
|
||||
~CMatchSessionOnlineClient();
|
||||
|
||||
public:
|
||||
void OnClientFullyConnectedToSession();
|
||||
void OnEndGameToLobby();
|
||||
|
||||
protected:
|
||||
void InitializeGameSettings();
|
||||
|
||||
void OnRunCommand( KeyValues *pCommand );
|
||||
void OnRunCommand_QueueConnect( KeyValues *pCommand );
|
||||
void ConnectGameServer();
|
||||
|
||||
protected:
|
||||
KeyValues *m_pSettings;
|
||||
KeyValues::AutoDelete m_autodelete_pSettings;
|
||||
|
||||
KeyValues *m_pSysData;
|
||||
KeyValues::AutoDelete m_autodelete_pSysData;
|
||||
|
||||
enum State_t
|
||||
{
|
||||
STATE_INIT,
|
||||
STATE_CREATING,
|
||||
STATE_LOBBY,
|
||||
STATE_GAME,
|
||||
STATE_ENDING,
|
||||
STATE_MIGRATE
|
||||
};
|
||||
State_t m_eState;
|
||||
|
||||
CSysSessionClient *m_pSysSession;
|
||||
};
|
||||
|
||||
#endif
|
||||
1676
matchmaking/mm_session_online_host.cpp
Normal file
1676
matchmaking/mm_session_online_host.cpp
Normal file
File diff suppressed because it is too large
Load Diff
116
matchmaking/mm_session_online_host.h
Normal file
116
matchmaking/mm_session_online_host.h
Normal file
@@ -0,0 +1,116 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_SESSION_ONLINE_HOST_H
|
||||
#define MM_SESSION_ONLINE_HOST_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
class CMatchSessionOnlineTeamSearch;
|
||||
class CMatchSessionOnlineSearch;
|
||||
|
||||
//
|
||||
// CMatchSessionOnlineHost
|
||||
//
|
||||
// Implementation of an online session of a host machine
|
||||
//
|
||||
|
||||
class CMatchSessionOnlineHost : public IMatchSessionInternal
|
||||
{
|
||||
// Methods of IMatchSession
|
||||
public:
|
||||
// Get an internal pointer to session system-specific data
|
||||
virtual KeyValues * GetSessionSystemData();
|
||||
|
||||
// Get an internal pointer to session settings
|
||||
virtual KeyValues * GetSessionSettings();
|
||||
|
||||
// Update session settings, only changing keys and values need
|
||||
// to be passed and they will be updated
|
||||
virtual void UpdateSessionSettings( KeyValues *pSettings );
|
||||
|
||||
virtual void UpdateTeamProperties( KeyValues *pTeamProperties );
|
||||
|
||||
// Issue a session command
|
||||
virtual void Command( KeyValues *pCommand );
|
||||
|
||||
virtual uint64 GetSessionID();
|
||||
|
||||
// Run a frame update
|
||||
virtual void Update();
|
||||
|
||||
// Destroy the session object
|
||||
virtual void Destroy();
|
||||
|
||||
// Debug print a session object
|
||||
virtual void DebugPrint();
|
||||
|
||||
// Check if another session is joinable
|
||||
virtual bool IsAnotherSessionJoinable( char const *pszAnotherSessionInfo );
|
||||
|
||||
// Process event
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
public:
|
||||
explicit CMatchSessionOnlineHost( KeyValues *pSettings );
|
||||
explicit CMatchSessionOnlineHost( CSysSessionClient *pSysSession, KeyValues *pExtendedSettings );
|
||||
~CMatchSessionOnlineHost();
|
||||
|
||||
protected:
|
||||
void InitializeGameSettings();
|
||||
void MigrateGameSettings();
|
||||
|
||||
void OnRunCommand( KeyValues *pCommand );
|
||||
void OnRunCommand_Start();
|
||||
void OnRunCommand_StartDsSearchFinished();
|
||||
void OnRunCommand_StartListenServerStarted( uint32 externalIP );
|
||||
void OnRunCommand_Cancel_DsSearch();
|
||||
void OnRunCommand_Match();
|
||||
void OnRunCommand_QueueConnect( KeyValues *pCommand );
|
||||
void OnRunCommand_Cancel_Match();
|
||||
void ConnectGameServer( CDsSearcher::DsResult_t *pDsResult );
|
||||
void StartListenServerMap();
|
||||
void OnEndGameToLobby();
|
||||
void SetSessionActiveGameplayState( bool bActive, char const *szSecureServerAddress );
|
||||
void InviteTeam();
|
||||
|
||||
//
|
||||
// Overrides
|
||||
//
|
||||
protected:
|
||||
virtual void OnGamePrepareLobbyForGame();
|
||||
virtual void OnGamePlayerMachinesConnected( int numMachines );
|
||||
|
||||
protected:
|
||||
KeyValues *m_pSettings;
|
||||
KeyValues::AutoDelete m_autodelete_pSettings;
|
||||
|
||||
KeyValues *m_pSysData;
|
||||
KeyValues::AutoDelete m_autodelete_pSysData;
|
||||
|
||||
enum State_t
|
||||
{
|
||||
STATE_INIT,
|
||||
STATE_CREATING,
|
||||
STATE_LOBBY,
|
||||
STATE_MATCHING, // host is running matchmaking for opponents
|
||||
STATE_MATCHINGRESTART, // team communication failed and matching needs to restart
|
||||
STATE_STARTING, // host is running a dedicated search or other things before actual game server kicks in
|
||||
STATE_LOADING, // host is starting a server map
|
||||
STATE_GAME,
|
||||
STATE_ENDING,
|
||||
STATE_MIGRATE
|
||||
};
|
||||
State_t m_eState;
|
||||
|
||||
CSysSessionHost *m_pSysSession;
|
||||
CDsSearcher *m_pDsSearcher;
|
||||
CMatchSessionOnlineTeamSearch *m_pTeamSearcher;
|
||||
CMatchSessionOnlineSearch *m_pMatchSearcher;
|
||||
};
|
||||
|
||||
#endif
|
||||
771
matchmaking/mm_session_online_search.cpp
Normal file
771
matchmaking/mm_session_online_search.cpp
Normal file
@@ -0,0 +1,771 @@
|
||||
//===== Copyright 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
#include "vstdlib/random.h"
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
static ConVar mm_ignored_sessions_forget_time( "mm_ignored_sessions_forget_time", "600", FCVAR_DEVELOPMENTONLY );
|
||||
static ConVar mm_ignored_sessions_forget_pass( "mm_ignored_sessions_forget_pass", "5", FCVAR_DEVELOPMENTONLY );
|
||||
|
||||
class CIgnoredSessionsMgr
|
||||
{
|
||||
public:
|
||||
CIgnoredSessionsMgr();
|
||||
|
||||
public:
|
||||
void Reset();
|
||||
void OnSearchStarted();
|
||||
bool IsIgnored( XNKID xid );
|
||||
void Ignore( XNKID xid );
|
||||
|
||||
protected:
|
||||
struct SessionSearchPass_t
|
||||
{
|
||||
double m_flTime;
|
||||
int m_nSearchCounter;
|
||||
};
|
||||
|
||||
static bool XNKID_LessFunc( const XNKID &lhs, const XNKID &rhs )
|
||||
{
|
||||
return ( (uint64 const&) lhs ) < ( (uint64 const&) rhs );
|
||||
}
|
||||
|
||||
CUtlMap< XNKID, SessionSearchPass_t > m_IgnoredSessionsAndTime;
|
||||
int m_nSearchCounter;
|
||||
};
|
||||
|
||||
static CIgnoredSessionsMgr g_IgnoredSessionsMgr;
|
||||
static CUtlMap< uint32, float > g_mapValidatedWhitelistCacheTimestamps( DefLessFunc( uint32 ) );
|
||||
|
||||
CON_COMMAND_F( mm_ignored_sessions_reset, "Reset ignored sessions", FCVAR_DEVELOPMENTONLY )
|
||||
{
|
||||
g_IgnoredSessionsMgr.Reset();
|
||||
DevMsg( "Reset ignored sessions" );
|
||||
}
|
||||
|
||||
CIgnoredSessionsMgr::CIgnoredSessionsMgr() :
|
||||
m_IgnoredSessionsAndTime( XNKID_LessFunc ),
|
||||
m_nSearchCounter( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
void CIgnoredSessionsMgr::Reset()
|
||||
{
|
||||
m_nSearchCounter = 0;
|
||||
m_IgnoredSessionsAndTime.RemoveAll();
|
||||
}
|
||||
|
||||
void CIgnoredSessionsMgr::OnSearchStarted()
|
||||
{
|
||||
++ m_nSearchCounter;
|
||||
|
||||
double fNow = Plat_FloatTime();
|
||||
double const fKeepIgnoredTime = mm_ignored_sessions_forget_time.GetFloat();
|
||||
int const numIgnoredSearches = mm_ignored_sessions_forget_pass.GetInt();
|
||||
|
||||
// Keep sessions for only so long...
|
||||
for ( int x = m_IgnoredSessionsAndTime.FirstInorder();
|
||||
x != m_IgnoredSessionsAndTime.InvalidIndex(); )
|
||||
{
|
||||
SessionSearchPass_t ssp = m_IgnoredSessionsAndTime.Element( x );
|
||||
int xNext = m_IgnoredSessionsAndTime.NextInorder( x );
|
||||
if ( fabs( fNow - ssp.m_flTime ) > fKeepIgnoredTime &&
|
||||
m_nSearchCounter - ssp.m_nSearchCounter > numIgnoredSearches )
|
||||
{
|
||||
m_IgnoredSessionsAndTime.RemoveAt( x );
|
||||
}
|
||||
x = xNext;
|
||||
}
|
||||
}
|
||||
|
||||
bool CIgnoredSessionsMgr::IsIgnored( XNKID xid )
|
||||
{
|
||||
return ( m_IgnoredSessionsAndTime.Find( xid ) != m_IgnoredSessionsAndTime.InvalidIndex() );
|
||||
}
|
||||
|
||||
void CIgnoredSessionsMgr::Ignore( XNKID xid )
|
||||
{
|
||||
if ( ( const uint64 & )xid == 0ull )
|
||||
return;
|
||||
|
||||
SessionSearchPass_t ssp = { Plat_FloatTime(), m_nSearchCounter };
|
||||
m_IgnoredSessionsAndTime.InsertOrReplace( xid, ssp );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CMatchSessionOnlineSearch
|
||||
//
|
||||
// Implementation of an online session search (aka matchmaking)
|
||||
//
|
||||
|
||||
CMatchSessionOnlineSearch::CMatchSessionOnlineSearch( KeyValues *pSettings ) :
|
||||
m_pSettings( pSettings->MakeCopy() ),
|
||||
m_autodelete_pSettings( m_pSettings ),
|
||||
m_eState( STATE_INIT ),
|
||||
m_pSysSession( NULL ),
|
||||
m_pMatchSearcher( NULL ),
|
||||
m_result( RESULT_UNDEFINED ),
|
||||
m_pSysSessionConTeam (NULL),
|
||||
#if !defined( NO_STEAM )
|
||||
m_pServerListListener( NULL ),
|
||||
#endif
|
||||
m_flInitializeTimestamp( 0.0f )
|
||||
{
|
||||
DevMsg( "Created CMatchSessionOnlineSearch:\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
}
|
||||
|
||||
CMatchSessionOnlineSearch::CMatchSessionOnlineSearch() :
|
||||
m_pSettings( NULL ),
|
||||
m_autodelete_pSettings( (KeyValues*)NULL ),
|
||||
m_eState( STATE_INIT ),
|
||||
m_pSysSession( NULL ),
|
||||
m_pMatchSearcher( NULL ),
|
||||
m_result( RESULT_UNDEFINED ),
|
||||
m_pSysSessionConTeam (NULL),
|
||||
m_flInitializeTimestamp( 0.0f )
|
||||
{
|
||||
}
|
||||
|
||||
CMatchSessionOnlineSearch::~CMatchSessionOnlineSearch()
|
||||
{
|
||||
if ( m_pMatchSearcher )
|
||||
m_pMatchSearcher->Destroy();
|
||||
m_pMatchSearcher = NULL;
|
||||
|
||||
DevMsg( "Destroying CMatchSessionOnlineSearch:\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
}
|
||||
|
||||
KeyValues * CMatchSessionOnlineSearch::GetSessionSettings()
|
||||
{
|
||||
return m_pSettings;
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::UpdateSessionSettings( KeyValues *pSettings )
|
||||
{
|
||||
Warning( "CMatchSessionOnlineSearch::UpdateSessionSettings is unavailable in state %d!\n", m_eState );
|
||||
Assert( !"CMatchSessionOnlineSearch::UpdateSessionSettings is unavailable!\n" );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::Command( KeyValues *pCommand )
|
||||
{
|
||||
Warning( "CMatchSessionOnlineSearch::Command is unavailable!\n" );
|
||||
Assert( !"CMatchSessionOnlineSearch::Command is unavailable!\n" );
|
||||
}
|
||||
|
||||
uint64 CMatchSessionOnlineSearch::GetSessionID()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !defined( NO_STEAM )
|
||||
extern volatile uint32 *g_hRankingSetupCallHandle;
|
||||
void CMatchSessionOnlineSearch::SetupSteamRankingConfiguration()
|
||||
{
|
||||
KeyValues *kvNotification = new KeyValues( "SetupSteamRankingConfiguration" );
|
||||
kvNotification->SetPtr( "settingsptr", m_pSettings );
|
||||
kvNotification->SetPtr( "callhandleptr", ( void * ) &g_hRankingSetupCallHandle );
|
||||
|
||||
g_pMatchEventsSubscription->BroadcastEvent( kvNotification );
|
||||
}
|
||||
|
||||
bool CMatchSessionOnlineSearch::IsSteamRankingConfigured() const
|
||||
{
|
||||
return !g_hRankingSetupCallHandle || !*g_hRankingSetupCallHandle;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern ConVar mm_session_sys_ranking_timeout;
|
||||
extern ConVar mm_session_search_qos_timeout;
|
||||
|
||||
void CMatchSessionOnlineSearch::Update()
|
||||
{
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_INIT:
|
||||
if ( !m_flInitializeTimestamp )
|
||||
{
|
||||
m_flInitializeTimestamp = Plat_FloatTime();
|
||||
#if !defined( NO_STEAM )
|
||||
SetupSteamRankingConfiguration();
|
||||
#endif
|
||||
}
|
||||
#if !defined( NO_STEAM )
|
||||
if ( !IsSteamRankingConfigured() && ( Plat_FloatTime() < m_flInitializeTimestamp + mm_session_sys_ranking_timeout.GetFloat() ) )
|
||||
break;
|
||||
#endif
|
||||
|
||||
m_eState = STATE_SEARCHING;
|
||||
|
||||
// Kick off the search
|
||||
g_IgnoredSessionsMgr.OnSearchStarted();
|
||||
m_pMatchSearcher = OnStartSearching();
|
||||
|
||||
// Update our settings with match searcher
|
||||
m_pSettings->deleteThis();
|
||||
m_pSettings = m_pMatchSearcher->GetSearchSettings()->MakeCopy();
|
||||
m_autodelete_pSettings.Assign( m_pSettings );
|
||||
|
||||
// Run the first frame update on the searcher
|
||||
m_pMatchSearcher->Update();
|
||||
break;
|
||||
|
||||
case STATE_SEARCHING:
|
||||
// Waiting for session search to complete
|
||||
m_pMatchSearcher->Update();
|
||||
break;
|
||||
|
||||
case STATE_JOIN_NEXT:
|
||||
StartJoinNextFoundSession();
|
||||
break;
|
||||
|
||||
#if !defined( NO_STEAM )
|
||||
case STATE_VALIDATING_WHITELIST:
|
||||
if ( Plat_FloatTime() > m_flInitializeTimestamp + mm_session_search_qos_timeout.GetFloat() )
|
||||
{
|
||||
DevWarning( "Steam whitelist validation timed out.\n" );
|
||||
Steam_OnDedicatedServerListFetched();
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case STATE_JOINING:
|
||||
// Waiting for the join negotiation
|
||||
if ( m_pSysSession )
|
||||
{
|
||||
m_pSysSession->Update();
|
||||
}
|
||||
|
||||
if (m_pSysSessionConTeam)
|
||||
{
|
||||
m_pSysSessionConTeam->Update();
|
||||
|
||||
switch ( m_pSysSessionConTeam->GetResult() )
|
||||
{
|
||||
case CSysSessionConTeamHost::RESULT_SUCCESS:
|
||||
OnSearchCompletedSuccess( NULL, m_pSettings );
|
||||
break;
|
||||
|
||||
case CSysSessionConTeamHost::RESULT_FAIL:
|
||||
|
||||
m_pSysSessionConTeam->Destroy();
|
||||
m_pSysSessionConTeam = NULL;
|
||||
// Try next session
|
||||
m_eState = STATE_JOIN_NEXT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::Destroy()
|
||||
{
|
||||
// Stop the search
|
||||
if ( m_pMatchSearcher )
|
||||
{
|
||||
m_pMatchSearcher->Destroy();
|
||||
m_pMatchSearcher = NULL;
|
||||
}
|
||||
|
||||
// If we are in the middle of connecting,
|
||||
// abort
|
||||
if ( m_pSysSession )
|
||||
{
|
||||
m_pSysSession->Destroy();
|
||||
m_pSysSession = NULL;
|
||||
}
|
||||
|
||||
if ( m_pSysSessionConTeam )
|
||||
{
|
||||
m_pSysSessionConTeam->Destroy();
|
||||
m_pSysSessionConTeam = NULL;
|
||||
}
|
||||
|
||||
#if !defined( NO_STEAM )
|
||||
if ( m_pServerListListener )
|
||||
{
|
||||
m_pServerListListener->Destroy();
|
||||
m_pServerListListener = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::DebugPrint()
|
||||
{
|
||||
DevMsg( "CMatchSessionOnlineSearch [ state=%d ]\n", m_eState );
|
||||
|
||||
DevMsg( "System data:\n" );
|
||||
KeyValuesDumpAsDevMsg( GetSessionSystemData(), 1 );
|
||||
|
||||
DevMsg( "Settings data:\n" );
|
||||
KeyValuesDumpAsDevMsg( GetSessionSettings(), 1 );
|
||||
|
||||
if ( m_pSysSession )
|
||||
m_pSysSession->DebugPrint();
|
||||
else
|
||||
DevMsg( "SysSession is NULL\n" );
|
||||
|
||||
DevMsg( "Search results outstanding: %d\n", m_arrSearchResults.Count() );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::OnEvent( KeyValues *pEvent )
|
||||
{
|
||||
char const *szEvent = pEvent->GetName();
|
||||
|
||||
if ( !Q_stricmp( "mmF->SysSessionUpdate", szEvent ) )
|
||||
{
|
||||
if ( m_pSysSession && pEvent->GetPtr( "syssession", NULL ) == m_pSysSession )
|
||||
{
|
||||
// This is our session
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_JOINING:
|
||||
// Session was creating
|
||||
if ( char const *szError = pEvent->GetString( "error", NULL ) )
|
||||
{
|
||||
// Destroy the session
|
||||
m_pSysSession->Destroy();
|
||||
m_pSysSession = NULL;
|
||||
|
||||
// Go ahead and join next available session
|
||||
m_eState = STATE_JOIN_NEXT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have received an entirely new "settings" data and copied that to our "settings" data
|
||||
m_eState = STATE_CLOSING;
|
||||
|
||||
// Now we need to create a new client session
|
||||
CSysSessionClient *pSysSession = m_pSysSession;
|
||||
KeyValues *pSettings = m_pSettings;
|
||||
|
||||
// Release ownership of the resources since new match session now owns them
|
||||
m_pSysSession = NULL;
|
||||
m_pSettings = NULL;
|
||||
m_autodelete_pSettings.Assign( NULL );
|
||||
|
||||
OnSearchCompletedSuccess( pSysSession, pSettings );
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::OnSearchEvent( KeyValues *pNotify )
|
||||
{
|
||||
g_pMatchEventsSubscription->BroadcastEvent( pNotify );
|
||||
}
|
||||
|
||||
CSysSessionClient * CMatchSessionOnlineSearch::OnBeginJoiningSearchResult()
|
||||
{
|
||||
return new CSysSessionClient( m_pSettings );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::OnSearchDoneNoResultsMatch()
|
||||
{
|
||||
m_eState = STATE_CLOSING;
|
||||
|
||||
// Reset ignored session tracker
|
||||
g_IgnoredSessionsMgr.Reset();
|
||||
|
||||
// Just go ahead and create the session
|
||||
KeyValues *pSettings = m_pSettings;
|
||||
|
||||
m_pSettings = NULL;
|
||||
m_autodelete_pSettings.Assign( NULL );
|
||||
|
||||
OnSearchCompletedEmpty( pSettings );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::OnSearchCompletedSuccess( CSysSessionClient *pSysSession, KeyValues *pSettings )
|
||||
{
|
||||
m_result = RESULT_SUCCESS;
|
||||
|
||||
// Note that m_pSysSessionConTeam will be NULL if this is an individual joining a
|
||||
// match that was started with con team
|
||||
KeyValues *teamMatch = pSettings->FindKey( "options/conteammatch" );
|
||||
if ( teamMatch && m_pSysSessionConTeam )
|
||||
{
|
||||
DevMsg( "OnlineSearch - ConTeam host reserved session\n" );
|
||||
KeyValuesDumpAsDevMsg( pSettings );
|
||||
int numPlayers, sides[10];
|
||||
uint64 playerIds[10];
|
||||
|
||||
if ( !m_pSysSessionConTeam->GetPlayerSidesAssignment( &numPlayers, playerIds, sides ) )
|
||||
{
|
||||
// Something went badly wrong, bail
|
||||
m_result = RESULT_FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send out a "joinsession" event. This is picked up by the host and sent out
|
||||
// to all machines machines in lobby.
|
||||
KeyValues *joinSession = new KeyValues(
|
||||
"OnMatchSessionUpdate",
|
||||
"state", "joinconteamsession"
|
||||
);
|
||||
#if defined (_X360)
|
||||
|
||||
uint64 sessionId = pSettings->GetUint64( "options/sessionid", 0 );
|
||||
const char *sessionInfo = pSettings->GetString( "options/sessioninfo", "" );
|
||||
|
||||
joinSession->SetUint64( "sessionid", sessionId );
|
||||
joinSession->SetString( "sessioninfo", sessionInfo );
|
||||
|
||||
// Unpack sessionHostData
|
||||
KeyValues *pSessionHostData = (KeyValues*)pSettings->GetPtr( "options/sessionHostData" );
|
||||
if ( pSessionHostData )
|
||||
{
|
||||
KeyValues *pSessionHostDataUnpacked = joinSession->CreateNewKey();
|
||||
pSessionHostDataUnpacked->SetName("sessionHostDataUnpacked");
|
||||
pSessionHostData->CopySubkeys( pSessionHostDataUnpacked );
|
||||
}
|
||||
|
||||
#else
|
||||
joinSession->SetUint64( "sessionid", m_pSysSessionConTeam->GetSessionID() );
|
||||
#endif
|
||||
KeyValues *pTeamMembers = joinSession->CreateNewKey();
|
||||
pTeamMembers->SetName( "teamMembers" );
|
||||
pTeamMembers->SetInt( "numPlayers", numPlayers );
|
||||
|
||||
// Assign players to different teams
|
||||
for ( int i = 0; i < numPlayers; i++ )
|
||||
{
|
||||
KeyValues *pTeamPlayer = pTeamMembers->CreateNewKey();
|
||||
pTeamPlayer->SetName( CFmtStr( "player%d", i ) );
|
||||
pTeamPlayer->SetUint64( "xuid", playerIds[i] );
|
||||
pTeamPlayer->SetInt( "team", sides[i] );
|
||||
}
|
||||
|
||||
OnSearchEvent( joinSession );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Destroy our instance and point at the new match interface
|
||||
CMatchSessionOnlineClient *pNewSession = new CMatchSessionOnlineClient( pSysSession, pSettings );
|
||||
g_pMMF->SetCurrentMatchSession( pNewSession );
|
||||
|
||||
this->Destroy();
|
||||
|
||||
DevMsg( "OnlineSearch - client fully connected to session, search finished.\n" );
|
||||
pNewSession->OnClientFullyConnectedToSession();
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::OnSearchCompletedEmpty( KeyValues *pSettings )
|
||||
{
|
||||
KeyValues::AutoDelete autodelete_pSettings( pSettings );
|
||||
|
||||
m_result = RESULT_FAIL;
|
||||
|
||||
KeyValues *notify = new KeyValues(
|
||||
"OnMatchSessionUpdate",
|
||||
"state", "progress",
|
||||
"progress", "searchempty"
|
||||
);
|
||||
notify->SetPtr( "settingsptr", pSettings );
|
||||
|
||||
OnSearchEvent( notify );
|
||||
if ( !Q_stricmp( pSettings->GetString( "options/searchempty" ), "close" ) )
|
||||
{
|
||||
g_pMatchFramework->CloseSession();
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is a team session then stop here and let the team host decide what
|
||||
// to do next
|
||||
KeyValues *teamMatch = pSettings->FindKey( "options/conteammatch" );
|
||||
if ( teamMatch )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Preserve the "options/bypasslobby" key
|
||||
bool bypassLobby = pSettings->GetBool( "options/bypasslobby", false );
|
||||
|
||||
// Preserve the "options/server" key
|
||||
char serverType[64];
|
||||
const char *prevServerType = pSettings->GetString( "options/server", NULL );
|
||||
if ( prevServerType )
|
||||
{
|
||||
Q_strncpy( serverType, prevServerType, sizeof( serverType ) );
|
||||
}
|
||||
|
||||
// Remove "options" key
|
||||
if ( KeyValues *kvOptions = pSettings->FindKey( "options" ) )
|
||||
{
|
||||
pSettings->RemoveSubKey( kvOptions );
|
||||
kvOptions->deleteThis();
|
||||
}
|
||||
|
||||
pSettings->SetString( "options/createreason", "searchempty" );
|
||||
if ( bypassLobby )
|
||||
{
|
||||
pSettings->SetBool( "options/bypasslobby", bypassLobby );
|
||||
}
|
||||
if ( prevServerType )
|
||||
{
|
||||
pSettings->SetString( "options/server", serverType );
|
||||
}
|
||||
|
||||
DevMsg( "Search completed empty - creating a new session\n" );
|
||||
KeyValuesDumpAsDevMsg( pSettings );
|
||||
|
||||
g_pMatchFramework->CreateSession( pSettings );
|
||||
}
|
||||
|
||||
|
||||
void CMatchSessionOnlineSearch::UpdateTeamProperties( KeyValues *pTeamProperties )
|
||||
{
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::StartJoinNextFoundSession()
|
||||
{
|
||||
if ( !m_arrSearchResults.Count() )
|
||||
{
|
||||
OnSearchDoneNoResultsMatch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Session is joining
|
||||
KeyValues *notify = new KeyValues(
|
||||
"OnMatchSessionUpdate",
|
||||
"state", "progress",
|
||||
"progress", "searchresult"
|
||||
);
|
||||
notify->SetInt( "numResults", m_arrSearchResults.Count() );
|
||||
OnSearchEvent( notify );
|
||||
|
||||
// Peek at the next search result
|
||||
CMatchSearcher::SearchResult_t const &sr = *m_arrSearchResults.Head();
|
||||
|
||||
// Register it into the ignored session pool
|
||||
g_IgnoredSessionsMgr.Ignore( sr.GetXNKID() );
|
||||
|
||||
// Make a validation query
|
||||
ValidateSearchResultWhitelist();
|
||||
}
|
||||
|
||||
static uint32 OfficialWhitelistClientCachedAddress( uint32 uiServerIP )
|
||||
{
|
||||
/** Removed for partner depot **/
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::ValidateSearchResultWhitelist()
|
||||
{
|
||||
#if !defined( NO_STEAM )
|
||||
// In case of official matchmaking we need to validate that the server
|
||||
// that we are about to join actually is whitelisted official server
|
||||
if ( !m_pSettings->GetInt( "game/hosted" ) )
|
||||
{
|
||||
// Peek at the next search result
|
||||
CMatchSearcher::SearchResult_t const &sr = *m_arrSearchResults.Head();
|
||||
if ( ( g_mapValidatedWhitelistCacheTimestamps.Find( sr.m_svAdr.GetIPHostByteOrder() ) == g_mapValidatedWhitelistCacheTimestamps.InvalidIndex() ) &&
|
||||
!OfficialWhitelistClientCachedAddress( sr.m_svAdr.GetIPHostByteOrder() )
|
||||
)
|
||||
{
|
||||
// This server needs to be validated
|
||||
m_flInitializeTimestamp = Plat_FloatTime();
|
||||
m_eState = STATE_VALIDATING_WHITELIST;
|
||||
|
||||
CUtlVector< MatchMakingKeyValuePair_t > filters;
|
||||
filters.EnsureCapacity( 10 );
|
||||
filters.AddToTail( MatchMakingKeyValuePair_t( "gamedir", COM_GetModDirectory() ) );
|
||||
filters.AddToTail( MatchMakingKeyValuePair_t( "addr", sr.m_svAdr.ToString() ) );
|
||||
filters.AddToTail( MatchMakingKeyValuePair_t( "white", "1" ) );
|
||||
|
||||
m_pServerListListener = new CServerListListener( this, filters );
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
ConnectJoinLobbyNextFoundSession();
|
||||
}
|
||||
|
||||
#if !defined( NO_STEAM )
|
||||
|
||||
CMatchSessionOnlineSearch::CServerListListener::CServerListListener( CMatchSessionOnlineSearch *pDsSearcher, CUtlVector< MatchMakingKeyValuePair_t > &filters ) :
|
||||
m_pOuter( pDsSearcher ),
|
||||
m_hRequest( NULL )
|
||||
{
|
||||
MatchMakingKeyValuePair_t *pFilter = filters.Base();
|
||||
DevMsg( 1, "Requesting dedicated whitelist validation...\n" );
|
||||
for (int i = 0; i < filters.Count(); i++)
|
||||
{
|
||||
DevMsg("Filter %d: %s=%s\n", i, filters.Element(i).m_szKey, filters.Element(i).m_szValue);
|
||||
}
|
||||
m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestInternetServerList(
|
||||
( AppId_t ) g_pMatchFramework->GetMatchTitle()->GetTitleID(), &pFilter,
|
||||
filters.Count(), this );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::CServerListListener::Destroy()
|
||||
{
|
||||
m_pOuter = NULL;
|
||||
|
||||
if ( m_hRequest )
|
||||
steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
|
||||
m_hRequest = NULL;
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::CServerListListener::HandleServerResponse( HServerListRequest hReq, int iServer, bool bResponded )
|
||||
{
|
||||
// Register the result
|
||||
if ( bResponded )
|
||||
{
|
||||
gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()
|
||||
->GetServerDetails( hReq, iServer );
|
||||
DevMsg( 1, "Successfully validated whitelist for %s...\n", pServer->m_NetAdr.GetConnectionAddressString() );
|
||||
g_mapValidatedWhitelistCacheTimestamps.InsertOrReplace( pServer->m_NetAdr.GetIP(), Plat_FloatTime() );
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::CServerListListener::RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response )
|
||||
{
|
||||
if ( m_pOuter )
|
||||
{
|
||||
m_pOuter->Steam_OnDedicatedServerListFetched();
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineSearch::Steam_OnDedicatedServerListFetched()
|
||||
{
|
||||
if ( m_pServerListListener )
|
||||
{
|
||||
m_pServerListListener->Destroy();
|
||||
m_pServerListListener = NULL;
|
||||
}
|
||||
|
||||
// Peek at the next search result
|
||||
if ( m_arrSearchResults.Count() )
|
||||
{
|
||||
CMatchSearcher::SearchResult_t const &sr = *m_arrSearchResults.Head();
|
||||
if ( g_mapValidatedWhitelistCacheTimestamps.Find( sr.m_svAdr.GetIPHostByteOrder() ) == g_mapValidatedWhitelistCacheTimestamps.InvalidIndex() )
|
||||
{
|
||||
DevWarning( 1, "Failed to validate whitelist for %s...\n", sr.m_svAdr.ToString( true ) );
|
||||
FOR_EACH_VEC_BACK( m_arrSearchResults, itSR )
|
||||
{
|
||||
if ( m_arrSearchResults[itSR]->m_svAdr.GetIPHostByteOrder() == sr.m_svAdr.GetIPHostByteOrder() )
|
||||
{
|
||||
m_arrSearchResults.Remove( itSR );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_eState = STATE_JOIN_NEXT;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void CMatchSessionOnlineSearch::ConnectJoinLobbyNextFoundSession()
|
||||
{
|
||||
// Pop the search result
|
||||
CMatchSearcher::SearchResult_t const &sr = *m_arrSearchResults.Head();
|
||||
m_arrSearchResults.RemoveMultipleFromHead( 1 );
|
||||
|
||||
// Set the settings to connect with
|
||||
if ( KeyValues *kvOptions = m_pSettings->FindKey( "options", true ) )
|
||||
{
|
||||
#ifdef _X360
|
||||
kvOptions->SetUint64( "sessionid", ( const uint64 & ) sr.m_info.sessionID );
|
||||
|
||||
char chSessionInfo[ XSESSION_INFO_STRING_LENGTH ] = {0};
|
||||
MMX360_SessionInfoToString( sr.m_info, chSessionInfo );
|
||||
kvOptions->SetString( "sessioninfo", chSessionInfo );
|
||||
|
||||
kvOptions->SetPtr( "sessionHostData", sr.GetGameDetails() );
|
||||
|
||||
KeyValuesDumpAsDevMsg( sr.GetGameDetails(), 1, 2 );
|
||||
#else
|
||||
kvOptions->SetUint64( "sessionid", sr.m_uiLobbyId );
|
||||
#endif
|
||||
}
|
||||
|
||||
// Trigger client session creation
|
||||
Msg( "[MM] Joining session %llx, %d search results remaining...\n",
|
||||
m_pSettings->GetUint64( "options/sessionid", 0ull ),
|
||||
m_arrSearchResults.Count() );
|
||||
|
||||
KeyValues *teamMatch = m_pSettings->FindKey( "options/conteammatch" );
|
||||
if ( teamMatch )
|
||||
{
|
||||
m_pSysSessionConTeam = new CSysSessionConTeamHost( m_pSettings );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pSysSession = OnBeginJoiningSearchResult();
|
||||
}
|
||||
|
||||
m_eState = STATE_JOINING;
|
||||
}
|
||||
|
||||
|
||||
CMatchSearcher_OnlineSearch::CMatchSearcher_OnlineSearch( CMatchSessionOnlineSearch *pSession, KeyValues *pSettings ) :
|
||||
CMatchSearcher( pSettings ),
|
||||
m_pSession( pSession )
|
||||
{
|
||||
}
|
||||
|
||||
void CMatchSearcher_OnlineSearch::OnSearchEvent( KeyValues *pNotify )
|
||||
{
|
||||
m_pSession->OnSearchEvent( pNotify );
|
||||
}
|
||||
|
||||
void CMatchSearcher_OnlineSearch::OnSearchDone()
|
||||
{
|
||||
// Let the base searcher finalize results
|
||||
CMatchSearcher::OnSearchDone();
|
||||
|
||||
// Iterate over search results
|
||||
for ( int k = 0, kNum = GetNumSearchResults(); k < kNum; ++ k )
|
||||
{
|
||||
SearchResult_t const &sr = GetSearchResult( k );
|
||||
if ( !g_IgnoredSessionsMgr.IsIgnored( sr.GetXNKID() ) )
|
||||
m_pSession->m_arrSearchResults.AddToTail( &sr );
|
||||
}
|
||||
|
||||
if ( !m_pSession->m_arrSearchResults.Count() )
|
||||
{
|
||||
m_pSession->OnSearchDoneNoResultsMatch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Go ahead and start joining the results
|
||||
DevMsg( "Establishing connection with %d search results.\n", m_pSession->m_arrSearchResults.Count() );
|
||||
m_pSession->m_eState = m_pSession->STATE_JOIN_NEXT;
|
||||
}
|
||||
|
||||
CMatchSearcher * CMatchSessionOnlineSearch::OnStartSearching()
|
||||
{
|
||||
CMatchSearcher *pMS = new CMatchSearcher_OnlineSearch( this, m_pSettings->MakeCopy() );
|
||||
|
||||
// Let the title extend the game settings
|
||||
g_pMMF->GetMatchTitleGameSettingsMgr()->InitializeGameSettings( pMS->GetSearchSettings(), "search_online" );
|
||||
|
||||
DevMsg( "CMatchSearcher_OnlineSearch title adjusted settings:\n" );
|
||||
KeyValuesDumpAsDevMsg( pMS->GetSearchSettings(), 1 );
|
||||
|
||||
return pMS;
|
||||
}
|
||||
161
matchmaking/mm_session_online_search.h
Normal file
161
matchmaking/mm_session_online_search.h
Normal file
@@ -0,0 +1,161 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_SESSION_ONLINE_SEARCH_H
|
||||
#define MM_SESSION_ONLINE_SEARCH_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
//
|
||||
// CMatchSessionOnlineSearch
|
||||
//
|
||||
// Implementation of an online session search (aka matchmaking)
|
||||
//
|
||||
|
||||
class CMatchSearcher_OnlineSearch;
|
||||
|
||||
class CMatchSessionOnlineSearch : public IMatchSessionInternal
|
||||
{
|
||||
// Methods of IMatchSession
|
||||
public:
|
||||
// Get an internal pointer to session system-specific data
|
||||
virtual KeyValues * GetSessionSystemData() { return NULL; }
|
||||
|
||||
// Get an internal pointer to session settings
|
||||
virtual KeyValues * GetSessionSettings();
|
||||
|
||||
// Update session settings, only changing keys and values need
|
||||
// to be passed and they will be updated
|
||||
virtual void UpdateSessionSettings( KeyValues *pSettings );
|
||||
|
||||
virtual void UpdateTeamProperties( KeyValues *pTeamProperties );
|
||||
|
||||
// Issue a session command
|
||||
virtual void Command( KeyValues *pCommand );
|
||||
|
||||
virtual uint64 GetSessionID();
|
||||
|
||||
// Run a frame update
|
||||
virtual void Update();
|
||||
|
||||
// Destroy the session object
|
||||
virtual void Destroy();
|
||||
|
||||
// Debug print a session object
|
||||
virtual void DebugPrint();
|
||||
|
||||
// Check if another session is joinable
|
||||
virtual bool IsAnotherSessionJoinable( char const *pszAnotherSessionInfo ) { return true; }
|
||||
|
||||
// Process event
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
enum Result
|
||||
{
|
||||
RESULT_UNDEFINED,
|
||||
RESULT_SUCCESS,
|
||||
RESULT_FAIL
|
||||
};
|
||||
|
||||
Result GetResult() { return m_result; }
|
||||
|
||||
|
||||
public:
|
||||
explicit CMatchSessionOnlineSearch( KeyValues *pSettings );
|
||||
virtual ~CMatchSessionOnlineSearch();
|
||||
|
||||
protected:
|
||||
CMatchSessionOnlineSearch(); // for derived classes construction
|
||||
|
||||
//
|
||||
// Overrides when search is used as a nested object
|
||||
//
|
||||
protected:
|
||||
virtual CMatchSearcher *OnStartSearching();
|
||||
virtual void OnSearchCompletedEmpty( KeyValues *pSettings );
|
||||
virtual void OnSearchCompletedSuccess( CSysSessionClient *pSysSession, KeyValues *pSettings );
|
||||
virtual void OnSearchEvent( KeyValues *pNotify );
|
||||
virtual CSysSessionClient * OnBeginJoiningSearchResult();
|
||||
|
||||
protected:
|
||||
void StartJoinNextFoundSession();
|
||||
void ValidateSearchResultWhitelist();
|
||||
void ConnectJoinLobbyNextFoundSession();
|
||||
void OnSearchDoneNoResultsMatch();
|
||||
|
||||
protected:
|
||||
KeyValues *m_pSettings;
|
||||
KeyValues::AutoDelete m_autodelete_pSettings;
|
||||
|
||||
friend class CMatchSearcher_OnlineSearch;
|
||||
CMatchSearcher *m_pMatchSearcher;
|
||||
|
||||
Result m_result;
|
||||
|
||||
enum State_t
|
||||
{
|
||||
STATE_INIT,
|
||||
STATE_SEARCHING,
|
||||
STATE_JOIN_NEXT,
|
||||
#if !defined( NO_STEAM )
|
||||
STATE_VALIDATING_WHITELIST,
|
||||
#endif
|
||||
STATE_JOINING,
|
||||
STATE_CLOSING,
|
||||
};
|
||||
State_t m_eState;
|
||||
|
||||
CUtlVector< CMatchSearcher::SearchResult_t const * > m_arrSearchResults;
|
||||
|
||||
CSysSessionClient *m_pSysSession;
|
||||
CSysSessionConTeamHost *m_pSysSessionConTeam;
|
||||
|
||||
#if !defined( NO_STEAM )
|
||||
void SetupSteamRankingConfiguration();
|
||||
bool IsSteamRankingConfigured() const;
|
||||
|
||||
class CServerListListener : public ISteamMatchmakingServerListResponse
|
||||
{
|
||||
public:
|
||||
explicit CServerListListener( CMatchSessionOnlineSearch *pDsSearcher, CUtlVector< MatchMakingKeyValuePair_t > &filters );
|
||||
void Destroy();
|
||||
|
||||
public:
|
||||
// Server has responded ok with updated data
|
||||
virtual void ServerResponded( HServerListRequest hReq, int iServer ) { HandleServerResponse( hReq, iServer, true ); }
|
||||
// Server has failed to respond
|
||||
virtual void ServerFailedToRespond( HServerListRequest hReq, int iServer ) { HandleServerResponse( hReq, iServer, false ); }
|
||||
// A list refresh you had initiated is now 100% completed
|
||||
virtual void RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response );
|
||||
|
||||
protected:
|
||||
void HandleServerResponse( HServerListRequest hReq, int iServer, bool bResponded );
|
||||
CMatchSessionOnlineSearch *m_pOuter;
|
||||
HServerListRequest m_hRequest;
|
||||
};
|
||||
friend class CServerListListener;
|
||||
CServerListListener *m_pServerListListener;
|
||||
void Steam_OnDedicatedServerListFetched();
|
||||
#endif
|
||||
|
||||
float m_flInitializeTimestamp;
|
||||
};
|
||||
|
||||
class CMatchSearcher_OnlineSearch : public CMatchSearcher
|
||||
{
|
||||
public:
|
||||
CMatchSearcher_OnlineSearch( CMatchSessionOnlineSearch *pSession, KeyValues *pSettings );
|
||||
|
||||
public:
|
||||
virtual void OnSearchEvent( KeyValues *pNotify );
|
||||
virtual void OnSearchDone();
|
||||
|
||||
protected:
|
||||
CMatchSessionOnlineSearch *m_pSession;
|
||||
};
|
||||
|
||||
#endif
|
||||
858
matchmaking/mm_session_online_teamsearch.cpp
Normal file
858
matchmaking/mm_session_online_teamsearch.cpp
Normal file
@@ -0,0 +1,858 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
#include "vstdlib/random.h"
|
||||
#include "fmtstr.h"
|
||||
#include "steam_datacenterjobs.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
ConVar mm_teamsearch_errortime( "mm_teamsearch_errortime", "3.0", FCVAR_DEVELOPMENTONLY, "Time team search is in error state until it self-cancels" );
|
||||
ConVar mm_teamsearch_nostart( "mm_teamsearch_nostart", "0", FCVAR_DEVELOPMENTONLY, "Team search will fake cancel before searching for server" );
|
||||
|
||||
#ifndef _GAMECONSOLE
|
||||
ConVar sv_search_team_key( "sv_search_team_key", "public", FCVAR_RELEASE, "When initiating team search, set this key to match with known opponents team" );
|
||||
#endif
|
||||
|
||||
//
|
||||
//
|
||||
// Specialized implementation of SysSessions for team search
|
||||
//
|
||||
//
|
||||
|
||||
template < typename TBaseSession >
|
||||
class CSysSessionStubForTeamSearch : public TBaseSession
|
||||
{
|
||||
public:
|
||||
explicit CSysSessionStubForTeamSearch( KeyValues *pSettings, CMatchSessionOnlineTeamSearch *pMatchSession ) :
|
||||
TBaseSession( pSettings ),
|
||||
m_pMatchSession( pMatchSession )
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void OnSessionEvent( KeyValues *notify )
|
||||
{
|
||||
if ( !notify )
|
||||
return;
|
||||
|
||||
if ( m_pMatchSession )
|
||||
m_pMatchSession->OnSessionEvent( notify );
|
||||
|
||||
notify->deleteThis();
|
||||
}
|
||||
|
||||
protected:
|
||||
// No voice
|
||||
virtual void Voice_ProcessTalkers( KeyValues *pMachine, bool bAdd ) {}
|
||||
virtual void Voice_CaptureAndTransmitLocalVoiceData() {}
|
||||
virtual void Voice_Playback( KeyValues *msg, XUID xuidSrc ) OVERRIDE {}
|
||||
virtual void Voice_UpdateLocalHeadsetsStatus() {}
|
||||
virtual void Voice_UpdateMutelist() {}
|
||||
|
||||
protected:
|
||||
// No p2p connections, just two hosts talking
|
||||
virtual void XP2P_Interconnect() {}
|
||||
|
||||
protected:
|
||||
CMatchSessionOnlineTeamSearch *m_pMatchSession;
|
||||
};
|
||||
|
||||
typedef CSysSessionStubForTeamSearch< CSysSessionClient > CSysTeamSearchClient;
|
||||
typedef CSysSessionStubForTeamSearch< CSysSessionHost > CSysTeamSearchHost;
|
||||
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
// CMatchSessionOnlineTeamSearch implementation
|
||||
//
|
||||
//
|
||||
|
||||
CMatchSessionOnlineTeamSearch::CMatchSessionOnlineTeamSearch( KeyValues *pSettings, CMatchSessionOnlineHost *pHost ) :
|
||||
m_pHostSession( pHost ),
|
||||
m_eState( STATE_SEARCHING ),
|
||||
m_pSysSessionHost( NULL ),
|
||||
m_pSysSessionClient( NULL ),
|
||||
m_pDsSearcher( NULL ),
|
||||
m_flActionTime( 0.0f ),
|
||||
m_pUpdateHostSessionPacket( NULL ),
|
||||
m_autodelete_pUpdateHostSessionPacket( m_pUpdateHostSessionPacket ),
|
||||
m_iLinkState( 0 ),
|
||||
m_xuidLinkPeer( 0ull ),
|
||||
#ifdef _X360
|
||||
m_pXlspConnection( NULL ),
|
||||
m_pXlspCommandBatch( NULL ),
|
||||
#endif
|
||||
m_flCreationTime( Plat_FloatTime() )
|
||||
{
|
||||
m_pSettings = pSettings->MakeCopy();
|
||||
m_autodelete_pSettings.Assign( m_pSettings );
|
||||
|
||||
#ifndef _GAMECONSOLE
|
||||
m_pSettings->SetString( "options/searchteamkey", sv_search_team_key.GetString() );
|
||||
#endif
|
||||
|
||||
if ( IsPC() )
|
||||
{
|
||||
// On PC limit server selection for team games to official and listen
|
||||
if ( Q_stricmp( m_pSettings->GetString( "options/server" ), "listen" ) )
|
||||
m_pSettings->SetString( "options/server", "official" );
|
||||
}
|
||||
|
||||
DevMsg( "Created CMatchSessionOnlineTeamSearch:\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
}
|
||||
|
||||
CMatchSessionOnlineTeamSearch::~CMatchSessionOnlineTeamSearch()
|
||||
{
|
||||
DevMsg( "Destroying CMatchSessionOnlineTeamSearch.\n" );
|
||||
}
|
||||
|
||||
CMatchSearcher * CMatchSessionOnlineTeamSearch::OnStartSearching()
|
||||
{
|
||||
return new CMatchSearcher_OnlineTeamSearch( this, m_pSettings->MakeCopy() );
|
||||
}
|
||||
|
||||
CMatchSearcher_OnlineTeamSearch::CMatchSearcher_OnlineTeamSearch( CMatchSessionOnlineTeamSearch *pSession, KeyValues *pSettings ) :
|
||||
CMatchSearcher_OnlineSearch( pSession, pSettings )
|
||||
{
|
||||
// Search settings cannot be locked as it will interfere with syssessions
|
||||
m_pSettings->SetString( "system/lock", "" );
|
||||
|
||||
// Team versus searchable lobbies are public
|
||||
m_pSettings->SetString( "system/access", "public" );
|
||||
|
||||
// If we happen to host the session it will be a special teamlink session
|
||||
m_pSettings->SetString( "system/netflag", "teamlink" );
|
||||
|
||||
if ( IMatchSession *pMainSession = g_pMatchFramework->GetMatchSession() )
|
||||
{
|
||||
KeyValues *kvSystemData = pMainSession->GetSessionSystemData();
|
||||
m_pSettings->SetUint64( "System/dependentlobby", kvSystemData->GetUint64( "xuidReserve" ) );
|
||||
}
|
||||
|
||||
// Double the number of slots since we assume two teams playing
|
||||
m_pSettings->SetInt( "Members/numSlots", g_pMatchFramework->GetMatchTitle()->GetTotalNumPlayersSupported() );
|
||||
|
||||
// Let the title extend the game settings
|
||||
g_pMMF->GetMatchTitleGameSettingsMgr()->InitializeGameSettings( m_pSettings, "search_onlineteam" );
|
||||
|
||||
DevMsg( "CMatchSearcher_OnlineTeamSearch title adjusted settings:\n" );
|
||||
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
||||
}
|
||||
|
||||
void CMatchSearcher_OnlineTeamSearch::StartSearchPass( KeyValues *pSearchPass )
|
||||
{
|
||||
#ifndef _GAMECONSOLE
|
||||
pSearchPass->SetString( "Filter=/options:searchteamkey", sv_search_team_key.GetString() );
|
||||
#endif
|
||||
|
||||
CMatchSearcher_OnlineSearch::StartSearchPass( pSearchPass );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearch::OnSearchEvent( KeyValues *pNotify )
|
||||
{
|
||||
if ( !pNotify )
|
||||
return;
|
||||
|
||||
if ( m_pHostSession )
|
||||
m_pHostSession->OnEvent( pNotify );
|
||||
|
||||
pNotify->deleteThis();
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearch::OnSessionEvent( KeyValues *pEvent )
|
||||
{
|
||||
OnEvent( pEvent );
|
||||
|
||||
// Searching state is handled entirely by the base class
|
||||
if ( m_eState == STATE_SEARCHING )
|
||||
return;
|
||||
|
||||
char const *szEvent = pEvent->GetName();
|
||||
|
||||
if ( !Q_stricmp( "mmF->SysSessionUpdate", szEvent ) )
|
||||
{
|
||||
if ( m_pSysSessionHost && pEvent->GetPtr( "syssession", NULL ) == m_pSysSessionHost )
|
||||
{
|
||||
// We had a session error
|
||||
if ( char const *szError = pEvent->GetString( "error", NULL ) )
|
||||
{
|
||||
// Destroy the session
|
||||
m_pSysSessionHost->Destroy();
|
||||
m_pSysSessionHost = NULL;
|
||||
|
||||
// Handle error
|
||||
m_eState = STATE_ERROR;
|
||||
m_flActionTime = Plat_FloatTime() + mm_teamsearch_errortime.GetFloat();
|
||||
OnSearchEvent( new KeyValues(
|
||||
"OnMatchSessionUpdate",
|
||||
"state", "progress",
|
||||
"progress", "searcherror"
|
||||
) );
|
||||
return;
|
||||
}
|
||||
|
||||
// This is our session
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_CREATING:
|
||||
// Session created successfully and we are awaiting peer
|
||||
m_eState = STATE_AWAITING_PEER;
|
||||
OnSearchEvent( new KeyValues(
|
||||
"OnMatchSessionUpdate",
|
||||
"state", "progress",
|
||||
"progress", "searchawaitingpeer"
|
||||
) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "mmF->SysSessionCommand", szEvent ) )
|
||||
{
|
||||
if ( m_pSysSessionHost && pEvent->GetPtr( "syssession", NULL ) == m_pSysSessionHost )
|
||||
{
|
||||
KeyValues *pCommand = pEvent->GetFirstTrueSubKey();
|
||||
if ( pCommand )
|
||||
{
|
||||
OnRunSessionCommand( pCommand );
|
||||
}
|
||||
}
|
||||
if ( m_pSysSessionClient && pEvent->GetPtr( "syssession", NULL ) == m_pSysSessionClient )
|
||||
{
|
||||
KeyValues *pCommand = pEvent->GetFirstTrueSubKey();
|
||||
if ( pCommand )
|
||||
{
|
||||
OnRunSessionCommand( pCommand );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "OnPlayerMachinesConnected", szEvent ) )
|
||||
{
|
||||
// Another team is challenging us
|
||||
m_eState = STATE_LINK_HOST;
|
||||
m_xuidLinkPeer = pEvent->GetUint64( "id" );
|
||||
OnSearchEvent( new KeyValues(
|
||||
"OnMatchSessionUpdate",
|
||||
"state", "progress",
|
||||
"progress", "searchlinked"
|
||||
) );
|
||||
|
||||
// Lock the session to prevent other teams challenges
|
||||
m_pSettings->SetString( "system/lock", "linked" );
|
||||
m_pSysSessionHost->OnUpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
|
||||
"update",
|
||||
" update { "
|
||||
" system { "
|
||||
" lock linked "
|
||||
" } "
|
||||
" } "
|
||||
) ) );
|
||||
|
||||
LinkHost().LinkInit();
|
||||
}
|
||||
else if ( !Q_stricmp( "OnPlayerRemoved", szEvent ) )
|
||||
{
|
||||
// Assert( m_xuidLinkPeer == pEvent->GetUint64( "xuid" ) );
|
||||
// the peer gave up before we finished negotiation, start the process all over
|
||||
ResetAndRestartTeamSearch();
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearch::ResetAndRestartTeamSearch()
|
||||
{
|
||||
m_eState = STATE_ERROR;
|
||||
m_flActionTime = 0.0f;
|
||||
OnSearchEvent( new KeyValues(
|
||||
"OnMatchSessionUpdate",
|
||||
"state", "progress",
|
||||
"progress", "restart"
|
||||
) );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearch::RememberHostSessionUpdatePacket( KeyValues *pPacket )
|
||||
{
|
||||
if ( m_pUpdateHostSessionPacket )
|
||||
m_pUpdateHostSessionPacket->deleteThis();
|
||||
|
||||
m_pUpdateHostSessionPacket = pPacket;
|
||||
m_autodelete_pUpdateHostSessionPacket.Assign( m_pUpdateHostSessionPacket );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearch::ApplyHostSessionUpdatePacket()
|
||||
{
|
||||
if ( m_pUpdateHostSessionPacket )
|
||||
{
|
||||
KeyValues *pUpdatePkt = m_pUpdateHostSessionPacket;
|
||||
m_pUpdateHostSessionPacket = NULL;
|
||||
m_autodelete_pUpdateHostSessionPacket.Assign( NULL );
|
||||
|
||||
m_pHostSession->UpdateSessionSettings( pUpdatePkt );
|
||||
|
||||
pUpdatePkt->deleteThis();
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearch::OnRunSessionCommand( KeyValues *pCommand )
|
||||
{
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_LINK_HOST:
|
||||
LinkHost().LinkCommand( pCommand );
|
||||
break;
|
||||
case STATE_LINK_CLIENT:
|
||||
LinkClient().LinkCommand( pCommand );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CMatchSessionOnlineTeamSearchLinkHost & CMatchSessionOnlineTeamSearch::LinkHost()
|
||||
{
|
||||
return static_cast< CMatchSessionOnlineTeamSearchLinkHost & >( *this );
|
||||
}
|
||||
|
||||
CMatchSessionOnlineTeamSearchLinkClient & CMatchSessionOnlineTeamSearch::LinkClient()
|
||||
{
|
||||
return static_cast< CMatchSessionOnlineTeamSearchLinkClient & >( *this );
|
||||
}
|
||||
|
||||
CSysSessionBase * CMatchSessionOnlineTeamSearch::LinkSysSession()
|
||||
{
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_LINK_HOST:
|
||||
return m_pSysSessionHost;
|
||||
case STATE_LINK_CLIENT:
|
||||
return m_pSysSessionClient;
|
||||
default:
|
||||
Assert( !m_pSysSessionClient || !m_pSysSessionHost );
|
||||
if ( m_pSysSessionHost )
|
||||
return m_pSysSessionHost;
|
||||
else if ( m_pSysSessionClient )
|
||||
return m_pSysSessionClient;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CSysSessionClient * CMatchSessionOnlineTeamSearch::OnBeginJoiningSearchResult()
|
||||
{
|
||||
return new CSysTeamSearchClient( m_pSettings, this );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearch::OnSearchCompletedSuccess( CSysSessionClient *pSysSession, KeyValues *pSettings )
|
||||
{
|
||||
// Push the settings back into searcher since we are going to be using them
|
||||
m_pSettings = pSettings;
|
||||
m_autodelete_pSettings.Assign( m_pSettings );
|
||||
m_pSysSessionClient = pSysSession;
|
||||
|
||||
// Established connection with another team
|
||||
m_eState = STATE_LINK_CLIENT;
|
||||
m_xuidLinkPeer = pSysSession->GetHostXuid();
|
||||
OnSearchEvent( new KeyValues(
|
||||
"OnMatchSessionUpdate",
|
||||
"state", "progress",
|
||||
"progress", "searchlinked"
|
||||
) );
|
||||
|
||||
LinkClient().LinkInit();
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearch::OnSearchCompletedEmpty( KeyValues *pSettings )
|
||||
{
|
||||
// Push the settings back into searcher since we are going to be searching again
|
||||
m_pSettings = pSettings;
|
||||
m_autodelete_pSettings.Assign( m_pSettings );
|
||||
|
||||
// Idle out for some time
|
||||
m_eState = STATE_CREATING;
|
||||
|
||||
// Notify everybody that our search idled out
|
||||
OnSearchEvent( new KeyValues(
|
||||
"OnMatchSessionUpdate",
|
||||
"state", "progress",
|
||||
"progress", "searchidle"
|
||||
) );
|
||||
|
||||
// Allocate the hosting object to accept matches
|
||||
m_pSysSessionHost = new CSysTeamSearchHost( m_pSettings, this );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearch::DebugPrint()
|
||||
{
|
||||
DevMsg( "CMatchSessionOnlineTeamSearch::CMatchSessionOnlineSearch\n" );
|
||||
|
||||
CMatchSessionOnlineSearch::DebugPrint();
|
||||
|
||||
DevMsg( "CMatchSessionOnlineTeamSearch [ state=%d ]\n", m_eState );
|
||||
DevMsg( " linkstate: %d\n", m_iLinkState );
|
||||
DevMsg( " linkpeer: %llx\n", m_xuidLinkPeer );
|
||||
DevMsg( " actiontime:%.3f\n", m_flActionTime ? m_flActionTime - Plat_FloatTime() : 0.0f );
|
||||
|
||||
if ( m_pDsSearcher )
|
||||
DevMsg( "TeamSearch: Dedicated search in progress\n" );
|
||||
else
|
||||
DevMsg( "TeamSearch: Dedicated search not active\n" );
|
||||
|
||||
DevMsg( "TeamSearch: SysSession host state:\n" );
|
||||
if ( m_pSysSessionHost )
|
||||
m_pSysSessionHost->DebugPrint();
|
||||
else
|
||||
DevMsg( "SysSession is NULL\n" );
|
||||
|
||||
DevMsg( "TeamSearch: SysSession client state:\n" );
|
||||
if ( m_pSysSessionClient )
|
||||
m_pSysSessionClient->DebugPrint();
|
||||
else
|
||||
DevMsg( "SysSession is NULL\n" );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearch::OnEvent( KeyValues *pEvent )
|
||||
{
|
||||
CMatchSessionOnlineSearch::OnEvent( pEvent );
|
||||
|
||||
// Let the dedicated search handle the responses too
|
||||
if ( m_pDsSearcher )
|
||||
m_pDsSearcher->OnEvent( pEvent );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearch::Update()
|
||||
{
|
||||
switch ( m_eState )
|
||||
{
|
||||
case STATE_ERROR:
|
||||
if ( m_flActionTime && Plat_FloatTime() > m_flActionTime )
|
||||
{
|
||||
m_flActionTime = 0.0f;
|
||||
if ( m_pHostSession )
|
||||
m_pHostSession->Command( KeyValues::AutoDeleteInline( new KeyValues( "Stop" ) ) );
|
||||
}
|
||||
return;
|
||||
|
||||
case STATE_AWAITING_PEER:
|
||||
break;
|
||||
|
||||
case STATE_LINK_CLIENT:
|
||||
LinkClient().LinkUpdate();
|
||||
break;
|
||||
|
||||
case STATE_LINK_HOST:
|
||||
LinkHost().LinkUpdate();
|
||||
break;
|
||||
}
|
||||
|
||||
CMatchSessionOnlineSearch::Update();
|
||||
|
||||
if ( CSysSessionBase *pLinkSysSession = LinkSysSession() )
|
||||
pLinkSysSession->Update();
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearch::Destroy()
|
||||
{
|
||||
if ( m_pSysSessionHost )
|
||||
{
|
||||
m_pSysSessionHost->Destroy();
|
||||
m_pSysSessionHost = NULL;
|
||||
}
|
||||
if ( m_pSysSessionClient )
|
||||
{
|
||||
m_pSysSessionClient->Destroy();
|
||||
m_pSysSessionClient = NULL;
|
||||
}
|
||||
if ( m_pDsSearcher )
|
||||
{
|
||||
m_pDsSearcher->Destroy();
|
||||
m_pDsSearcher = NULL;
|
||||
}
|
||||
|
||||
#ifdef _X360
|
||||
if ( m_pXlspCommandBatch )
|
||||
{
|
||||
m_pXlspCommandBatch->Destroy();
|
||||
m_pXlspCommandBatch = NULL;
|
||||
}
|
||||
if ( m_pXlspConnection )
|
||||
{
|
||||
m_pXlspConnection->Destroy();
|
||||
m_pXlspConnection = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
CMatchSessionOnlineSearch::Destroy();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Link base implementation
|
||||
//
|
||||
//
|
||||
|
||||
void CMatchSessionOnlineTeamSearchLinkBase::LinkUpdate()
|
||||
{
|
||||
switch ( m_iLinkState )
|
||||
{
|
||||
case STATE_SEARCHING_DEDICATED:
|
||||
Assert( m_pDsSearcher );
|
||||
if ( m_pDsSearcher )
|
||||
{
|
||||
m_pDsSearcher->Update();
|
||||
if ( m_pDsSearcher->IsFinished() )
|
||||
{
|
||||
OnDedicatedSearchFinished();
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_HOSTING_LISTEN_SERVER:
|
||||
if ( KeyValues *pServer = m_pHostSession->GetSessionSettings()->FindKey( "server" ) )
|
||||
{
|
||||
// Listen server has started and we should notify the link peer
|
||||
if ( KeyValues *pPeerCommand = new KeyValues( "TeamSearchLink::ListenClient" ) )
|
||||
{
|
||||
KeyValues::AutoDelete autodelete( pPeerCommand );
|
||||
pPeerCommand->SetString( "run", "xuid" );
|
||||
pPeerCommand->SetUint64( "runxuid", m_xuidLinkPeer );
|
||||
|
||||
pPeerCommand->AddSubKey( pServer->MakeCopy() );
|
||||
pPeerCommand->SetString( "server/server", "externalpeer" ); // for other team it is not a listen server, but externalpeer
|
||||
pPeerCommand->SetInt( "server/team", 2 ); // other team is nominated team 2
|
||||
|
||||
LinkSysSession()->Command( pPeerCommand );
|
||||
}
|
||||
m_iLinkState = STATE_LINK_FINISHED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearchLinkBase::LinkCommand( KeyValues *pCommand )
|
||||
{
|
||||
char const *szCommand = pCommand->GetName();
|
||||
|
||||
if ( !Q_stricmp( "TeamSearchLink::Dedicated", szCommand ) ||
|
||||
!Q_stricmp( "TeamSearchLink::ListenClient", szCommand ) )
|
||||
{
|
||||
Assert( m_iLinkState == STATE_WAITING_FOR_PEER_SERVER );
|
||||
KeyValues *pServerInfo = pCommand->FindKey( "server", false );
|
||||
if ( !pServerInfo )
|
||||
{
|
||||
ResetAndRestartTeamSearch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply host session update packet
|
||||
ApplyHostSessionUpdatePacket();
|
||||
|
||||
// Notify our team about the server
|
||||
if ( KeyValues *pOurTeamNotify = new KeyValues( CFmtStr( "TeamSearchResult::%s", szCommand + strlen( "TeamSearchLink::" ) ) ) )
|
||||
{
|
||||
pOurTeamNotify->AddSubKey( pServerInfo->MakeCopy() );
|
||||
OnSearchEvent( pOurTeamNotify );
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearchLinkBase::StartHostingListenServer()
|
||||
{
|
||||
if ( mm_teamsearch_nostart.GetBool() )
|
||||
{
|
||||
// Fake-cancel the session for debugging
|
||||
m_pHostSession->Command( KeyValues::AutoDeleteInline( new KeyValues( "Cancel" ) ) );
|
||||
return;
|
||||
}
|
||||
|
||||
m_iLinkState = STATE_HOSTING_LISTEN_SERVER;
|
||||
|
||||
// Update host session settings packet
|
||||
ApplyHostSessionUpdatePacket();
|
||||
OnSearchEvent( new KeyValues( "TeamSearchResult::ListenHost" ) );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearchLinkBase::StartDedicatedServerSearch()
|
||||
{
|
||||
if ( mm_teamsearch_nostart.GetBool() )
|
||||
{
|
||||
// Fake-cancel the session for debugging
|
||||
m_pHostSession->Command( KeyValues::AutoDeleteInline( new KeyValues( "Cancel" ) ) );
|
||||
return;
|
||||
}
|
||||
|
||||
m_iLinkState = STATE_SEARCHING_DEDICATED;
|
||||
|
||||
// Notify everybody that we are searching for dedicated server now
|
||||
OnSearchEvent( new KeyValues(
|
||||
"OnMatchSessionUpdate",
|
||||
"state", "progress",
|
||||
"progress", "dedicated"
|
||||
) );
|
||||
|
||||
m_pDsSearcher = new CDsSearcher( m_pSettings, m_pHostSession->GetSessionSystemData()->GetUint64( "xuidReserve" ), NULL );
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearchLinkBase::OnDedicatedSearchFinished()
|
||||
{
|
||||
Assert( m_pDsSearcher );
|
||||
CDsSearcher::DsResult_t dsResult = m_pDsSearcher->GetResult();
|
||||
|
||||
m_pDsSearcher->Destroy();
|
||||
m_pDsSearcher = NULL;
|
||||
|
||||
if ( !dsResult.m_bDedicated )
|
||||
{
|
||||
StartHostingListenServer();
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Serialize the information about the dedicated server
|
||||
//
|
||||
KeyValues *pServerInfo = new KeyValues( "server" );
|
||||
dsResult.CopyToServerKey( pServerInfo );
|
||||
pServerInfo->SetUint64( "reservationid", m_pHostSession->GetSessionSystemData()->GetUint64( "xuidReserve" ) );
|
||||
|
||||
// Apply host session update packet
|
||||
ApplyHostSessionUpdatePacket();
|
||||
|
||||
//
|
||||
// Notify the peer team about the server
|
||||
//
|
||||
if ( KeyValues *pPeerCommand = new KeyValues( "TeamSearchLink::Dedicated" ) )
|
||||
{
|
||||
KeyValues::AutoDelete autodelete( pPeerCommand );
|
||||
pPeerCommand->SetString( "run", "xuid" );
|
||||
pPeerCommand->SetUint64( "runxuid", m_xuidLinkPeer );
|
||||
pServerInfo->SetInt( "team", 2 );
|
||||
pPeerCommand->AddSubKey( pServerInfo->MakeCopy() );
|
||||
LinkSysSession()->Command( pPeerCommand );
|
||||
}
|
||||
|
||||
// Notify our team about the server
|
||||
if ( KeyValues *pOurTeamNotify = new KeyValues( "TeamSearchResult::Dedicated" ) )
|
||||
{
|
||||
pOurTeamNotify->SetPtr( "dsresult", &dsResult );
|
||||
pServerInfo->SetInt( "team", 1 );
|
||||
pOurTeamNotify->AddSubKey( pServerInfo );
|
||||
OnSearchEvent( pOurTeamNotify );
|
||||
}
|
||||
|
||||
m_iLinkState = STATE_LINK_FINISHED;
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearchLinkBase::StartWaitingForPeerServer()
|
||||
{
|
||||
m_iLinkState = STATE_WAITING_FOR_PEER_SERVER;
|
||||
|
||||
// Notify everybody that peer is searching for game server
|
||||
OnSearchEvent( new KeyValues(
|
||||
"OnMatchSessionUpdate",
|
||||
"state", "progress",
|
||||
"progress", "peerserver"
|
||||
) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Link host implementation
|
||||
//
|
||||
//
|
||||
|
||||
void CMatchSessionOnlineTeamSearchLinkHost::LinkInit()
|
||||
{
|
||||
m_iLinkState = STATE_SUBMIT_STATS;
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearchLinkHost::LinkUpdate()
|
||||
{
|
||||
switch ( m_iLinkState )
|
||||
{
|
||||
case STATE_SUBMIT_STATS:
|
||||
{
|
||||
#ifdef _X360
|
||||
m_pXlspConnection = new CXlspConnection( false );
|
||||
CUtlVector< KeyValues * > arrCommands;
|
||||
#endif
|
||||
if ( KeyValues *pCmd = new KeyValues( "stat_agg" ) )
|
||||
{
|
||||
int numSeconds = int( 1 + Plat_FloatTime() - m_flCreationTime );
|
||||
numSeconds = ClampArrayBounds( numSeconds, 30 * 60 ); // 30 minutes
|
||||
pCmd->SetInt( "search_team_time", numSeconds );
|
||||
|
||||
#ifdef _X360
|
||||
arrCommands.AddToTail( pCmd );
|
||||
#elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
|
||||
CGCClientJobUpdateStats *pJob = new CGCClientJobUpdateStats( pCmd );
|
||||
pJob->StartJob( NULL );
|
||||
#else
|
||||
pCmd->deleteThis();
|
||||
#endif
|
||||
|
||||
}
|
||||
#ifdef _X360
|
||||
m_pXlspCommandBatch = new CXlspConnectionCmdBatch( m_pXlspConnection, arrCommands );
|
||||
#endif
|
||||
}
|
||||
m_iLinkState = STATE_REPORTING_STATS;
|
||||
break;
|
||||
|
||||
case STATE_REPORTING_STATS:
|
||||
#ifdef _X360
|
||||
m_pXlspCommandBatch->Update();
|
||||
if ( !m_pXlspCommandBatch->IsFinished() )
|
||||
return;
|
||||
|
||||
m_pXlspCommandBatch->Destroy();
|
||||
m_pXlspCommandBatch = NULL;
|
||||
|
||||
m_pXlspConnection->Destroy();
|
||||
m_pXlspConnection = NULL;
|
||||
#endif
|
||||
m_iLinkState = STATE_CONFIRM_JOIN;
|
||||
break;
|
||||
|
||||
case STATE_CONFIRM_JOIN:
|
||||
if ( KeyValues *pPeerCommand = new KeyValues( "TeamSearchLink::HostConfirmJoinReady" ) )
|
||||
{
|
||||
KeyValues::AutoDelete autodelete( pPeerCommand );
|
||||
pPeerCommand->SetString( "run", "xuid" );
|
||||
pPeerCommand->SetUint64( "runxuid", m_xuidLinkPeer );
|
||||
pPeerCommand->AddSubKey( m_pHostSession->GetSessionSettings()->MakeCopy() ); // Submit a copy of our session settings as well
|
||||
m_pSysSessionHost->Command( pPeerCommand );
|
||||
}
|
||||
m_iLinkState = STATE_CONFIRM_JOIN_WAIT;
|
||||
break;
|
||||
|
||||
case STATE_CONFIRM_JOIN_WAIT:
|
||||
// Waiting for client to tell us how to select server
|
||||
break;
|
||||
}
|
||||
|
||||
CMatchSessionOnlineTeamSearchLinkBase::LinkUpdate();
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearchLinkHost::LinkCommand( KeyValues *pCommand )
|
||||
{
|
||||
char const *szCommand = pCommand->GetName();
|
||||
|
||||
if ( !Q_stricmp( "TeamSearchLink::TeamLinkSessionUpdate", szCommand ) )
|
||||
{
|
||||
if ( KeyValues *pUpdatePkt = pCommand->GetFirstTrueSubKey() )
|
||||
{
|
||||
m_pSettings->MergeFrom( pUpdatePkt );
|
||||
RememberHostSessionUpdatePacket( pUpdatePkt->MakeCopy() );
|
||||
}
|
||||
}
|
||||
else if ( !Q_stricmp( "TeamSearchLink::HostHosting", szCommand ) )
|
||||
{
|
||||
// We are going to host
|
||||
char const *szLinkHostServer = m_pSettings->GetString( "options/server", "" );
|
||||
if ( !Q_stricmp( szLinkHostServer, "listen" ) )
|
||||
StartHostingListenServer();
|
||||
else
|
||||
StartDedicatedServerSearch();
|
||||
return;
|
||||
}
|
||||
else if ( !Q_stricmp( "TeamSearchLink::ClientHosting", szCommand ) )
|
||||
{
|
||||
// Our peer is going to host
|
||||
StartWaitingForPeerServer();
|
||||
return;
|
||||
}
|
||||
|
||||
CMatchSessionOnlineTeamSearchLinkBase::LinkCommand( pCommand );
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Link client implementation
|
||||
//
|
||||
//
|
||||
|
||||
void CMatchSessionOnlineTeamSearchLinkClient::LinkInit()
|
||||
{
|
||||
m_iLinkState = STATE_WAITING_FOR_HOST_READY;
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearchLinkClient::LinkUpdate()
|
||||
{
|
||||
switch ( m_iLinkState )
|
||||
{
|
||||
case STATE_WAITING_FOR_HOST_READY:
|
||||
// We are waiting for host to report statistics and get the merged teamlink lobby ready
|
||||
return;
|
||||
|
||||
case STATE_CONFIRM_JOIN:
|
||||
// m_pSettings has been set to the aggregate of host settings
|
||||
// and our members, so we can inspect host's preference of the Server to
|
||||
// decide who is going to search for the game server
|
||||
{
|
||||
char const *szLinkHostServer = m_pSettings->GetString( "options/server", "" );
|
||||
char const *szOurServer = m_pHostSession->GetSessionSettings()->GetString( "options/server", "" );
|
||||
if ( Q_stricmp( szLinkHostServer, "listen" ) &&
|
||||
!Q_stricmp( szOurServer, "listen" ) )
|
||||
{
|
||||
// Host doesn't care, but we need listen server
|
||||
m_pSysSessionClient->Command( KeyValues::AutoDeleteInline( new KeyValues(
|
||||
"TeamSearchLink::ClientHosting", "run", "host" ) ) );
|
||||
StartHostingListenServer();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let host find the server
|
||||
m_pSysSessionClient->Command( KeyValues::AutoDeleteInline( new KeyValues(
|
||||
"TeamSearchLink::HostHosting", "run", "host" ) ) );
|
||||
StartWaitingForPeerServer();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CMatchSessionOnlineTeamSearchLinkBase::LinkUpdate();
|
||||
}
|
||||
|
||||
void CMatchSessionOnlineTeamSearchLinkClient::LinkCommand( KeyValues *pCommand )
|
||||
{
|
||||
char const *szCommand = pCommand->GetName();
|
||||
|
||||
if ( !Q_stricmp( "TeamSearchLink::HostConfirmJoinReady", szCommand ) )
|
||||
{
|
||||
// Host has sent us full settings of the host session,
|
||||
// let's try to reconcile and summarize the settings and prepare an update package if needed
|
||||
KeyValues *pRemoteSettings = pCommand->GetFirstTrueSubKey();
|
||||
KeyValues *pLocalSettings = m_pHostSession->GetSessionSettings();
|
||||
|
||||
if ( KeyValues *pUpdatePkt = g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareTeamLinkForGame( pLocalSettings, pRemoteSettings ) )
|
||||
{
|
||||
if ( KeyValues *pHostCmd = new KeyValues( "TeamSearchLink::TeamLinkSessionUpdate", "run", "host" ) )
|
||||
{
|
||||
pHostCmd->AddSubKey( pUpdatePkt->MakeCopy() );
|
||||
m_pSysSessionClient->Command( KeyValues::AutoDeleteInline( pHostCmd ) );
|
||||
}
|
||||
|
||||
m_pSettings->MergeFrom( pUpdatePkt );
|
||||
RememberHostSessionUpdatePacket( pUpdatePkt );
|
||||
}
|
||||
|
||||
// Host is now ready
|
||||
if ( m_iLinkState == STATE_WAITING_FOR_HOST_READY )
|
||||
m_iLinkState = STATE_CONFIRM_JOIN;
|
||||
return;
|
||||
}
|
||||
|
||||
CMatchSessionOnlineTeamSearchLinkBase::LinkCommand( pCommand );
|
||||
}
|
||||
|
||||
181
matchmaking/mm_session_online_teamsearch.h
Normal file
181
matchmaking/mm_session_online_teamsearch.h
Normal file
@@ -0,0 +1,181 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_SESSION_ONLINE_TEAM_SEARCH_H
|
||||
#define MM_SESSION_ONLINE_TEAM_SEARCH_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
class CMatchSessionOnlineTeamSearch;
|
||||
class CMatchSessionOnlineTeamSearchLinkHost;
|
||||
class CMatchSessionOnlineTeamSearchLinkClient;
|
||||
|
||||
//
|
||||
// CMatchSessionOnlineTeamSearch
|
||||
//
|
||||
// Implementation of an online team session search (aka team-on-team matchmaking)
|
||||
//
|
||||
|
||||
class CMatchSessionOnlineTeamSearch : public CMatchSessionOnlineSearch
|
||||
{
|
||||
public:
|
||||
explicit CMatchSessionOnlineTeamSearch( KeyValues *pSettings, CMatchSessionOnlineHost *pHost );
|
||||
virtual ~CMatchSessionOnlineTeamSearch();
|
||||
|
||||
//
|
||||
// Overrides when search is used as a nested object
|
||||
//
|
||||
protected:
|
||||
virtual CMatchSearcher * OnStartSearching();
|
||||
virtual void OnSearchCompletedSuccess( CSysSessionClient *pSysSession, KeyValues *pSettings );
|
||||
virtual void OnSearchCompletedEmpty( KeyValues *pSettings );
|
||||
virtual void OnSearchEvent( KeyValues *pNotify );
|
||||
virtual CSysSessionClient * OnBeginJoiningSearchResult();
|
||||
|
||||
// Hooks for the nested sys sessions
|
||||
public:
|
||||
virtual void OnSessionEvent( KeyValues *pNotify );
|
||||
CSysSessionBase * LinkSysSession();
|
||||
|
||||
//
|
||||
// Match session overrides
|
||||
//
|
||||
public:
|
||||
// Run a frame update
|
||||
virtual void Update();
|
||||
|
||||
// Destroy the session object
|
||||
virtual void Destroy();
|
||||
|
||||
// Debug print a session object
|
||||
virtual void DebugPrint();
|
||||
|
||||
// Process event
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
protected:
|
||||
void OnRunSessionCommand( KeyValues *pCommand );
|
||||
|
||||
CMatchSessionOnlineTeamSearchLinkHost & LinkHost();
|
||||
CMatchSessionOnlineTeamSearchLinkClient & LinkClient();
|
||||
|
||||
void ResetAndRestartTeamSearch();
|
||||
|
||||
void RememberHostSessionUpdatePacket( KeyValues *pPacket );
|
||||
void ApplyHostSessionUpdatePacket();
|
||||
|
||||
protected:
|
||||
enum State_t
|
||||
{
|
||||
STATE_SEARCHING,
|
||||
STATE_CREATING,
|
||||
STATE_AWAITING_PEER,
|
||||
STATE_LINK_HOST,
|
||||
STATE_LINK_CLIENT,
|
||||
STATE_ERROR,
|
||||
};
|
||||
State_t m_eState;
|
||||
int m_iLinkState;
|
||||
XUID m_xuidLinkPeer;
|
||||
|
||||
CMatchSessionOnlineHost *m_pHostSession; // parent object that contains our TeamSearch
|
||||
|
||||
CSysSessionHost *m_pSysSessionHost; // nested SysSessionHost object when we are hosting
|
||||
CSysSessionClient *m_pSysSessionClient; // nested SysSessionClient object when we are peer
|
||||
CDsSearcher *m_pDsSearcher; // for searching dedicated servers
|
||||
float m_flActionTime; // time to perform action of the current state
|
||||
|
||||
KeyValues *m_pUpdateHostSessionPacket; // packet that should be applied to host session before the game commences
|
||||
// this packet is the case of team sessions being reconciled and unknown settings resolved
|
||||
KeyValues::AutoDelete m_autodelete_pUpdateHostSessionPacket;
|
||||
|
||||
// LINK HOST STATE
|
||||
float m_flCreationTime;
|
||||
#ifdef _X360
|
||||
CXlspConnection *m_pXlspConnection;
|
||||
CXlspConnectionCmdBatch *m_pXlspCommandBatch;
|
||||
#endif
|
||||
};
|
||||
|
||||
class CMatchSessionOnlineTeamSearchLinkBase : public CMatchSessionOnlineTeamSearch
|
||||
{
|
||||
private:
|
||||
CMatchSessionOnlineTeamSearchLinkBase();
|
||||
|
||||
public:
|
||||
enum State_t
|
||||
{
|
||||
STATE_HOSTING_LISTEN_SERVER,
|
||||
STATE_WAITING_FOR_PEER_SERVER,
|
||||
STATE_SEARCHING_DEDICATED,
|
||||
STATE_LINK_FINISHED,
|
||||
STATE_LINK_BASE_LAST
|
||||
};
|
||||
|
||||
protected:
|
||||
void StartHostingListenServer();
|
||||
void StartDedicatedServerSearch();
|
||||
void StartWaitingForPeerServer();
|
||||
|
||||
void OnDedicatedSearchFinished();
|
||||
|
||||
public:
|
||||
void LinkCommand( KeyValues *pCommand );
|
||||
void LinkUpdate();
|
||||
};
|
||||
|
||||
class CMatchSessionOnlineTeamSearchLinkHost : public CMatchSessionOnlineTeamSearchLinkBase
|
||||
{
|
||||
private:
|
||||
CMatchSessionOnlineTeamSearchLinkHost();
|
||||
|
||||
public:
|
||||
enum State_t
|
||||
{
|
||||
STATE_LINK_INITIAL = STATE_LINK_BASE_LAST,
|
||||
STATE_SUBMIT_STATS,
|
||||
STATE_REPORTING_STATS,
|
||||
STATE_CONFIRM_JOIN,
|
||||
STATE_CONFIRM_JOIN_WAIT,
|
||||
};
|
||||
|
||||
public:
|
||||
void LinkInit();
|
||||
void LinkCommand( KeyValues *pCommand );
|
||||
void LinkUpdate();
|
||||
};
|
||||
|
||||
class CMatchSessionOnlineTeamSearchLinkClient : public CMatchSessionOnlineTeamSearchLinkBase
|
||||
{
|
||||
private:
|
||||
CMatchSessionOnlineTeamSearchLinkClient();
|
||||
|
||||
public:
|
||||
enum State_t
|
||||
{
|
||||
STATE_LINK_INITIAL = STATE_LINK_BASE_LAST,
|
||||
STATE_WAITING_FOR_HOST_READY,
|
||||
STATE_CONFIRM_JOIN,
|
||||
};
|
||||
|
||||
public:
|
||||
void LinkInit();
|
||||
void LinkCommand( KeyValues *pCommand );
|
||||
void LinkUpdate();
|
||||
};
|
||||
|
||||
class CMatchSearcher_OnlineTeamSearch : public CMatchSearcher_OnlineSearch
|
||||
{
|
||||
public:
|
||||
CMatchSearcher_OnlineTeamSearch( CMatchSessionOnlineTeamSearch *pSession, KeyValues *pSettings );
|
||||
|
||||
protected:
|
||||
virtual void StartSearchPass( KeyValues *pSearchPass );
|
||||
};
|
||||
|
||||
#endif
|
||||
38
matchmaking/mm_title_main.h
Normal file
38
matchmaking/mm_title_main.h
Normal file
@@ -0,0 +1,38 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_TITLE_MAIN_H
|
||||
#define MM_TITLE_MAIN_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
extern InitReturnVal_t MM_Title_Init();
|
||||
extern void MM_Title_Shutdown();
|
||||
|
||||
extern IMatchTitle *g_pIMatchTitle;
|
||||
extern IMatchEventsSink *g_pIMatchTitleEventsSink;
|
||||
|
||||
extern IMatchTitleGameSettingsMgr *g_pIMatchTitleGameSettingsMgr;
|
||||
|
||||
|
||||
//
|
||||
// LINK_MATCHMAKING_LIB() macro must be included in the matchmaking.dll code
|
||||
// to force all required matchmaking objects linked into the DLL.
|
||||
//
|
||||
extern void LinkMatchmakingLib();
|
||||
#define LINK_MATCHMAKING_LIB() \
|
||||
namespace { \
|
||||
static class CLinkMatchmakingLib { \
|
||||
public: \
|
||||
CLinkMatchmakingLib() { \
|
||||
LinkMatchmakingLib(); \
|
||||
} \
|
||||
} s_LinkHelper; \
|
||||
};
|
||||
|
||||
|
||||
#endif // MM_EVENTS_H
|
||||
563
matchmaking/mm_voice.cpp
Normal file
563
matchmaking/mm_voice.cpp
Normal file
@@ -0,0 +1,563 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_voice.h"
|
||||
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#if defined( _GAMECONSOLE ) && !defined( _CERT )
|
||||
ConVar mm_voice_fulldebug( "mm_voice_fulldebug", "0", FCVAR_DEVELOPMENTONLY );
|
||||
#define MMVOICEMSG(...) if ( mm_voice_fulldebug.GetInt() > 0 ) { Msg( "[MMVOICE] " __VA_ARGS__ ); }
|
||||
#define MMVOICEMSG2(...) if ( mm_voice_fulldebug.GetInt() > 1 ) { Msg( "[MMVOICE] " __VA_ARGS__ ); }
|
||||
#else
|
||||
#define MMVOICEMSG(...) ((void)0)
|
||||
#define MMVOICEMSG2(...) ((void)0)
|
||||
#endif
|
||||
|
||||
#if !defined(NO_STEAM) && !defined( SWDS )
|
||||
static inline bool FriendRelationshipMute( int iRelationship )
|
||||
{
|
||||
switch ( iRelationship )
|
||||
{
|
||||
case k_EFriendRelationshipBlocked:
|
||||
case k_EFriendFlagIgnored:
|
||||
case k_EFriendFlagIgnoredFriend:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Construction/destruction
|
||||
//
|
||||
|
||||
CMatchVoice::CMatchVoice()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
CMatchVoice::~CMatchVoice()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
static CMatchVoice g_MatchVoice;
|
||||
CMatchVoice *g_pMatchVoice = &g_MatchVoice;
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
// Whether remote player talking can be visualized / audible
|
||||
bool CMatchVoice::CanPlaybackTalker( XUID xuidTalker )
|
||||
{
|
||||
if ( IsMachineMutingLocalTalkers( xuidTalker ) )
|
||||
{
|
||||
MMVOICEMSG2( "CanPlaybackTalker(0x%llX)=false(IsMachineMutingLocalTalkers)\n", xuidTalker );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( IsMachineMuted( xuidTalker ) )
|
||||
{
|
||||
MMVOICEMSG2( "CanPlaybackTalker(0x%llX)=false(IsMachineMuted)\n", xuidTalker );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Whether we are explicitly muting a remote player
|
||||
bool CMatchVoice::IsTalkerMuted( XUID xuidTalker )
|
||||
{
|
||||
#if defined( _PS3 ) && !defined( NO_STEAM )
|
||||
if ( steamapicontext->SteamFriends()->GetUserRestrictions() )
|
||||
{
|
||||
MMVOICEMSG( "IsTalkerMuted(0x%llX)=true(GetUserRestrictions)\n", xuidTalker );
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(NO_STEAM) && !defined( SWDS )
|
||||
if ( FriendRelationshipMute( steamapicontext->SteamFriends()->GetFriendRelationship( xuidTalker ) ) )
|
||||
{
|
||||
MMVOICEMSG( "IsTalkerMuted(0x%llX)=true(GetFriendRelationship=0x%X)\n", xuidTalker, steamapicontext->SteamFriends()->GetFriendRelationship( xuidTalker ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( m_arrMutedTalkers.Find( xuidTalker ) != m_arrMutedTalkers.InvalidIndex() )
|
||||
{
|
||||
MMVOICEMSG( "IsTalkerMuted(0x%llX)=true(locallist)\n", xuidTalker );
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined( _GAMECONSOLE ) && !defined( _CERT )
|
||||
XUID xuidOriginal = xuidTalker; xuidOriginal;
|
||||
#endif
|
||||
xuidTalker = RemapTalkerXuid( xuidTalker );
|
||||
|
||||
#if !defined(NO_STEAM) && !defined( SWDS )
|
||||
if ( FriendRelationshipMute( steamapicontext->SteamFriends()->GetFriendRelationship( xuidTalker ) ) )
|
||||
{
|
||||
MMVOICEMSG( "IsTalkerMuted(0x%llX/0x%llX)=true(GetFriendRelationship=0x%X)\n", xuidTalker, xuidOriginal, steamapicontext->SteamFriends()->GetFriendRelationship( xuidTalker ) );
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _X360
|
||||
if ( MMX360_GetUserCtrlrIndex( xuidTalker ) >= 0 )
|
||||
// local players are never considered muted locally
|
||||
return false;
|
||||
|
||||
for ( DWORD dwCtrlr = 0; dwCtrlr < XUSER_MAX_COUNT; ++ dwCtrlr )
|
||||
{
|
||||
int iSlot = ( XBX_GetNumGameUsers() > 0 ) ? XBX_GetSlotByUserId( dwCtrlr ) : -1;
|
||||
|
||||
if ( iSlot >= 0 && iSlot < ( int ) XBX_GetNumGameUsers() &&
|
||||
XBX_GetUserIsGuest( iSlot ) )
|
||||
continue;
|
||||
|
||||
BOOL mutedInGuide = false;
|
||||
if ( ERROR_SUCCESS == g_pMatchExtensions->GetIXOnline()->XUserMuteListQuery( dwCtrlr, xuidTalker, &mutedInGuide ) &&
|
||||
mutedInGuide )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( m_arrMutedTalkers.Find( xuidTalker ) != m_arrMutedTalkers.InvalidIndex() )
|
||||
{
|
||||
MMVOICEMSG( "IsTalkerMuted(0x%llX/0x%llX)=true(locallist)\n", xuidTalker, xuidOriginal );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Whether we are muting any player on the player's machine
|
||||
bool CMatchVoice::IsMachineMuted( XUID xuidPlayer )
|
||||
{
|
||||
#ifdef _X360
|
||||
if ( MMX360_GetUserCtrlrIndex( xuidPlayer ) >= 0 )
|
||||
// local players are never considered muted locally
|
||||
return false;
|
||||
|
||||
// Find the session and the talker within session members
|
||||
IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pMatchSession )
|
||||
return IsTalkerMutedWithPrivileges( -1, xuidPlayer );
|
||||
|
||||
KeyValues *pSettings = pMatchSession->GetSessionSettings();
|
||||
|
||||
KeyValues *pMachine = NULL;
|
||||
KeyValues *pTalker = SessionMembersFindPlayer( pSettings, xuidPlayer, &pMachine );
|
||||
if ( !pTalker || !pMachine )
|
||||
return IsTalkerMutedWithPrivileges( -1, xuidPlayer );
|
||||
|
||||
// Walk all users from that machine
|
||||
int numPlayers = pMachine->GetInt( "numPlayers" );
|
||||
for ( int k = 0; k < numPlayers; ++ k )
|
||||
{
|
||||
KeyValues *pOtherPlayer = pMachine->FindKey( CFmtStr( "player%d", k ) );
|
||||
if ( !pOtherPlayer )
|
||||
continue;
|
||||
|
||||
char const *szOtherName = pOtherPlayer->GetString( "name" );
|
||||
if ( strchr( szOtherName, '(' ) )
|
||||
continue;
|
||||
|
||||
XUID xuidOther = pOtherPlayer->GetUint64( "xuid" );
|
||||
if ( IsTalkerMutedWithPrivileges( -1, xuidOther ) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
return IsTalkerMuted( xuidPlayer );
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _PS3
|
||||
struct TalkerXuidRemap_t
|
||||
{
|
||||
XUID xuidSteamId;
|
||||
XUID xuidPsnId;
|
||||
};
|
||||
#define TALKER_REMAP_CACHE_SIZE 4
|
||||
static CUtlVector< TalkerXuidRemap_t > g_arrTalkerRemapCache( 0, TALKER_REMAP_CACHE_SIZE );
|
||||
#endif
|
||||
// X360: Remap XUID of a player to a valid LIVE-enabled XUID
|
||||
// PS3: Remap SteamID of a player to a PSN ID
|
||||
XUID CMatchVoice::RemapTalkerXuid( XUID xuidTalker )
|
||||
{
|
||||
if ( !IsGameConsole() )
|
||||
return xuidTalker;
|
||||
|
||||
#ifdef _PS3
|
||||
for ( int k = 0; k < g_arrTalkerRemapCache.Count(); ++ k )
|
||||
if ( g_arrTalkerRemapCache[k].xuidSteamId == xuidTalker )
|
||||
return g_arrTalkerRemapCache[k].xuidPsnId;
|
||||
#endif
|
||||
|
||||
// Find the session and the talker within session members
|
||||
IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pMatchSession )
|
||||
return xuidTalker;
|
||||
|
||||
KeyValues *pSettings = pMatchSession->GetSessionSettings();
|
||||
|
||||
KeyValues *pMachine = NULL;
|
||||
KeyValues *pTalker = SessionMembersFindPlayer( pSettings, xuidTalker, &pMachine );
|
||||
if ( !pTalker || !pMachine )
|
||||
return xuidTalker;
|
||||
|
||||
#ifdef _PS3
|
||||
XUID xuidPsnId = pMachine->GetUint64( "psnid" );
|
||||
if ( !xuidPsnId )
|
||||
return xuidTalker;
|
||||
if ( g_arrTalkerRemapCache.Count() >= TALKER_REMAP_CACHE_SIZE )
|
||||
g_arrTalkerRemapCache.SetCountNonDestructively( TALKER_REMAP_CACHE_SIZE - 1 );
|
||||
TalkerXuidRemap_t txr = { xuidTalker, xuidPsnId };
|
||||
g_arrTalkerRemapCache.AddToHead( txr );
|
||||
return xuidPsnId;
|
||||
#endif
|
||||
|
||||
// Check this user name if he is a guest
|
||||
char const *szTalkerName = pTalker->GetString( "name" );
|
||||
char const *pchr = strchr( szTalkerName, '(' );
|
||||
if ( !pchr )
|
||||
return xuidTalker; // user is not a guest
|
||||
|
||||
// Find another user from the same machine
|
||||
int numPlayers = pMachine->GetInt( "numPlayers" );
|
||||
for ( int k = 0; k < numPlayers; ++ k )
|
||||
{
|
||||
KeyValues *pOtherPlayer = pMachine->FindKey( CFmtStr( "player%d", k ) );
|
||||
if ( !pOtherPlayer )
|
||||
continue;
|
||||
|
||||
char const *szOtherName = pOtherPlayer->GetString( "name" );
|
||||
if ( strchr( szOtherName, '(' ) )
|
||||
continue;
|
||||
|
||||
XUID xuidOther = pOtherPlayer->GetUint64( "xuid" );
|
||||
if ( xuidOther )
|
||||
return xuidOther;
|
||||
}
|
||||
|
||||
// No remapping
|
||||
return xuidTalker;
|
||||
}
|
||||
|
||||
// Check player-player voice privileges for machine blocking purposes
|
||||
bool CMatchVoice::IsTalkerMutedWithPrivileges( int dwCtrlr, XUID xuidTalker )
|
||||
{
|
||||
#ifdef _X360
|
||||
if ( -1 == dwCtrlr ) // all controllers should be considered
|
||||
{
|
||||
for ( dwCtrlr = 0; dwCtrlr < XUSER_MAX_COUNT; ++ dwCtrlr )
|
||||
{
|
||||
if ( IsTalkerMutedWithPrivileges( dwCtrlr, xuidTalker ) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Analyze this particular local controller against the given talker
|
||||
int iSlot = ( XBX_GetNumGameUsers() > 0 ) ? XBX_GetSlotByUserId( dwCtrlr ) : -1;
|
||||
|
||||
if ( iSlot >= 0 && iSlot < ( int ) XBX_GetNumGameUsers() &&
|
||||
XBX_GetUserIsGuest( iSlot ) )
|
||||
// Guest has no say
|
||||
return false;
|
||||
|
||||
XUSER_SIGNIN_INFO xsi;
|
||||
if ( ERROR_SUCCESS == XUserGetSigninInfo( dwCtrlr, XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) )
|
||||
{
|
||||
if ( xsi.dwInfoFlags & XUSER_INFO_FLAG_GUEST )
|
||||
// LIVE guests have no say
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOL mutedInGuide = false;
|
||||
if ( ERROR_SUCCESS == g_pMatchExtensions->GetIXOnline()->XUserMuteListQuery( dwCtrlr, xuidTalker, &mutedInGuide ) &&
|
||||
mutedInGuide )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check permissions to see if this player has friends-only or no communication set
|
||||
// Don't check permissions against other local players
|
||||
// Check for open privileges
|
||||
BOOL bHasPrivileges;
|
||||
DWORD dwResult = XUserCheckPrivilege( dwCtrlr, XPRIVILEGE_COMMUNICATIONS, &bHasPrivileges );
|
||||
if ( dwResult == ERROR_SUCCESS )
|
||||
{
|
||||
if ( !bHasPrivileges )
|
||||
{
|
||||
// Second call checks for friends-only
|
||||
XUserCheckPrivilege( dwCtrlr, XPRIVILEGE_COMMUNICATIONS_FRIENDS_ONLY, &bHasPrivileges );
|
||||
|
||||
if ( bHasPrivileges )
|
||||
{
|
||||
// Privileges are set to friends-only. See if the remote player is on our friends list.
|
||||
BOOL bIsFriend;
|
||||
dwResult = XUserAreUsersFriends( dwCtrlr, &xuidTalker, 1, &bIsFriend, NULL );
|
||||
if ( dwResult != ERROR_SUCCESS || !bIsFriend )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Privilege is nobody, mute them all
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( m_arrMutedTalkers.Find( xuidTalker ) != m_arrMutedTalkers.InvalidIndex() )
|
||||
{
|
||||
MMVOICEMSG( "IsTalkerMutedWithPrivileges(%d/0x%llX)=true(locallist)\n", dwCtrlr, xuidTalker );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if player machine is muting any of local players
|
||||
bool CMatchVoice::IsMachineMutingLocalTalkers( XUID xuidPlayer )
|
||||
{
|
||||
// Find the session and the talker within session members
|
||||
IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
|
||||
if ( !pMatchSession )
|
||||
return false;
|
||||
|
||||
KeyValues *pSettings = pMatchSession->GetSessionSettings();
|
||||
|
||||
KeyValues *pMachine = NULL;
|
||||
SessionMembersFindPlayer( pSettings, xuidPlayer, &pMachine );
|
||||
if ( !pMachine )
|
||||
return false;
|
||||
|
||||
// Find the local player record in the session
|
||||
XUID xuidLocalId = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
|
||||
KeyValues *pLocalMachine = NULL;
|
||||
SessionMembersFindPlayer( pSettings, xuidLocalId, &pLocalMachine );
|
||||
if ( !pLocalMachine || pLocalMachine == pMachine )
|
||||
return false;
|
||||
int numLocalPlayers = pLocalMachine->GetInt( "numPlayers" );
|
||||
|
||||
// Check the mutelist on the machine
|
||||
if ( KeyValues *pMutelist = pMachine->FindKey( "Mutelist" ) )
|
||||
{
|
||||
for ( KeyValues *val = pMutelist->GetFirstValue(); val; val = val->GetNextValue() )
|
||||
{
|
||||
XUID xuidMuted = val->GetUint64();
|
||||
if ( !xuidMuted )
|
||||
continue;
|
||||
|
||||
for ( int iLocal = 0; iLocal < numLocalPlayers; ++ iLocal )
|
||||
{
|
||||
XUID xuidLocal = pLocalMachine->GetUint64( CFmtStr( "player%d/xuid", iLocal ) );
|
||||
if ( xuidMuted == xuidLocal )
|
||||
{
|
||||
MMVOICEMSG2( "IsMachineMutingLocalTalkers(0x%llX/0x%llX)=true(mutelist)\n", xuidPlayer, xuidLocal );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Whether voice recording mode is currently active
|
||||
bool CMatchVoice::IsVoiceRecording()
|
||||
{
|
||||
#if !defined(_X360) && !defined(NO_STEAM) && !defined( SWDS )
|
||||
|
||||
#ifdef _PS3
|
||||
EVoiceResult res = steamapicontext->SteamUser()->GetAvailableVoice( NULL, NULL, 11025 );
|
||||
#else
|
||||
EVoiceResult res = steamapicontext->SteamUser()->GetAvailableVoice( NULL, NULL, 0 );
|
||||
#endif
|
||||
|
||||
switch ( res )
|
||||
{
|
||||
case k_EVoiceResultOK:
|
||||
case k_EVoiceResultNoData:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable or disable voice recording
|
||||
void CMatchVoice::SetVoiceRecording( bool bRecordingEnabled )
|
||||
{
|
||||
#if !defined(_X360) && !defined(NO_STEAM) && !defined( SWDS )
|
||||
if ( bRecordingEnabled )
|
||||
steamapicontext->SteamUser()->StartVoiceRecording();
|
||||
else
|
||||
steamapicontext->SteamUser()->StopVoiceRecording();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Enable or disable voice mute for a given talker
|
||||
void CMatchVoice::MuteTalker( XUID xuidTalker, bool bMute )
|
||||
{
|
||||
#if !defined(_X360) && !defined(NO_STEAM) && !defined( SWDS )
|
||||
if ( !xuidTalker )
|
||||
{
|
||||
if ( !bMute )
|
||||
m_arrMutedTalkers.Purge();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_arrMutedTalkers.FindAndFastRemove( xuidTalker );
|
||||
if ( bMute )
|
||||
{
|
||||
m_arrMutedTalkers.AddToTail( xuidTalker );
|
||||
}
|
||||
}
|
||||
|
||||
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues( "OnSysMuteListChanged" ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
CON_COMMAND( voice_reset_mutelist, "Reset all mute information for all players who were ever muted." )
|
||||
{
|
||||
g_pMatchVoice->MuteTalker( 0ull, false );
|
||||
Msg( "Mute list cleared.\n" );
|
||||
}
|
||||
|
||||
#if !defined( _X360 ) && !defined( NO_STEAM )
|
||||
CON_COMMAND( voice_mute, "Mute a specific Steam user" )
|
||||
{
|
||||
if ( args.ArgC() != 2 )
|
||||
{
|
||||
goto usage;
|
||||
}
|
||||
else
|
||||
{
|
||||
int iUserId = Q_atoi( args.Arg( 1 ) );
|
||||
player_info_t pi;
|
||||
if ( !g_pMatchExtensions->GetIVEngineClient()->GetPlayerInfo( iUserId, &pi ) || !pi.xuid )
|
||||
{
|
||||
Msg( "Player# is invalid or refers to a bot, please use \"voice_show_mute\" command.\n" );
|
||||
goto usage;
|
||||
}
|
||||
|
||||
g_pMatchVoice->MuteTalker( pi.xuid, true );
|
||||
if ( !g_pMatchExtensions->GetIVEngineClient()->GetDemoPlaybackParameters() )
|
||||
{
|
||||
Msg( "%s is now muted.\n", pi.name );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
usage:
|
||||
Msg( "Example usage: voice_mute player# - where player# is a number that you can find with \"voice_show_mute\" command.\n" );
|
||||
}
|
||||
|
||||
CON_COMMAND( voice_unmute, "Unmute a specific Steam user, or `all` to unmute all connected players." )
|
||||
{
|
||||
if ( args.ArgC() != 2 )
|
||||
{
|
||||
goto usage;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !Q_stricmp( "all", args.Arg(1) ) )
|
||||
{
|
||||
XUID xuidLocal = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
|
||||
int maxClients = g_pMatchExtensions->GetIVEngineClient()->GetMaxClients();
|
||||
for ( int i = 1; i <= maxClients; ++ i )
|
||||
{
|
||||
// Get the player info from the engine
|
||||
player_info_t pi;
|
||||
if ( !g_pMatchExtensions->GetIVEngineClient()->GetPlayerInfo( i, &pi ) )
|
||||
continue;
|
||||
if ( !pi.xuid )
|
||||
continue;
|
||||
if ( pi.xuid == xuidLocal )
|
||||
continue;
|
||||
|
||||
g_pMatchVoice->MuteTalker( pi.xuid, false );
|
||||
}
|
||||
Msg( "All connected players have been unmuted.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
int iUserId = Q_atoi( args.Arg( 1 ) );
|
||||
player_info_t pi;
|
||||
if ( !g_pMatchExtensions->GetIVEngineClient()->GetPlayerInfo( iUserId, &pi ) || !pi.xuid )
|
||||
{
|
||||
Msg( "Player# is invalid or refers to a bot, please use \"voice_show_mute\" command.\n" );
|
||||
goto usage;
|
||||
}
|
||||
|
||||
g_pMatchVoice->MuteTalker( pi.xuid, false );
|
||||
if ( !g_pMatchExtensions->GetIVEngineClient()->GetDemoPlaybackParameters() )
|
||||
{
|
||||
Msg( "%s is now unmuted.\n", pi.name );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
usage:
|
||||
Msg( "Example usage: voice_unmute {player#|all} - where player# is a number that you can find with \"voice_show_mute\" command, or all to unmute all connected players.\n" );
|
||||
}
|
||||
|
||||
CON_COMMAND( voice_show_mute, "Show whether current players are muted." )
|
||||
{
|
||||
if ( g_pMatchExtensions->GetIVEngineClient()->GetDemoPlaybackParameters() )
|
||||
return;
|
||||
|
||||
bool bPrinted = false;
|
||||
XUID xuidLocal = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
|
||||
int maxClients = g_pMatchExtensions->GetIVEngineClient()->GetMaxClients();
|
||||
for ( int i = 1; i <= maxClients; ++ i )
|
||||
{
|
||||
// Get the player info from the engine
|
||||
player_info_t pi;
|
||||
if ( !g_pMatchExtensions->GetIVEngineClient()->GetPlayerInfo( i, &pi ) )
|
||||
continue;
|
||||
if ( !pi.xuid )
|
||||
continue;
|
||||
if ( pi.xuid == xuidLocal )
|
||||
continue;
|
||||
|
||||
if ( !bPrinted )
|
||||
{
|
||||
bPrinted = true;
|
||||
Msg( "Player# Player Name\n" );
|
||||
Msg( "------- ----------------\n" );
|
||||
}
|
||||
|
||||
Msg( " % 2d %s %s\n", i, g_pMatchVoice->IsTalkerMuted( pi.xuid ) ? "(muted)" : " ", pi.name );
|
||||
}
|
||||
|
||||
if ( bPrinted )
|
||||
{
|
||||
Msg( "------- ----------------\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "No players currently connected who can be muted.\n" );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
59
matchmaking/mm_voice.h
Normal file
59
matchmaking/mm_voice.h
Normal file
@@ -0,0 +1,59 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MM_VOICE_H
|
||||
#define MM_VOICE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "mm_framework.h"
|
||||
#include "utlvector.h"
|
||||
|
||||
class CMatchVoice : public IMatchVoice
|
||||
{
|
||||
// Methods of IMatchVoice
|
||||
public:
|
||||
// Whether remote player talking can be visualized / audible
|
||||
virtual bool CanPlaybackTalker( XUID xuidTalker );
|
||||
|
||||
// Whether we are explicitly muting a remote player
|
||||
virtual bool IsTalkerMuted( XUID xuidTalker );
|
||||
|
||||
// Whether we are muting any player on the player's machine
|
||||
virtual bool IsMachineMuted( XUID xuidPlayer );
|
||||
|
||||
// Whether voice recording mode is currently active
|
||||
virtual bool IsVoiceRecording();
|
||||
|
||||
// Enable or disable voice recording
|
||||
virtual void SetVoiceRecording( bool bRecordingEnabled );
|
||||
|
||||
// Enable or disable voice mute for a given talker
|
||||
virtual void MuteTalker( XUID xuidTalker, bool bMute );
|
||||
|
||||
protected:
|
||||
// Remap XUID of a player to a valid LIVE-enabled XUID
|
||||
XUID RemapTalkerXuid( XUID xuidTalker );
|
||||
|
||||
// Check player-player voice privileges for machine blocking purposes
|
||||
bool IsTalkerMutedWithPrivileges( int iCtrlr, XUID xuidTalker );
|
||||
|
||||
// Check if player machine is muting any of local players
|
||||
bool IsMachineMutingLocalTalkers( XUID xuidPlayer );
|
||||
|
||||
public:
|
||||
CMatchVoice();
|
||||
~CMatchVoice();
|
||||
|
||||
protected:
|
||||
CUtlVector< XUID > m_arrMutedTalkers;
|
||||
};
|
||||
|
||||
// Match events subscription singleton
|
||||
extern CMatchVoice *g_pMatchVoice;
|
||||
|
||||
#endif // MM_VOICE_H
|
||||
2992
matchmaking/player.cpp
Normal file
2992
matchmaking/player.cpp
Normal file
File diff suppressed because it is too large
Load Diff
306
matchmaking/player.h
Normal file
306
matchmaking/player.h
Normal file
@@ -0,0 +1,306 @@
|
||||
//========= Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
|
||||
#ifndef _PLAYER_H_
|
||||
#define _PLAYER_H_
|
||||
|
||||
class PlayerFriend;
|
||||
class PlayerLocal;
|
||||
|
||||
#include "mm_framework.h"
|
||||
#include "matchmaking/iplayer.h"
|
||||
#include "matchmaking/cstrike15/imatchext_cstrike15.h"
|
||||
#include "playerrankingdata.h"
|
||||
#include "mathlib/expressioncalculator.h"
|
||||
|
||||
|
||||
//Players can be created several ways
|
||||
// -They are the local player and this is a reference to themselves
|
||||
// -They are created by the friends enumerator
|
||||
// -They are part of a party that a player joins
|
||||
// -They are part of a game that a player joins.
|
||||
|
||||
template < typename TBaseInterface >
|
||||
class Player : public TBaseInterface
|
||||
{
|
||||
protected:
|
||||
Player() : m_xuid( 0 ), m_iController( XUSER_INDEX_NONE ), m_eOnlineState( IPlayer::STATE_OFFLINE )
|
||||
{
|
||||
memset( m_szName, 0, sizeof( m_szName ) );
|
||||
strcpy(m_szName, "EMPTY");
|
||||
}
|
||||
virtual ~Player() {}
|
||||
|
||||
public:
|
||||
// Updates connection info if needed.
|
||||
virtual void Update() = 0;
|
||||
|
||||
// Deletes the player class instance
|
||||
virtual void Destroy() = 0;
|
||||
|
||||
//
|
||||
// IPlayer implementation
|
||||
//
|
||||
public:
|
||||
virtual XUID GetXUID() { return m_xuid; }
|
||||
virtual int GetPlayerIndex() { return m_iController; }
|
||||
|
||||
virtual const char * GetName() { return m_szName; }
|
||||
|
||||
virtual IPlayer::OnlineState_t GetOnlineState() { return m_eOnlineState; }
|
||||
|
||||
protected:
|
||||
XUID m_xuid;
|
||||
int m_iController;
|
||||
IPlayer::OnlineState_t m_eOnlineState;
|
||||
char m_szName[MAX_PLAYER_NAME_LENGTH];
|
||||
};
|
||||
|
||||
class PlayerFriend : public Player< IPlayerFriend >
|
||||
{
|
||||
public:
|
||||
struct FriendInfo_t
|
||||
{
|
||||
char const *m_szName;
|
||||
wchar_t const *m_wszRichPresence;
|
||||
XNKID m_xSessionID;
|
||||
uint64 m_uiTitleID;
|
||||
uint32 m_uiGameServerIP;
|
||||
KeyValues *m_pGameDetails;
|
||||
};
|
||||
public:
|
||||
explicit PlayerFriend( XUID xuid, FriendInfo_t const *pFriendInfo = NULL );
|
||||
|
||||
//
|
||||
// IPlayerFriend implementation
|
||||
//
|
||||
public:
|
||||
virtual wchar_t const * GetRichPresence();
|
||||
|
||||
virtual KeyValues *GetGameDetails();
|
||||
virtual KeyValues *GetPublishedPresence();
|
||||
|
||||
virtual bool IsJoinable();
|
||||
virtual void Join();
|
||||
virtual uint64 GetTitleID();
|
||||
virtual uint32 GetGameServerIP();
|
||||
|
||||
//
|
||||
// Player<IPlayerFriend> implementation
|
||||
//
|
||||
public:
|
||||
virtual void Update();
|
||||
virtual void Destroy();
|
||||
|
||||
public:
|
||||
void SetFriendMark( unsigned maskSetting );
|
||||
unsigned GetFriendMark( );
|
||||
|
||||
void SetIsStale( bool bStale );
|
||||
bool GetIsStale();
|
||||
|
||||
void UpdateFriendInfo( FriendInfo_t const *pFriendInfo );
|
||||
bool IsUpdatingInfo();
|
||||
|
||||
public:
|
||||
void StartSearchForSessionInfo();
|
||||
void StartSearchForSessionInfoImpl();
|
||||
void AbortSearch();
|
||||
|
||||
protected:
|
||||
unsigned m_uFriendMark;
|
||||
bool m_bIsStale;
|
||||
wchar_t m_wszRichPresence[MAX_RICHPRESENCE_SIZE];
|
||||
|
||||
uint64 m_uiTitleID;
|
||||
uint32 m_uiGameServerIP;
|
||||
XNKID m_xSessionID;
|
||||
XSESSION_INFO m_GameSessionInfo;
|
||||
KeyValues *m_pDetails;
|
||||
KeyValues *m_pPublishedPresence;
|
||||
|
||||
enum SearchState_t
|
||||
{
|
||||
SEARCH_NONE,
|
||||
SEARCH_QUEUED,
|
||||
#ifdef _X360
|
||||
SEARCH_XNKID,
|
||||
SEARCH_QOS,
|
||||
#elif !defined( NO_STEAM )
|
||||
SEARCH_WAIT_LOBBY_DATA,
|
||||
#endif
|
||||
SEARCH_COMPLETED
|
||||
};
|
||||
SearchState_t m_eSearchState;
|
||||
#ifdef _X360
|
||||
XSESSION_INFO m_xsiSearchState;
|
||||
XNADDR const *m_pQOS_xnaddr;
|
||||
XNKID const *m_pQOS_xnkid;
|
||||
XNKEY const *m_pQOS_xnkey;
|
||||
XNQOS *m_XNQOS;
|
||||
XOVERLAPPED m_SessionSearchOverlapped;
|
||||
|
||||
CUtlBuffer m_bufSessionSearchResults;
|
||||
XSESSION_SEARCHRESULT_HEADER * GetXSearchResults() { return ( XSESSION_SEARCHRESULT_HEADER * ) m_bufSessionSearchResults.Base(); }
|
||||
|
||||
void Live_Update_SearchXNKID();
|
||||
void Live_Update_Search_QOS();
|
||||
#elif !defined( NO_STEAM )
|
||||
STEAM_CALLBACK_MANUAL( PlayerFriend, Steam_OnLobbyDataUpdate, LobbyDataUpdate_t, m_CallbackOnLobbyDataUpdate );
|
||||
#endif
|
||||
};
|
||||
|
||||
class PlayerLocal : public Player< IPlayerLocal >
|
||||
{
|
||||
public:
|
||||
explicit PlayerLocal( int iController );
|
||||
~PlayerLocal();
|
||||
|
||||
public:
|
||||
void RecomputeXUID( char const *szNetwork );
|
||||
void DetectOnlineState();
|
||||
|
||||
//
|
||||
// IPlayerLocal implementation
|
||||
//
|
||||
public:
|
||||
virtual const UserProfileData& GetPlayerProfileData();
|
||||
|
||||
virtual MatchmakingData * GetPlayerMatchmakingData( void );
|
||||
virtual void UpdatePlayerMatchmakingData( int mmDataType );
|
||||
virtual void ResetPlayerMatchmakingData( int mmDataScope );
|
||||
|
||||
virtual const void * GetPlayerTitleData( int iTitleDataIndex );
|
||||
virtual void UpdatePlayerTitleData( TitleDataFieldsDescription_t const *fdKey, const void *pvNewTitleData, int numNewBytes );
|
||||
|
||||
virtual void GetLeaderboardData( KeyValues *pLeaderboardInfo );
|
||||
virtual void UpdateLeaderboardData( KeyValues *pLeaderboardInfo );
|
||||
|
||||
virtual void GetAwardsData( KeyValues *pAwardsData );
|
||||
virtual void UpdateAwardsData( KeyValues *pAwardsData );
|
||||
|
||||
virtual void SetNeedsSave( void );
|
||||
|
||||
#if defined ( _X360 )
|
||||
virtual bool IsTitleDataValid() { return m_bIsTitleDataValid; };
|
||||
virtual bool IsTitleDataBlockValid( int blockId );
|
||||
virtual void SetIsTitleDataValid( bool isValid ) { m_bIsTitleDataValid = isValid; }
|
||||
virtual bool IsFreshPlayerProfile( void ) { return m_bIsFreshPlayerProfile; }
|
||||
virtual void ClearBufTitleData( void );
|
||||
#endif
|
||||
|
||||
//
|
||||
// Player<IPlayerLocal> implementation
|
||||
//
|
||||
public:
|
||||
virtual void Update();
|
||||
virtual void Destroy();
|
||||
|
||||
public:
|
||||
XUSER_SIGNIN_STATE GetAssumedSigninState();
|
||||
void LoadPlayerProfileData();
|
||||
void LoadTitleData();
|
||||
void SetTitleDataWriteTime( float flTime );
|
||||
void WriteTitleData();
|
||||
bool IsTitleDataStorageConnected( void );
|
||||
void OnLeaderboardRequestFinished( KeyValues *pLeaderboardData );
|
||||
|
||||
void SetFlag_AwaitingTitleData() { m_uiPlayerFlags |= PLAYER_INVITE_AWAITING_TITLEDATA; }
|
||||
#if defined ( _X360 )
|
||||
bool m_bIsTitleDataValid;
|
||||
bool m_bIsFreshPlayerProfile;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
//stats
|
||||
XUSER_SIGNIN_STATE m_eLoadedTitleData;
|
||||
float m_flLastSave;
|
||||
|
||||
enum Flags_t
|
||||
{
|
||||
PLAYER_INVITE_AWAITING_TITLEDATA = ( 1 << 0 )
|
||||
};
|
||||
uint32 m_uiPlayerFlags;
|
||||
|
||||
enum Consts_t
|
||||
{
|
||||
TITLE_DATA_COUNT_X360 = 3, // number of Xbox LIVE - backed title data buffers
|
||||
TITLE_DATA_COUNT = 3, // number of valid title data buffers
|
||||
};
|
||||
|
||||
UserProfileData m_ProfileData;
|
||||
MatchmakingData m_MatchmakingData;
|
||||
CUtlVector<int> m_arrAchievementsEarned, m_arrAvatarAwardsEarned;
|
||||
char m_bufTitleData[TITLE_DATA_COUNT][XPROFILE_SETTING_MAX_SIZE];
|
||||
bool m_bSaveTitleData[TITLE_DATA_COUNT];
|
||||
#if defined ( _X360 )
|
||||
bool m_bIsTitleDataBlockValid[TITLE_DATA_COUNT];
|
||||
#endif
|
||||
|
||||
KeyValues *m_pLeaderboardData;
|
||||
KeyValues::AutoDelete m_autodelete_pLeaderboardData;
|
||||
|
||||
#ifdef _X360
|
||||
struct XPendingAsyncAward_t
|
||||
{
|
||||
float m_flStartTimestamp;
|
||||
XOVERLAPPED m_xOverlapped;
|
||||
PlayerLocal *m_pLocalPlayer;
|
||||
enum Type_t
|
||||
{
|
||||
TYPE_ACHIEVEMENT,
|
||||
TYPE_AVATAR_AWARD
|
||||
};
|
||||
Type_t m_eType;
|
||||
union
|
||||
{
|
||||
TitleAchievementsDescription_t const *m_pAchievementDesc;
|
||||
TitleAvatarAwardsDescription_t const *m_pAvatarAwardDesc;
|
||||
};
|
||||
union
|
||||
{
|
||||
XUSER_ACHIEVEMENT m_xAchievement;
|
||||
XUSER_AVATARASSET m_xAvatarAsset;
|
||||
};
|
||||
};
|
||||
static CUtlVector< XPendingAsyncAward_t * > s_arrPendingAsyncAwards;
|
||||
void UpdatePendingAwardsState();
|
||||
#elif !defined( NO_STEAM )
|
||||
void UpdatePlayersSteamLogon();
|
||||
|
||||
STEAM_CALLBACK_MANUAL( PlayerLocal, Steam_OnUserStatsReceived, UserStatsReceived_t, m_CallbackOnUserStatsReceived );
|
||||
STEAM_CALLBACK_MANUAL( PlayerLocal, Steam_OnPersonaStateChange, PersonaStateChange_t, m_CallbackOnPersonaStateChange );
|
||||
STEAM_CALLBACK_MANUAL( PlayerLocal, Steam_OnServersConnected, SteamServersConnected_t, m_CallbackOnServersConnected );
|
||||
STEAM_CALLBACK_MANUAL( PlayerLocal, Steam_OnServersDisconnected, SteamServersDisconnected_t, m_CallbackOnServersDisconnected );
|
||||
#endif
|
||||
|
||||
void EvaluateAwardsStateBasedOnStats();
|
||||
void LoadGuestsTitleData();
|
||||
void OnProfileTitleDataLoaded( int iErrorCode );
|
||||
|
||||
protected:
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// Players XWrite opportunities
|
||||
//
|
||||
|
||||
// XWrite opportunities
|
||||
enum MM_XWriteOpportunity
|
||||
{
|
||||
MMXWO_NONE = 0,
|
||||
MMXWO_SETTINGS,
|
||||
MMXWO_SESSION_STARTED,
|
||||
MMXWO_CHECKPOINT,
|
||||
MMXWO_SESSION_FINISHED
|
||||
};
|
||||
|
||||
void SignalXWriteOpportunity( MM_XWriteOpportunity eXWO );
|
||||
|
||||
|
||||
#endif
|
||||
1245
matchmaking/playermanager.cpp
Normal file
1245
matchmaking/playermanager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
141
matchmaking/playermanager.h
Normal file
141
matchmaking/playermanager.h
Normal file
@@ -0,0 +1,141 @@
|
||||
//========= Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
|
||||
#ifndef _PLAYERMANAGER_H_
|
||||
#define _PLAYERMANAGER_H_
|
||||
|
||||
|
||||
class PlayerManager;
|
||||
|
||||
#include <list>
|
||||
#include "utlvector.h"
|
||||
#include "utlmap.h"
|
||||
|
||||
#ifndef SWDS
|
||||
|
||||
#include "player.h"
|
||||
|
||||
|
||||
class PlayerManager: public IPlayerManager, public IMatchEventsSink
|
||||
{
|
||||
public :
|
||||
PlayerManager();
|
||||
virtual ~PlayerManager();
|
||||
|
||||
//IPlayerManager
|
||||
public:
|
||||
//
|
||||
// EnableServersUpdate
|
||||
// controls whether friends data is being updated in the background
|
||||
//
|
||||
virtual void EnableFriendsUpdate( bool bEnable );
|
||||
|
||||
//
|
||||
// GetLocalPlayer
|
||||
// returns a local player interface for a given controller index
|
||||
//
|
||||
virtual IPlayerLocal * GetLocalPlayer( int iController );
|
||||
|
||||
//
|
||||
// GetNumFriends
|
||||
// returns number of friends discovered and for which data is available
|
||||
//
|
||||
virtual int GetNumFriends();
|
||||
|
||||
//
|
||||
// GetFriend
|
||||
// returns player interface to the given friend or NULL if friend not found or not available
|
||||
//
|
||||
virtual IPlayerFriend * GetFriendByIndex( int index );
|
||||
virtual IPlayerFriend * GetFriendByXUID( XUID xuid );
|
||||
|
||||
//
|
||||
// FindPlayer
|
||||
// returns player interface by player's XUID or NULL if friend not found or not available
|
||||
//
|
||||
virtual IPlayer * FindPlayer( XUID xuid );
|
||||
|
||||
// IMatchEventsSink
|
||||
public:
|
||||
virtual void OnEvent( KeyValues *pEvent );
|
||||
|
||||
public:
|
||||
void OnGameUsersChanged();
|
||||
void Update();
|
||||
void OnLocalPlayerDisconnectedFromLive( int iCtrlr );
|
||||
void RecomputePlayerXUIDs( char const *szNetwork );
|
||||
|
||||
PlayerFriend *FindPlayerFriend( XUID xuid );
|
||||
PlayerLocal *FindPlayerLocal( XUID xuid );
|
||||
void RequestStoreStats();
|
||||
|
||||
protected:
|
||||
void MarkOldFriends();
|
||||
void RemoveOldFriends();
|
||||
void OnSigninChange( KeyValues *pEvent );
|
||||
void OnLostConnectionToConsoleNetwork();
|
||||
#if defined( _PS3 ) && !defined( NO_STEAM )
|
||||
STEAM_CALLBACK( PlayerManager, Steam_OnPS3PSNStatusChange, PS3PSNStatusChange_t, m_CallbackOnPS3PSNStatusChange );
|
||||
#endif
|
||||
|
||||
private:
|
||||
void CreateFriendEnumeration( int iCtrlr );
|
||||
void CreateLanSearch();
|
||||
void UpdateLanSearch();
|
||||
void ExecuteStoreStatsRequest();
|
||||
|
||||
// Players instances
|
||||
PlayerLocal * mLocalPlayer[ XUSER_MAX_COUNT ]; // local Players are cached off here for convienience.
|
||||
CUtlVector< PlayerFriend * > mFriendsList;
|
||||
|
||||
bool m_bUpdateEnabled; // whether data should be auto-updated
|
||||
float m_flNextUpdateTime; // when next update cycle should occur
|
||||
|
||||
struct SFriendSearchData
|
||||
{
|
||||
SFriendSearchData() { memset( this, 0, sizeof( *this ) ); }
|
||||
|
||||
bool mSearchInProgress;
|
||||
void * mFriendBuffer;
|
||||
int mFriendBufferSize;
|
||||
int mFriendsStartIndex;
|
||||
#ifdef _X360
|
||||
HANDLE mFriendEnumHandle;
|
||||
XOVERLAPPED mFriendsOverlapped;
|
||||
#endif
|
||||
XUID mXuid;
|
||||
};
|
||||
SFriendSearchData m_searchData[ XUSER_MAX_COUNT ];
|
||||
|
||||
struct SLanSearchData_t
|
||||
{
|
||||
SLanSearchData_t() { memset( this, 0, sizeof( *this ) ); }
|
||||
|
||||
bool m_bSearchInProgress;
|
||||
float m_flStartTime;
|
||||
float m_flLastBroadcastTime;
|
||||
};
|
||||
SLanSearchData_t m_lanSearchData;
|
||||
|
||||
int m_searchesPending; // Number of searches in progress
|
||||
bool m_bRequestStoreStats;
|
||||
};
|
||||
|
||||
#else // SWDS
|
||||
|
||||
class PlayerManager: public IPlayerManager, public IMatchEventsSink
|
||||
{
|
||||
public:
|
||||
// SWDS declaration is intentionally left stripped
|
||||
virtual void Update() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
extern class PlayerManager *g_pPlayerManager;
|
||||
|
||||
#endif // _PLAYERMANAGER_H_
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user