Made the Steam class in STK and SSM more compatible. Improve error

handling, and cache frequently used values (e.g. name and id).
This commit is contained in:
hiker 2017-04-23 00:32:47 +10:00
parent eb4989c2ef
commit de795b766a
3 changed files with 99 additions and 41 deletions

View File

@ -1518,11 +1518,10 @@ int main(int argc, char *argv[] )
Steam *steam = new Steam(); Steam *steam = new Steam();
bool steam_avail = steam->isSteamAvailable(); bool steam_avail = steam->isSteamAvailable();
std::string id = steam->getId(); std::string id = steam->getSteamID();
std::string name = steam->getName(); std::string name = steam->getUserName();
int n = steam->saveAvatarAs("test.png"); int n = steam->saveAvatarAs("test.png");
//std::vector<std::string> friends = steam->getFriends(); delete steam;
//std::string quit = steam->sendCommand("quit");
CrashReporting::installHandlers(); CrashReporting::installHandlers();

View File

@ -26,6 +26,7 @@
Steam::Steam() Steam::Steam()
{ {
m_steam_available = false;
#ifdef WIN32 #ifdef WIN32
// Based on: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx // Based on: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
SECURITY_ATTRIBUTES sec_attr; SECURITY_ATTRIBUTES sec_attr;
@ -40,47 +41,85 @@ Steam::Steam()
if (!CreatePipe(&m_child_stdout_read, &m_child_stdout_write, &sec_attr, 0)) if (!CreatePipe(&m_child_stdout_read, &m_child_stdout_write, &sec_attr, 0))
{ {
Log::error("Steam", "Error creating StdoutRd CreatePipe"); Log::error("Steam", "Error creating StdoutRd CreatePipe");
return;
} }
// Ensure the read handle to the pipe for STDOUT is not inherited. // Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(m_child_stdout_read, HANDLE_FLAG_INHERIT, 0)) if (!SetHandleInformation(m_child_stdout_read, HANDLE_FLAG_INHERIT, 0))
{ {
Log::error("Steam", "Stdout SetHandleInformation"); Log::error("Steam", "Stdout SetHandleInformation");
return;
} }
// Create a pipe for the child process's STDIN. // Create a pipe for the child process's STDIN.
if (!CreatePipe(&m_child_stdin_read, &m_child_stdin_write, &sec_attr, 0)) if (!CreatePipe(&m_child_stdin_read, &m_child_stdin_write, &sec_attr, 0))
{ {
Log::error("Steam", "Stdin CreatePipe"); Log::error("Steam", "Stdin CreatePipe");
return;
} }
// Ensure the write handle to the pipe for STDIN is not inherited. // Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(m_child_stdin_write, HANDLE_FLAG_INHERIT, 0)) if (!SetHandleInformation(m_child_stdin_write, HANDLE_FLAG_INHERIT, 0))
{ {
Log::error("Steam", "Stdin SetHandleInformation"); Log::error("Steam", "Stdin SetHandleInformation");
return;
} }
// Create the child process. // Create the child process.
createChildProcess(); if (!createChildProcess())
{
Log::error("Steam", "Could not start ssm.exe");
return;
}
#endif #endif
std::string s = sendCommand("init");
if (s != "1")
{
Log::error("Steam", "Could not initialise Steam API.");
return;
}
s = sendCommand("name");
m_user_name = decodeString(s);
if (m_user_name == "")
{
Log::error("Steam", "Can not get Steam user name.");
return;
}
s = sendCommand("id");
m_steam_id = decodeString(s);
if (m_steam_id== "")
{
Log::error("Steam", "Can not get Steam id.");
return;
}
m_steam_available = true;
} // Steam } // Steam
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Terminates the child processes and shuts down the Steam API.
*/
Steam::~Steam() Steam::~Steam()
{ {
std::string s = sendCommand("quit");
if (s != "quit")
{
Log::error("Steam", "Could not shutdown Steam process properly");
}
} // ~Steam } // ~Steam
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
int Steam::createChildProcess() /** Starts ssm.exe as a child process and sets up communication via pipes.
* \return True if the child process creation was successful.
*/
bool Steam::createChildProcess()
{ {
#ifdef WIN32 #ifdef WIN32
TCHAR command_line[] = TEXT("ssm.exe 1"); TCHAR command_line[] = TEXT("ssm.exe 1");
PROCESS_INFORMATION piProcInfo; PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo; STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure. // Set up members of the PROCESS_INFORMATION structure.
@ -98,7 +137,7 @@ int Steam::createChildProcess()
// Create the child process. // Create the child process.
bSuccess = CreateProcess(NULL, bool success = CreateProcess(NULL,
command_line, // command line command_line, // command line
NULL, // process security attributes NULL, // process security attributes
NULL, // primary thread security attributes NULL, // primary thread security attributes
@ -107,32 +146,31 @@ int Steam::createChildProcess()
NULL, // use parent's environment NULL, // use parent's environment
NULL, // use parent's current directory NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer &siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION &piProcInfo) != 0; // receives PROCESS_INFORMATION
// If an error occurs, exit the application. if (!success)
if (!bSuccess)
{ {
Log::error("Steam", "CreateProcess"); return false;
} }
else
{
// Close handles to the child process and its primary thread. // Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status // Some applications might keep these handles to monitor the status
// of the child process, for example. // of the child process, for example.
CloseHandle(piProcInfo.hProcess); CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread); CloseHandle(piProcInfo.hThread);
}
#endif #endif
return 0; return true;
} // createChildProcess } // createChildProcess
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Reads a command from the input pipe.
*/
std::string Steam::getLine() std::string Steam::getLine()
{ {
#define BUFSIZE 1024 #define BUFSIZE 1024
CHAR buffer[BUFSIZE]; char buffer[BUFSIZE];
DWORD bytes_read; DWORD bytes_read;
// Read from pipe that is the standard output for child process. // Read from pipe that is the standard output for child process.
bool success = ReadFile(m_child_stdout_read, buffer, BUFSIZE-1, bool success = ReadFile(m_child_stdout_read, buffer, BUFSIZE-1,
@ -147,6 +185,9 @@ std::string Steam::getLine()
} // getLine } // getLine
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Sends a command to the SSM via a pipe, and reads the answer.
* \return Answer from SSM.
*/
std::string Steam::sendCommand(const std::string &command) std::string Steam::sendCommand(const std::string &command)
{ {
#ifdef WIN32 #ifdef WIN32
@ -163,6 +204,12 @@ std::string Steam::sendCommand(const std::string &command)
} // sendCommand } // sendCommand
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** All answer strings from 'SSM' are in the form: "length string", i.e. the
* length of the string, followed by a space and then the actual strings.
* This allows for checking on some potential problems (e.g. if a pipe should
* only send part of the answer string - todo: handle this problem instead of
* ignoring it.
*/
std::string Steam::decodeString(const std::string &s) std::string Steam::decodeString(const std::string &s)
{ {
std::vector<std::string> l = StringUtils::split(s, ' '); std::vector<std::string> l = StringUtils::split(s, ' ');
@ -180,21 +227,21 @@ std::string Steam::decodeString(const std::string &s)
/** Returns the steam user name. SSM returns 'N name" where N is /** Returns the steam user name. SSM returns 'N name" where N is
* the length of the name. * the length of the name.
*/ */
std::string Steam::getName() const std::string& Steam::getUserName()
{ {
std::string s = sendCommand("name"); assert(m_steam_available);
return decodeString(s); return m_user_name;
} // getName } // getUserName
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Returns a unique id (string) from steam. SSM returns 'N ID" where N is /** Returns a unique id (string) from steam. SSM returns 'N ID" where N is
* the length of the ID. * the length of the ID.
*/ */
std::string Steam::getId() const std::string& Steam::getSteamID()
{ {
std::string s = sendCommand("id"); assert(m_steam_available);
return decodeString(s); return m_steam_id;
} // getId } // getSteamID
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Returns a std::vector with the names of all friends. SSM returns a first /** Returns a std::vector with the names of all friends. SSM returns a first
@ -214,6 +261,10 @@ std::vector<std::string> Steam::getFriends()
return result; return result;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Instructs the SSM to save the avatar of the user with the specified
* filename. Note that the avatar is always saved in png format (independent
* on what is specified as filename).
*/
int Steam::saveAvatarAs(const std::string filename) int Steam::saveAvatarAs(const std::string filename)
{ {
//std::string s = sendCommand(std::string("avatar ")+filename); //std::string s = sendCommand(std::string("avatar ")+filename);

View File

@ -34,6 +34,15 @@
class Steam class Steam
{ {
private: private:
/** True if a connection to steam was made successfully. */
bool m_steam_available;
/** Steam user name. Only defined if m_steam_available. */
std::string m_user_name;
/** Unique steam id. */
std::string m_steam_id;
#ifdef WIN32 #ifdef WIN32
// Various handles for the window pipes // Various handles for the window pipes
HANDLE m_child_stdin_read; HANDLE m_child_stdin_read;
@ -41,24 +50,23 @@ private:
HANDLE m_child_stdout_read; HANDLE m_child_stdout_read;
HANDLE m_child_stdout_write; HANDLE m_child_stdout_write;
int createChildProcess(); bool createChildProcess();
#endif #endif
bool m_steam_available;
std::string decodeString(const std::string &s); std::string decodeString(const std::string &s);
std::string sendCommand(const std::string &command); std::string sendCommand(const std::string &command);
std::string getLine(); std::string getLine();
public: public:
Steam(); Steam();
~Steam(); ~Steam();
std::string getName(); const std::string& getUserName();
std::string getId(); const std::string& getSteamID();
int saveAvatarAs(const std::string filename); int saveAvatarAs(const std::string filename);
std::vector<std::string> getFriends(); std::vector<std::string> getFriends();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns true if the connection to the Steam API was successful, i.e. /** Returns true if the connection to the Steam API was successful, i.e.
* connection to steam worked, and SteamWorks API could be initialised. */ * connection to steam worked, and SteamWorks API could be initialised. */
bool isSteamAvailable() { return m_steam_available; } bool isSteamAvailable() const { return m_steam_available; }
}; // class Steam }; // class Steam
#endif // HEADER_STEAM_HPP #endif // HEADER_STEAM_HPP