mirror of
https://github.com/zebrajr/SamRewritten.git
synced 2025-12-06 00:19:47 +01:00
Implement Socket Server Game Logic Backend
Convert CameEmulator child side to GameServer Convert gameEmulator parent side into MySteam somewhat Define basic protocol for client-server communication. This protocol is added as actions in. types/Actions.h. These still need to be finalized and implemented in yajl. Add back method of retrieving cached owned games and get_user_steamId3 for now because the new way is far too slow. Improve control flow over the GameEmulator implementation. Less inherent and more explicit behavior. Simplify buffer logic to just use NULL terminators. Then JSON requests can be sent as plaintext json strings. Add some NULL checks.
This commit is contained in:
parent
b8acaa6308
commit
769c645d96
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
|
|
@ -48,7 +48,11 @@
|
|||
"type_traits": "cpp",
|
||||
"tuple": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"utility": "cpp"
|
||||
"utility": "cpp",
|
||||
"bit": "cpp",
|
||||
"map": "cpp",
|
||||
"string": "cpp",
|
||||
"algorithm": "cpp"
|
||||
},
|
||||
"C_Cpp.intelliSenseEngine": "Tag Parser"
|
||||
}
|
||||
|
|
@ -1,10 +1,18 @@
|
|||
#include "GameServerManager.h"
|
||||
|
||||
#include "MyGameServer.h"
|
||||
|
||||
// TODO: shouldn't really need MySteam inside here
|
||||
#include "MySteam.h"
|
||||
#include "globals.h"
|
||||
#include <signal.h>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
void handle_sigint_gameserv(int signum) {
|
||||
g_steam->quit_game();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
MyClientSocket*
|
||||
GameServerManager::quick_server_create(AppId_t appid)
|
||||
{
|
||||
|
|
@ -16,7 +24,6 @@ GameServerManager::quick_server_create(AppId_t appid)
|
|||
server.run();
|
||||
|
||||
// The server will stop running when the client sends a quit request.
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else if (pid == -1) {
|
||||
|
|
@ -26,7 +33,9 @@ GameServerManager::quick_server_create(AppId_t appid)
|
|||
else {
|
||||
// Parent process
|
||||
// TODO watch out for zombie processes
|
||||
// TODO: don't use signal; use sigaction
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
signal(SIGINT, handle_sigint_gameserv);
|
||||
return new MyClientSocket(appid);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,12 @@
|
|||
#pragma once
|
||||
#include "sockets/MyClientSocket.h"
|
||||
|
||||
// This class is more the old GameEmulator class
|
||||
// It handles
|
||||
// - Creation of GameServer which acts like a Steam game
|
||||
// - Creation of a client socket which the main window uses to
|
||||
// communicate with that server
|
||||
|
||||
class GameServerManager
|
||||
{
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "MyGameServer.h"
|
||||
#include <iostream>
|
||||
|
||||
MyGameServer::MyGameServer(AppId_t appid) : m_appid(appid), m_socket(appid)
|
||||
{
|
||||
|
|
@ -9,6 +10,13 @@ void
|
|||
MyGameServer::run()
|
||||
{
|
||||
setenv("SteamAppId", std::to_string(m_appid).c_str(), 1);
|
||||
SteamAPI_Init();
|
||||
|
||||
if (!SteamAPI_Init()) {
|
||||
std::cerr << "An error occurred launching the Steam API. Aborting." << std::endl;
|
||||
std::cerr << "Make sure you are trying to run an app you own, and running with lauch.sh" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
m_socket.run_server();
|
||||
SteamAPI_Shutdown();
|
||||
}
|
||||
120
src/MySteam.cpp
120
src/MySteam.cpp
|
|
@ -4,6 +4,7 @@
|
|||
#include <algorithm>
|
||||
#include <dirent.h>
|
||||
#include "types/Game.h"
|
||||
#include "types/Actions.h"
|
||||
#include "SteamAppDAO.h"
|
||||
#include "GameEmulator.h"
|
||||
#include "common/functions.h"
|
||||
|
|
@ -47,14 +48,18 @@ MySteam::launch_game(AppId_t appID) {
|
|||
// //TODO if
|
||||
// emulator->init_app(appID);
|
||||
|
||||
if (m_ipc_socket != nullptr)
|
||||
{
|
||||
if (m_ipc_socket != nullptr) {
|
||||
std::cerr << "I will not launch the game as one is already running" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ipc_socket = m_server_manager.quick_server_create(appID);
|
||||
|
||||
if (m_ipc_socket == nullptr) {
|
||||
std::cerr << "Failed to get connection to game" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// => launch_game
|
||||
|
|
@ -67,10 +72,15 @@ bool
|
|||
MySteam::quit_game() {
|
||||
// GameEmulator* emulator = GameEmulator::get_instance();
|
||||
// return emulator->kill_running_app();
|
||||
m_ipc_socket->kill_server();
|
||||
delete m_ipc_socket;
|
||||
m_ipc_socket = nullptr;
|
||||
return true;
|
||||
|
||||
if (m_ipc_socket != nullptr) {
|
||||
m_ipc_socket->kill_server();
|
||||
delete m_ipc_socket;
|
||||
m_ipc_socket = nullptr;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// => quit_game
|
||||
|
||||
|
|
@ -83,6 +93,41 @@ MySteam::quit_game() {
|
|||
*/
|
||||
void
|
||||
MySteam::refresh_owned_apps() {
|
||||
//TODO at bottom of function
|
||||
|
||||
const std::string path_to_cache_dir(MySteam::get_steam_install_path() + "/appcache/stats/");
|
||||
DIR* dirp = opendir(path_to_cache_dir.c_str());
|
||||
struct dirent * dp;
|
||||
std::string filename;
|
||||
const std::string prefix("UserGameStats_" + MySteam::get_user_steamId3() + "_");
|
||||
const std::string input_scheme_c(prefix + "%lu.bin");
|
||||
Game_t game;
|
||||
unsigned long app_id;
|
||||
SteamAppDAO* appDAO = SteamAppDAO::get_instance();
|
||||
|
||||
// The whole update will really occur only once in a while, no worries
|
||||
appDAO->update_name_database();
|
||||
m_all_subscribed_apps.clear();
|
||||
|
||||
while ((dp = readdir(dirp)) != NULL) {
|
||||
filename = dp->d_name;
|
||||
if(filename.rfind(prefix, 0) == 0) {
|
||||
if(sscanf(dp->d_name, input_scheme_c.c_str(), &app_id) == 1) {
|
||||
game.app_id = app_id;
|
||||
game.app_name = appDAO->get_app_name(app_id);
|
||||
|
||||
m_all_subscribed_apps.push_back(game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(m_all_subscribed_apps.begin(), m_all_subscribed_apps.end(), comp_app_name);
|
||||
closedir(dirp);
|
||||
|
||||
/*
|
||||
// TODO: Scanning through all apps using app_is_owned is far too slow
|
||||
// It takes minutes for SAM to start up if this code is enabled
|
||||
// Implement something to speed this code up before re-enabling it
|
||||
Game_t game;
|
||||
SteamAppDAO* appDAO = SteamAppDAO::get_instance();
|
||||
|
||||
|
|
@ -103,9 +148,52 @@ MySteam::refresh_owned_apps() {
|
|||
}
|
||||
|
||||
std::sort(m_all_subscribed_apps.begin(), m_all_subscribed_apps.end(), comp_app_name);
|
||||
|
||||
*/
|
||||
}
|
||||
// => refresh_owned_apps
|
||||
|
||||
/**
|
||||
* Could parse /home/user/.local/share/Steam/config/loginusers.vdf, but wrong id type
|
||||
* Parses STEAM/logs/parental_log.txt, hoping those logs can't be disabled
|
||||
* Return the most recently logged in user id
|
||||
* Returns empty string on error
|
||||
*/
|
||||
std::string
|
||||
MySteam::get_user_steamId3() {
|
||||
static const std::string file_path(MySteam::get_steam_install_path() + "/logs/parental_log.txt");
|
||||
std::ifstream input(file_path, std::ios::in);
|
||||
std::string word;
|
||||
|
||||
if(!input) {
|
||||
std::cerr << "Could not open " << file_path << std::endl;
|
||||
std::cerr << "Make sure you have a default steam installation, and that logging is not disabled." << std::endl;
|
||||
input.close();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
//We're done setting up and checking, let's parse this file
|
||||
bool next_is_id = false;
|
||||
std::string latest_id = "";
|
||||
|
||||
while(input >> word) {
|
||||
if(word == "ID:") {
|
||||
next_is_id = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(next_is_id) {
|
||||
latest_id = word;
|
||||
next_is_id = false;
|
||||
}
|
||||
}
|
||||
|
||||
input.close();
|
||||
|
||||
return latest_id;
|
||||
}
|
||||
// => get_user_steamId3
|
||||
|
||||
|
||||
/**
|
||||
* Tries to locate the steam folder in multiple locations,
|
||||
|
|
@ -160,6 +248,26 @@ MySteam::refresh_icons() {
|
|||
}
|
||||
// => refresh_icons
|
||||
|
||||
|
||||
std::vector<Achievement_t>
|
||||
MySteam::get_achievements() {
|
||||
|
||||
if (m_ipc_socket == nullptr) {
|
||||
std::cerr << "Connection to game is broken" << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
m_ipc_socket->request_response(GET_ACHIEVEMENTS_STR);
|
||||
|
||||
//TODO: process reponse achievements
|
||||
//This type might not end up being a std::vector<Achievement_t> type,
|
||||
//it may just conform to the JSON types
|
||||
std::vector<Achievement_t> yes;
|
||||
yes.clear();
|
||||
return yes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an achievement to the list of achievements to unlock/lock
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include "types/Game.h"
|
||||
#include "types/Achievement.h"
|
||||
#include "GameServerManager.h"
|
||||
#include "sockets/MyClientSocket.h"
|
||||
#include <string>
|
||||
|
|
@ -18,6 +19,14 @@ public:
|
|||
*/
|
||||
static MySteam* get_instance();
|
||||
|
||||
/**
|
||||
* Returns the steamId3 of the last user who logged in on the
|
||||
* machine. Make sure all logs are enabled, or this may result
|
||||
* in an error.
|
||||
*/
|
||||
static std::string get_user_steamId3();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the absolute path to the steam installation folder.
|
||||
* This is not failsafe and may require some tweaking to add
|
||||
|
|
@ -73,6 +82,16 @@ public:
|
|||
*/
|
||||
std::vector<Game_t> get_all_games_with_stats() { return m_all_subscribed_apps; };
|
||||
|
||||
/**
|
||||
* Get achievements of the launched app
|
||||
*
|
||||
* For now use an Achievement_t for ease of extension
|
||||
* to count-based achievements
|
||||
*
|
||||
* TODO: maybe don't name this the same as GameServer::get_achievements?
|
||||
*/
|
||||
std::vector<Achievement_t> get_achievements();
|
||||
|
||||
/**
|
||||
* Adds a modification to be done on the launched app.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <iostream>
|
||||
|
||||
pid_t create_process()
|
||||
{
|
||||
|
|
@ -19,9 +20,29 @@ pid_t create_process()
|
|||
|
||||
void read_count(int fd, void *buf, size_t count)
|
||||
{
|
||||
size_t bytes_read;
|
||||
for (size_t i = 0; i < count; i += bytes_read) {
|
||||
bytes_read = read(fd, (void*)((char*)buf+i), count-i);
|
||||
//bytes read per call
|
||||
ssize_t bytes;
|
||||
for (size_t i = 0; i < count; i += bytes) {
|
||||
errno = 0;
|
||||
bytes = read(fd, (void*)((char*)buf+i), count-i);
|
||||
if ((bytes == -1) || (bytes == 0 && errno > 0)) {
|
||||
std::cerr << "Read pipe encountered fatal error." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_count(int fd, void *buf, size_t count)
|
||||
{
|
||||
//bytes written per call
|
||||
ssize_t bytes;
|
||||
for (size_t i = 0; i < count; i += bytes) {
|
||||
errno = 0;
|
||||
bytes = write(fd, (void*)((char*)buf+i), count-i);
|
||||
if ((bytes == -1) || (bytes == 0 && errno > 0)) {
|
||||
std::cerr << "Write pipe encountered fatal error." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,14 @@
|
|||
pid_t create_process();
|
||||
|
||||
/**
|
||||
* Wrapper for read() to actually read count bytes
|
||||
* instead of reading up to count bytes
|
||||
* Wrapper for read/write to actually read/write count
|
||||
* bytes instead of reading/writing up to count bytes,
|
||||
* or fail hard on error
|
||||
*/
|
||||
void read_count(int fd, void *buf, size_t count);
|
||||
|
||||
void write_count(int fd, void *buf, size_t count);
|
||||
|
||||
/**
|
||||
* Feed it a path to a file name, will return whether the file
|
||||
* exists or not on the current machine
|
||||
|
|
|
|||
|
|
@ -96,7 +96,16 @@ extern "C"
|
|||
if( appId != 0 ) {
|
||||
g_main_gui->switch_to_stats_page();
|
||||
g_steam->launch_game(appId);
|
||||
|
||||
// get_achievements from launched child
|
||||
std::vector<Achievement_t> ach_list = g_steam->get_achievements();
|
||||
//TODO: populate achievements in GUI (rip out update view from GameEmulator)
|
||||
/*
|
||||
for(unsigned i = 0; i < m_achievement_count; i++) {
|
||||
g_main_gui->add_to_achievement_list(m_achievement_list[i]);
|
||||
}
|
||||
|
||||
g_main_gui->confirm_stats_list();
|
||||
*/
|
||||
} else {
|
||||
std::cerr << "An error occurred figuring out which app to launch.. You can report this to the developer." << std::endl;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "MyClientSocket.h"
|
||||
#include "../types/Actions.h"
|
||||
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
|
@ -47,6 +48,8 @@ MyClientSocket::request_response(std::string request)
|
|||
connect_to_server();
|
||||
send_message(request);
|
||||
std::string ret = receive_message();
|
||||
std::cerr << "client receieved" << ret << std::endl;
|
||||
// need to parse this in the case of GET_ACHIEVEMENTS
|
||||
disconnect();
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -60,5 +63,5 @@ MyClientSocket::disconnect()
|
|||
void
|
||||
MyClientSocket::kill_server()
|
||||
{
|
||||
request_response(END_OF_SERVICE);
|
||||
request_response(QUIT_GAME_STR);
|
||||
}
|
||||
|
|
@ -1,9 +1,164 @@
|
|||
#include "MyGameSocket.h"
|
||||
#include "../types/Actions.h"
|
||||
|
||||
MyGameSocket::MyGameSocket(AppId_t appid) :
|
||||
MyServerSocket(appid),
|
||||
m_CallbackUserStatsReceived( this, &MyGameSocket::OnUserStatsReceived )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string
|
||||
MyGameSocket::process_request(std::string request) {
|
||||
// Logic goes here TODO
|
||||
|
||||
SteamAPI_RunCallbacks();
|
||||
return "{my json encoded response}";
|
||||
//TODO: yajl parse the JSON-encoded request string (then get request type?) or are strings good enough?
|
||||
//SAM_ACTION request_type = get_request_type(request)
|
||||
std::string ret;
|
||||
|
||||
// switch (request_type) {
|
||||
if (request == GET_ACHIEVEMENTS_STR) {
|
||||
//case GET_ACHIEVEMENTS:
|
||||
//this requires an async callback
|
||||
std::vector<Achievement_t> achievements = get_achievements();
|
||||
// Steam api is launched in this context, other possible imlementation: game_utils->get_achievements()
|
||||
//ret = JSON::achievement_vector(achievements);
|
||||
//break;
|
||||
} else if (request == STORE_ACHIEVEMENTS_STR) {
|
||||
//case STORE_ACHIEVEMENTS:
|
||||
// TODO: achievement ID string, lock or relock boolean
|
||||
//std::vector<std::pair<string, bool>> operations = JSON::parse_achievement_array(request);
|
||||
//long term TODO: extend to stats
|
||||
|
||||
//process_achievements(operations); // or game_utils->process_achievements(..)
|
||||
//ret = SAM_ACK_BUT_IN_JSON;
|
||||
//break;
|
||||
} else if (request == QUIT_GAME_STR) {
|
||||
//case QUIT_GAME:
|
||||
SteamAPI_Shutdown();
|
||||
} else {
|
||||
//default:
|
||||
std::cerr << "Invalid command" << std::endl;
|
||||
ret.clear();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<Achievement_t>
|
||||
MyGameSocket::get_achievements() {
|
||||
|
||||
m_stats_callback_received = false;
|
||||
|
||||
ISteamUserStats *stats_api = SteamUserStats();
|
||||
if (!stats_api->RequestCurrentStats()) {
|
||||
std::cerr << "ERROR: User not logged in, exiting" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while (!m_stats_callback_received) {
|
||||
// for debugging how long steam callbacks take
|
||||
//std::cerr << "waiting for callback" << std::endl;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
SteamAPI_RunCallbacks();
|
||||
}
|
||||
|
||||
return m_achievement_list;
|
||||
}
|
||||
|
||||
void
|
||||
MyGameSocket::OnUserStatsReceived(UserStatsReceived_t *callback) {
|
||||
|
||||
// Check if we received the values for the correct app
|
||||
if (std::string(getenv("SteamAppId")) == std::to_string(callback->m_nGameID)) {
|
||||
if ( k_EResultOK == callback->m_eResult ) {
|
||||
ISteamUserStats *stats_api = SteamUserStats();
|
||||
|
||||
// ==============================
|
||||
// RETRIEVE IDS
|
||||
// ==============================
|
||||
const unsigned num_ach = stats_api->GetNumAchievements();
|
||||
|
||||
if (num_ach == 0) {
|
||||
std::cerr << "No achievements for current game" << std::endl;
|
||||
}
|
||||
|
||||
m_achievement_list.clear();
|
||||
m_achievement_list.reserve(num_ach);
|
||||
|
||||
for (unsigned i = 0; i < num_ach ; i++) {
|
||||
// TODO: strncpy is slow, because it fills the remaining space with NULLs
|
||||
// This is last stage optimisation but, could have used strcpy, or sprintf,
|
||||
// making sure strings are NULL terminated
|
||||
// see "man strncpy" for a possible implementation
|
||||
strncpy(
|
||||
m_achievement_list[i].id,
|
||||
stats_api->GetAchievementName(i),
|
||||
MAX_ACHIEVEMENT_ID_LENGTH);
|
||||
|
||||
strncpy(
|
||||
m_achievement_list[i].name,
|
||||
stats_api->GetAchievementDisplayAttribute(m_achievement_list[i].id, "name"),
|
||||
MAX_ACHIEVEMENT_NAME_LENGTH);
|
||||
|
||||
strncpy(
|
||||
m_achievement_list[i].desc,
|
||||
stats_api->GetAchievementDisplayAttribute(m_achievement_list[i].id, "desc"),
|
||||
MAX_ACHIEVEMENT_DESC_LENGTH);
|
||||
|
||||
// TODO
|
||||
// https://partner.steamgames.com/doc/api/ISteamUserStats#RequestGlobalAchievementPercentages
|
||||
//stats_api->GetAchievementAchievedPercent(m_achievement_list[i].id, &(m_achievement_list[i].global_achieved_rate));
|
||||
m_achievement_list[i].global_achieved_rate = 0;
|
||||
stats_api->GetAchievement(m_achievement_list[i].id, &(m_achievement_list[i].achieved));
|
||||
m_achievement_list[i].hidden = (bool)strcmp(stats_api->GetAchievementDisplayAttribute( m_achievement_list[i].id, "hidden" ), "0");
|
||||
m_achievement_list[i].icon_handle = stats_api->GetAchievementIcon( m_achievement_list[i].id );
|
||||
}
|
||||
|
||||
m_stats_callback_received = true;
|
||||
|
||||
} else {
|
||||
std::cerr << "Received stats for the game, but an error occurrred." << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Received stats for wrong game" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MyGameSocket::process_achievements(std::vector<std::pair<std::string, bool>> changes) {
|
||||
//Untested
|
||||
|
||||
ISteamUserStats *stats_api = SteamUserStats();
|
||||
|
||||
for (unsigned i = 0; i < changes.size(); i++)
|
||||
{
|
||||
const char* achievement_id = changes[i].first.c_str();
|
||||
|
||||
if (changes[i].second) {
|
||||
// We want to unlock an achievement
|
||||
if (!stats_api->SetAchievement(achievement_id)) {
|
||||
std::cerr << "Unlocking achievement " << achievement_id << " failed " << std::endl;
|
||||
} else {
|
||||
std::cerr << "Unlocked achievement " << achievement_id << std::endl;
|
||||
}
|
||||
} else {
|
||||
// We want to relock an achievement
|
||||
if (!stats_api->ClearAchievement(achievement_id)) {
|
||||
std::cerr << "Relocking achievement " << achievement_id << " failed" << std::endl;
|
||||
} else {
|
||||
std::cerr << "Relocked achievement " << achievement_id << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: stats
|
||||
|
||||
}
|
||||
|
||||
// Auto-commit after we receive everything
|
||||
if (!stats_api->StoreStats()) {
|
||||
std::cerr << "Committing changes failed" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +1,32 @@
|
|||
#pragma once
|
||||
#include "MyServerSocket.h"
|
||||
#include "../types/Achievement.h"
|
||||
#include "../../steam/steam_api.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
class MyGameSocket : public MyServerSocket
|
||||
{
|
||||
public:
|
||||
std::string process_request(std::string request);
|
||||
using MyServerSocket::MyServerSocket;
|
||||
std::vector<Achievement_t> get_achievements(void);
|
||||
void process_achievements(std::vector<std::pair<std::string, bool>> changes);
|
||||
|
||||
MyGameSocket(AppId_t appid);
|
||||
|
||||
/**
|
||||
* Steam API callback to handle the received stats and achievements
|
||||
*/
|
||||
STEAM_CALLBACK( MyGameSocket, OnUserStatsReceived, UserStatsReceived_t, m_CallbackUserStatsReceived );
|
||||
|
||||
private:
|
||||
std::atomic_bool m_stats_callback_received;
|
||||
std::vector<Achievement_t> m_achievement_list;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "MyServerSocket.h"
|
||||
|
||||
#include "../common/functions.h"
|
||||
#include "../types/Actions.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
MyServerSocket::MyServerSocket(AppId_t appid) : MySocket(appid)
|
||||
|
|
@ -10,7 +12,7 @@ MyServerSocket::MyServerSocket(AppId_t appid) : MySocket(appid)
|
|||
{
|
||||
std::cerr << "It looks like the server before me did not shutdown properly." << std::endl;
|
||||
if(unlink(m_socket_path.c_str()) < 0) {
|
||||
std::cout << "Something is wrong. Are you the right user? Exitting." << std::endl;
|
||||
std::cout << "Something is wrong. Are you the right user? Exiting." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +20,7 @@ MyServerSocket::MyServerSocket(AppId_t appid) : MySocket(appid)
|
|||
errno = 0;
|
||||
if ((m_socket_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) == -1)
|
||||
{
|
||||
std::cerr << "Could not create the server socket. Exitting. Code: " << errno << std::endl;
|
||||
std::cerr << "Could not create the server socket. Exiting. Code: " << errno << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +36,7 @@ MyServerSocket::MyServerSocket(AppId_t appid) : MySocket(appid)
|
|||
|
||||
if (listen(m_socket_fd, 20) < 0)
|
||||
{
|
||||
std::cerr << "Unable to listen to the socket. Exitting." << std::endl;
|
||||
std::cerr << "Unable to listen to the socket. Exiting." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
|
@ -59,16 +61,24 @@ MyServerSocket::run_server()
|
|||
for (;;) {
|
||||
/* Wait for incoming connection. */
|
||||
data_socket = accept(m_socket_fd, NULL, NULL);
|
||||
|
||||
std::cerr << "Received connection" << std:: endl;
|
||||
|
||||
if (data_socket == -1) {
|
||||
std::cerr << "Server failed to accept. Exitting." << std::endl;
|
||||
std::cerr << "Server failed to accept. Exiting." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Read all the client's request
|
||||
std::string request = receive_message(data_socket);
|
||||
|
||||
if (request == END_OF_SERVICE)
|
||||
std::cerr << "Server received request: " << request << std:: endl;
|
||||
|
||||
send_message(data_socket, process_request(request));
|
||||
|
||||
if (request == QUIT_GAME_STR)
|
||||
{
|
||||
std::cout << "shutting down" << request << std:: endl;
|
||||
send_message(data_socket, "SAM_ACK");
|
||||
close(data_socket);
|
||||
close(m_socket_fd);
|
||||
|
|
@ -76,8 +86,6 @@ MyServerSocket::run_server()
|
|||
break;
|
||||
}
|
||||
|
||||
send_message(data_socket, process_request(request));
|
||||
|
||||
/* Close socket. */
|
||||
close(data_socket);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "MySocket.h"
|
||||
|
||||
#include "../globals.h"
|
||||
#include "../common/functions.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
MySocket::MySocket(AppId_t appid) : m_appid(appid), m_socket_fd(-1)
|
||||
|
|
@ -23,25 +25,24 @@ std::string
|
|||
MySocket::receive_message(const int fd)
|
||||
{
|
||||
std::string ret("");
|
||||
char buffer[BUFFER_SIZE];
|
||||
char buffer[BUFFER_SIZE+1];
|
||||
/* Ensure buffer is 0-terminated. */
|
||||
buffer[BUFFER_SIZE] = '\0';
|
||||
|
||||
for (;;) {
|
||||
|
||||
/* Wait for next data packet. */
|
||||
if (read(fd, buffer, BUFFER_SIZE) == -1) {
|
||||
std::cerr << "Socket could not read." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
read_count(fd, buffer, BUFFER_SIZE);
|
||||
//std::cerr << " received packet: " << buffer << std::endl;
|
||||
ret += std::string(buffer);
|
||||
|
||||
/* Ensure buffer is 0-terminated. */
|
||||
buffer[BUFFER_SIZE - 1] = 0;
|
||||
|
||||
if (!strncmp(buffer, END_OF_MESSAGE, BUFFER_SIZE)) {
|
||||
if (strlen(buffer) < BUFFER_SIZE) {
|
||||
// Got a NULL in the actual buffer, so must be the end
|
||||
// of the string
|
||||
break;
|
||||
}
|
||||
|
||||
ret += std::string(buffer);
|
||||
}
|
||||
|
||||
//std::cerr << "PID: " << getpid() << " received message: " << ret << std::endl;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -54,29 +55,19 @@ MySocket::send_message(const std::string message)
|
|||
void
|
||||
MySocket::send_message(const int fd, const std::string message)
|
||||
{
|
||||
char buffer[BUFFER_SIZE];
|
||||
char buffer[BUFFER_SIZE+1]; //+1 for printing chunks
|
||||
buffer[BUFFER_SIZE] = '\0';
|
||||
|
||||
unsigned iterator = 0;
|
||||
//std::cerr << "PID: " << getpid() << " sending message: " << message << std::endl;
|
||||
|
||||
for (;;) {
|
||||
strncpy(buffer, message.substr(iterator, BUFFER_SIZE - 1).c_str(), BUFFER_SIZE - 1);
|
||||
strncpy(buffer, message.substr(iterator, BUFFER_SIZE).c_str(), BUFFER_SIZE);
|
||||
//std::cerr << " sending packet: " << buffer << std::endl;
|
||||
write_count(fd, buffer, BUFFER_SIZE);
|
||||
|
||||
std::cerr << getpid() << " sending packet: " << buffer << std::endl;
|
||||
|
||||
if (write(fd, buffer, strlen(buffer) + 1) == -1) {
|
||||
std::cerr << getpid() << ": there was an error writing the message." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
iterator += BUFFER_SIZE - 1;
|
||||
|
||||
if (iterator > message.size())
|
||||
{
|
||||
// Close the request
|
||||
strcpy(buffer, END_OF_MESSAGE);
|
||||
if (write(fd, buffer, strlen(buffer) + 1) == -1) {
|
||||
std::cerr << "There was an error closing the message." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
iterator += BUFFER_SIZE;
|
||||
if (iterator > message.size()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@
|
|||
#include <sys/un.h>
|
||||
|
||||
#define BUFFER_SIZE 12
|
||||
#define END_OF_MESSAGE "SAM_STOP"
|
||||
#define END_OF_SERVICE "SAM_QUIT"
|
||||
|
||||
// Strings are the fundamental unit of transmission,
|
||||
// so just use NULL terminator as end of message
|
||||
|
||||
class MySocket
|
||||
{
|
||||
|
|
|
|||
52
src/types/Actions.h
Normal file
52
src/types/Actions.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
// Message format shall be
|
||||
// all GET format
|
||||
#define GET_ACHIEVEMENTS_STR "GET_ACHIEVEMENTS"
|
||||
#define STORE_ACHIEVEMENTS_STR "STORE_ACHIEVEMENTS"
|
||||
#define QUIT_GAME_STR "QUIT_GAME"
|
||||
|
||||
// TODO: Add SAM_START as an action to this too?
|
||||
// Would require more reorg of code structure
|
||||
enum SAM_ACTION {
|
||||
GET_ACHIEVEMENTS,
|
||||
STORE_ACHIEVEMENTS,
|
||||
QUIT_GAME,
|
||||
INVALID
|
||||
};
|
||||
|
||||
// support for the full achievement type shall be added
|
||||
// for now just implement achieved/not achieved
|
||||
|
||||
/* JSON format shall be
|
||||
messages are sent as plaintext strings
|
||||
messages are delimited by the usual string NULL terminator
|
||||
|
||||
get all achievements for active game
|
||||
{
|
||||
GET_ACHIEVEMENTS_STR
|
||||
}
|
||||
response
|
||||
{
|
||||
int NUM_ACH //maybe not necessary if we have .length function?
|
||||
[{
|
||||
str ACH_NAME
|
||||
bool ACHIEVED
|
||||
}]
|
||||
}
|
||||
|
||||
store a list of achievement changes
|
||||
{
|
||||
int NUM_ACH //maybe not necessary if we have .length function?
|
||||
[{
|
||||
str ACH_NAME
|
||||
bool ACHIEVED
|
||||
}]
|
||||
}
|
||||
|
||||
quit active game
|
||||
{
|
||||
SAM_QUIT_STR
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user