Add json interface

This adds an initial version of a json interface to bygfoot.  This
is still in development so the interface may change in the future.  This
interface can be used for writing tests or for communication between a
client and server.

To use the interface, you can pass a json file with commands to bygfoot
using the --json option.
This commit is contained in:
Tom Stellard 2020-12-23 17:36:49 -08:00
parent 3097ed92bc
commit 0524050959
14 changed files with 1242 additions and 3 deletions

View File

@ -9,6 +9,20 @@ jobs:
package-linux:
runs-on:
- ubuntu-16.04
strategy:
fail-fast: false
matrix:
build-type:
- full
- minimal
include:
- build-type: full
extra-deps: libjson-c-dev
build-suffix: ""
- build-type: minimal
extra-deps: ""
build-suffix: -minimal
steps:
- name: Checkout sources
uses: actions/checkout@v2
@ -16,13 +30,17 @@ jobs:
- name: Install dependencies
run: sudo apt-get install libglib2.0-dev libgtk2.0-dev libpango1.0-dev libatk1.0-dev libfreetype6-dev ninja-build
- name: Install extra dependencies
if: ${{ matrix.extra-deps != '' }}
run: sudo apt-get install ${{ matrix.extra-deps }}
- name: Package Bygfoot
run: ./scripts/package.sh .
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: "bygfoot-${{ env.BYGFOOT_VERSION }}-linux"
name: "bygfoot-${{ env.BYGFOOT_VERSION }}-linux${{ matrix.build-suffix }}"
path: "*.bz2"
package-windows:

View File

@ -26,6 +26,31 @@ include(GNUInstallDirs)
add_definitions("-DPACKAGE_DATA_DIR=\"${CMAKE_INSTALL_FULL_DATADIR}\"")
add_definitions("-DPACKAGE=\"${PROJECT_NAME}\"")
find_package(JSON-C)
if (JSON-C_FOUND)
set(JSON-C_LIBRARIES json-c::json-c)
else()
pkg_check_modules(JSON-C json-c)
endif()
if (JSON-C_FOUND)
set(JSON_FILES src/json_interface.c src/json_serialize.c)
add_definitions("-DENABLE_JSON")
include_directories (${JSON-C_INCLUDE_DIRS})
link_directories (${JSON-C_LIBRARY_DIRS})
include(CheckSymbolExists)
set(CMAKE_REQUIRED_LIBRARIES ${JSON-C_LIBRARIES})
check_symbol_exists(json_object_new_array_ext "json-c/json.h"
HAVE_JSON_OBJECT_NEW_ARRAY_EXT)
if (HAVE_JSON_OBJECT_NEW_ARRAY_EXT)
add_definitions("-DHAVE_JSON_OBJECT_NEW_ARRAY_EXT")
endif()
endif()
add_executable(bygfoot WIN32
src/bet.c src/bet.h src/bet_struct.h
src/bygfoot.c src/bygfoot.h
@ -111,13 +136,14 @@ add_executable(bygfoot WIN32
src/youth_academy.c src/youth_academy.h
src/zip/unzip.c src/zip/unzip.h
src/zip/zip.c src/zip/zip.h
${JSON_FILES}
)
# Some gtk headers use deprecated glib features, so disable this warning
# since we can't do anything about it.
target_compile_options(bygfoot PRIVATE -Wno-deprecated-declarations)
# Link the target to the GTK+ libraries
target_link_libraries (bygfoot ${GTK2_LIBRARIES} ${GLIB_LIBRARIES} m
${ZLIB_LIBRARIES})
${ZLIB_LIBRARIES} ${JSON-C_LIBRARIES})
install(TARGETS bygfoot)

View File

@ -1,9 +1,12 @@
#include "bygfoot.h"
#include "file.h"
#include "load_save.h"
#include "gui.h"
#include "misc.h"
#include "start_end.h"
#include "user.h"
#include "start_end.h"
#include "xml_country.h"
void
@ -19,6 +22,23 @@ bygfoot_init(Bygfoot *bygfoot, enum BygfootFrontend frontend)
}
}
void
bygfoot_load_bygfoot(Bygfoot *bygfoot, const gchar *id)
{
char save_dir[256];
char save_path[256];
/* FIXME: This is not secure */
file_get_bygfoot_dir(save_dir);
/* FIXME: There should be a helper function for this */
sprintf(save_path, "%s%ssaves%s%s", save_dir, G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S, id);
}
Country *bygfoot_load_country(Bygfoot *bygfoot, const gchar *country_name)
{
xml_country_read(country_name, NULL);
return &country;
}
User *bygfoot_add_user(Bygfoot *bygfoot, const gchar *username, Team *tm)
{
User new_user = user_new();

View File

@ -205,7 +205,13 @@ typedef struct
gint paned_pos;
} Windows;
typedef struct {
gchar *json_filename;
} CommandLineArgs;
void bygfoot_init(Bygfoot *bygfoot, enum BygfootFrontend frontend);
void bygfoot_load_bygfoot(Bygfoot *bygfoot, const gchar *id);
Country *bygfoot_load_country(Bygfoot *bygfoot, const gchar *country_name);
User *bygfoot_add_user(Bygfoot *bygfoot, const gchar *username, Team *tm);
void bygfoot_start_game(Bygfoot *bygfoot);
void bygfoot_show_progress(const Bygfoot *bygfoot, gfloat value, const gchar *text, gint pictype);

View File

@ -1,8 +1,11 @@
#ifndef BYGFOOT_STRUCT_H
#define BYGFOOT_STRUCT_H
#include <glib.h>
enum BygfootFrontend {
BYGFOOT_FRONTEND_GTK2
BYGFOOT_FRONTEND_GTK2,
BYGFOOT_FRONTEND_CONSOLE,
};
/** This struct holds all of the global state for a bygfoot game. The goal
@ -20,4 +23,5 @@ typedef struct
/* @} */
} Bygfoot;
#endif

View File

@ -71,6 +71,9 @@ file_load_user_conf_file(User *user);
gboolean
file_check_home_dir(void);
void
file_check_home_dir_create_dirs(void);
void
file_check_home_dir_get_conf_files(GPtrArray **files_to_copy);

5
src/json_compat.h Normal file
View File

@ -0,0 +1,5 @@
#ifndef HAVE_JSON_OBJECT_NEW_ARRAY_EXT
#define json_object_new_array_ext(size) json_object_new_array();
#endif

663
src/json_interface.c Normal file
View File

@ -0,0 +1,663 @@
#include <json-c/json.h>
#include <json-c/json_tokener.h>
#include <json-c/json_object.h>
#include "json_interface.h"
#include "json_serialize.h"
#include "user.h"
#include "league_struct.h"
#include "load_save.h"
#include "misc.h"
#include "team.h"
#include "option.h"
#include "start_end.h"
static int bygfoot_json_do_commands(Bygfoot *bygfoot, const json_object *commands);
static int bygfoot_json_do_add_user(Bygfoot *bygfoot, const json_object *args);
static struct json_object *bygfoot_json_call_load_bygfoot(Bygfoot *bygfoot, const json_object *args);
static struct json_object *bygfoot_json_call_save_bygfoot(Bygfoot *bygfoot, const json_object *args);
static struct json_object *bygfoot_json_call_add_country(Bygfoot *bygfoot, const json_object *args);
static struct json_object *bygfoot_json_call_add_user(Bygfoot *bygfoot, const json_object *args);
static struct json_object *bygfoot_json_call_start_bygfoot(Bygfoot *bygfoot, const json_object *args);
static struct json_object *bygfoot_json_call_simulate_games(Bygfoot *bygfoot,
const json_object *args);
static struct json_object *bygfoot_json_call_get_tables(Bygfoot *bygfoot,
const json_object *args);
static struct json_object *bygfoot_json_call_get_players(Bygfoot *bygfoot,
const json_object *args);
static struct json_object *bygfoot_json_call_get_fixtures(Bygfoot *bygfoot,
const json_object *args);
static struct json_object *bygfoot_json_call_get_cups(Bygfoot *bygfoot,
const json_object *args);
static struct json_object *bygfoot_json_response_error(const char *command,
const char *error);
static json_object *
bygfoot_json_fixture_to_json(const Fixture *fixture);
static json_object * bygfoot_json_player_to_json(const Player *player);
static json_object *bygfoot_json_user_to_json(const User *user);
static json_object *bygfoot_json_table_to_json(const Table *table);
struct json_field {
const gchar *name;
enum json_type type;
};
static gboolean
bygfoot_json_validate_arg_types(const struct json_object *args,
const struct json_field *fields)
{
const struct json_field *field;
for (field = fields; field->name; field++ ) {
struct json_object *value;
if (!json_object_object_get_ex(args, field->name, &value))
continue;
if (!json_object_is_type(value, field->type))
return FALSE;
}
return TRUE;
}
int bygfoot_json_main(Bygfoot *bygfoot, const CommandLineArgs *cl_args)
{
gchar *contents;
GError *error;
struct json_object *json;
enum json_tokener_error json_error;
/* Disable option to skip rounds without user games, so the simulate_games
* command works correctly. */
opt_set_int("int_opt_skip", 0);
if (!g_file_get_contents(cl_args->json_filename, &contents, NULL, &error)) {
misc_print_error(&error, FALSE);
return 1;
}
json = json_tokener_parse_verbose(contents, &json_error);
if (!json) {
fprintf(stderr, "Failed to parse json %s:\n", cl_args->json_filename);
fprintf(stderr, "%s\n",
json_tokener_error_desc(json_error));
return 1;
}
/* Handle configuration options */
/* TODO: Get option from json */
//bygfoot_set_save_dir(bygfoot, NULL);
json_object_object_foreach(json, key, val) {
if (!strcmp("commands", key))
return bygfoot_json_do_commands(bygfoot, val);
}
fprintf(stderr, "commands key is not present");
return 1;
}
static int bygfoot_json_do_commands(Bygfoot *bygfoot, const json_object *commands)
{
size_t i, num_commands;
static const struct json_func {
const gchar *command;
json_object* (*func)(Bygfoot *, const json_object *);
} json_funcs[] = {
{ "load_bygfoot", bygfoot_json_call_load_bygfoot },
{ "save_bygfoot", bygfoot_json_call_save_bygfoot },
{ "add_country", bygfoot_json_call_add_country },
{ "add_user", bygfoot_json_call_add_user },
{ "start_bygfoot", bygfoot_json_call_start_bygfoot },
{ "simulate_games", bygfoot_json_call_simulate_games },
{ "get_tables", bygfoot_json_call_get_tables },
{ "get_players", bygfoot_json_call_get_players },
{ "get_fixtures", bygfoot_json_call_get_fixtures },
{ "cups", bygfoot_json_call_get_cups },
{ NULL, NULL}
};
if (!json_object_is_type(commands, json_type_array)) {
fprintf(stderr, "json commands should be in an array\n");
return 1;
}
num_commands = json_object_array_length(commands);
for (i = 0; i < num_commands; i++) {
const json_object *command = json_object_array_get_idx(commands, i);
struct json_object *response = NULL;
// TODO: CHECK COMMAND SIZE
json_object_object_foreach(command, key, val) {
const struct json_func *json_func;
for (json_func = json_funcs; json_func->command; json_func++) {
if (!strcmp(json_func->command, key)) {
response = json_func->func(bygfoot, val);
break;
}
}
if (!response) {
response = bygfoot_json_response_error("commands", "command not recognized");
}
fprintf(stderr, "%s\n", json_object_to_json_string_ext(response, JSON_C_TO_STRING_PRETTY));
json_object_put(response);
}
}
return 0;
}
static const gchar *
bygfoot_json_validate_bygfoot_id(const json_object *args, json_object **error)
{
const gchar *id;
/* Get the id field */
json_object *id_obj = json_object_object_get(args, "id");
if (id_obj) {
if(!json_object_is_type(id_obj, json_type_string)) {
*error = bygfoot_json_response_error("",
"field 'id' must be a string");
return NULL;
}
return json_object_get_string(id_obj);
}
*error = bygfoot_json_response_error("", "field 'id' is missing or NULL");
return NULL;
}
static struct json_object *
bygfoot_json_call_load_bygfoot(Bygfoot *bygfoot, const json_object *args)
{
const gchar *id = NULL;
struct json_object *filename_obj;
const gchar *filename;
static const struct json_field fields [] = {
{ "filename", json_type_string }
};
if (!bygfoot_json_validate_arg_types(args, fields))
return bygfoot_json_response_error("load_bygfoot",
"wrong type for argument");
if (!json_object_object_get_ex(args, "filename", &filename_obj))
return bygfoot_json_response_error("load_bygfoot",
"filename argument is required");
filename = json_object_get_string(filename_obj);
if (!g_file_test(filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
return bygfoot_json_response_error("load_bygfoot", "file not found");
load_save_load_game(bygfoot, filename, FALSE);
return json_object_new_object();
}
static struct json_object *
bygfoot_json_call_save_bygfoot(Bygfoot *bygfoot, const json_object *args)
{
const gchar *id = NULL;
struct json_object *filename_obj;
const gchar *filename;
static const struct json_field fields [] = {
{ "filename", json_type_string }
};
if (!bygfoot_json_validate_arg_types(args, fields))
return bygfoot_json_response_error("save_bygfoot",
"wrong type for argument");
if (!json_object_object_get_ex(args, "filename", &filename_obj))
return bygfoot_json_response_error("save_bygfoot",
"filename argument is required");
filename = json_object_get_string(filename_obj);
load_save_save_game(bygfoot, filename);
return json_object_new_object();
}
static json_object *
bygfoot_json_call_add_country(Bygfoot *bygfoot, const json_object *args)
{
gchar const *country_name = NULL;
static const struct json_field fields [] = {
{ "name", json_type_string },
{ NULL, json_type_null }
};
const struct json_field *field;
for (field = fields; field->name; field++ ) {
struct json_object *value = json_object_object_get(args, field->name);
if (!value)
continue;
if (!json_object_is_type(value, field->type))
continue;
if (!strcmp(field->name, "name"))
country_name = json_object_get_string(value);
}
if (!country_name)
return bygfoot_json_response_error("add_country", "field 'country' is required");
bygfoot_load_country(bygfoot, country_name);
return json_object_new_string("success");
}
static json_object *bygfoot_json_call_add_user(Bygfoot *bygfoot, const json_object *args)
{
const char *command = "add_user";
const char *username = NULL;
const char *country_name = NULL;
const char *team_name = NULL;
struct json_object *response;
Country *country;
Team *tm;
User *user;
json_object_object_foreach(args, key, val) {
if (!strcmp("username", key)) {
if (!json_object_is_type(val, json_type_string)) {
return bygfoot_json_response_error(command, "username must be of type string");
}
username = json_object_get_string(val);
}
if (!strcmp("country", key)) {
if (!json_object_is_type(val, json_type_string)) {
return bygfoot_json_response_error(command, "country must be of type string");
}
country_name = json_object_get_string(val);
}
if (!strcmp("team", key)) {
if (!json_object_is_type(val, json_type_string)) {
return bygfoot_json_response_error(command, "team must be of type string");
}
team_name = json_object_get_string(val);
}
}
if (!username) {
return bygfoot_json_response_error(command, "field 'username' is required");
}
if (!country_name) {
return bygfoot_json_response_error(command, "field 'country' is required");
}
if (!team_name) {
return bygfoot_json_response_error(command, "field 'team' is required");
}
country = bygfoot_load_country(bygfoot, country_name);
tm = team_of_sid(team_name, country);
user = bygfoot_add_user(bygfoot, username, tm);
response = json_object_new_object();
json_object_object_add(response, "success", bygfoot_json_user_to_json(user));
return response;
}
static json_object *bygfoot_json_call_start_bygfoot(Bygfoot *bygfoot,
const json_object *args)
{
unsigned i;
bygfoot_start_game(bygfoot);
/* FIXME: We should really be doing this when the user is added, but
* the user options get loaded during the bygfoot_start_bygfoot() call and w
* can't add options before that.
*/
for (i = 0; i < users->len; i++) {
/* We need to set this option to avoid activating the GUI.
* FIXME: More work is needed to separate the logic from the GUI */
option_set_int("int_opt_user_show_live_game", &usr(i).options, 0);
}
return json_object_new_string("success");
}
static void simulate_weeks(Bygfoot *bygfoot, gint weeks)
{
gint num_weeks = 0;
gint current_week = week;
while (num_weeks < weeks) {
do {
end_week_round(bygfoot);
} while (current_week == week);
current_week = week;
num_weeks++;
}
}
static struct json_object *bygfoot_json_call_simulate_games(Bygfoot *bygfoot,
const json_object *args)
{
int32_t rounds = 0;
int32_t weeks = 0;
int32_t seasons = 0;
unsigned i;
json_object_object_foreach(args, key, val) {
if (!strcmp("rounds", key)) {
rounds = json_object_get_int(val);
for (i = 0; i < rounds; i++) {
end_week_round(bygfoot);
}
} else if (!strcmp("weeks", key)) {
weeks = json_object_get_int(val);
simulate_weeks(bygfoot, weeks);
} else if (!strcmp("seasons", key)) {
gint num_seasons = 0;
gint start_week = week;
gint current_season = season;
seasons = json_object_get_int(val);
while (num_seasons < seasons) {
do {
end_week_round(bygfoot);
} while (current_season == season);
current_season = season;
num_seasons++;
}
simulate_weeks(bygfoot, start_week);
}
}
return json_object_new_string("success");
}
static json_object *
bygfoot_json_call_get_fixtures(Bygfoot *bygfoot, const json_object *args)
{
struct json_object *fixtures_obj = json_object_new_array();
struct json_object *response, *data;
int i;
for (i = 0; i < country.leagues->len; i++) {
const League *league = &g_array_index(country.leagues, League, i);
int j;
for (j = 0; j < league->fixtures->len; j++) {
const Fixture *fixture = &g_array_index(league->fixtures, Fixture, j);
json_object_array_add(fixtures_obj, bygfoot_json_fixture_to_json(fixture));
}
}
for (i = 0; i < country.cups->len; i++) {
const Cup *cup = &g_array_index(country.cups, Cup, i);
int j;
for (j = 0; j < cup->fixtures->len; j++) {
const Fixture *fixture = &g_array_index(cup->fixtures, Fixture, j);
json_object_array_add(fixtures_obj, bygfoot_json_fixture_to_json(fixture));
}
}
data = json_object_new_object();
response = json_object_new_object();
json_object_object_add(data, "fixtures", fixtures_obj);
json_object_object_add(response, "success", data);
return response;
}
static json_object *
bygfoot_json_call_get_tables(Bygfoot *bygfoot, const json_object *args)
{
gchar const *country_name = NULL;
static const struct json_field fields [] = {
{ NULL, json_type_null }
};
const struct json_field *field;
int i;
struct json_object *response, *data, *tables;
for (field = fields; field->name; field++ ) {
struct json_object *value = json_object_object_get(args, field->name);
if (!value)
continue;
if (!json_object_is_type(value, field->type))
continue;
}
tables = json_object_new_array();
//GArray *leagues, *cups;
for (i = 0; i < country.leagues->len; i++) {
const League *league = &g_array_index(country.leagues, League, i);
int j;
for (j = 0; j < league->tables->len; j++) {
const Table *table = &g_array_index(league->tables, Table, j);
json_object_array_add(tables, bygfoot_json_table_to_json(table));
}
}
response = json_object_new_object();
data = json_object_new_object();
json_object_object_add(data, "tables", tables);
json_object_object_add(response, "success", data);
return response;
}
static json_object *
bygfoot_json_call_get_players(Bygfoot *bygfoot, const json_object *args)
{
struct json_object *players_obj = json_object_new_array();
struct json_object *response, *data;
int i;
for (i = 0; i < country.leagues->len; i++) {
const League *league = &g_array_index(country.leagues, League, i);
int j;
for (j = 0; j < league->teams->len; j++) {
const Team *team = &g_array_index(league->teams, Team, j);
int k;
for (k = 0; k < team->players->len; k++) {
const Player *player = &g_array_index(team->players, Player, k);
json_object_array_add(players_obj, bygfoot_json_player_to_json(player));
}
}
}
response = json_object_new_object();
data = json_object_new_object();
json_object_object_add(data, "players", players_obj);
json_object_object_add(response, "success", data);
return response;
}
static json_object *
bygfoot_json_call_get_cups(Bygfoot *bygfoot, const json_object *args)
{
struct json_object *cups_obj = json_object_new_array_ext(country.cups->len);
int i;
for (i = 0; i < country.cups->len; i++) {
const Cup *cup = &g_array_index(country.cups, Cup, i);
json_object_array_add(cups_obj, bygfoot_json_serialize_cup(cup, NULL));
}
return cups_obj;
}
static json_object *
bygfoot_json_live_game_stats_to_json(const LiveGameStats *stats, gint team_index)
{
}
static json_object *
bygfoot_json_fixture_stats_to_json(const Fixture *fixture, gint team_index)
{
struct json_object *team_obj = json_object_new_object();
struct json_object *stats_obj = json_object_new_object();
static const struct key_index {
const gchar *key;
gint index;
} result_fields[] = {
{ "goals_regulation", 0 },
{ "goals_extra_time", 1 },
{ "goals_penalty_shootout", 2 },
{ NULL, 0 }
};
static const struct key_index live_stats_fields[] = {
{ "goals_regular", LIVE_GAME_STAT_VALUE_GOALS_REGULAR },
{ "shots", LIVE_GAME_STAT_VALUE_SHOTS },
{ "shot_percentage", LIVE_GAME_STAT_VALUE_SHOT_PERCENTAGE },
{ "possession", LIVE_GAME_STAT_VALUE_POSSESSION },
{ "penalties", LIVE_GAME_STAT_VALUE_PENALTIES },
{ "fouls", LIVE_GAME_STAT_VALUE_FOULS },
{ "cards", LIVE_GAME_STAT_VALUE_CARDS },
{ "reds", LIVE_GAME_STAT_VALUE_REDS } ,
{ "injuries", LIVE_GAME_STAT_VALUE_INJURIES },
{ NULL, LIVE_GAME_STAT_VALUE_END }
};
const struct key_index *iter;
if (!fixture->live_game)
return stats_obj;
for (iter = result_fields; iter->key; iter++) {
gint value = fixture->result[team_index][iter->index];
json_object_object_add(stats_obj, iter->key, json_object_new_int64(value));
}
/* Note it seems like fixture->live_game does not persist when the game is
* over, so that is why we are not including it in the json. */
return stats_obj;
}
static json_object *
bygfoot_json_fixture_to_json(const Fixture *fixture)
{
struct json_object *fixture_obj = json_object_new_object();
struct json_object *home_team_obj = json_object_new_object();
struct json_object *away_team_obj = json_object_new_object();
json_object_object_add(fixture_obj, "id", json_object_new_int64(fixture->id));
json_object_object_add(fixture_obj, "league_id", json_object_new_int64(fixture->clid));
json_object_object_add(fixture_obj, "round", json_object_new_int64(fixture->round));
json_object_object_add(fixture_obj, "replay_number", json_object_new_int64(fixture->replay_number));
json_object_object_add(fixture_obj, "week", json_object_new_int64(fixture->week_number));
json_object_object_add(fixture_obj, "round", json_object_new_int64(fixture->week_round_number));
json_object_object_add(home_team_obj, "id", json_object_new_int64(fixture->team_ids[0]));
json_object_object_add(home_team_obj, "stats",
bygfoot_json_fixture_stats_to_json(fixture, 0));
json_object_object_add(fixture_obj, "home_team", home_team_obj);
json_object_object_add(away_team_obj, "id", json_object_new_int64(fixture->team_ids[1]));
json_object_object_add(away_team_obj, "stats",
bygfoot_json_fixture_stats_to_json(fixture, 1));
json_object_object_add(fixture_obj, "away_team", away_team_obj);
return fixture_obj;
}
static json_object *
bygfoot_json_player_games_goals_to_json(const GArray *stats)
{
int i;
struct json_object *stats_obj = json_object_new_array_ext(stats->len);
for (i = 0; i < stats->len; i++) {
const PlayerGamesGoals *league_stats = &g_array_index(stats,
PlayerGamesGoals, i);
struct json_object *league_stats_obj = json_object_new_object();
json_object_object_add(league_stats_obj, "league_id",
json_object_new_int64(league_stats->clid));
json_object_object_add(league_stats_obj, "games",
json_object_new_int64(league_stats->games));
json_object_object_add(league_stats_obj, "goals",
json_object_new_int64(league_stats->goals));
json_object_object_add(league_stats_obj, "shots",
json_object_new_int64(league_stats->shots));
json_object_array_add(stats_obj, league_stats_obj);
}
return stats_obj;
}
static json_object *
bygfoot_json_player_to_json(const Player *player)
{
struct json_object *player_obj = json_object_new_object();
json_object_object_add(player_obj, "name", json_object_new_string(player->name));
json_object_object_add(player_obj, "season_stats",
bygfoot_json_player_games_goals_to_json(player->games_goals));
return player_obj;
}
static json_object *
bygfoot_json_team_to_json(const Team *team)
{
struct json_object *team_obj = json_object_new_object();
json_object_object_add(team_obj, "name",
json_object_new_string(team->name));
return team_obj;
}
static json_object *
bygfoot_json_table_element_to_json(const TableElement *element)
{
struct json_object *element_obj = json_object_new_object();
json_object_object_add(element_obj, "team",
bygfoot_json_team_to_json(element->team));
json_object_object_add(element_obj, "old_rank",
json_object_new_int64(element->old_rank));
json_object_object_add(element_obj, "played",
json_object_new_int64(element->values[TABLE_PLAYED]));
json_object_object_add(element_obj, "won",
json_object_new_int64(element->values[TABLE_WON]));
json_object_object_add(element_obj, "draw",
json_object_new_int64(element->values[TABLE_DRAW]));
json_object_object_add(element_obj, "lost",
json_object_new_int64(element->values[TABLE_LOST]));
json_object_object_add(element_obj, "gf",
json_object_new_int64(element->values[TABLE_GF]));
json_object_object_add(element_obj, "ga",
json_object_new_int64(element->values[TABLE_GA]));
json_object_object_add(element_obj, "gd",
json_object_new_int64(element->values[TABLE_GD]));
json_object_object_add(element_obj, "pts",
json_object_new_int64(element->values[TABLE_PTS]));
return element_obj;
}
static json_object *
bygfoot_json_table_to_json(const Table *table)
{
struct json_object *table_obj = json_object_new_object();
struct json_object *elements_obj = json_object_new_array_ext(table->elements->len);
int i;
json_object_object_add(table_obj, "name", json_object_new_string(table->name));
json_object_object_add(table_obj, "id", json_object_new_int64(table->clid));
json_object_object_add(table_obj, "round", json_object_new_int64(table->round));
for (i = 0; i < table->elements->len; i++) {
const TableElement *element = &g_array_index(table->elements,
TableElement, i);
json_object_array_add(elements_obj,
bygfoot_json_table_element_to_json(element));
}
json_object_object_add(table_obj, "elements", elements_obj);
return table_obj;
}
static json_object *bygfoot_json_user_to_json(const User *user)
{
struct json_object *json = json_object_new_object();
struct json_object *user_obj = json_object_new_object();
json_object_object_add(user_obj, "name", json_object_new_string(user->name));
json_object_object_add(json, "user", user_obj);
return json;
}
static struct json_object *bygfoot_json_response_error(const char *command,
const char *error)
{
struct json_object *json = json_object_new_object();
json_object_object_add(json, "message", json_object_new_string(error));
return json;
}
static void bygfoot_json_error_to_console(const char *command, const char *error)
{
struct json_object *json = bygfoot_json_response_error(command, error);
fprintf(stderr, "%s\n", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY));
json_object_put(json);
}

12
src/json_interface.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef JSON_INTERFACE_H
#define JSON_INTERFACE_H
#include <json-c/json_object.h>
#include "bygfoot.h"
int bygfoot_json_main(Bygfoot *bygfoot, const CommandLineArgs *cl_args);
struct json_object *bygfoot_json_call_get_countries(Bygfoot *bygfoot, const json_object *args);
#endif

284
src/json_serialize.c Normal file
View File

@ -0,0 +1,284 @@
/** \file json_serialize.c
*
*
* This contains functions for converting Bygfoot objects to json. The general
* strategy is to only serialize values that are unchanged during a Bygfoot
* game.
*
* Object members that can change as the result of game play should be
* queried with functions directly.
*
* Parent: Full, Child: Full : Fields == NULL, recursive = TRUE
* Parent: Full, Child: ID: Fields == NULL, recursive = FALSE
* Parent: ID: Fields == IDs
* Child field in filter: IDs
* Child field in filter, recursive = TRUE: FULL
*
*/
#include <json-c/json_object.h>
#include <glib.h>
#include "json_serialize.h"
#include "league_struct.h"
static void
serialize_gchar_array_callback(gpointer string, gpointer user_data)
{
struct json_object *obj = (struct json_object*)user_data;
json_object_array_add(obj, json_object_new_string(string));
}
static struct json_object*
serialize_gchar_array(GPtrArray* ptr_array)
{
struct json_object *array_obj = json_object_new_array_ext(ptr_array->len);
int i;
g_ptr_array_foreach(ptr_array, serialize_gchar_array_callback, array_obj);
return array_obj;
}
static GHashTable*
fields_to_hash_table(gchar **fields)
{
gchar *field;
GHashTable *hash_table;
if (!fields)
return NULL;
hash_table = g_hash_table_new(g_str_hash, g_str_equal);
for (field = *fields; field; field++) {
g_hash_table_add(hash_table, field);
}
return hash_table;
}
struct json_object *
bygfoot_json_serialize_country_list(GPtrArray *country_list)
{
return serialize_gchar_array(country_list);
}
#define SERIALIZE_OBJECT_FIELD_FILTER(json_object, object, field, \
serialize_func, field_list) \
if (!field_list || g_hash_table_contains(field_list, #field)) { \
json_object_object_add(json_object, #field, serialize_func(object->field)); \
}
#define SERIALIZE_OBJECT_FIELD_STRUCT(json_object, object, field, \
serialize_func, field_list) \
if (!field_list || g_hash_table_contains(field_list, #field)) { \
json_object_object_add(json_object, #field, serialize_func(object->field, field_list)); \
}
#define SERIALIZE_OBJECT_FIELD(json_object, object, field, serialize_func) \
SERIALIZE_OBJECT_FIELD_FILTER(json_object, object, field, serialize_func, NULL)
struct json_object *
bygfoot_json_serialize_country(const Country *country)
{
struct json_object *country_obj = json_object_new_object();
#define SERIALIZE_COUNTRY_FIELD(field, serialize_func) \
SERIALIZE_OBJECT_FIELD(country_obj, country, field, serialize_func)
SERIALIZE_COUNTRY_FIELD(name, json_object_new_string);
SERIALIZE_COUNTRY_FIELD(symbol, json_object_new_string);
SERIALIZE_COUNTRY_FIELD(sid, json_object_new_string);
SERIALIZE_COUNTRY_FIELD(rating, json_object_new_int64);
SERIALIZE_COUNTRY_FIELD(reserve_promotion_rules, json_object_new_int64);
// SERIALIZE_COUNTRY_FIELD(leagues, bygfoot_json_serialize_league_array);
// SERIALIZE_COUNTRY_FIELD(cups, bygfoot_json_serialize_cup_array);
// SERIALIZE_COUNTRY_FIELD(allcups, bygfoot_json_serialize_cup_ptr_array);
#undef SERIALIZE_COUNTRY_FIELD
return country_obj;
}
struct json_object *
bygfoot_json_serialize_league_array(const GArray *league_array)
{
struct json_object *league_array_obj =
json_object_new_array_ext(league_array->len);
gint i;
for (i = 0; i < league_array->len; i++) {
const League *league = &g_array_index(league_array, League, i);
json_object_array_add(league_array_obj,
bygfoot_json_serialize_league(league));
}
return league_array_obj;
}
struct json_object *
bygfoot_json_serialize_league(const League *league)
{
struct json_object *league_obj = json_object_new_object();
#define SERIALIZE_LEAGUE_FIELD(field, serialize_func) \
SERIALIZE_OBJECT_FIELD(league_obj, league, field, serialize_func);
SERIALIZE_LEAGUE_FIELD(name, json_object_new_string);
SERIALIZE_LEAGUE_FIELD(short_name, json_object_new_string);
SERIALIZE_LEAGUE_FIELD(sid, json_object_new_string);
SERIALIZE_LEAGUE_FIELD(symbol, json_object_new_string);
SERIALIZE_LEAGUE_FIELD(names_file, json_object_new_string);
}
struct json_object *
bygfoot_json_serialize_stadium(Stadium stadium, GHashTable *fields)
{
struct json_object *stadium_obj = json_object_new_object();
Stadium *stadium_ptr = &stadium;
#define SERIALIZE_STADIUM_FIELD(field, serialize_func) \
SERIALIZE_OBJECT_FIELD_FILTER(stadium_obj, stadium_ptr, field, serialize_func, fields);
SERIALIZE_STADIUM_FIELD(name, json_object_new_string);
SERIALIZE_STADIUM_FIELD(capacity, json_object_new_int64);
SERIALIZE_STADIUM_FIELD(average_attendance, json_object_new_int64);
SERIALIZE_STADIUM_FIELD(possible_attendance, json_object_new_int64);
SERIALIZE_STADIUM_FIELD(safety, json_object_new_double);
SERIALIZE_STADIUM_FIELD(ticket_price, json_object_new_double);
return stadium_obj;
}
struct json_object *
bygfoot_json_serialize_team(const Team *team, GHashTable *fields)
{
struct json_object *team_obj = json_object_new_object();
#define SERIALIZE_TEAM_FIELD(field, serialize_func) \
SERIALIZE_OBJECT_FIELD_FILTER(team_obj, team, field, serialize_func, fields);
#define SERIALIZE_TEAM_FIELD_STRUCT(field, serialize_func) \
SERIALIZE_OBJECT_FIELD_STRUCT(team_obj, team, field, serialize_func, fields);
SERIALIZE_TEAM_FIELD(name, json_object_new_string);
SERIALIZE_TEAM_FIELD(symbol, json_object_new_string);
SERIALIZE_TEAM_FIELD(names_file, json_object_new_string);
SERIALIZE_TEAM_FIELD(def_file, json_object_new_string);
SERIALIZE_TEAM_FIELD(strategy_sid, json_object_new_string);
SERIALIZE_TEAM_FIELD(clid, json_object_new_int64);
SERIALIZE_TEAM_FIELD(id, json_object_new_int64);
SERIALIZE_TEAM_FIELD(structure, json_object_new_int64);
SERIALIZE_TEAM_FIELD(style, json_object_new_int64);
SERIALIZE_TEAM_FIELD(boost, json_object_new_int64);
SERIALIZE_TEAM_FIELD(average_talent, json_object_new_double);
SERIALIZE_TEAM_FIELD(luck, json_object_new_double);
SERIALIZE_TEAM_FIELD_STRUCT(stadium, bygfoot_json_serialize_stadium);
//SERIALIZE_TEAM_FIELD_STRUCT(players, bygfoot_json_serialize_players);
SERIALIZE_TEAM_FIELD(first_team_sid, json_object_new_string);
SERIALIZE_TEAM_FIELD(first_team_id, json_object_new_int64);
SERIALIZE_TEAM_FIELD(reserve_level, json_object_new_int64);
return team_obj;
}
static void
serialize_team_pointers_callback(gpointer team, gpointer user_data)
{
gchar *fields[] = {
"name",
"id"
};
GHashTable *hash_table = fields_to_hash_table(fields);
struct json_object *obj = (struct json_object*)user_data;
json_object_array_add(obj, bygfoot_json_serialize_team((Team*)team, hash_table));
}
struct json_object *
bygfoot_json_serialize_team_ptrs(GPtrArray *team_ptrs, GHashTable *fields)
{
struct json_object *array_obj = json_object_new_array_ext(team_ptrs->len);
g_ptr_array_foreach(team_ptrs, serialize_team_pointers_callback, array_obj);
return array_obj;
}
struct json_object *
bygfoot_json_serialize_cup_round(const CupRound *round,
GHashTable *fields)
{
struct json_object *cup_round_obj = json_object_new_object();
#define SERIALIZE_CUP_ROUND_FIELD(field, serialize_func) \
SERIALIZE_OBJECT_FIELD_FILTER(cup_round_obj, round, field, serialize_func, fields);
#define SERIALIZE_CUP_ROUND_FIELD_STRUCT(field, serialize_func) \
SERIALIZE_OBJECT_FIELD_STRUCT(cup_round_obj, round, field, serialize_func, fields);
SERIALIZE_CUP_ROUND_FIELD(name, json_object_new_string);
SERIALIZE_CUP_ROUND_FIELD(home_away, json_object_new_boolean);
SERIALIZE_CUP_ROUND_FIELD(replay, json_object_new_int64);
SERIALIZE_CUP_ROUND_FIELD(neutral, json_object_new_boolean);
SERIALIZE_CUP_ROUND_FIELD(randomise_teams, json_object_new_boolean);
SERIALIZE_CUP_ROUND_FIELD(round_robin_number_of_groups, json_object_new_int64);
SERIALIZE_CUP_ROUND_FIELD(round_robin_number_of_advance, json_object_new_int64);
SERIALIZE_CUP_ROUND_FIELD(round_robin_number_of_best_advance, json_object_new_int64);
SERIALIZE_CUP_ROUND_FIELD(round_robins, json_object_new_int64);
//SERIALIZE_CUP_ROUND_FIELD(rr_breaks, bygfoot_json_serialize_int_array);
SERIALIZE_CUP_ROUND_FIELD(new_teams, json_object_new_int64);
SERIALIZE_CUP_ROUND_FIELD(byes, json_object_new_int64);
SERIALIZE_CUP_ROUND_FIELD(delay, json_object_new_int64);
//SERIALIZE_CUP_ROUND_FIELD(two_match_weeks, bygfoot_json_serialize_int_int_array);
SERIALIZE_CUP_ROUND_FIELD_STRUCT(team_ptrs, bygfoot_json_serialize_team_ptrs);
//SERIALIZE_CUP_ROUND_FIELD(choose_teams, bygfoot_json_serialize_choose_teams);
//SERIALIZE_CUP_ROUND_FIELD(tables, bygfoot_json_serialize_tables);
//SERIALIZE_CUP_ROUND_FIELD(waits, bygfoot_json_seriialize_waits);
return cup_round_obj;
}
struct json_object *
bygfoot_json_serialize_cup_rounds(const GArray *rounds,
GHashTable *fields)
{
struct json_object *rounds_array_obj =
json_object_new_array_ext(rounds->len);
gint i;
for (i = 0; i < rounds->len; i++) {
const CupRound *round = &g_array_index(rounds, CupRound, i);
json_object_array_add(rounds_array_obj,
bygfoot_json_serialize_cup_round(round, fields));
}
return rounds_array_obj;
}
struct json_object *
bygfoot_json_serialize_cup(const Cup *cup, GHashTable *fields)
{
struct json_object *cup_obj = json_object_new_object();
#define SERIALIZE_CUP_FIELD(field, serialize_func) \
SERIALIZE_OBJECT_FIELD_FILTER(cup_obj, cup, field, serialize_func, fields);
#define SERIALIZE_CUP_FIELD_STRUCT(field, serialize_func) \
SERIALIZE_OBJECT_FIELD_STRUCT(cup_obj, cup, field, serialize_func, fields);
SERIALIZE_CUP_FIELD(name, json_object_new_string);
SERIALIZE_CUP_FIELD(short_name, json_object_new_string);
SERIALIZE_CUP_FIELD(symbol, json_object_new_string);
SERIALIZE_CUP_FIELD(sid, json_object_new_string);
SERIALIZE_CUP_FIELD(id, json_object_new_int64);
SERIALIZE_CUP_FIELD(group, json_object_new_int64);
SERIALIZE_CUP_FIELD(last_week, json_object_new_int64);
SERIALIZE_CUP_FIELD(week_gap, json_object_new_int64);
SERIALIZE_CUP_FIELD(add_week, json_object_new_int64);
SERIALIZE_CUP_FIELD(yellow_red, json_object_new_int64);
SERIALIZE_CUP_FIELD(talent_diff, json_object_new_double);
SERIALIZE_CUP_FIELD(next_fixture_update_week, json_object_new_int64);
SERIALIZE_CUP_FIELD(next_fixture_update_week_round, json_object_new_int64);
SERIALIZE_CUP_FIELD(properties, serialize_gchar_array);
SERIALIZE_CUP_FIELD_STRUCT(rounds, bygfoot_json_serialize_cup_rounds);
// SERIALIZE_CUP_FIELD(bye
SERIALIZE_CUP_FIELD_STRUCT(teams, bygfoot_json_serialize_team_ptrs);
SERIALIZE_CUP_FIELD(team_names, serialize_gchar_array);
// SERIALIZE_CUP_FIELD(fixtures,
// SERIALIZE_CUP_FIELD(week_breaks
// SERIALIZE_CUP_FIELD(skip_weeks_with
return cup_obj;
}

27
src/json_serialize.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef JSON_SERIALIZE_H
#define JSON_SERIALIZE_H
#include "bygfoot.h"
#include "cup_struct.h"
#include "league_struct.h"
#include "json_compat.h"
struct json_object *
bygfoot_json_serialize_country_list(GPtrArray *country_list);
struct json_object *
bygfoot_json_serialize_country(const Country *country);
struct json_object *
bygfoot_json_serialize_league_array(const GArray *league_array);
struct json_object *
bygfoot_json_serialize_league(const League *league);
struct json_object *
bygfoot_json_serialize_team_ptrs(GPtrArray *team_ptrs,
GHashTable *fields);
struct json_object *
bygfoot_json_serialize_cup(const Cup *cup, GHashTable *fields);
#endif

View File

@ -38,6 +38,9 @@
#include "file.h"
#include "free.h"
#include "job_struct.h"
#ifdef ENABLE_JSON
#include "json_interface.h"
#endif
#include "language.h"
#include "lg_commentary.h"
#include "live_game.h"
@ -70,6 +73,39 @@
(cl switch -l). */
gboolean load_last_save;
/** Parse command line options used for selecting the frontend or backend to
* use with bygfoot.
*/
static void
main_parse_frontend_backend_cl_arguments(gint *argc, gchar ***argv, CommandLineArgs *args)
{
GError *error = NULL;
GOptionContext *context = NULL;
GOptionEntry entries[] = {
{ "json", 0, 0, G_OPTION_ARG_FILENAME, &args->json_filename,
"JSON file containing commands to run. bygfoot will run the "
"commands in this file and then exit", "FILE"},
{NULL}
};
if(argc == NULL || argv == NULL)
return;
context = g_option_context_new(_("- a simple and addictive GTK2 football manager"));
g_option_context_set_ignore_unknown_options(context, TRUE);
g_option_context_add_main_entries(context, entries, GETTEXT_PACKAGE);
g_option_context_add_group(context, gtk_get_option_group (FALSE));
g_option_context_parse(context, argc, argv, &error);
g_option_context_free(context);
if(error != NULL)
{
misc_print_error(&error, FALSE);
return;
}
}
/** Parse the command line arguments given by the user. */
void
main_parse_cl_arguments(gint *argc, gchar ***argv, Bygfoot *bygfoot)
@ -411,6 +447,7 @@ main_init(gint *argc, gchar ***argv, Bygfoot *bygfoot)
main (gint argc, gchar *argv[])
{
Bygfoot bygfoot;
CommandLineArgs cl_args;
#ifdef DEBUG
printf("main\n");
#endif
@ -425,6 +462,18 @@ main (gint argc, gchar *argv[])
dup2 (fd1, 1);
int fd2 = open ("stderr.log", O_CREAT|O_WRONLY|O_TRUNC, 0666);
dup2 (fd2, 2);
#endif
memset(&cl_args, 0, sizeof(cl_args));
#ifdef ENABLE_JSON
main_parse_frontend_backend_cl_arguments(&argc, &argv, &cl_args);
if (cl_args.json_filename) {
bygfoot_init(&bygfoot, BYGFOOT_FRONTEND_CONSOLE);
main_init(&argc, &argv, &bygfoot);
file_check_home_dir_create_dirs();
validate_country_files();
return bygfoot_json_main(&bygfoot, &cl_args);
}
#endif
bygfoot_init(&bygfoot, BYGFOOT_FRONTEND_GTK2);
gtk_init (&argc, &argv);

86
test/test-country-defs.sh Normal file
View File

@ -0,0 +1,86 @@
bygfoot_bin=$1
bygfoot_bindir=`dirname $bygfoot_bin`
status=0
countries=" \
albania \
andorra \
armenia \
austria \
azerbaijan \
belarus \
belgium \
bosnia_herzegovina \
bulgaria \
croatia \
cyprus \
czech \
denmark \
england \
estonia \
faroe_islands \
finland \
france \
n_macedonia \
georgia \
germany \
gibraltar \
greece \
hungary \
iceland \
ireland \
israel \
italy \
kazakhstan \
kosovo \
latvia \
liechtenstein \
lithuania \
luxembourg \
malta \
moldova \
montenegro \
n_ireland \
netherlands \
norway \
poland \
portugal \
romania \
russia \
san_marino \
scotland \
serbia \
slovakia \
slovenia \
spain \
sweden \
switzerland \
turkey \
ukraine \
wales"
json_file=`mktemp`
pushd $bygfoot_bindir
for c in $countries; do
cat <<EOF > $json_file
{ 'commands' : [
{ 'add_country' : { 'name' : '$c' }},
{ 'start_bygfoot' : { }},
{ 'simulate_games' : {'seasons' : 2 }},
]}
EOF
tmphome=`mktemp -d`
if HOME=$tmphome ./bygfoot --random-seed=1 --json=$json_file; then
echo "$c: PASS"
else
echo "$c: FAIL"
status=1
fi
done
exit $status

36
test/test-load-save.sh Normal file
View File

@ -0,0 +1,36 @@
set -e
bygfoot_bin=$1
bygfoot_bindir=`dirname $bygfoot_bin`
json_file=`mktemp`
save_dir=`mktemp -d`
cat <<EOF > $json_file
{ 'commands' : [
{ 'add_country' : { 'name' : 'faroe_islands' }},
{ 'start_bygfoot' : {}},
{ 'simulate_games' : {'weeks' : 5}},
{ 'save_bygfoot' : {'filename' : '$save_dir/save0.zip'}},
{ 'load_bygfoot' : {'filename' : '$save_dir/save0.zip'}},
{ 'save_bygfoot' : {'filename' : '$save_dir/save1.zip'}},
{ 'simulate_games' : {'years' : 1}},
]}
EOF
echo $save_dir
tmphome=`mktemp -d`
pushd $bygfoot_bindir
HOME=$tmphome ./bygfoot --random-seed=1 --json=$json_file
for f in save0 save1; do
mkdir -p $save_dir/$f
unzip -q $save_dir/$f.zip -d $save_dir/$f
sed -i "s/${f}___//g" $save_dir/$f/*
for file in `ls $save_dir/$f`; do
new_name=`echo $file | sed "s/${f}___//g"`
mv $save_dir/$f/$file $save_dir/$f/$new_name
done
done
diff -r $save_dir/save0 $save_dir/save1