basic impl for steam timeline

This commit is contained in:
otavepto 2024-07-01 11:52:32 +03:00
parent ddda06d248
commit f5ae22a1e6
5 changed files with 222 additions and 7 deletions

View File

@ -55,6 +55,7 @@
#include "steam_gameserver.h"
#include "steam_gameserverstats.h"
#include "steam_gamestats.h"
#include "steam_timeline.h"
#include "steam_masterserver_updater.h"
#include "overlay/steam_overlay.h"
@ -138,6 +139,7 @@ public:
Steam_RemotePlay *steam_remoteplay{};
Steam_TV *steam_tv{};
Steam_GameStats *steam_gamestats{};
Steam_Timeline *steam_timeline{};
Steam_GameServer *steam_gameserver{};
Steam_Utils *steam_gameserver_utils{};
@ -236,6 +238,9 @@ public:
// game stats
ISteamGameStats *GetISteamGameStats( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// game timeline
ISteamTimeline *GetISteamTimeline( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// Deprecated. Applications should use SteamAPI_RunCallbacks() or SteamGameServer_RunCallbacks() instead.
STEAM_PRIVATE_API( void RunFrame() );

View File

@ -23,13 +23,57 @@
class Steam_Timeline :
public ISteamTimeline
{
private:
struct TimelineEvent_t
{
// 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();
// 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
std::string pchIcon{};
// Title-provided localized string in the language returned by SteamUtils()->GetSteamUILanguage().
std::string pchTitle{};
// Title-provided localized string in the language returned by SteamUtils()->GetSteamUILanguage().
std::string pchDescription{};
// Provide the priority to use when the UI is deciding which icons to display in crowded parts of the timeline. Events with larger priority values will be displayed more prominently than events with smaller priority values. This value must be between 0 and k_unMaxTimelinePriority.
uint32 unPriority{};
// One use of this parameter is to handle events whose significance is not clear until after the fact. For instance if the player starts a damage over time effect on another player, which kills them 3.5 seconds later, the game could pass -3.5 as the start offset and cause the event to appear in the timeline where the effect started.
float flStartOffsetSeconds{};
// The duration of the event, in seconds. Pass 0 for instantaneous events.
float flDurationSeconds{};
// Allows the game to describe events that should be suggested to the user as possible video clips.
ETimelineEventClipPriority ePossibleClip{};
};
struct TimelineState_t
{
// 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::string description{}; // A localized string in the language returned by SteamUtils()->GetSteamUILanguage()
ETimelineGameMode bar_color{}; // the color of the timeline bar
};
class Settings *settings{};
class Networking *network{};
class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb{};
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
FSteamNetworkingSocketsDebugOutput debug_function{};
std::vector<TimelineEvent_t> timeline_events{};
std::vector<TimelineState_t> timeline_states{TimelineState_t{}}; // it seems to always start with a default event
// unconditional periodic callback
void RunCallbacks();
// network callback, triggered once we have a network message
void Callback(Common_Message *msg);
static void steam_callback(void *object, Common_Message *msg);
static void steam_run_every_runcb(void *object);
@ -46,10 +90,6 @@ public:
void SetTimelineGameMode( ETimelineGameMode eMode );
void RunCallbacks();
void Callback(Common_Message *msg);
};
#endif // __INCLUDED_STEAM_TIMELINE_H__
#endif // __INCLUDED_STEAM_TIMELINE_H__

View File

@ -123,6 +123,7 @@ Steam_Client::Steam_Client()
steam_remoteplay = new Steam_RemotePlay(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_tv = new Steam_TV(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_gamestats = new Steam_GameStats(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_timeline = new Steam_Timeline(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
// server
PRINT_DEBUG("init gameserver");
@ -202,6 +203,7 @@ Steam_Client::~Steam_Client()
DEL_INST(steam_remoteplay);
DEL_INST(steam_tv);
DEL_INST(steam_gamestats);
DEL_INST(steam_timeline);
DEL_INST(steam_utils);
DEL_INST(steam_friends);

View File

@ -18,6 +18,19 @@
#include "dll/steam_client.h"
// retrieves the ISteamTimeline interface associated with the handle
ISteamTimeline *Steam_Client::GetISteamTimeline( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("%s", pchVersion);
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return nullptr;
if (strcmp(pchVersion, STEAMTIMELINE_INTERFACE_VERSION) == 0) {
return reinterpret_cast<ISteamTimeline *>(static_cast<ISteamTimeline *>(steam_timeline));
}
report_missing_impl_and_exit(pchVersion, EMU_FUNC_NAME);
}
// retrieves the ISteamGameStats interface associated with the handle
ISteamGameStats *Steam_Client::GetISteamGameStats( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{

155
dll/steam_timeline.cpp Normal file
View File

@ -0,0 +1,155 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "dll/steam_timeline.h"
// https://partner.steamgames.com/doc/api/ISteamTimeline
void Steam_Timeline::steam_callback(void *object, Common_Message *msg)
{
// PRINT_DEBUG_ENTRY();
auto instance = (Steam_Timeline *)object;
instance->Callback(msg);
}
void Steam_Timeline::steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
auto instance = (Steam_Timeline *)object;
instance->RunCallbacks();
}
Steam_Timeline::Steam_Timeline(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->callback_results = callback_results;
this->callbacks = callbacks;
this->run_every_runcb = run_every_runcb;
// this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Timeline::steam_callback, this);
this->run_every_runcb->add(&Steam_Timeline::steam_run_every_runcb, this);
}
Steam_Timeline::~Steam_Timeline()
{
// this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Timeline::steam_callback, this);
this->run_every_runcb->remove(&Steam_Timeline::steam_run_every_runcb, this);
}
void Steam_Timeline::SetTimelineStateDescription( const char *pchDescription, float flTimeDelta )
{
PRINT_DEBUG("'%s' %f", pchDescription, flTimeDelta);
std::lock_guard lock(global_mutex);
const auto target_timepoint = std::chrono::system_clock::now() + std::chrono::milliseconds(static_cast<long>(flTimeDelta * 1000));
// reverse iterators to search from end
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;
});
if (timeline_states.rend() != event_it) {
PRINT_DEBUG("setting timeline state description");
if (pchDescription) {
event_it->description = pchDescription;
} else {
event_it->description.clear();
}
}
}
void Steam_Timeline::ClearTimelineStateDescription( float flTimeDelta )
{
PRINT_DEBUG("%f", flTimeDelta);
std::lock_guard lock(global_mutex);
const auto target_timepoint = std::chrono::system_clock::now() + std::chrono::milliseconds(static_cast<long>(flTimeDelta * 1000));
// reverse iterators to search from end
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;
});
if (timeline_states.rend() != event_it) {
PRINT_DEBUG("clearing timeline state description");
event_it->description.clear();
}
}
void Steam_Timeline::AddTimelineEvent( const char *pchIcon, const char *pchTitle, const char *pchDescription, uint32 unPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip )
{
PRINT_DEBUG("'%s' | '%s' - '%s', %u, [%f, %f) %i", pchIcon, pchTitle, pchDescription, unPriority, flStartOffsetSeconds, flDurationSeconds, (int)ePossibleClip);
std::lock_guard lock(global_mutex);
auto &new_event = timeline_events.emplace_back(TimelineEvent_t{});
new_event.pchIcon = pchIcon;
new_event.pchTitle = pchTitle;
new_event.pchDescription = pchDescription;
new_event.unPriority = unPriority;
new_event.flStartOffsetSeconds = flStartOffsetSeconds;
// for instantanious event with flDurationSeconds=0 steam creates 8 sec clip
if (static_cast<long>(flDurationSeconds * 1000) <= 100) { // <= 100ms
flDurationSeconds = 8;
}
new_event.flDurationSeconds = flDurationSeconds;
new_event.ePossibleClip = ePossibleClip;
}
void Steam_Timeline::SetTimelineGameMode( ETimelineGameMode eMode )
{
PRINT_DEBUG("%i", (int)eMode);
std::lock_guard lock(global_mutex);
if (timeline_states.empty()) return;
timeline_states.back().bar_color = eMode;
}
void Steam_Timeline::RunCallbacks()
{
}
void Steam_Timeline::Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::CONNECT) {
}
if (msg->low_level().type() == Low_Level::DISCONNECT) {
}
}
}