update implementation of sdk 1.61

This commit is contained in:
a 2024-11-25 04:57:08 +02:00
parent b5c1a2461b
commit 73be5714c0
13 changed files with 989 additions and 113 deletions

View File

@ -153,8 +153,11 @@ extern const std::unordered_set<std::string> client_known_interfaces = {
"STEAMSCREENSHOTS_INTERFACE_VERSION003",
"SteamStreamLauncher001",
"STEAMTIMELINE_INTERFACE_V001",
"STEAMTV_INTERFACE_V001",
"STEAMTV_INTERFACE_V002",
"STEAMTIMELINE_INTERFACE_V002",
"STEAMTIMELINE_INTERFACE_V003",
"STEAMTIMELINE_INTERFACE_V004",
"STEAMTV_INTERFACE_V001", // removed since sdk v1.61
"STEAMTV_INTERFACE_V002", // removed since sdk v1.61
"STEAMUGC_INTERFACE_VERSION001",
"STEAMUGC_INTERFACE_VERSION002",
"STEAMUGC_INTERFACE_VERSION003",
@ -208,6 +211,7 @@ extern const std::unordered_set<std::string> client_known_interfaces = {
"STEAMUSERSTATS_INTERFACE_VERSION010",
"STEAMUSERSTATS_INTERFACE_VERSION011",
"STEAMUSERSTATS_INTERFACE_VERSION012",
"STEAMUSERSTATS_INTERFACE_VERSION013",
"SteamUtils001",
"SteamUtils002",
"SteamUtils003",

View File

@ -22,15 +22,43 @@
class Steam_Timeline :
public ISteamTimeline,
public ISteamTimeline003,
public ISteamTimeline002,
public ISteamTimeline001
{
private:
constexpr const static float PRIORITY_CLIP_MIN_SEC = 8.0f;
// "Steam Client Update - November 12th": * Increased Instant Clip default duration from 10s to 30s.
constexpr const static float PRIORITY_CLIP_MIN_SEC = 30.0f;
// TimelineState_t controls the bar of the timeline, independant of anything else even the events
struct TimelineState_t
{
private:
// emu specific: time when this state was changed via 'Steam_Timeline::SetTimelineGameMode()'
std::chrono::system_clock::time_point time_added = std::chrono::system_clock::now();
public:
const std::chrono::system_clock::time_point& get_time_added() const;
std::string description{}; // A localized string in the language returned by SteamUtils()->GetSteamUILanguage()
ETimelineGameMode bar_color{}; // the color of the timeline bar
};
// TimelineEvent_t are little clips/markers added over the bar of the timeline
struct TimelineEvent_t
{
private:
// emu specific: time when this event was added to the list via 'Steam_Timeline::AddTimelineEvent()'
const std::chrono::system_clock::time_point time_added = std::chrono::system_clock::now();
std::chrono::system_clock::time_point time_added = std::chrono::system_clock::now();
public:
// TODO not documented in public SDK yet
struct Recording_t
{
};
const std::chrono::system_clock::time_point& get_time_added() const;
// The name of the icon to show at the timeline at this point. This can be one of the icons uploaded through the Steamworks partner Site for your title, or one of the provided icons that start with steam_. The Steam Timelines overview includes a list of available icons.
// https://partner.steamgames.com/doc/features/timeline#icons
@ -53,25 +81,66 @@ private:
// Allows the game to describe events that should be suggested to the user as possible video clips.
ETimelineEventClipPriority ePossibleClip{};
bool ended = false;
// TODO not documented in public SDK yet
// emu specific: available recordings for this event
std::vector<Recording_t> recordings{};
};
struct TimelineState_t
struct TimelineGamePhase_t
{
private:
// emu specific: time when this state was changed via 'Steam_Timeline::SetTimelineGameMode()'
const std::chrono::system_clock::time_point time_added = std::chrono::system_clock::now();
std::chrono::system_clock::time_point time_added = std::chrono::system_clock::now();
std::string description{}; // A localized string in the language returned by SteamUtils()->GetSteamUILanguage()
ETimelineGameMode bar_color{}; // the color of the timeline bar
public:
struct Attribute_t
{
std::string pchAttributeValue{};
uint32 unPriority{};
};
struct Tag_t
{
std::string pchTagName{};
std::string pchTagIcon{};
uint32 unPriority{};
};
// TODO not documented in public SDK yet
struct Recording_t
{
};
const std::chrono::system_clock::time_point& get_time_added() const;
std::string pchTagIcon{}; // The name of a game provided timeline icon or builtin "steam_" icon
std::string pchPhaseID{}; // A game-provided persistent ID for a game phase. This could be a the match ID in a multiplayer game, etc...
std::string pchTagName{}; // The localized name of the tag in the language returned by SteamUtils()->GetSteamUILanguage()
std::string pchTagGroup{}; // The localized name of the tag group
std::map<std::string, Attribute_t> attributes{};
std::map<std::string, std::vector<Tag_t>> tags{};
bool ended = false;
// emu specific: available recordings for this phase
std::vector<Recording_t> recordings{};
};
std::recursive_mutex timeline_mutex{};
class Settings *settings{};
class Networking *network{};
class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb{};
std::vector<TimelineEvent_t> timeline_events{};
std::vector<TimelineState_t> timeline_states{};
std::vector<TimelineEvent_t> timeline_events{};
std::vector<TimelineGamePhase_t> timeline_game_phases{};
// unconditional periodic callback
void RunCallbacks();
@ -85,21 +154,22 @@ public:
Steam_Timeline(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
~Steam_Timeline();
void SetTimelineStateDescription( const char *pchDescription, float flTimeDelta );
void SetTimelineTooltip( const char *pchDescription, float flTimeDelta );
void ClearTimelineTooltip( float flTimeDelta );
void ClearTimelineStateDescription( float flTimeDelta );
void AddTimelineEvent( const char *pchIcon, const char *pchTitle, const char *pchDescription, uint32 unPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip );
void SetTimelineStateDescription( const char *pchDescription, float flTimeDelta ); // renamed to SetTimelineTooltip() in sdk v1.61
void ClearTimelineStateDescription( float flTimeDelta ); // renamed to ClearTimelineTooltip() in sdk v1.61
// Changes the color of the timeline bar. See ETimelineGameMode comments for how to use each value
void SetTimelineGameMode( ETimelineGameMode eMode );
void SetTimelineTooltip(const char* pchDescription, float flTimeDelta);
void ClearTimelineTooltip(float flTimeDelta);
TimelineEventHandle_t AddInstantaneousTimelineEvent( const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unIconPriority, float flStartOffsetSeconds = 0.f, ETimelineEventClipPriority ePossibleClip = k_ETimelineEventClipPriority_None );
TimelineEventHandle_t AddRangeTimelineEvent( const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unIconPriority, float flStartOffsetSeconds = 0.f, float flDuration = 0.f, ETimelineEventClipPriority ePossibleClip = k_ETimelineEventClipPriority_None );
TimelineEventHandle_t AddRangeTimelineEvent( const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unIconPriority, float flStartOffsetSeconds = 0.f, float flDuration = 0.f, ETimelineEventClipPriority ePossibleClip = k_ETimelineEventClipPriority_None );
TimelineEventHandle_t AddTimelineEvent( const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unIconPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip );
void AddTimelineEvent_old( const char *pchIcon, const char *pchTitle, const char *pchDescription, uint32 unPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip );
TimelineEventHandle_t StartRangeTimelineEvent( const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unPriority, float flStartOffsetSeconds, ETimelineEventClipPriority ePossibleClip );
TimelineEventHandle_t StartRangeTimelineEvent( const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unPriority, float flStartOffsetSeconds, ETimelineEventClipPriority ePossibleClip );
void UpdateRangeTimelineEvent( TimelineEventHandle_t ulEvent, const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unPriority, ETimelineEventClipPriority ePossibleClip );
@ -107,14 +177,15 @@ public:
void RemoveTimelineEvent( TimelineEventHandle_t ulEvent );
STEAM_CALL_RESULT( SteamTimelineEventRecordingExists_t )
SteamAPICall_t DoesEventRecordingExist( TimelineEventHandle_t ulEvent );
void StartGamePhase();
void EndGamePhase();
void SetGamePhaseID( const char *pchPhaseID );
STEAM_CALL_RESULT( SteamTimelineGamePhaseRecordingExists_t )
SteamAPICall_t DoesGamePhaseRecordingExist( const char *pchPhaseID );
void AddGamePhaseTag( const char *pchTagName, const char *pchTagIcon, const char *pchTagGroup, uint32 unPriority );
@ -125,6 +196,15 @@ public:
void OpenOverlayToTimelineEvent( const TimelineEventHandle_t ulEvent );
uint32 unknown_ret0_1();
uint32 unknown_ret0_2();
void unknown_nop_3();
void unknown_nop_4();
void unknown_nop_5();
void unknown_nop_6();
void unknown_nop_7();
};
#endif // __INCLUDED_STEAM_TIMELINE_H__

View File

@ -67,6 +67,7 @@ public ISteamUserStats008,
public ISteamUserStats009,
public ISteamUserStats010,
public ISteamUserStats011,
public ISteamUserStats012,
public ISteamUserStats
{
public:

View File

@ -5529,7 +5529,7 @@ STEAMAPI_API void SteamAPI_ISteamTimeline_ClearTimelineStateDescription( ISteamT
STEAMAPI_API void SteamAPI_ISteamTimeline_AddTimelineEvent( ISteamTimeline* self, const char * pchIcon, const char * pchTitle, const char * pchDescription, uint32 unPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip )
{
(get_steam_client()->steam_timeline)->AddTimelineEvent(pchIcon, pchTitle, pchDescription, unPriority, flStartOffsetSeconds, flDurationSeconds, ePossibleClip);
get_steam_client()->steam_timeline->AddTimelineEvent_old(pchIcon, pchTitle, pchDescription, unPriority, flStartOffsetSeconds, flDurationSeconds, ePossibleClip);
}
STEAMAPI_API void SteamAPI_ISteamTimeline_SetTimelineGameMode( ISteamTimeline* self, ETimelineGameMode eMode )

View File

@ -39,9 +39,11 @@ ISteamTimeline *Steam_Client::GetISteamTimeline( HSteamUser hSteamUser, HSteamPi
if (strcmp(pchVersion, "STEAMTIMELINE_INTERFACE_V001") == 0) {
return reinterpret_cast<ISteamTimeline *>(static_cast<ISteamTimeline001 *>(steam_timeline));
}
// Todo: Add non existing but reversed v2-v3
else if (strcmp(pchVersion, STEAMTIMELINE_INTERFACE_VERSION) == 0) {
} else if (strcmp(pchVersion, "STEAMTIMELINE_INTERFACE_V002") == 0) {
return reinterpret_cast<ISteamTimeline *>(static_cast<ISteamTimeline002 *>(steam_timeline));
} else if (strcmp(pchVersion, "STEAMTIMELINE_INTERFACE_V003") == 0) {
return reinterpret_cast<ISteamTimeline *>(static_cast<ISteamTimeline003 *>(steam_timeline));
} else if (strcmp(pchVersion, STEAMTIMELINE_INTERFACE_VERSION) == 0) {
return reinterpret_cast<ISteamTimeline *>(static_cast<ISteamTimeline *>(steam_timeline));
}
@ -145,7 +147,7 @@ ISteamGameServer *Steam_Client::GetISteamGameServer( HSteamUser hSteamUser, HSte
} else if (strcmp(pchVersion, "SteamGameServer012") == 0) {
return reinterpret_cast<ISteamGameServer *>(static_cast<ISteamGameServer012 *>(steam_gameserver));
} else if (strcmp(pchVersion, "SteamGameServer013") == 0) {
gameserver_has_ipv6_functions = true;
gameserver_has_ipv6_functions = true;
return reinterpret_cast<ISteamGameServer *>(static_cast<ISteamGameServer013 *>(steam_gameserver));
} else if (strcmp(pchVersion, "SteamGameServer014") == 0) {
gameserver_has_ipv6_functions = true;
@ -490,7 +492,7 @@ ISteamUserStats *Steam_Client::GetISteamUserStats( HSteamUser hSteamUser, HSteam
} else if (strcmp(pchVersion, "STEAMUSERSTATS_INTERFACE_VERSION011") == 0) {
return reinterpret_cast<ISteamUserStats *>(static_cast<ISteamUserStats011 *>(steam_user_stats));
} else if (strcmp(pchVersion, "STEAMUSERSTATS_INTERFACE_VERSION012") == 0) {
return reinterpret_cast<ISteamUserStats *>(static_cast<ISteamUserStats *>(steam_user_stats));
return reinterpret_cast<ISteamUserStats *>(static_cast<ISteamUserStats012 *>(steam_user_stats));
} else if (strcmp(pchVersion, STEAMUSERSTATS_INTERFACE_VERSION) == 0) {
return reinterpret_cast<ISteamUserStats *>(static_cast<ISteamUserStats *>(steam_user_stats));
}

View File

@ -17,7 +17,26 @@
#include "dll/steam_timeline.h"
// https://partner.steamgames.com/doc/api/ISteamTimeline
const std::chrono::system_clock::time_point& Steam_Timeline::TimelineEvent_t::get_time_added() const
{
return time_added;
}
const std::chrono::system_clock::time_point& Steam_Timeline::TimelineState_t::get_time_added() const
{
return time_added;
}
const std::chrono::system_clock::time_point& Steam_Timeline::TimelineGamePhase_t::get_time_added() const
{
return time_added;
}
void Steam_Timeline::steam_callback(void *object, Common_Message *msg)
@ -72,16 +91,16 @@ Steam_Timeline::~Steam_Timeline()
// - pchDescription: provide a localized string in the language returned by SteamUtils()->GetSteamUILanguage()
// - flTimeDelta: The time offset in seconds to apply to this event. Negative times indicate an
// event that happened in the past.
void Steam_Timeline::SetTimelineStateDescription( const char *pchDescription, float flTimeDelta )
void Steam_Timeline::SetTimelineTooltip( const char *pchDescription, float flTimeDelta )
{
PRINT_DEBUG("'%s' %f", pchDescription, flTimeDelta);
std::lock_guard lock(global_mutex);
std::lock_guard lock(timeline_mutex);
const auto target_timepoint = std::chrono::system_clock::now() + std::chrono::milliseconds(static_cast<long>(flTimeDelta * 1000));
const auto target_timepoint = std::chrono::system_clock::now() + std::chrono::milliseconds(static_cast<long long>(flTimeDelta * 1000));
// reverse iterators to search from end
// reverse iterators to find last/nearest match in recent time
auto event_it = std::find_if(timeline_states.rbegin(), timeline_states.rend(), [this, &target_timepoint](const TimelineState_t &item) {
return target_timepoint >= item.time_added;
return target_timepoint >= item.get_time_added();
});
if (timeline_states.rend() != event_it) {
@ -95,16 +114,16 @@ void Steam_Timeline::SetTimelineStateDescription( const char *pchDescription, fl
}
void Steam_Timeline::ClearTimelineStateDescription( float flTimeDelta )
void Steam_Timeline::ClearTimelineTooltip( float flTimeDelta )
{
PRINT_DEBUG("%f", flTimeDelta);
std::lock_guard lock(global_mutex);
std::lock_guard lock(timeline_mutex);
const auto target_timepoint = std::chrono::system_clock::now() + std::chrono::milliseconds(static_cast<long>(flTimeDelta * 1000));
const auto target_timepoint = std::chrono::system_clock::now() + std::chrono::milliseconds(static_cast<long long>(flTimeDelta * 1000));
// reverse iterators to search from end
// reverse iterators to find last/nearest match in recent time
auto event_it = std::find_if(timeline_states.rbegin(), timeline_states.rend(), [this, &target_timepoint](const TimelineState_t &item) {
return target_timepoint >= item.time_added;
return target_timepoint >= item.get_time_added();
});
if (timeline_states.rend() != event_it) {
@ -114,6 +133,73 @@ void Steam_Timeline::ClearTimelineStateDescription( float flTimeDelta )
}
void Steam_Timeline::SetTimelineStateDescription( const char *pchDescription, float flTimeDelta )
{
PRINT_DEBUG("old v1");
SetTimelineTooltip(pchDescription, flTimeDelta);
}
void Steam_Timeline::ClearTimelineStateDescription( float flTimeDelta )
{
PRINT_DEBUG("old v1");
ClearTimelineTooltip(flTimeDelta);
}
void Steam_Timeline::SetTimelineGameMode( ETimelineGameMode eMode )
{
PRINT_DEBUG("%i", (int)eMode);
std::lock_guard lock(timeline_mutex);
auto &new_timeline_state = timeline_states.emplace_back(TimelineState_t{});
new_timeline_state.bar_color = eMode;
}
TimelineEventHandle_t Steam_Timeline::AddInstantaneousTimelineEvent( const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unIconPriority, float flStartOffsetSeconds, ETimelineEventClipPriority ePossibleClip )
{
PRINT_DEBUG_TODO();
return AddRangeTimelineEvent(pchTitle, pchDescription, pchIcon, unIconPriority, flStartOffsetSeconds, 0, ePossibleClip);
}
TimelineEventHandle_t Steam_Timeline::AddRangeTimelineEvent( const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unIconPriority, float flStartOffsetSeconds, float flDuration, ETimelineEventClipPriority ePossibleClip)
{
PRINT_DEBUG("'%s' ('%s') icon='%s', %u, [%f, %f) %i", pchTitle, pchDescription, pchIcon, unIconPriority, flStartOffsetSeconds, flDuration, (int)ePossibleClip);
std::lock_guard lock(timeline_mutex);
auto event_id = StartRangeTimelineEvent(pchTitle, pchDescription, pchIcon, unIconPriority, flStartOffsetSeconds, ePossibleClip);
if (!event_id || event_id > timeline_events.size()) return event_id;
auto& my_event = timeline_events[static_cast<size_t>(event_id - 1)];
my_event.ended = true; // ranged and instantaneous events are ended/closed events, they can't be modified later according to docs
// make events last at least 1 sec
if (static_cast<long long>(flDuration * 1000) < 1000LL) { // < 1000ms
flDuration = 1;
}
// for events with priority=ETimelineEventClipPriority::k_ETimelineEventClipPriority_Featured steam creates ~30 sec clip
if (flDuration < PRIORITY_CLIP_MIN_SEC && ePossibleClip == ETimelineEventClipPriority::k_ETimelineEventClipPriority_Featured) {
flDuration = PRIORITY_CLIP_MIN_SEC;
}
if (flDuration > k_flMaxTimelineEventDuration) {
flDuration = k_flMaxTimelineEventDuration;
}
my_event.flDurationSeconds = flDuration;
return event_id;
}
TimelineEventHandle_t Steam_Timeline::AddTimelineEvent( const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unIconPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip )
{
PRINT_DEBUG("undocumented v2/v3");
// this is how actual steamclient64.dll implements it
if (flDurationSeconds > 0) {
return AddRangeTimelineEvent(pchTitle, pchDescription, pchIcon, unIconPriority, flStartOffsetSeconds, flDurationSeconds, ePossibleClip);
} else {
return AddInstantaneousTimelineEvent(pchTitle, pchDescription, pchIcon, unIconPriority, flStartOffsetSeconds, ePossibleClip);
}
}
// Use this to mark an event on the Timeline. The event can be instantaneous or take some amount of time
// to complete, depending on the value passed in flDurationSeconds
//
@ -140,126 +226,290 @@ void Steam_Timeline::ClearTimelineStateDescription( float flTimeDelta )
// - ePossibleClip: By setting this parameter to Featured or Standard, the game indicates to Steam that it
// would be appropriate to offer this range as a clip to the user. For instantaneous events, the
// suggested clip will be for a short time before and after the event itself.
void Steam_Timeline::AddTimelineEvent( const char *pchIcon, const char *pchTitle, const char *pchDescription, uint32 unPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip )
void Steam_Timeline::AddTimelineEvent_old( const char *pchIcon, const char *pchTitle, const char *pchDescription, uint32 unPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip )
{
PRINT_DEBUG("icon='%s' | '%s' - '%s', %u, [%f, %f) %i", pchIcon, pchTitle, pchDescription, unPriority, flStartOffsetSeconds, flDurationSeconds, (int)ePossibleClip);
std::lock_guard lock(global_mutex);
PRINT_DEBUG("old v1");
// this is how actual steamclient64.dll implements it
if (flDurationSeconds > 0) {
AddRangeTimelineEvent(pchTitle, pchDescription, pchIcon, unPriority, flStartOffsetSeconds, flDurationSeconds, ePossibleClip);
} else {
AddInstantaneousTimelineEvent(pchTitle, pchDescription, pchIcon, unPriority, flStartOffsetSeconds, ePossibleClip);
}
}
TimelineEventHandle_t Steam_Timeline::StartRangeTimelineEvent( const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unPriority, float flStartOffsetSeconds, ETimelineEventClipPriority ePossibleClip )
{
PRINT_DEBUG("'%s' ('%s') icon='%s', %u, @[%f]sec %i", pchTitle, pchDescription, pchIcon, unPriority, flStartOffsetSeconds, (int)ePossibleClip);
std::lock_guard lock(timeline_mutex);
// this adds a new event, but the duration is set once EndRangeTimelineEvent is called
// also its "ended" flag is not set
auto &new_event = timeline_events.emplace_back(TimelineEvent_t{});
new_event.pchIcon = pchIcon;
new_event.pchTitle = pchTitle;
new_event.pchDescription = pchDescription;
new_event.pchTitle = pchTitle ? pchTitle : "";
new_event.pchDescription = pchDescription ? pchDescription : "";
new_event.pchIcon = pchIcon ? pchIcon : "";
new_event.unPriority = unPriority;
new_event.flStartOffsetSeconds = flStartOffsetSeconds;
// make events last at least 1 sec
if (static_cast<long>(flDurationSeconds * 1000) < 1000) { // < 1000ms
flDurationSeconds = 1;
}
// for events with priority=ETimelineEventClipPriority::k_ETimelineEventClipPriority_Featured steam creates ~8 sec clip
// seen here: https://www.youtube.com/watch?v=YwBD0E4-EsI
if (flDurationSeconds < PRIORITY_CLIP_MIN_SEC && ePossibleClip == ETimelineEventClipPriority::k_ETimelineEventClipPriority_Featured) {
flDurationSeconds = PRIORITY_CLIP_MIN_SEC;
}
new_event.flDurationSeconds = flDurationSeconds;
new_event.ePossibleClip = ePossibleClip;
auto new_event_id = timeline_events.size(); // never return 0, most APIs in other interfaces use it for invalid IDs
PRINT_DEBUG(" new event ID = [%zu]", new_event_id);
return static_cast<TimelineEventHandle_t>(new_event_id);
}
// Changes the color of the timeline bar. See ETimelineGameMode comments for how to use each value
void Steam_Timeline::SetTimelineGameMode( ETimelineGameMode eMode )
void Steam_Timeline::UpdateRangeTimelineEvent( TimelineEventHandle_t ulEvent, const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unPriority, ETimelineEventClipPriority ePossibleClip )
{
PRINT_DEBUG("%i", (int)eMode);
std::lock_guard lock(global_mutex);
PRINT_DEBUG("[%llu] '%s' ('%s') | icon='%s', %u, %i", ulEvent, pchTitle, pchDescription, pchIcon, unPriority, (int)ePossibleClip);
std::lock_guard lock(timeline_mutex);
auto &new_timeline_state = timeline_states.emplace_back(TimelineState_t{});
new_timeline_state.bar_color = eMode;
if (!ulEvent || ulEvent > timeline_events.size()) return;
auto& my_event = timeline_events[static_cast<size_t>(ulEvent - 1)];
if (my_event.ended) return;
if (pchTitle) {
my_event.pchTitle = pchTitle;
} else {
my_event.pchTitle.clear();
}
if (pchDescription) {
my_event.pchDescription = pchDescription;
} else {
my_event.pchDescription.clear();
}
if (pchIcon) {
my_event.pchIcon = pchIcon;
} else {
my_event.pchIcon.clear();
}
my_event.unPriority = unPriority;
my_event.ePossibleClip = ePossibleClip;
PRINT_DEBUG(" updated event");
}
void Steam_Timeline::SetTimelineTooltip(const char* pchDescription, float flTimeDelta)
void Steam_Timeline::EndRangeTimelineEvent( TimelineEventHandle_t ulEvent, float flEndOffsetSeconds )
{
SetTimelineStateDescription(pchDescription, flTimeDelta);
PRINT_DEBUG("[%llu] %f", ulEvent, flEndOffsetSeconds);
std::lock_guard lock(timeline_mutex);
if (!ulEvent || ulEvent > timeline_events.size()) return;
auto& my_event = timeline_events[static_cast<size_t>(ulEvent - 1)];
if (my_event.ended) return;
my_event.ended = true;
auto end_timepoint = std::chrono::system_clock::now();
auto start_timepoint = my_event.get_time_added() + std::chrono::milliseconds(static_cast<long long>(my_event.flStartOffsetSeconds * 1000));
auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end_timepoint - start_timepoint);
my_event.flDurationSeconds = duration_ms.count() / 1000.0f;
PRINT_DEBUG(" ended event // TODO show in the UI");
}
void Steam_Timeline::ClearTimelineTooltip(float flTimeDelta)
void Steam_Timeline::RemoveTimelineEvent( TimelineEventHandle_t ulEvent )
{
ClearTimelineStateDescription(flTimeDelta);
}
TimelineEventHandle_t Steam_Timeline::AddInstantaneousTimelineEvent(const char* pchTitle, const char* pchDescription, const char* pchIcon, uint32 unIconPriority, float flStartOffsetSeconds, ETimelineEventClipPriority ePossibleClip)
{
return 0;
}
TimelineEventHandle_t Steam_Timeline::AddRangeTimelineEvent(const char* pchTitle, const char* pchDescription, const char* pchIcon, uint32 unIconPriority, float flStartOffsetSeconds, float flDuration, ETimelineEventClipPriority ePossibleClip)
{
return 0;
}
TimelineEventHandle_t Steam_Timeline::StartRangeTimelineEvent(const char* pchTitle, const char* pchDescription, const char* pchIcon, uint32 unPriority, float flStartOffsetSeconds, ETimelineEventClipPriority ePossibleClip)
{
return 0;
}
void Steam_Timeline::UpdateRangeTimelineEvent(TimelineEventHandle_t ulEvent, const char* pchTitle, const char* pchDescription, const char* pchIcon, uint32 unPriority, ETimelineEventClipPriority ePossibleClip)
{
}
void Steam_Timeline::EndRangeTimelineEvent(TimelineEventHandle_t ulEvent, float flEndOffsetSeconds)
{
}
void Steam_Timeline::RemoveTimelineEvent(TimelineEventHandle_t ulEvent)
{
PRINT_DEBUG("[%llu]", ulEvent);
std::lock_guard lock(timeline_mutex);
if (!ulEvent || ulEvent > timeline_events.size()) return;
timeline_events.erase(timeline_events.begin() + static_cast<size_t>(ulEvent - 1));
PRINT_DEBUG(" removed event // TODO remove from the UI");
}
STEAM_CALL_RESULT( SteamTimelineEventRecordingExists_t )
SteamAPICall_t Steam_Timeline::DoesEventRecordingExist(TimelineEventHandle_t ulEvent)
{
return 0;
PRINT_DEBUG("[%llu] // TODO", ulEvent);
std::lock_guard lock(timeline_mutex);
if (!ulEvent || ulEvent > timeline_events.size()) {
SteamTimelineEventRecordingExists_t data_invalid{};
data_invalid.m_bRecordingExists = false;
data_invalid.m_ulEventID = ulEvent;
auto ret = callback_results->addCallResult(data_invalid.k_iCallback, &data_invalid, sizeof(data_invalid));
callbacks->addCBResult(data_invalid.k_iCallback, &data_invalid, sizeof(data_invalid));
return ret;
}
auto& my_event = timeline_events[static_cast<size_t>(ulEvent - 1)];
auto recordings_count = my_event.recordings.size();
SteamTimelineEventRecordingExists_t data{};
data.m_bRecordingExists = !my_event.recordings.empty();
data.m_ulEventID = ulEvent;
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
}
void Steam_Timeline::StartGamePhase()
{
PRINT_DEBUG_ENTRY();
std::lock_guard lock(timeline_mutex);
timeline_game_phases.emplace_back(TimelineGamePhase_t{});
}
void Steam_Timeline::EndGamePhase()
{
PRINT_DEBUG_ENTRY();
std::lock_guard lock(timeline_mutex);
if (timeline_game_phases.empty()) return;
auto &last_game_phase = timeline_game_phases.back();
last_game_phase.ended = true;
}
void Steam_Timeline::SetGamePhaseID( const char *pchPhaseID )
{
PRINT_DEBUG("['%s']", pchPhaseID);
std::lock_guard lock(timeline_mutex);
if (timeline_game_phases.empty()) return;
auto &last_game_phase = timeline_game_phases.back();
if (last_game_phase.ended) return;
last_game_phase.pchPhaseID = pchPhaseID ? pchPhaseID : "";
PRINT_DEBUG(" changed phase ID");
}
STEAM_CALL_RESULT( SteamTimelineGamePhaseRecordingExists_t )
SteamAPICall_t Steam_Timeline::DoesGamePhaseRecordingExist( const char *pchPhaseID )
{
PRINT_DEBUG("'%s' // TODO", pchPhaseID);
std::lock_guard lock(timeline_mutex);
if (!pchPhaseID) pchPhaseID = "";
std::string_view game_phase_id_view(pchPhaseID);
const auto trigger_failure = [game_phase_id_view, this]() {
SteamTimelineGamePhaseRecordingExists_t data_invalid{};
auto chars_copied = game_phase_id_view.copy(data_invalid.m_rgchPhaseID, sizeof(data_invalid.m_rgchPhaseID) - 1);
data_invalid.m_rgchPhaseID[chars_copied] = 0;
data_invalid.m_ulLongestClipMS = 0;
data_invalid.m_ulRecordingMS = 0;
data_invalid.m_unClipCount = 0;
data_invalid.m_unScreenshotCount = 0;
auto ret = callback_results->addCallResult(data_invalid.k_iCallback, &data_invalid, sizeof(data_invalid));
callbacks->addCBResult(data_invalid.k_iCallback, &data_invalid, sizeof(data_invalid));
return ret;
};
if (timeline_game_phases.empty()) {
return trigger_failure();
}
auto phase_it = std::find_if(timeline_game_phases.begin(), timeline_game_phases.end(), [game_phase_id_view](const TimelineGamePhase_t &item){
return game_phase_id_view == item.pchPhaseID;
});
if (timeline_game_phases.end() == phase_it) {
return trigger_failure();
}
// TODO return actual count ?
auto recordings_count = phase_it->recordings.size();
return trigger_failure();
}
void Steam_Timeline::AddGamePhaseTag( const char *pchTagName, const char *pchTagIcon, const char *pchTagGroup, uint32 unPriority )
{
PRINT_DEBUG("['%s']: '%s' '%s' <%u>", pchTagGroup, pchTagName, pchTagIcon, unPriority);
std::lock_guard lock(timeline_mutex);
if (timeline_game_phases.empty()) return;
auto &last_game_phase = timeline_game_phases.back();
if (last_game_phase.ended) return;
if (!pchTagGroup) pchTagGroup = "";
auto &phase_tag = last_game_phase.tags[pchTagGroup].emplace_back(Steam_Timeline::TimelineGamePhase_t::Tag_t{});
phase_tag.pchTagName = pchTagName ? pchTagName : "";
phase_tag.pchTagIcon = pchTagIcon ? pchTagIcon : "";
phase_tag.unPriority = unPriority;
PRINT_DEBUG(" added phase tag");
}
void Steam_Timeline::SetGamePhaseAttribute( const char *pchAttributeGroup, const char *pchAttributeValue, uint32 unPriority )
{
PRINT_DEBUG("['%s']: '%s' <%u>", pchAttributeGroup, pchAttributeValue, unPriority);
std::lock_guard lock(timeline_mutex);
if (timeline_game_phases.empty()) return;
auto &last_game_phase = timeline_game_phases.back();
if (last_game_phase.ended) return;
if (!pchAttributeGroup) pchAttributeGroup = "";
auto &phase_att = last_game_phase.attributes[pchAttributeGroup];
phase_att.pchAttributeValue = pchAttributeValue ? pchAttributeValue : "";
phase_att.unPriority = unPriority;
PRINT_DEBUG(" changed phase attribute");
}
void Steam_Timeline::OpenOverlayToGamePhase( const char *pchPhaseID )
{
PRINT_DEBUG("['%s'] // TODO", pchPhaseID);
std::lock_guard lock(timeline_mutex);
}
void Steam_Timeline::SetGamePhaseID(const char* pchPhaseID)
void Steam_Timeline::OpenOverlayToTimelineEvent( const TimelineEventHandle_t ulEvent )
{
PRINT_DEBUG("[%llu] // TODO", ulEvent);
std::lock_guard lock(timeline_mutex);
}
SteamAPICall_t Steam_Timeline::DoesGamePhaseRecordingExist(const char* pchPhaseID)
uint32 Steam_Timeline::unknown_ret0_1()
{
PRINT_DEBUG_TODO();
return 0;
}
void Steam_Timeline::AddGamePhaseTag(const char* pchTagName, const char* pchTagIcon, const char* pchTagGroup, uint32 unPriority)
uint32 Steam_Timeline::unknown_ret0_2()
{
PRINT_DEBUG_TODO();
return 0;
}
void Steam_Timeline::SetGamePhaseAttribute(const char* pchAttributeGroup, const char* pchAttributeValue, uint32 unPriority)
void Steam_Timeline::unknown_nop_3()
{
PRINT_DEBUG_TODO();
}
void Steam_Timeline::OpenOverlayToGamePhase(const char* pchPhaseID)
void Steam_Timeline::unknown_nop_4()
{
PRINT_DEBUG_TODO();
}
void Steam_Timeline::OpenOverlayToTimelineEvent(const TimelineEventHandle_t ulEvent)
void Steam_Timeline::unknown_nop_5()
{
PRINT_DEBUG_TODO();
}
void Steam_Timeline::unknown_nop_6()
{
PRINT_DEBUG_TODO();
}
void Steam_Timeline::unknown_nop_7()
{
PRINT_DEBUG_TODO();
}

View File

@ -59,7 +59,7 @@ public:
// - ePossibleClip: By setting this parameter to Featured or Standard, the game indicates to Steam that it
// would be appropriate to offer this range as a clip to the user. For instantaneous events, the
// suggested clip will be for a short time before and after the event itself.
virtual void AddTimelineEvent( const char *pchIcon, const char *pchTitle, const char *pchDescription, uint32 unPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip ) = 0;
virtual void AddTimelineEvent_old( const char *pchIcon, const char *pchTitle, const char *pchDescription, uint32 unPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip ) = 0;
// Changes the color of the timeline bar. See ETimelineGameMode comments for how to use each value
virtual void SetTimelineGameMode( ETimelineGameMode eMode ) = 0;

View File

@ -0,0 +1,130 @@
#ifndef ISTEAMTIMELINE002_H
#define ISTEAMTIMELINE002_H
#ifdef STEAM_WIN32
#pragma once
#endif
// this interface version is not found in public SDK archives, it is based on reversing the returned vftable from steamclient64.dll
class ISteamTimeline002
{
public:
// Sets a description for the current game state in the timeline. These help the user to find specific
// moments in the timeline when saving clips. Setting a new state description replaces any previous
// description.
//
// Examples could include:
// * Where the user is in the world in a single player game
// * Which round is happening in a multiplayer game
// * The current score for a sports game
//
// Parameters:
// - pchDescription: provide a localized string in the language returned by SteamUtils()->GetSteamUILanguage()
// - flTimeDelta: The time offset in seconds to apply to this event. Negative times indicate an
// event that happened in the past.
virtual void SetTimelineTooltip( const char *pchDescription, float flTimeDelta ) = 0;
virtual void ClearTimelineTooltip( float flTimeDelta ) = 0;
// Changes the color of the timeline bar. See ETimelineGameMode comments for how to use each value
virtual void SetTimelineGameMode( ETimelineGameMode eMode ) = 0;
///////////////////////////////////// the following functions are not found in public SDK
virtual TimelineEventHandle_t AddTimelineEvent( const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unIconPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip ) = 0;
virtual uint32 unknown_ret0_1() = 0; // xor eax, eax; ret;
virtual uint32 unknown_ret0_2() = 0; // xor eax, eax; ret;
virtual void unknown_nop_3() = 0; // ret;
/////////////////////////////////////
// delete the event from the timeline. This can be called on a timeline event from AddInstantaneousTimelineEvent,
// AddRangeTimelineEvent, or StartRangeTimelineEvent/EndRangeTimelineEvent. The timeline event handle must be from the
// current game process.
virtual void RemoveTimelineEvent( TimelineEventHandle_t ulEvent ) = 0;
///////////////////////////////////// the following functions are not found in public SDK
virtual void unknown_nop_4() = 0; // ret;
virtual void unknown_nop_5() = 0; // ret;
virtual void unknown_nop_6() = 0; // ret;
/////////////////////////////////////
// add a tag to whatever time range is represented by the event
STEAM_CALL_RESULT( SteamTimelineEventRecordingExists_t )
virtual SteamAPICall_t DoesEventRecordingExist( TimelineEventHandle_t ulEvent ) = 0;
/******************* Game Phases *******************/
// Game phases allow the user to navigate their background recordings and clips. Exactly what a game phase means will vary game to game, but
// the game phase should be a section of gameplay that is usually between 10 minutes and a few hours in length, and should be the
// main way a user would think to divide up the game. These are presented to the user in a UI that shows the date the game was played,
// with one row per game slice. Game phases should be used to mark sections of gameplay that the user might be interested in watching.
//
// Examples could include:
// * A single match in a multiplayer PvP game
// * A chapter of a story-based singleplayer game
// * A single run in a roguelike
//
// Game phases are started with StartGamePhase, and while a phase is still happening, they can have tags and attributes added to them.
//
// Phase attributes represent generic text fields that can be updated throughout the duration of the phase. They are meant
// to be used for phase metadata that is not part of a well defined set of options. For example, a KDA attribute that starts
// with the value "0/0/0" and updates as the phase progresses, or something like a played-entered character name. Attributes
// can be set as many times as the game likes with SetGamePhaseAttribute, and only the last value will be shown to the user.
//
// Phase tags represent data with a well defined set of options, which could be data such as match resolution, hero played,
// game mode, etc. Tags can have an icon in addition to a text name. Multiple tags within the same group may be added per phase
// and all will be remembered. For example, AddGamePhaseTag may be called multiple times for a "Bosses Defeated" group, with
// different names and icons for each boss defeated during the phase, all of which will be shown to the user.
//
// The phase will continue until the game exits, until the game calls EndGamePhase, or until the game calls
// StartGamePhase to start a new phase.
//
// The game phase functions take these parameters:
// - pchTagIcon: The name of a game provided timeline icon or builtin "steam_" icon.
// - pchPhaseID: A game-provided persistent ID for a game phase. This could be a the match ID in a multiplayer game, a chapter name in a
// single player game, the ID of a character, etc.
// - pchTagName: The localized name of the tag in the language returned by SteamUtils()->GetSteamUILanguage().
// - pchTagGroup: The localized name of the tag group.
// - pchAttributeValue: The localized name of the attribute.
// - pchAttributeGroup: The localized name of the attribute group.
// - unPriority: Used to order tags and attributes in the UI displayed to the user, with higher priority values leading
// to more prominent positioning. In contexts where there is limited space, lower priority items may be hidden.
virtual void StartGamePhase() = 0;
///////////////////////////////////// the following functions are not found in public SDK
virtual void unknown_nop_7() = 0; // ret;
/////////////////////////////////////
virtual void EndGamePhase() = 0;
// Games can set a phase ID so they can refer back to a phase in OpenOverlayToPhase
virtual void SetGamePhaseID( const char *pchPhaseID ) = 0;
STEAM_CALL_RESULT( SteamTimelineGamePhaseRecordingExists_t )
virtual SteamAPICall_t DoesGamePhaseRecordingExist( const char *pchPhaseID ) = 0;
// Add a tag that applies to the entire phase
virtual void AddGamePhaseTag( const char *pchTagName, const char *pchTagIcon, const char *pchTagGroup, uint32 unPriority ) = 0;
/******************* Opening the overlay *******************/
// Opens the Steam overlay to a game phase.
//
// Parameters:
// - pchPhaseID: The ID of a phase that was previously provided by the game in SetGamePhaseID.
virtual void OpenOverlayToGamePhase( const char *pchPhaseID ) = 0;
// Opens the Steam overlay to a timeline event.
//
// Parameters:
// - ulEventID: The ID of a timeline event returned by StartEvent or AddSimpleTimelineEvent
virtual void OpenOverlayToTimelineEvent( const TimelineEventHandle_t ulEvent ) = 0;
};
#endif // ISTEAMTIMELINE002_H

View File

@ -0,0 +1,126 @@
#ifndef ISTEAMTIMELINE003_H
#define ISTEAMTIMELINE003_H
#ifdef STEAM_WIN32
#pragma once
#endif
// this interface version is not found in public SDK archives, it is based on reversing the returned vftable from steamclient64.dll
class ISteamTimeline003
{
public:
// Sets a description for the current game state in the timeline. These help the user to find specific
// moments in the timeline when saving clips. Setting a new state description replaces any previous
// description.
//
// Examples could include:
// * Where the user is in the world in a single player game
// * Which round is happening in a multiplayer game
// * The current score for a sports game
//
// Parameters:
// - pchDescription: provide a localized string in the language returned by SteamUtils()->GetSteamUILanguage()
// - flTimeDelta: The time offset in seconds to apply to this event. Negative times indicate an
// event that happened in the past.
virtual void SetTimelineTooltip( const char *pchDescription, float flTimeDelta ) = 0;
virtual void ClearTimelineTooltip( float flTimeDelta ) = 0;
// Changes the color of the timeline bar. See ETimelineGameMode comments for how to use each value
virtual void SetTimelineGameMode( ETimelineGameMode eMode ) = 0;
///////////////////////////////////// the following functions are not found in public SDK
virtual TimelineEventHandle_t AddTimelineEvent( const char *pchTitle, const char *pchDescription, const char *pchIcon, uint32 unIconPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip ) = 0;
virtual uint32 unknown_ret0_1() = 0; // xor eax, eax; ret;
virtual uint32 unknown_ret0_2() = 0; // xor eax, eax; ret;
virtual void unknown_nop_3() = 0; // ret;
/////////////////////////////////////
// delete the event from the timeline. This can be called on a timeline event from AddInstantaneousTimelineEvent,
// AddRangeTimelineEvent, or StartRangeTimelineEvent/EndRangeTimelineEvent. The timeline event handle must be from the
// current game process.
virtual void RemoveTimelineEvent( TimelineEventHandle_t ulEvent ) = 0;
///////////////////////////////////// the following functions are not found in public SDK
virtual void unknown_nop_4() = 0; // ret;
virtual void unknown_nop_5() = 0; // ret;
virtual void unknown_nop_6() = 0; // ret;
/////////////////////////////////////
// add a tag to whatever time range is represented by the event
STEAM_CALL_RESULT( SteamTimelineEventRecordingExists_t )
virtual SteamAPICall_t DoesEventRecordingExist( TimelineEventHandle_t ulEvent ) = 0;
/******************* Game Phases *******************/
// Game phases allow the user to navigate their background recordings and clips. Exactly what a game phase means will vary game to game, but
// the game phase should be a section of gameplay that is usually between 10 minutes and a few hours in length, and should be the
// main way a user would think to divide up the game. These are presented to the user in a UI that shows the date the game was played,
// with one row per game slice. Game phases should be used to mark sections of gameplay that the user might be interested in watching.
//
// Examples could include:
// * A single match in a multiplayer PvP game
// * A chapter of a story-based singleplayer game
// * A single run in a roguelike
//
// Game phases are started with StartGamePhase, and while a phase is still happening, they can have tags and attributes added to them.
//
// Phase attributes represent generic text fields that can be updated throughout the duration of the phase. They are meant
// to be used for phase metadata that is not part of a well defined set of options. For example, a KDA attribute that starts
// with the value "0/0/0" and updates as the phase progresses, or something like a played-entered character name. Attributes
// can be set as many times as the game likes with SetGamePhaseAttribute, and only the last value will be shown to the user.
//
// Phase tags represent data with a well defined set of options, which could be data such as match resolution, hero played,
// game mode, etc. Tags can have an icon in addition to a text name. Multiple tags within the same group may be added per phase
// and all will be remembered. For example, AddGamePhaseTag may be called multiple times for a "Bosses Defeated" group, with
// different names and icons for each boss defeated during the phase, all of which will be shown to the user.
//
// The phase will continue until the game exits, until the game calls EndGamePhase, or until the game calls
// StartGamePhase to start a new phase.
//
// The game phase functions take these parameters:
// - pchTagIcon: The name of a game provided timeline icon or builtin "steam_" icon.
// - pchPhaseID: A game-provided persistent ID for a game phase. This could be a the match ID in a multiplayer game, a chapter name in a
// single player game, the ID of a character, etc.
// - pchTagName: The localized name of the tag in the language returned by SteamUtils()->GetSteamUILanguage().
// - pchTagGroup: The localized name of the tag group.
// - pchAttributeValue: The localized name of the attribute.
// - pchAttributeGroup: The localized name of the attribute group.
// - unPriority: Used to order tags and attributes in the UI displayed to the user, with higher priority values leading
// to more prominent positioning. In contexts where there is limited space, lower priority items may be hidden.
virtual void StartGamePhase() = 0;
virtual void EndGamePhase() = 0;
// Games can set a phase ID so they can refer back to a phase in OpenOverlayToPhase
virtual void SetGamePhaseID( const char *pchPhaseID ) = 0;
STEAM_CALL_RESULT( SteamTimelineGamePhaseRecordingExists_t )
virtual SteamAPICall_t DoesGamePhaseRecordingExist( const char *pchPhaseID ) = 0;
// Add a tag that applies to the entire phase
virtual void AddGamePhaseTag( const char *pchTagName, const char *pchTagIcon, const char *pchTagGroup, uint32 unPriority ) = 0;
// Add a text attribute that applies to the entire phase
virtual void SetGamePhaseAttribute( const char *pchAttributeGroup, const char *pchAttributeValue, uint32 unPriority ) = 0;
/******************* Opening the overlay *******************/
// Opens the Steam overlay to a game phase.
//
// Parameters:
// - pchPhaseID: The ID of a phase that was previously provided by the game in SetGamePhaseID.
virtual void OpenOverlayToGamePhase( const char *pchPhaseID ) = 0;
// Opens the Steam overlay to a timeline event.
//
// Parameters:
// - ulEventID: The ID of a timeline event returned by StartEvent or AddSimpleTimelineEvent
virtual void OpenOverlayToTimelineEvent( const TimelineEventHandle_t ulEvent ) = 0;
};
#endif // ISTEAMTIMELINE003_H

View File

@ -93,8 +93,7 @@ public:
// Note: this call is no longer required as it is managed by the Steam client
// The game stats and achievements will be synchronized with Steam before
// the game process begins.
STEAM_CALL_BACK( UserStatsReceived_t )
virtual bool RequestCurrentStats() = 0;
// virtual bool RequestCurrentStats() = 0;
// Data accessors
STEAM_FLAT_NAME( GetStatInt32 )

View File

@ -0,0 +1,230 @@
#ifndef ISTEAMUSERSTATS012_H
#define ISTEAMUSERSTATS012_H
#ifdef STEAM_WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
// Purpose: Functions for accessing stats, achievements, and leaderboard information
//-----------------------------------------------------------------------------
class ISteamUserStats012
{
public:
// Ask the server to send down this user's data and achievements for this game
STEAM_CALL_BACK( UserStatsReceived_t )
virtual bool RequestCurrentStats() = 0;
// Data accessors
STEAM_FLAT_NAME( GetStatInt32 )
virtual bool GetStat( const char *pchName, int32 *pData ) = 0;
STEAM_FLAT_NAME( GetStatFloat )
virtual bool GetStat( const char *pchName, float *pData ) = 0;
// Set / update data
STEAM_FLAT_NAME( SetStatInt32 )
virtual bool SetStat( const char *pchName, int32 nData ) = 0;
STEAM_FLAT_NAME( SetStatFloat )
virtual bool SetStat( const char *pchName, float fData ) = 0;
virtual bool UpdateAvgRateStat( const char *pchName, float flCountThisSession, double dSessionLength ) = 0;
// Achievement flag accessors
virtual bool GetAchievement( const char *pchName, bool *pbAchieved ) = 0;
virtual bool SetAchievement( const char *pchName ) = 0;
virtual bool ClearAchievement( const char *pchName ) = 0;
// Get the achievement status, and the time it was unlocked if unlocked.
// If the return value is true, but the unlock time is zero, that means it was unlocked before Steam
// began tracking achievement unlock times (December 2009). Time is seconds since January 1, 1970.
virtual bool GetAchievementAndUnlockTime( const char *pchName, bool *pbAchieved, uint32 *punUnlockTime ) = 0;
// Store the current data on the server, will get a callback when set
// And one callback for every new achievement
//
// If the callback has a result of k_EResultInvalidParam, one or more stats
// uploaded has been rejected, either because they broke constraints
// or were out of date. In this case the server sends back updated values.
// The stats should be re-iterated to keep in sync.
virtual bool StoreStats() = 0;
// Achievement / GroupAchievement metadata
// Gets the icon of the achievement, which is a handle to be used in ISteamUtils::GetImageRGBA(), or 0 if none set.
// A return value of 0 may indicate we are still fetching data, and you can wait for the UserAchievementIconFetched_t callback
// which will notify you when the bits are ready. If the callback still returns zero, then there is no image set for the
// specified achievement.
virtual int GetAchievementIcon( const char *pchName ) = 0;
// Get general attributes for an achievement. Accepts the following keys:
// - "name" and "desc" for retrieving the localized achievement name and description (returned in UTF8)
// - "hidden" for retrieving if an achievement is hidden (returns "0" when not hidden, "1" when hidden)
virtual const char *GetAchievementDisplayAttribute( const char *pchName, const char *pchKey ) = 0;
// Achievement progress - triggers an AchievementProgress callback, that is all.
// Calling this w/ N out of N progress will NOT set the achievement, the game must still do that.
virtual bool IndicateAchievementProgress( const char *pchName, uint32 nCurProgress, uint32 nMaxProgress ) = 0;
// Used for iterating achievements. In general games should not need these functions because they should have a
// list of existing achievements compiled into them
virtual uint32 GetNumAchievements() = 0;
// Get achievement name iAchievement in [0,GetNumAchievements)
virtual const char *GetAchievementName( uint32 iAchievement ) = 0;
// Friends stats & achievements
// downloads stats for the user
// returns a UserStatsReceived_t received when completed
// if the other user has no stats, UserStatsReceived_t.m_eResult will be set to k_EResultFail
// these stats won't be auto-updated; you'll need to call RequestUserStats() again to refresh any data
STEAM_CALL_RESULT( UserStatsReceived_t )
virtual SteamAPICall_t RequestUserStats( CSteamID steamIDUser ) = 0;
// requests stat information for a user, usable after a successful call to RequestUserStats()
STEAM_FLAT_NAME( GetUserStatInt32 )
virtual bool GetUserStat( CSteamID steamIDUser, const char *pchName, int32 *pData ) = 0;
STEAM_FLAT_NAME( GetUserStatFloat )
virtual bool GetUserStat( CSteamID steamIDUser, const char *pchName, float *pData ) = 0;
virtual bool GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchieved ) = 0;
// See notes for GetAchievementAndUnlockTime above
virtual bool GetUserAchievementAndUnlockTime( CSteamID steamIDUser, const char *pchName, bool *pbAchieved, uint32 *punUnlockTime ) = 0;
// Reset stats
virtual bool ResetAllStats( bool bAchievementsToo ) = 0;
// Leaderboard functions
// asks the Steam back-end for a leaderboard by name, and will create it if it's not yet
// This call is asynchronous, with the result returned in LeaderboardFindResult_t
STEAM_CALL_RESULT(LeaderboardFindResult_t)
virtual SteamAPICall_t FindOrCreateLeaderboard( const char *pchLeaderboardName, ELeaderboardSortMethod eLeaderboardSortMethod, ELeaderboardDisplayType eLeaderboardDisplayType ) = 0;
// as above, but won't create the leaderboard if it's not found
// This call is asynchronous, with the result returned in LeaderboardFindResult_t
STEAM_CALL_RESULT( LeaderboardFindResult_t )
virtual SteamAPICall_t FindLeaderboard( const char *pchLeaderboardName ) = 0;
// returns the name of a leaderboard
virtual const char *GetLeaderboardName( SteamLeaderboard_t hSteamLeaderboard ) = 0;
// returns the total number of entries in a leaderboard, as of the last request
virtual int GetLeaderboardEntryCount( SteamLeaderboard_t hSteamLeaderboard ) = 0;
// returns the sort method of the leaderboard
virtual ELeaderboardSortMethod GetLeaderboardSortMethod( SteamLeaderboard_t hSteamLeaderboard ) = 0;
// returns the display type of the leaderboard
virtual ELeaderboardDisplayType GetLeaderboardDisplayType( SteamLeaderboard_t hSteamLeaderboard ) = 0;
// Asks the Steam back-end for a set of rows in the leaderboard.
// This call is asynchronous, with the result returned in LeaderboardScoresDownloaded_t
// LeaderboardScoresDownloaded_t will contain a handle to pull the results from GetDownloadedLeaderboardEntries() (below)
// You can ask for more entries than exist, and it will return as many as do exist.
// k_ELeaderboardDataRequestGlobal requests rows in the leaderboard from the full table, with nRangeStart & nRangeEnd in the range [1, TotalEntries]
// k_ELeaderboardDataRequestGlobalAroundUser requests rows around the current user, nRangeStart being negate
// e.g. DownloadLeaderboardEntries( hLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -3, 3 ) will return 7 rows, 3 before the user, 3 after
// k_ELeaderboardDataRequestFriends requests all the rows for friends of the current user
STEAM_CALL_RESULT( LeaderboardScoresDownloaded_t )
virtual SteamAPICall_t DownloadLeaderboardEntries( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardDataRequest eLeaderboardDataRequest, int nRangeStart, int nRangeEnd ) = 0;
// as above, but downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers
// if a user doesn't have a leaderboard entry, they won't be included in the result
// a max of 100 users can be downloaded at a time, with only one outstanding call at a time
STEAM_METHOD_DESC(Downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers)
STEAM_CALL_RESULT( LeaderboardScoresDownloaded_t )
virtual SteamAPICall_t DownloadLeaderboardEntriesForUsers( SteamLeaderboard_t hSteamLeaderboard,
STEAM_ARRAY_COUNT_D(cUsers, Array of users to retrieve) CSteamID *prgUsers, int cUsers ) = 0;
// Returns data about a single leaderboard entry
// use a for loop from 0 to LeaderboardScoresDownloaded_t::m_cEntryCount to get all the downloaded entries
// e.g.
// void OnLeaderboardScoresDownloaded( LeaderboardScoresDownloaded_t *pLeaderboardScoresDownloaded )
// {
// for ( int index = 0; index < pLeaderboardScoresDownloaded->m_cEntryCount; index++ )
// {
// LeaderboardEntry_t leaderboardEntry;
// int32 details[3]; // we know this is how many we've stored previously
// GetDownloadedLeaderboardEntry( pLeaderboardScoresDownloaded->m_hSteamLeaderboardEntries, index, &leaderboardEntry, details, 3 );
// assert( leaderboardEntry.m_cDetails == 3 );
// ...
// }
// once you've accessed all the entries, the data will be free'd, and the SteamLeaderboardEntries_t handle will become invalid
virtual bool GetDownloadedLeaderboardEntry( SteamLeaderboardEntries_t hSteamLeaderboardEntries, int index, LeaderboardEntry_t *pLeaderboardEntry, int32 *pDetails, int cDetailsMax ) = 0;
// Uploads a user score to the Steam back-end.
// This call is asynchronous, with the result returned in LeaderboardScoreUploaded_t
// Details are extra game-defined information regarding how the user got that score
// pScoreDetails points to an array of int32's, cScoreDetailsCount is the number of int32's in the list
STEAM_CALL_RESULT( LeaderboardScoreUploaded_t )
virtual SteamAPICall_t UploadLeaderboardScore( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32 *pScoreDetails, int cScoreDetailsCount ) = 0;
// Attaches a piece of user generated content the user's entry on a leaderboard.
// hContent is a handle to a piece of user generated content that was shared using ISteamUserRemoteStorage::FileShare().
// This call is asynchronous, with the result returned in LeaderboardUGCSet_t.
STEAM_CALL_RESULT( LeaderboardUGCSet_t )
virtual SteamAPICall_t AttachLeaderboardUGC( SteamLeaderboard_t hSteamLeaderboard, UGCHandle_t hUGC ) = 0;
// Retrieves the number of players currently playing your game (online + offline)
// This call is asynchronous, with the result returned in NumberOfCurrentPlayers_t
STEAM_CALL_RESULT( NumberOfCurrentPlayers_t )
virtual SteamAPICall_t GetNumberOfCurrentPlayers() = 0;
// Requests that Steam fetch data on the percentage of players who have received each achievement
// for the game globally.
// This call is asynchronous, with the result returned in GlobalAchievementPercentagesReady_t.
STEAM_CALL_RESULT( GlobalAchievementPercentagesReady_t )
virtual SteamAPICall_t RequestGlobalAchievementPercentages() = 0;
// Get the info on the most achieved achievement for the game, returns an iterator index you can use to fetch
// the next most achieved afterwards. Will return -1 if there is no data on achievement
// percentages (ie, you haven't called RequestGlobalAchievementPercentages and waited on the callback).
virtual int GetMostAchievedAchievementInfo( char *pchName, uint32 unNameBufLen, float *pflPercent, bool *pbAchieved ) = 0;
// Get the info on the next most achieved achievement for the game. Call this after GetMostAchievedAchievementInfo or another
// GetNextMostAchievedAchievementInfo call passing the iterator from the previous call. Returns -1 after the last
// achievement has been iterated.
virtual int GetNextMostAchievedAchievementInfo( int iIteratorPrevious, char *pchName, uint32 unNameBufLen, float *pflPercent, bool *pbAchieved ) = 0;
// Returns the percentage of users who have achieved the specified achievement.
virtual bool GetAchievementAchievedPercent( const char *pchName, float *pflPercent ) = 0;
// Requests global stats data, which is available for stats marked as "aggregated".
// This call is asynchronous, with the results returned in GlobalStatsReceived_t.
// nHistoryDays specifies how many days of day-by-day history to retrieve in addition
// to the overall totals. The limit is 60.
STEAM_CALL_RESULT( GlobalStatsReceived_t )
virtual SteamAPICall_t RequestGlobalStats( int nHistoryDays ) = 0;
// Gets the lifetime totals for an aggregated stat
STEAM_FLAT_NAME( GetGlobalStatInt64 )
virtual bool GetGlobalStat( const char *pchStatName, int64 *pData ) = 0;
STEAM_FLAT_NAME( GetGlobalStatDouble )
virtual bool GetGlobalStat( const char *pchStatName, double *pData ) = 0;
// Gets history for an aggregated stat. pData will be filled with daily values, starting with today.
// So when called, pData[0] will be today, pData[1] will be yesterday, and pData[2] will be two days ago,
// etc. cubData is the size in bytes of the pubData buffer. Returns the number of
// elements actually set.
STEAM_FLAT_NAME( GetGlobalStatHistoryInt64 )
virtual int32 GetGlobalStatHistory( const char *pchStatName, STEAM_ARRAY_COUNT(cubData) int64 *pData, uint32 cubData ) = 0;
STEAM_FLAT_NAME( GetGlobalStatHistoryDouble )
virtual int32 GetGlobalStatHistory( const char *pchStatName, STEAM_ARRAY_COUNT(cubData) double *pData, uint32 cubData ) = 0;
// For achievements that have related Progress stats, use this to query what the bounds of that progress are.
// You may want this info to selectively call IndicateAchievementProgress when appropriate milestones of progress
// have been made, to show a progress notification to the user.
STEAM_FLAT_NAME( GetAchievementProgressLimitsInt32 )
virtual bool GetAchievementProgressLimits( const char *pchName, int32 *pnMinProgress, int32 *pnMaxProgress ) = 0;
STEAM_FLAT_NAME( GetAchievementProgressLimitsFloat )
virtual bool GetAchievementProgressLimits( const char *pchName, float *pfMinProgress, float *pfMaxProgress ) = 0;
};
#endif // ISTEAMUSERSTATS012_H

View File

@ -93,6 +93,7 @@
#include "isteammatchmaking007.h"
#include "isteammatchmaking008.h"
#include "isteamuserstats.h"
#include "isteamuserstats012.h"
#include "isteamuserstats011.h"
#include "isteamuserstats010.h"
#include "isteamuserstats009.h"
@ -192,6 +193,8 @@
#include "isteaminventory001.h"
#include "isteaminventory002.h"
#include "isteamtimeline.h"
#include "isteamtimeline003.h"
#include "isteamtimeline002.h"
#include "isteamtimeline001.h"
#include "isteamvideo.h"
#include "isteamvideo001.h"

View File

@ -281,7 +281,9 @@ struct SteamNetworkingIdentity
void SetPSNID( uint64 id );
uint64 GetPSNID() const; // Returns 0 if not PSN
// removed in sdk v1.61
void SetStadiaID( uint64 id );
// removed in sdk v1.61
uint64 GetStadiaID() const; // Returns 0 if not Stadia
void SetIPAddr( const SteamNetworkingIPAddr &addr ); // Set to specified IP:port
@ -1387,6 +1389,17 @@ enum ESteamNetworkingConfigValue
/// generic platform UI. (Only available on Steam.)
k_ESteamNetworkingConfig_EnableDiagnosticsUI = 46,
/// [connection int32] Send of time-since-previous-packet values in each UDP packet.
/// This add a small amount of packet overhead but allows for detailed jitter measurements
/// to be made by the receiver.
///
/// - 0: disables the sending
/// - 1: enables sending
/// - -1: (the default) Use the default for the connection type. For plain UDP connections,
/// this is disabled, and for relayed connections, it is enabled. Note that relays
/// always send the value.
k_ESteamNetworkingConfig_SendTimeSincePreviousPacket = 59,
//
// Simulating network conditions
//
@ -1404,15 +1417,53 @@ enum ESteamNetworkingConfigValue
k_ESteamNetworkingConfig_FakePacketLag_Send = 4,
k_ESteamNetworkingConfig_FakePacketLag_Recv = 5,
/// [global float] 0-100 Percentage of packets we will add additional delay
/// to (causing them to be reordered)
/// Simulated jitter/clumping.
///
/// For each packet, a jitter value is determined (which may
/// be zero). This amount is added as extra delay to the
/// packet. When a subsequent packet is queued, it receives its
/// own random jitter amount from the current time. if this would
/// result in the packets being delivered out of order, the later
/// packet queue time is adjusted to happen after the first packet.
/// Thus simulating jitter by itself will not reorder packets, but it
/// can "clump" them.
///
/// - Avg: A random jitter time is generated using an exponential
/// distribution using this value as the mean (ms). The default
/// is zero, which disables random jitter.
/// - Max: Limit the random jitter time to this value (ms).
/// - Pct: odds (0-100) that a random jitter value for the packet
/// will be generated. Otherwise, a jitter value of zero
/// is used, and the packet will only be delayed by the jitter
/// system if necessary to retain order, due to the jitter of a
/// previous packet.
///
/// All values are [global float]
///
/// Fake jitter is simulated after fake lag, but before reordering.
k_ESteamNetworkingConfig_FakePacketJitter_Send_Avg = 53,
k_ESteamNetworkingConfig_FakePacketJitter_Send_Max = 54,
k_ESteamNetworkingConfig_FakePacketJitter_Send_Pct = 55,
k_ESteamNetworkingConfig_FakePacketJitter_Recv_Avg = 56,
k_ESteamNetworkingConfig_FakePacketJitter_Recv_Max = 57,
k_ESteamNetworkingConfig_FakePacketJitter_Recv_Pct = 58,
/// [global float] 0-100 Percentage of packets we will add additional
/// delay to. If other packet(s) are sent/received within this delay
/// window (that doesn't also randomly receive the same extra delay),
/// then the packets become reordered.
///
/// This mechanism is primarily intended to generate out-of-order
/// packets. To simulate random jitter, use the FakePacketJitter.
/// Fake packet reordering is applied after fake lag and jitter
k_ESteamNetworkingConfig_FakePacketReorder_Send = 6,
k_ESteamNetworkingConfig_FakePacketReorder_Recv = 7,
/// [global int32] Extra delay, in ms, to apply to reordered packets.
/// [global int32] Extra delay, in ms, to apply to reordered
/// packets. The same time value is used for sending and receiving.
k_ESteamNetworkingConfig_FakePacketReorder_Time = 8,
/// [global float 0--100] Globally duplicate some percentage of packets we send
/// [global float 0--100] Globally duplicate some percentage of packets.
k_ESteamNetworkingConfig_FakePacketDup_Send = 26,
k_ESteamNetworkingConfig_FakePacketDup_Recv = 27,