//============================================================================= // SteamStatsAndAchievementsBase //============================================================================= // Interface between Steam Stats/Achievements and RO/Mods //============================================================================= // Red Orchestra Source // Copyright (C) 2003-2009 Tripwire Interactive LLC // Created by Dayle Flowers //============================================================================= class SteamStatsAndAchievementsBase extends Actor native; struct native export SteamStatInt { var const int Value; // Value of this Stat(can be set by calling SetStatInt) }; struct native export SteamStatFloat { var const float Value; // Value of this Stat(can be set by calling SetStatFloat) }; // Steam updates an Averaging stat by adding SessionNumerator to the existing Numerator in the Steam Database and SessionDenominator // to the existing Denominator in the Steam Database(which only exists on the client). These stats are only able to be grabbed as // floats from Steam, but when the client sends the new Session Values Averages to Steam, the Steam Database does a full Numerator // over Denominator calculation with the new Session Data. In short, this is not a reliable system for checking Achievements on the // Server in real-time, as you only get the resulting Float *after* the client sends the data to Steam and the Steam Database has // time to recalculate the new average. The latest Float Value is only valid in OnStatsAndAchievementsReady and should not be // considered reliable outside of that function. struct native export SteamStatAverage { var const float NumeratorValue; // Numerator Value of this Stat during this Session var const float DenominatorValue; // Denominator Value of this Stat during this Session }; // Set to true once we've pulled Stats from the Database(prevents Steam screwing up and erasing Stats in Single Player) var bool bInitialized; // Owner of these Stats and Achievements var PlayerController PCOwner; // Set to true by the Timer function to allow us to completely control replication var bool bFlushStatsToClient; // Number of seconds between sending Stats to client var float FlushStatsToClientDelay; // Steam Names var array SteamNameStat; var array SteamNameAchievement; // Used to track if God Mode, weapon cheats, summoning, and others have ever been used var bool bUsedCheats; // Debug var globalconfig bool bResetStats; var globalconfig bool bDebugStats; // Delegate callback for Stats and Achievements data initialization completion delegate OnDataInitialized(); // Gets the Server ready to handle Stats native final function bool Initialize(PlayerController MyOwner); // Requests the Owner's Stats and Achievements from Steam(calls OnStatsAndAchievementsReady when completed) native final function GetStatsAndAchievements(); // Functions to grab individual Stats and Achievements from Steam by name(generally done from the OnStatsAndAchievementsReady callback) native final function GetStatInt(SteamStatInt Stat, string SteamName); native final function GetStatFloat(SteamStatFloat Stat, string SteamName); native final function float GetStatAverage(string SteamName); native final function bool GetAchievementCompleted(string SteamName); // Used to natively Initialize the const Stat Variables(Server-side only) native final function InitStatInt(SteamStatInt Stat, int NewValue); native final function InitStatFloat(SteamStatFloat Stat, float NewValue); // Used to natively Set the const Stat Variables(Server-side only) native final function SetStatInt(SteamStatInt Stat, int NewValue); native final function SetStatFloat(SteamStatFloat Stat, float NewValue); native final function SetStatAverage(SteamStatAverage Stat, float NewNumerator, float NewDenominator); // Called to set bNetDirty which forces replication to the owning client at a preset interval native final function SetNetDirty(); // Send the specified Int/Float/Stat to the Local Steam Client, not the Steam Database(Client-Side only) // NOTE: For Average stats, we send Stat.Numerator - SavedStat.Numerator and Stat.Denominator - SavedStat.Denominator, // because Steam is keeping a running total behind the scenes. native final function FlushStatToSteamInt(SteamStatInt Stat, string SteamName); native final function FlushStatToSteamFloat(SteamStatFloat Stat, string SteamName); native final function FlushStatToSteamAverage(SteamStatAverage Stat, SteamStatAverage SavedStat, string SteamName); native final function SetAchievementCompleted(string SteamName); // Call after all Stats have been changed on the Client to send changed stats from the local Steam Client to the Steam Database native final function FlushStatsToSteamDatabase(); // Returns the displayable information set in the SteamWork's Database native final function GetAchievementDescription(string SteamName, out string DisplayName, out string Description); native simulated final function bool DoPotato(); // Returns an integer representation of Owned Weapon DLC Packs native final function int GetOwnedWeaponDLC(); // Determines whether or not this player owns a particular Weapon DLC AppID simulated function bool PlayerOwnsWeaponDLC(int AppID); native final function bool OwnsWeaponDLC(int AppID, SteamStatInt OwnedWeaponDLCStat); // Opens the Steam Store page for the specified AppID native final function PurchaseWeaponDLC(int AppID); // Fetches the name of the specified Weapons DLC Pack function string GetWeaponDLCPackName(int AppID); // Opens the Steam Overlay directly to the Game's Steam Workshop page native final function ShowWorkshopContent(); // Fetches a user ID for the owning PC native final function string GetSteamUserID(); cpptext { #include "SteamStatsAndAchievementsBase.h" } replication { reliable if ( Role == ROLE_Authority ) bUsedCheats; } // Called via Client to Server function in PlayerController to start the FlushStatsToClient timing function ServerSteamStatsAndAchievementsInitialized() { if ( bDebugStats ) log("STEAMSTATS: ServerSteamStatsAndAchievementsInitialized called"); bInitialized = true; SetTimer(FlushStatsToClientDelay, false); } event PostBeginPlay() { PlayerDied(); } // Overridden to Grab the Stats and Achievements from Steam on the Client simulated event PostNetBeginPlay() { if ( Level.NetMode == NM_Client ) { if ( bDebugStats ) log("STEAMSTATS: PostNetBeginPlay grabbing Stats - StatsObject="$self); GetStatsAndAchievements(); PCOwner = Level.GetLocalPlayerController(); } } // Overridden in subclasses to send the Stats/Achievements that have changed to Steam //simulated event PostNetReceive(); // Called at set interval to push all Stats to Client, then called again to reset // NETWORK: Server only event Timer() { // We use bFlushStatsToClient to control when things are sent if ( !bFlushStatsToClient ) { FlushStatsToClient(); } // Soon after FlushStatsToClientw is called above, Timer gets called again to clear bFlushStatsToClient else { bFlushStatsToClient = false; SetTimer(FlushStatsToClientDelay, false); } } // Called on Server(via Timer) to push all Stats to Client // NETWORK: Server only function FlushStatsToClient() { // if it's a local player, just send stuff off to Steam from here if ( Level.NetMode == NM_Standalone || (Level.NetMode == NM_ListenServer && PCOwner == Level.GetLocalPlayerController()) ) { PostNetReceive(); SetTimer(FlushStatsToClientDelay, false); } else { if ( bDebugStats ) log("STEAMSTATS: Flushing Stats to Client"); // Force Stats to be sent to Client immediately NetUpdateTime = Level.TimeSeconds - 1.0; bFlushStatsToClient = true; SetNetDirty(); // Clear bFlushStatsToClient once completed SetTimer(0.1, false); } } // Event Callback for GetStatsAndAchievements call(Call PCOwner.ServerSteamStatsAndAchievementsInitialized when done) // NETWORK: Client only simulated event OnStatsAndAchievementsReady() { if ( PCOwner != none && PCOwner.PlayerReplicationInfo != none ) { PCOwner.PlayerReplicationInfo.SteamStatsAndAchievements = self; } bInitialized = true; PCOwner.ServerSteamStatsAndAchievementsInitialized(); OnDataInitialized(); } // Called on Server to initialize Stats from Client replication, because Servers can't access Steam Stats directly function InitializeSteamStatInt(int Index, int Value); // Called on Server to initialize Stats from Client replication, because Servers can't access Steam Stats directly function InitializeSteamStatFloat(int Index, float Value); // Sets the specified Steam Achievement as completed; also, flushes all Stats and Achievements to the client function SetSteamAchievementCompleted(int Index) { if( PCOwner.PlayerReplicationInfo.bOnlySpectator ) { return; } if ( bDebugStats ) log("STEAMSTATS: SetSteamAchievementCompleted called - Name="$SteamNameAchievement[Index] @ "Player="$PCOwner.PlayerReplicationInfo.PlayerName); FlushStatsToClient(); SetLocalAchievementCompleted(Index); SetAchievementCompleted(SteamNameAchievement[Index]); } // Called from multiple locations on Client and Server to set Achievement booleans for later use simulated event SetLocalAchievementCompleted(int Index); // Called when the owner of this Stats Actor dies(used in subclasses to reset "in one life" stats) function PlayerDied(); native function SteamAPICall(string callString); function CheckEvents( Name EventName ); defaultproperties { FlushStatsToClientDelay=10.0 RemoteRole=ROLE_SimulatedProxy bAlwaysRelevant=false bOnlyRelevantToOwner=true bOnlyDirtyReplication=true bSkipActorPropertyReplication=true bNetNotify=true NetUpdateFrequency=5 bStatic=false bNoDelete=false bHidden=true }