/* * Copyright (C) 2003 Tim Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "client.h" #include "callbacks.h" #include "utils.h" struct client_s { int sock; FILE *stream; tiles_t *tiles; map_t *map; player_t *me; player_t *players[5]; alert_callback_t *alert_func; void *alert_rock; popreport_callback_t *popreport_func; void *popreport_rock; finances_callback_t *finances_func; void *finances_rock; finances_loan_callback_t *finances_loan_func; void *finances_loan_rock; spotinfo_callback_t *spotinfo_func; void *spotinfo_rock; gameinfo_callback_t *listgames_func; void *listgames_rock; stats_callback_t *stats_func; void *stats_rock; char *value_string; int tax_rate; char curline[4096*4]; int lineoffset; }; /* forward declarations */ static void read_and_parse(client_t *client, int sync, int *didsomething); static void client_write(client_t *client, int flush, char *msg, ...) { char str[4096]; va_list ap; va_start(ap, msg); vsnprintf(str, sizeof(str)-1, msg, ap); fprintf(client->stream,"%s", str); if (flush) { if (fflush(client->stream)) { printf(_("Flush error: %s\n"), strerror(errno)); } } va_end(ap); } static int player_login(client_t *client, const char *playername) { int i; client_write(client, 1, "LOGIN %s\r\n", playername); for (i = 0; i < 4; i++) { player_new("foobar", &client->players[i+1]); } read_and_parse(client, 1, NULL); return 0; } int client_init(const char *server, short port, const char *playername, alert_callback_t *alert_cb, void *alert_rock, client_t **client) { struct sockaddr_in addr; struct hostent *hp; int sock; client_t *ret; ret = calloc(sizeof(client_t), 1); if (!ret) return -1; if ((hp = gethostbyname(server)) == NULL) { printf("gethostbyname for %s:%d", server, port); perror("gethostbyname"); return -1; } if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return -1; } addr.sin_family = AF_INET; memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); addr.sin_port = htons(port); if (connect(sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) { perror("connect"); return -1; } ret->sock = sock; ret->stream = fdopen(sock, "w+"); ret->alert_func = alert_cb; ret->alert_rock = alert_rock; player_login(ret, playername); *client = ret; return 0; } #define MAX_ARGS 100 /* * Make into a args structure */ static int make_args(client_t *client, char *str, char **argv, int maxargs, int *argc) { int numargs = 0; while ((str) && (*str != '\0')) { char *end; char *p = str; int inquotes = 0; if (*p == '{') { int len = atoi(p+1); char *data = malloc(len+1); fread(data, 1, len, client->stream); data[len] = '\0'; argv[numargs++] = data; break; } while ((*p != '\0') && ((inquotes) || (*p != ' '))) { if (*p == '"') { inquotes = (inquotes + 1) % 2; } p++; } end = p; if (*end != '\0') { *end = '\0'; end++; } argv[numargs++] = str; str = end; } *argc = numargs; argv[numargs] = NULL; return 0; } static void read_quad(char *str, int *quad) { char *next; int num; num = strtol(str, &next, 10); if (*next != ',') return; quad[RANGE_CURRENT_MONTH] = num; num = strtol(next+1, &next, 10); if (*next != ',') return; quad[RANGE_LAST_MONTH] = num; num = strtol(next+1, &next, 10); if (*next != ',') return; quad[RANGE_CURRENT_YEAR] = num; num = strtol(next+1, &next, 10); if (*next != ',') return; quad[RANGE_LAST_YEAR] = num; num = strtol(next+1, &next, 10); if (*next != '\0') { return; } quad[RANGE_ALLTIME] = num; } static void parse_finances(client_t *client, char **argv, int argc) { int i; int total[RANGE_NUMRANGES]; int total_recurring[RANGE_NUMRANGES]; for (i = 0; i < RANGE_NUMRANGES; i++) { total[i] = 0; total_recurring[i] = 0; } if (!client->finances_func) { return; } for (i = 0; i < argc; i++) { int quad[RANGE_NUMRANGES]; int j; char *str; for (j = 0; j < RANGE_NUMRANGES; j++) { quad[j] = 0; } str = strchr(argv[i],'='); if (str) { *str = '\0'; read_quad(str+1, quad); client->finances_func(argv[i], quad, client->finances_rock); if (!is_one_time(argv[i])) { for (j = 0; j < RANGE_NUMRANGES; j++) { total_recurring[j] += quad[j]; } } for (j = 0; j < RANGE_NUMRANGES; j++) { total[j] += quad[j]; } } else { printf("Parse error: %s\n", argv[i]); } } client->finances_func("-", 0, client->finances_rock); client->finances_func(_("Total"), total, client->finances_rock); client->finances_func(_("Total (excluding one time charges)"), total_recurring, client->finances_rock); } static void parse_loan(client_t *client, char **argv, int argc) { int amount; int term; int payments; float rate; char str[256]; char amountstr[256]; int num; if (argc < 5) { printf("Wrong number of arguments for LOAN\n"); return; } num = atoi(argv[0]); amount = atoi(argv[1]); term = atoi(argv[2]); payments = atoi(argv[3]); rate = atof(argv[4]); prettyprint_money(amount, amountstr, sizeof(amountstr)-1); snprintf(str, sizeof(str)-1, _("%s at %.02f - %d%% paid off"), amountstr, rate, (payments*100/term)); client->finances_loan_func(num, str, client->finances_loan_rock); } static void parse_population(client_t *client, char **argv, int argc) { int age; int edlevel; int size = 0; int income = 0; int housed = 0; int work = 0; int happy = 0; int i; if (argc < 2) { printf("Wrong number of arguments for POPULATION\n"); return; } age = atoi(argv[0]); edlevel = atoi(argv[1]); for (i = 2; i < argc; i++) { if (strncasecmp(argv[i],"SIZE=",5) == 0) { size = atoi(argv[i]+5); } else if (strncasecmp(argv[i],"INCOME=",7) == 0) { income = atoi(argv[i]+7); } else if (strncasecmp(argv[i],"HAPPINESS=",10) == 0) { happy = atoi(argv[i]+10); } else if (strncasecmp(argv[i],"HOUSED=",7) == 0) { housed = atoi(argv[i]+7); } else if (strncasecmp(argv[i],"WORKING=",8) == 0) { work = atoi(argv[i]+8); } } if (client->popreport_func) { client->popreport_func(age, edlevel, size, income, housed, work, happy, client->popreport_rock); } else { printf("No popreport specified!\n"); } } static void parse_spotinfo(client_t *client, char **argv, int argc) { int i; for (i = 0; i < argc; i++) { char *name; char *value; name = argv[i]; value = strchr(name, '='); if (value) { *value = '\0'; value++; client->spotinfo_func(name, value, client->spotinfo_rock); } } } static void parse_data(client_t *client, char *line, int *changedmap) { char *space; char *argv[MAX_ARGS+1]; int argc = 0; space = strchr(line,' '); if (space) { *space = '\0'; space++; make_args(client, space, argv, MAX_ARGS, &argc); } if (strcasecmp(line,"BOARDSIZE") == 0) { int r; if (argc != 2) { printf("boardsize requires 2 arguments!\n"); return; } r = map_init(client->tiles, atoi(argv[0]), atoi(argv[1]), map_item_emptytype(), &client->map); if (r) { printf("Error making map\n"); } if (changedmap) *changedmap = 1; } else if ((strcasecmp(line,"BOARDLINE") == 0) && (client->map)) { int x; int y; int mapsizex; if (argc != 2) { printf("boardline requires 2 arguments!\n"); return; } y = atoi(argv[0]); mapsizex = map_get_sizex(client->map); for (x = 0; x < mapsizex; x++) { mapspot_t spot; map_string2spot(&argv[1][4*x], &spot); if (map_set_spot(client->map, NULL, x, y, 0, 1, &spot)) { printf("Error setting spot at %d,%d\n", x, y); } } if (changedmap) *changedmap = 1; } else if ((strcasecmp(line,"BOARDSPOT") == 0) && (client->map)) { int x; int y; mapspot_t spot; if (argc != 3) { printf("boardline requires 3 arguments!\n"); return; } x = atoi(argv[0]); y = atoi(argv[1]); map_string2spot(argv[2], &spot); map_set_spot(client->map, NULL, x, y, 0, 1, &spot); if (changedmap) *changedmap = 1; } else if (strcasecmp(line,"PLAYERINFO") == 0) { char *name = argv[0]; char *value = argv[1]; if (strcasecmp(name,"NUMBER") == 0) { client->me = client->players[atoi(value)]; player_setnumber(client->me, atoi(value)); } else if (strcasecmp(name,"CASH") == 0) { player_setmoney(client->me, atoi(value)); updatemoney(); } else { printf("Unknown playerinfo thing: %s\n",name); } } else if (strcasecmp(line,"CURTIME") == 0) { time_t curtime = atoi(argv[0]); set_current_time(curtime); } else if (strcasecmp(line,"WEATHER") == 0) { int temp = atoi(argv[0]); set_current_weather(argv[1], temp); } else if (strcasecmp(line,"SPEED") == 0) { int speed = atoi(argv[0]); set_speed_ui(speed); } else if (strcasecmp(line,"POPULATION") == 0) { parse_population(client, argv, argc); } else if (strcasecmp(line,"FINANCES") == 0) { parse_finances(client, argv, argc); } else if (strcasecmp(line,"LOAN") == 0) { parse_loan(client, argv, argc); } else if (strcasecmp(line,"SPOTINFO") == 0) { parse_spotinfo(client, argv, argc); } else if (strcasecmp(line,"REPOSITION") == 0) { int x; int y; if (argc != 2) { printf("reposition requires 2 arguments!\n"); return; } x = atoi(argv[0]); y = atoi(argv[1]); screen_center_on_xy(client->map, x, y); } else if (strcasecmp(line,"VALUE") == 0) { client->value_string = strdup(argv[0]); } else if (strcasecmp(line,"TAXRATE") == 0) { client->tax_rate = atoi(argv[0]); } else if (strcasecmp(line,"GAMEOVER") == 0) { client->alert_func(argv[0], client->alert_rock); } else if (strcasecmp(line,"GAME") == 0) { char *name = argv[0]; if (client->listgames_func) { client->listgames_func(name, client->listgames_rock); } } else if (strcasecmp(line,"STAT") == 0) { char *name = argv[0]; char *value = argv[1]; if (client->stats_func) { client->stats_func(name, value, client->stats_rock); } } else { printf("command not understood! '%s'\n",line); } } static int selectone(int fd) { return 0; /* * This messes up the FILE * */ #if 0 fd_set readfds; struct timeval timeout; int r; return 0; timeout.tv_sec = 0; timeout.tv_usec = 0; FD_ZERO(&readfds); FD_SET(fd, &readfds); r = select(fd+1, &readfds, NULL, NULL, &timeout); if (r > 0) { return 1; } else { return 0; } #endif /* 0 */ } static void read_and_parse(client_t *client, int sync, int *changedmap) { char *line; while (1) { int len; if ((!sync) && (!selectone(client->sock))) { return; } if (!fgets(client->curline + client->lineoffset, sizeof(client->curline)-1 - client->lineoffset, client->stream)) { continue; } len = strlen(client->curline); if (client->curline[len-1] != '\n') { client->lineoffset = len; continue; } client->lineoffset = 0; line = client->curline; /* remove any newline */ if (line[len-1] == '\n') { len--; } if (line[len-1] == '\r') { len--; } line[len] = '\0'; if (*line == '*') { parse_data(client, line+2, changedmap); } else if (strncasecmp(line,"OK",2) == 0) { return; } else if (strncasecmp(line,"NO",2) == 0) { printf("ERROR! %s\n",line+3); return; } else { printf("Unexpected! %s\n",line); } } printf("fgets returned NULL\n"); } map_t *client_getmap(client_t *client, tiles_t *tiles) { if (client->map) { return client->map; } client_write(client, 1, "REFRESHBOARD\r\n"); client->tiles = tiles; read_and_parse(client, 1, NULL); return client->map; } map_t *client_newmap(client_t *client, tiles_t *tiles, int sizex, int sizey) { client_write(client, 1, "NEWBOARD %d %d\r\n",sizex, sizey); client->tiles = tiles; read_and_parse(client, 1, NULL); return client->map; } int client_txn_start(client_t *client) { client_write(client, 1, "TXN_START\r\n"); read_and_parse(client, 1, NULL); return 0; } int client_txn_commit(client_t *client) { client_write(client, 1, "TXN_COMMIT\r\n"); read_and_parse(client, 1, NULL); return 0; } int client_setspot(client_t *client, int x, int y, mapobj_t mapobj) { mapspot_t spot; memset(&spot, 255, sizeof(mapspot_t)); spot.mapobj = mapobj; client_write(client, 1, "SET %d %d %s\r\n",x, y, map_spot2string(&spot)); read_and_parse(client, 1, NULL); return 0; } int client_changeheight(client_t *client, mapspot_list_t *list) { mapspot_list_t *sentlist = NULL; while (list) { int x; int y; mapspot_t spot; int change; mapspot_list_extract(&list, &x, &y, (void **)&change); map_get_spot(client->map, x, y, &spot); spot.height += (mapspot_list_num_within(sentlist, x, y, 0.0) + 1) * change; client_write(client, 1, "SET %d %d %s\r\n", x, y, map_spot2string(&spot)); read_and_parse(client, 1, NULL); mapspot_list_add(&sentlist, x, y, NULL); } mapspot_list_free(sentlist); return 0; } void client_checkpoint(client_t *client, int *mapchanged) { if (!client) return; client_write(client, 1, "NOOP\r\n"); read_and_parse(client, 1, mapchanged); } int client_buyland(client_t *client, int x, int y) { mapspot_t spot; memset(&spot, 255, sizeof(mapspot_t)); spot.owner = player_getnum(client->me); client_write(client, 1, "SET %d %d %s\r\n",x, y, map_spot2string(&spot)); read_and_parse(client, 1, NULL); return 0; } int client_sellland(client_t *client, int x, int y) { mapspot_t spot; memset(&spot, 255, sizeof(mapspot_t)); spot.owner = NO_OWNER; client_write(client, 1, "SET %d %d %s\r\n",x, y, map_spot2string(&spot)); read_and_parse(client, 1, NULL); return 0; } player_t *client_getme(client_t *client) { return client->me; } int client_getpopreport(client_t *client, popreport_callback_t *func, void *rock) { client->popreport_func = func; client->popreport_rock = rock; client_write(client, 1, "POPULATIONSTATS\r\n"); read_and_parse(client, 1, NULL); client->popreport_func = NULL; client->popreport_rock = NULL; return 0; } int client_getfinances(client_t *client, finances_callback_t *func, void *rock, finances_loan_callback_t *loan_func, void *loan_rock) { client->finances_func = func; client->finances_rock = rock; client->finances_loan_func = loan_func; client->finances_loan_rock = loan_rock; client_write(client, 1, "FINANCES\r\n"); read_and_parse(client, 1, NULL); client->finances_func = NULL; client->finances_rock = NULL; return 0; } int client_setspeed(client_t *client, game_speeds_t speed) { client_write(client, 1, "SETSPEED %d\r\n",speed); read_and_parse(client, 1, NULL); return 0; } int client_getspot_info(client_t *client, int x, int y, spotinfo_callback_t *func, void *rock) { client->spotinfo_func = func; client->spotinfo_rock = rock; client_write(client, 1, "SPOTINFO %d %d\r\n", x, y); read_and_parse(client, 1, NULL); client->spotinfo_func = NULL; client->spotinfo_rock = NULL; return 0; } int client_borrow(client_t *client, int amount) { client_write(client, 1, "BORROW %d\r\n", amount); read_and_parse(client, 1, NULL); return 0; } int client_listgames(client_t *client, gameinfo_callback_t *cb, void *rock) { client->listgames_func = cb; client->listgames_rock = rock; client_write(client, 1, "LISTGAMES\r\n"); read_and_parse(client, 1, NULL); client->listgames_func = NULL; client->listgames_rock = NULL; return 0; } int client_getstats(client_t *client, stats_callback_t *cb, void *rock) { client->stats_func = cb; client->stats_rock = rock; client_write(client, 1, "STATS\r\n"); read_and_parse(client, 1, NULL); client->stats_func = NULL; client->stats_rock = NULL; return 0; } char * client_getgoal(client_t *client) { client_write(client, 1, "GETGOAL\r\n"); read_and_parse(client, 1, NULL); return client->value_string; } char * client_gethappiness(client_t *client) { client_write(client, 1, "GETHAPPINESS\r\n"); read_and_parse(client, 1, NULL); return client->value_string; } int client_selectgame(client_t *client, const char *name) { client_write(client, 1, "SELECTGAME %s\r\n", name); read_and_parse(client, 1, NULL); return 0; } void client_close(client_t *client) { fclose(client->stream); close(client->sock); } void client_free(client_t *client) { free(client); } int client_payoff_loan(client_t *client, int num) { client_write(client, 1, "PAYBACK %d\r\n", num); read_and_parse(client, 1, NULL); return 0; } int client_gettaxrate(client_t *client, char *name) { client_write(client, 1, "GETTAX %s\r\n", name); read_and_parse(client, 1, NULL); return client->tax_rate; } void client_settaxrate(client_t *client, char *name, int rate) { client_write(client, 1, "SETTAX %s %d\r\n", name, rate); read_and_parse(client, 1, NULL); }