Implement first half of json encodings

Implemented all of get_achievements on server side and client side, ack, and
quit_game.

achievement_t to json transformation needs to be standardized, and will be done next
commit.
This commit is contained in:
William Pierce 2019-07-25 22:32:16 -07:00
parent 769c645d96
commit c6c1aaee9e
6 changed files with 242 additions and 34 deletions

View File

@ -3,6 +3,8 @@
#include <fstream>
#include <algorithm>
#include <dirent.h>
#include <yajl/yajl_gen.h>
#include <yajl/yajl_tree.h>
#include "types/Game.h"
#include "types/Actions.h"
#include "SteamAppDAO.h"
@ -125,9 +127,8 @@ MySteam::refresh_owned_apps() {
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
// TODO: Scanning through all apps with this method results in not receiving
// any apps
Game_t game;
SteamAppDAO* appDAO = SteamAppDAO::get_instance();
@ -249,23 +250,100 @@ MySteam::refresh_icons() {
// => refresh_icons
std::vector<Achievement_t>
std::vector<std::pair<std::string, bool>>
MySteam::get_achievements() {
std::vector<std::pair<std::string, bool>> achs;
std::string response;
const unsigned char * buf;
size_t len;
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;
achs.clear();
// Maybe these MySteam functions should be moved to a MyGameClient.cpp?
//TODO encapsulate these into a json generator
yajl_gen handle = yajl_gen_alloc(NULL);
yajl_gen_map_open(handle);
if (yajl_gen_string(handle, (const unsigned char *)SAM_ACTION_STR, strlen(SAM_ACTION_STR)) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
return achs;
}
if (yajl_gen_string(handle, (const unsigned char *)GET_ACHIEVEMENTS_STR, strlen(GET_ACHIEVEMENTS_STR)) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
return achs;
}
if (yajl_gen_map_close(handle) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
return achs;
}
yajl_gen_get_buf(handle, &buf, &len);
response = m_ipc_socket->request_response(std::string((const char*)buf));
yajl_gen_free(handle);
//parse response
yajl_val node = yajl_tree_parse(response.c_str(), NULL, 0);
if (node == NULL) {
std::cerr << "parsing error";
exit(EXIT_FAILURE);
}
const char * path1[] = { SAM_ACK_STR, (const char*)0 };
yajl_val v = yajl_tree_get(node, path1, yajl_t_string);
if (v == NULL || !YAJL_IS_STRING(v)) {
std::cerr << "failed to parse " << SAM_ACK_STR << std::endl;
exit(EXIT_FAILURE);
}
if (std::string(YAJL_GET_STRING(v)) != std::string(SAM_ACK_STR)) {
std::cerr << "failed to receive ack" << std::endl;
exit(EXIT_FAILURE);
}
const char * path2[] = { ACHIEVEMENT_LIST_STR, (const char*)0 };
v = yajl_tree_get(node, path2, yajl_t_array);
if (v == NULL) {
std::cerr << "parsing error" << std::endl;
exit(EXIT_FAILURE);
}
yajl_val *w = YAJL_GET_ARRAY(v)->values;
size_t array_len = YAJL_GET_ARRAY(v)->len;
for(unsigned i = 0; i < array_len; i++) {
const char * path3[] = { ACHIEVEMENT_NAME_STR, (const char*)0 };
const char * path4[] = { ACHIEVED_STR, (const char*)0 };
yajl_val cur_node = w[i];
yajl_val x = yajl_tree_get(cur_node, path3, yajl_t_string);
if (x == NULL) {
std::cerr << "parsing error" << std::endl;
exit(EXIT_FAILURE);
}
// why is bool parsing weird
yajl_val y = yajl_tree_get(cur_node, path4, yajl_t_any);
if (y == NULL) {
std::cerr << "parsing error" << std::endl;
exit(EXIT_FAILURE);
}
if (!YAJL_IS_TRUE(y) && !YAJL_IS_FALSE(y)) {
std::cerr << "bool parsing error" << std::endl;
exit(EXIT_FAILURE);
}
achs.push_back(std::pair<std::string, bool>(YAJL_GET_STRING(x), YAJL_IS_TRUE(y)));
}
return achs;
}
/**
@ -289,4 +367,19 @@ MySteam::remove_modification_ach(const std::string& ach_id) {
m_pending_ach_modifications.erase(ach_id);
}
}
// => remove_modification_ach
// => remove_modification_ach
/**
* Commit pending achievement changes
*/
void
MySteam::commit_changes(void) {
}

View File

@ -90,7 +90,7 @@ public:
*
* TODO: maybe don't name this the same as GameServer::get_achievements?
*/
std::vector<Achievement_t> get_achievements();
std::vector<std::pair<std::string, bool>> get_achievements();
/**
* Adds a modification to be done on the launched app.
@ -108,6 +108,11 @@ public:
*/
//void add_modification_stat(const std::string& stat_id, const double& new_value); // TODO: IMPLEMENT
/**
* Commit pending changes
*/
void commit_changes(void);
MySteam(MySteam const&) = delete;
void operator=(MySteam const&) = delete;
private:

View File

@ -96,16 +96,27 @@ 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]);
// get_achievements from game server
std::vector<std::pair<std::string, bool>> ach_list = g_steam->get_achievements();
g_main_gui->reset_achievements_list();
//TODO: just pass in the array directly?
for(unsigned i = 0; i < ach_list.size(); i++) {
// For now, for compatibility reasons, just convert to an Achievement_t
// These two types will need to be unified
Achievement_t ach = { 0 };
strncpy(ach.id, ach_list[i].first.c_str(), MAX_ACHIEVEMENT_ID_LENGTH);
// incorrect
strncpy(ach.name, ach_list[i].first.c_str(), MAX_ACHIEVEMENT_NAME_LENGTH);
ach.achieved = ach_list[i].second;
g_main_gui->add_to_achievement_list(ach);
}
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;
}

View File

@ -1,5 +1,6 @@
#include "MyClientSocket.h"
#include "../types/Actions.h"
#include <yajl/yajl_gen.h>
#include <thread>
#include <chrono>
@ -48,7 +49,7 @@ MyClientSocket::request_response(std::string request)
connect_to_server();
send_message(request);
std::string ret = receive_message();
std::cerr << "client receieved" << ret << std::endl;
std::cerr << "client receieved " << ret << std::endl;
// need to parse this in the case of GET_ACHIEVEMENTS
disconnect();
return ret;
@ -63,5 +64,24 @@ MyClientSocket::disconnect()
void
MyClientSocket::kill_server()
{
request_response(QUIT_GAME_STR);
const unsigned char * buf;
size_t len;
//TODO encapsulate these into a json generator
yajl_gen handle = yajl_gen_alloc(NULL);
yajl_gen_map_open(handle);
if (yajl_gen_string(handle, (const unsigned char *)SAM_ACTION_STR, strlen(SAM_ACTION_STR)) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
}
if (yajl_gen_string(handle, (const unsigned char *)QUIT_GAME_STR, strlen(QUIT_GAME_STR)) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
}
if (yajl_gen_map_close(handle) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
}
yajl_gen_get_buf(handle, &buf, &len);
request_response(std::string((const char*)buf));
yajl_gen_free(handle);
//TODO parse ack
}

View File

@ -1,3 +1,5 @@
#include <yajl/yajl_gen.h>
#include <yajl/yajl_tree.h>
#include "MyGameSocket.h"
#include "../types/Actions.h"
@ -10,21 +12,83 @@ m_CallbackUserStatsReceived( this, &MyGameSocket::OnUserStatsReceived )
std::string
MyGameSocket::process_request(std::string request) {
// Logic goes here TODO
//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)
//
//TODO encapsulate these into a json parser?
yajl_val node = yajl_tree_parse(request.c_str(), NULL, 0);
if (node == NULL) {
std::cerr << "Parsing error";
exit(EXIT_FAILURE);
}
const char * path[] = { SAM_ACTION_STR, (const char*)0 };
yajl_val v = yajl_tree_get(node, path, yajl_t_string);
if (v == NULL || !YAJL_IS_STRING(v)) {
std::cerr << "failed to get" << SAM_ACTION_STR << std::endl;
exit(EXIT_FAILURE);
}
std::string action(YAJL_GET_STRING(v));
std::string ret;
const unsigned char * buf;
size_t len;
// Generate the ack
//TODO encapsulate these into a json generator
yajl_gen handle = yajl_gen_alloc(NULL);
yajl_gen_map_open(handle);
if (yajl_gen_string(handle, (const unsigned char *)SAM_ACK_STR, strlen(SAM_ACK_STR)) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
}
if (yajl_gen_string(handle, (const unsigned char *)SAM_ACK_STR, strlen(SAM_ACK_STR)) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
}
// switch (request_type) {
if (request == GET_ACHIEVEMENTS_STR) {
if (action == 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);
if (yajl_gen_string(handle, (const unsigned char *)ACHIEVEMENT_LIST_STR, strlen(ACHIEVEMENT_LIST_STR)) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
}
if (yajl_gen_array_open(handle) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
}
// append the achievements to the ack
for (Achievement_t achievement : achievements) {
std::cout << "achievement.id " << achievement.id << std::endl;
yajl_gen_map_open(handle);
if (yajl_gen_string(handle, (const unsigned char *)ACHIEVEMENT_NAME_STR, strlen(ACHIEVEMENT_NAME_STR)) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
}
if (yajl_gen_string(handle, (const unsigned char *)achievement.id, strlen(achievement.id)) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
}
if (yajl_gen_string(handle, (const unsigned char *)ACHIEVED_STR, strlen(ACHIEVED_STR)) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
}
if (yajl_gen_bool(handle, achievement.achieved) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
}
yajl_gen_map_close(handle);
}
if (yajl_gen_array_close(handle) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
}
//break;
} else if (request == STORE_ACHIEVEMENTS_STR) {
} else if (action == 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);
@ -33,15 +97,25 @@ MyGameSocket::process_request(std::string request) {
//process_achievements(operations); // or game_utils->process_achievements(..)
//ret = SAM_ACK_BUT_IN_JSON;
//break;
} else if (request == QUIT_GAME_STR) {
} else if (action == QUIT_GAME_STR) {
//case QUIT_GAME:
SteamAPI_Shutdown();
} else {
//default:
std::cerr << "Invalid command" << std::endl;
ret.clear();
}
if (yajl_gen_map_close(handle) != yajl_gen_status_ok) {
std::cerr << "failed to make json" << std::endl;
}
yajl_gen_get_buf(handle, &buf, &len);
ret = std::string((const char*)buf);
yajl_gen_free(handle);
yajl_tree_free(node);
return ret;
}
@ -58,7 +132,6 @@ MyGameSocket::get_achievements() {
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();
}
@ -84,7 +157,7 @@ MyGameSocket::OnUserStatsReceived(UserStatsReceived_t *callback) {
}
m_achievement_list.clear();
m_achievement_list.reserve(num_ach);
m_achievement_list.resize(num_ach);
for (unsigned i = 0; i < num_ach ; i++) {
// TODO: strncpy is slow, because it fills the remaining space with NULLs

View File

@ -5,6 +5,12 @@
#define STORE_ACHIEVEMENTS_STR "STORE_ACHIEVEMENTS"
#define QUIT_GAME_STR "QUIT_GAME"
#define SAM_ACTION_STR "SAM_ACTION"
#define ACHIEVEMENT_LIST_STR "ACHIEVEMENT_LIST"
#define ACHIEVEMENT_NAME_STR "ACHIEVEMENT_NAME"
#define ACHIEVED_STR "ACHIEVED"
#define SAM_ACK_STR "SAM_ACK"
// TODO: Add SAM_START as an action to this too?
// Would require more reorg of code structure
enum SAM_ACTION {