static void bridge_next_bid(void) { /* closely based on Suaro code, below */ if (BRIDGE.pass_count == 4) { /* done bidding */ if (BRIDGE.contract == 0) { ggz_debug(DBG_GAME, "Four passes; redealing hand."); set_global_message("", "Everyone passed; redealing."); set_game_state(STATE_NEXT_HAND); /* redeal hand */ } else { ggz_debug(DBG_GAME, "Three passes; bidding is over."); game.bid_total = game.bid_count; /* contract was determined in game_handle_bid */ } } else { if (game.bid_count == 0) game.next_bid = game.dealer; else game.next_bid = (game.next_bid + 1) % game.num_players; } }
void game_get_bid(int possible_bids, bid_t * bid_choices, char **bid_texts, char **bid_descs) { /* We ignore bid_texts and bid_descs */ bid_t bid = get_bid(bid_choices, possible_bids); int i; ggz_debug(DBG_BID, "Making bid %d (%d/%d/%d).", bid.bid, bid.sbid.val, bid.sbid.suit, bid.sbid.spec); for (i = 0; i < possible_bids; i++) { if (bid.bid == bid_choices[i].bid) { client_send_bid(i); return; } } /* This can happen when some variant is enabled that the AI doesn't support. The best choice is to find the closest matching bid. */ ggz_debug(DBG_BID, "Uh oh; invalid bid!"); for (i = 0; i < possible_bids; i++) { if (bid.sbid.spec == bid_choices[i].sbid.spec) { client_send_bid(i); return; } } client_send_bid(random() % possible_bids); }
int _ggzcore_server_connect(GGZServer * server) { int status; char *errmsg; if (server) { if (thread_policy) { ggz_set_network_notify_func(connection_callback); reconnect_server = server; } _ggzcore_server_change_state(server, GGZ_TRANS_CONN_TRY); status = _ggzcore_net_connect(server->net); } else { ggz_set_network_notify_func(NULL); server = reconnect_server; reconnect_server = NULL; status = _ggzcore_net_get_fd(server->net); } if (status == GGZ_SOCKET_PENDING) { /* Asynchronous lookup */ ggz_debug(GGZCORE_DBG_SERVER, "<gai> start lookup"); return 0; } if (status < 0) { _ggzcore_server_change_state(server, GGZ_TRANS_CONN_FAIL); #ifdef HAVE_HSTRERROR /* Unknown shouldn't happen - see ggz-cmd vs. ggz-txt */ if (h_errno == 0) if (errno == 0) errmsg = _("Unknown reason"); else errmsg = strerror(errno); else errmsg = (char *)hstrerror(h_errno); #else /* Not all systems have hstrerror. */ errmsg = _("Unable to connect"); #endif _ggzcore_server_event(server, GGZ_CONNECT_FAIL, errmsg); } else _ggzcore_server_event(server, GGZ_CONNECTED, NULL); if (thread_policy) { ggz_debug(GGZCORE_DBG_SERVER, "<gai> return %i", status); } return status; }
static void server_print(gpointer server_ptr, gpointer data) { const Server *server = server_ptr; ggz_debug("servers", "Profile name: %s\n", server->name); ggz_debug("servers", " Host %s:%d\n", server->host, server->port); ggz_debug("servers", " Login type: %d\n", server->type); ggz_debug("servers", " Login: %s\n", server->login); if (server->type == GGZ_LOGIN) ggz_debug("servers", " Password: %s\n", server->password); }
void _ggzcore_state_transition(GGZTransID trans, GGZStateID *cur) { int i = 0; struct _GGZTransition *transitions; GGZStateID next = -1; transitions = _ggz_states[*cur].transitions; /* Look through valid transitions to see if this one is OK */ while (transitions[i].id != -1) { if (transitions[i].id == trans) { next = transitions[i].next; break; } ++i; } if (next != *cur && next != -1) { ggz_debug(GGZCORE_DBG_STATE, "State transition %s -> %s", _ggz_states[*cur].name, _ggz_states[next].name); *cur = next; } else if (next == -1) { ggz_error_msg("No state transition for %d from %s!", trans, _ggz_states[*cur].name); } }
static void put_global_message(char *mark, char *msg) { global_message_list_t *gml; ggz_debug(DBG_MISC, "Setting global message for '%s'. Length is %zd.", mark, strlen(msg)); if (!mark || !msg) ggz_error_msg("put_global_message called on NULL string."); for (gml = game.message_head; gml != NULL; gml = gml->next) { if (!strcmp(mark, gml->mark)) { ggz_free(gml->message); gml->message = ggz_strdup(msg); return; } } gml = ggz_malloc(sizeof(global_message_list_t)); gml->mark = ggz_strdup(mark); gml->message = ggz_strdup(msg); if (game.message_tail) game.message_tail->next = gml; game.message_tail = gml; if (!game.message_head) game.message_head = gml; }
static void ggz_server_error(const char *msg_type, const char *msg_explanation) { gchar *msg; ggz_debug("connection", "%s: %s.", msg_type, msg_explanation); assert(ggz_gtk.server_tag != 0); g_source_remove(ggz_gtk.server_tag); ggz_gtk.server_tag = 0; clear_room_list(); clear_player_list(); clear_table_list(); main_activate(); if (ggzcore_server_get_state(ggz_gtk.server) != GGZ_STATE_RECONNECTING) { msg = g_strdup_printf(_("%s: %s"), msg_type, msg_explanation); msgbox(msg, _("Error"), MSGBOX_OKONLY, MSGBOX_STOP, MSGBOX_NORMAL); g_free(msg); } }
/* Setup some of the table data structures. This is called just once immediately upon startup. */ void table_initialize(void) { static int call_count = 0; /* Just a sanity check; we don't want to call this function twice. */ assert(call_count == 0); call_count++; ggz_debug(DBG_TABLE, "Initializing table."); /* This starts our drawing code */ table = g_object_get_data(G_OBJECT(dlg_main), "fixed1"); table_style = gtk_widget_get_style(table); gtk_widget_show(table); table_drawing_area = gtk_drawing_area_new(); gtk_widget_set_size_request(table_drawing_area, get_table_width(), get_table_height()); gtk_fixed_put(GTK_FIXED(table), table_drawing_area, 0, 0); gtk_widget_show(table_drawing_area); g_signal_connect(table_drawing_area, "expose_event", GTK_SIGNAL_FUNC(on_table_expose_event), NULL); assert(table_drawing_area->window); assert(get_table_width() > 0 && get_table_height() > 0); table_buf = gdk_pixmap_new(table->window, get_table_width(), get_table_height(), -1); assert(table_buf); /* Redraw and display the table. */ table_redraw(); table_show_player_list(); }
/* Handle a redraw of necessary items, for instance when a Gtk style change is signaled. */ void table_redraw(void) { ggz_debug(DBG_TABLE, "Redrawing table. "); if (table_ready) { int p; /* Complete (zip) any animation in process */ animation_stop(TRUE); /* I really don't know why these are necessary... */ gtk_widget_grab_focus(dlg_main); table_style = gtk_widget_get_style(table); /* Redraw everything to the buffer */ table_clear_table(FALSE); draw_card_areas(FALSE); table_display_all_hands(FALSE); table_show_cards(FALSE); for (p = 0; p < ggzcards.num_players; p++) table_show_player_box(p, FALSE); /* Then draw the whole buffer to the window */ table_show_table(0, 0, get_table_width(), get_table_height()); /* There has GOT to be a better way to force the redraw! */ gdk_window_hide(table_drawing_area->window); gdk_window_show(table_drawing_area->window); } else { /* not if (table_ready) */ if (table_buf) draw_splash_screen(); } }
void _ggzcore_server_change_state(GGZServer * server, GGZTransID trans) { if (trans == GGZ_TRANS_NET_ERROR || trans == GGZ_TRANS_PROTO_ERROR) { #ifdef SUPPORT_RECONNECT if (reconnect_policy) { char *host; int port; GGZConnectionPolicy policy; ggz_debug(GGZCORE_DBG_SERVER, "Setting up reconnection attempt"); reconnect_server = server; host = ggz_strdup(_ggzcore_net_get_host(server->net)); port = _ggzcore_net_get_port(server->net); policy = _ggzcore_net_get_policy(server->net); _ggzcore_net_free(server->net); server->net = _ggzcore_net_new(); _ggzcore_net_init(server->net, server, host, port, policy); ggz_free(host); _ggzcore_server_clear_reconnect(server); server->state = GGZ_STATE_RECONNECTING; _ggzcore_server_event(server, GGZ_STATE_CHANGE, NULL); signal(SIGALRM, reconnect_alarm); alarm(reconnect_timeout); return; } #endif } _ggzcore_state_transition(trans, &server->state); _ggzcore_server_event(server, GGZ_STATE_CHANGE, NULL); }
void game_get_play(int play_hand, int num_valid_cards, card_t * valid_cards) { int play_num, hand_num; hand_t *hand = &ggzcards.players[play_hand].hand; bool valid_plays[hand->hand_size]; card_t play; assert(play_hand == ggzcards.play_hand); for (hand_num = 0; hand_num < hand->hand_size; hand_num++) valid_plays[hand_num] = FALSE; for (play_num = 0; play_num < num_valid_cards; play_num++) { card_t play_card = valid_cards[play_num]; for (hand_num = 0; hand_num < hand->hand_size; hand_num++) { card_t hand_card = hand->cards[hand_num]; if (are_cards_equal(play_card, hand_card)) { valid_plays[hand_num] = TRUE; break; } } assert(hand_num < hand->hand_size); } play = get_play(ggzcards.play_hand, valid_plays); ggz_debug(DBG_PLAY, "We're playing the %s of %s.", get_face_name(play.face), get_suit_name(play.suit)); make_play(play); }
/* Check for what card has been clicked and process it */ gboolean table_handle_cardclick_event(GdkEventButton * event) { /* this function is tricky. There are lots of different variables: x, y, w, h describe the card area itself, including the "selected card" area. xo and yo describe the offset given by the "selected card". x1 and y1 are specific coordinates of a card. xdiff and ydiff is the overlapping offset for cards in hand. There are so many variables because hands can be facing any direction, and it's possible to be playing from *any* hand (at least in theory). */ int target; int which = -1; int p = ggzcards.play_hand; /* player whose hand it is */ int card_width, card_height; int hand_size; /* If it's not our turn to play, we don't care. */ if (ggzcards.state != STATE_PLAY) return FALSE; assert(p >= 0 && p < ggzcards.num_players); ggz_debug(DBG_TABLE, "table_handle_click_event: " "click at %f %f.", event->x, event->y); /* This gets all of the layout information from the layout engine. Unfortunately, it's very dense code. */ card_width = get_card_width(get_orientation(p)); card_height = get_card_height(get_orientation(p)); /* Calculate our card target */ hand_size = preferences.collapse_hand ? ggzcards.players[p].hand.hand_size : ggzcards.players[p].u_hand_size; for (target = 0; target < hand_size; target++) { int x, y; if (!preferences.collapse_hand && !ggzcards.players[p].u_hand[target].is_valid) continue; get_card_pos(p, target, target == selected_card, &x, &y); if (event->x >= x && event->x <= x + card_width /* TODO: generalize for any orientation */ && event->y >= y && event->y <= y + card_height) which = target; } if (which == -1) /* The click wasn't actually on a card. */ return FALSE; /* Handle the click. */ table_card_clicked(which); return TRUE; }
/* Exposed function to show one player's hand. */ void table_display_hand(int p, int write_to_screen) { int i; card_t table_card = table_cards[p]; int hand_size; #if 0 /* It looks like the server violates this, although it's probably a bug in the server. */ assert(table_ready && game_started); #endif /* The server may send out a hand of size 0 when we first connect, but we just want to ignore it. */ if (!table_ready) return; ggz_debug(DBG_TABLE, "Displaying hand for player %d.", p); /* redraw outer rectangle */ clear_card_area(p); draw_card_box(p); /* Draw the cards */ hand_size = preferences.collapse_hand ? ggzcards.players[p].hand.hand_size : ggzcards.players[p].u_hand_size; for (i = 0; i < hand_size; i++) { card_t card; int x, y; if (preferences.collapse_hand) card = ggzcards.players[p].hand.cards[i]; else { if (!ggzcards.players[p].u_hand[i].is_valid) continue; card = ggzcards.players[p].u_hand[i].card; } if (card.face >= 0 && card.face == table_card.face && card.suit >= 0 && card.suit == table_card.suit && card.deck >= 0 && card.deck == table_card.deck) /* if the player has a card on the table _and_ it matches this card, skip over it. */ continue; get_card_pos(p, i, p == ggzcards.play_hand && i == selected_card, &x, &y); draw_card(card, get_orientation(p), x, y, table_buf); } /* And refresh the on-screen image for card areas */ if (write_to_screen) show_card_area(p); }
int _ggzcore_server_logout(GGZServer * server) { int status; ggz_debug(GGZCORE_DBG_SERVER, "Logging out"); status = _ggzcore_net_send_logout(server->net); if (status == 0) _ggzcore_server_change_state(server, GGZ_TRANS_LOGOUT_TRY); return status; }
int _ggzcore_server_disconnect(GGZServer * server) { ggz_debug(GGZCORE_DBG_SERVER, "Disconnecting and going offline"); _ggzcore_net_disconnect(server->net); /* Force the server object into the offline state */ server->state = GGZ_STATE_OFFLINE; _ggzcore_server_event(server, GGZ_STATE_CHANGE, NULL); return 0; }
static GGZHookReturn game_playing(GGZGameEvent id, const void *event_data, const void *user_data) { ggz_debug("game", "Game module connected to server over game channel"); if (launch_in_process()) launch_table(); else client_join_table(); return GGZ_HOOK_OK; }
/* Displays a player's message on the table. */ void table_set_player_message(int player, const char *message) { ggz_debug(DBG_TABLE, "Setting player message for %d.", player); if (player_messages[player]) ggz_free(player_messages[player]); player_messages[player] = ggz_strdup(message); if (table_ready) table_show_player_box(player, TRUE); }
void _ggzcore_server_set_login_status(GGZServer * server, GGZClientReqError status) { ggz_debug(GGZCORE_DBG_SERVER, "Status of login: %d", status); if (status == E_OK) { _ggzcore_server_change_state(server, GGZ_TRANS_LOGIN_OK); _ggzcore_server_event(server, GGZ_LOGGED_IN, NULL); } else { GGZErrorEventData error = {.status = status, .message = NULL}; switch (status) { case E_ALREADY_LOGGED_IN: error.message = _("Already logged in"); break; case E_USR_LOOKUP: error.message = _("The password was incorrect"); break; case E_USR_TAKEN: error.message = _("Name is already taken"); break; case E_USR_TYPE: error.message = _("This name is already registered " "so cannot be used by a guest"); break; case E_USR_FOUND: error.message = _("No such name was found"); break; case E_TOO_LONG: error.message = _("Name too long"); break; case E_BAD_USERNAME: error.message = _("Name contains forbidden " "characters"); break; case E_BAD_PASSWORD: error.message = _("You must enter a legitimate " "password"); break; case E_BAD_OPTIONS: error.message = _("Missing password or other bad " "options."); break; default: error.message = _("Unknown login error"); break; } _ggzcore_server_change_state(server, GGZ_TRANS_LOGIN_FAIL); _ggzcore_server_event(server, GGZ_LOGIN_FAIL, &error); } }
static void reconnect_alarm(int sig) { ggz_debug(GGZCORE_DBG_SERVER, "Trying to reconnect..."); if (_ggzcore_net_connect(reconnect_server->net) < 0) { reconnect_server->state = GGZ_STATE_RECONNECTING; alarm(reconnect_timeout); } else { _ggzcore_server_change_state(reconnect_server, GGZ_TRANS_CONN_OK); _ggzcore_server_event(reconnect_server, GGZ_CONNECTED, NULL); } }
int _ggzcore_server_create_channel(GGZServer *server) { int status; const char *host; unsigned int port; char *errmsg; /* FIXME: make sure we don't already have a channel */ server->channel = _ggzcore_net_new(); host = _ggzcore_net_get_host(server->net); port = _ggzcore_net_get_port(server->net); _ggzcore_net_init(server->channel, server, host, port, 0); status = _ggzcore_net_connect(server->channel); if (status < 0 && status != GGZ_SOCKET_PENDING) { ggz_debug(GGZCORE_DBG_SERVER, "Channel creation failed"); #ifdef HAVE_HSTRERROR errmsg = (char*)hstrerror(h_errno); #else /* Not all systems have hstrerror. */ errmsg = _("Unable to connect"); #endif _ggzcore_server_event(server, GGZ_CHANNEL_FAIL, errmsg); } else { if (status != GGZ_SOCKET_PENDING) { ggz_debug(GGZCORE_DBG_SERVER, "Channel created"); _ggzcore_server_event(server, GGZ_CHANNEL_CONNECTED, NULL); } else { if (thread_policy) { ggz_set_network_notify_func(channel_callback); reconnect_server = server; } ggz_debug(GGZCORE_DBG_SERVER, "Channel creation deferred"); } } return status; }
/* Display's a player's name on the table. */ void table_set_name(int player, const char *name) { ggz_debug(DBG_TABLE, "Setting player name: %d => %s.", player, name); if (player_names[player]) ggz_free(player_names[player]); player_names[player] = ggz_strdup(name); if (table_ready) table_show_player_box(player, TRUE); }
static GGZHookReturn ggz_connected(GGZServerEvent id, const void *event_data, const void *user_data) { int fd; if (id == GGZ_CONNECTED) { GIOChannel *channel; /* Add the fd to the gtk main loop */ ggz_debug("connection", "We're connected."); fd = ggzcore_server_get_fd(ggz_gtk.server); assert(ggz_gtk.server_tag == 0); assert(fd >= 0); channel = g_io_channel_unix_new(fd); ggz_gtk.server_tag = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN, ggz_check_fd, GINT_TO_POINTER(fd), ggz_input_removed); g_io_channel_unref(channel); } else if (id == GGZ_CHANNEL_CONNECTED) { GIOChannel *channel; /* Add the fd to the gtk main loop */ ggz_debug("connection", "Direct game channel connected."); fd = ggzcore_server_get_channel(ggz_gtk.server); assert(ggz_gtk.channel_tag == 0); assert(fd >= 0); channel = g_io_channel_unix_new(fd); ggz_gtk.channel_tag = g_io_add_watch(channel, G_IO_IN, ggz_check_fd, GINT_TO_POINTER(fd)); g_io_channel_unref(channel); } return GGZ_HOOK_OK; }
void set_player_message(player_t p) { if (game.data == NULL) { /* silently fail; it's easier to check here than elsewhere */ return; } ggz_debug(DBG_MISC, "Setting player %d/%s's message.", p, get_player_name(p)); if (p < 0 || p >= game.num_players) ggz_error_msg("set_player_message(%d) called.", p); game.data->set_player_message(p); broadcast_player_message(game.players[p].seat); }
static GGZHookReturn ggz_channel_ready(GGZGameEvent id, const void *event_data, const void *user_data) { ggz_debug("connection", "Direct game channel ready for game"); /* Remove channel from gdk input list */ assert(ggz_gtk.channel_tag != 0); g_source_remove(ggz_gtk.channel_tag); ggz_gtk.channel_tag = 0; game_channel_ready(); return GGZ_HOOK_OK; }
int _ggzcore_server_join_room(GGZServer * server, GGZRoom *room) { int status; int room_id = _ggzcore_room_get_id(room); ggz_debug(GGZCORE_DBG_SERVER, "Moving to room %d", room_id); status = _ggzcore_net_send_join_room(server->net, room_id); server->new_room = room; if (status == 0) _ggzcore_server_change_state(server, GGZ_TRANS_ENTER_TRY); return status; }
static void bridge_handle_bid(player_t p, bid_t bid) { assert(game.next_bid == p); /* closely based on the Suaro code */ ggz_debug(DBG_GAME, "The bid chosen is %d %s %d.", bid.sbid.val, short_bridge_suit_names[(int)bid.sbid.suit], bid.sbid.spec); if (bid.sbid.spec == BRIDGE_PASS) { BRIDGE.pass_count++; } else if (bid.sbid.spec == BRIDGE_DOUBLE || bid.sbid.spec == BRIDGE_REDOUBLE) { BRIDGE.pass_count = 1; BRIDGE.bonus *= 2; } else { BRIDGE.contract = bid.sbid.val; BRIDGE.contract_suit = bid.sbid.suit; BRIDGE.bonus = 1; BRIDGE.pass_count = 1; if (BRIDGE.opener[p % 2][BRIDGE.contract_suit] == -1) BRIDGE.opener[p % 2][BRIDGE.contract_suit] = p; BRIDGE.declarer = BRIDGE.opener[p % 2][BRIDGE.contract_suit]; BRIDGE.dummy = (BRIDGE.declarer + 2) % 4; ggz_debug(DBG_GAME, "Setting bridge contract to %d %s.", BRIDGE.contract, long_bridge_suit_names[BRIDGE.contract_suit]); if (bid.sbid.suit != BRIDGE_NOTRUMP) { set_trump_suit(bid.sbid.suit); } else { set_trump_suit(NO_SUIT); } } }
/* Handle a card that is clicked by either popping it forward or playing it if it is already selected. */ static void table_card_clicked(int card_num) { ggz_debug(DBG_TABLE, "table_card_clicked: Card %d clicked.", card_num); if (card_num == selected_card || preferences.single_click_play) { /* If you click on the already selected card, it gets played. There's also an option so that you need only click once. */ selected_card = -1; game_play_card(card_num); } else { /* Pop the card forward and select it */ selected_card = card_num; table_display_hand(ggzcards.play_hand, TRUE); } }
static GGZHookReturn ggz_logout(GGZServerEvent id, const void *event_data, const void *user_data) { ggz_debug("connection", "Logged out."); if (ggzcore_server_get_state(ggz_gtk.server) != GGZ_STATE_RECONNECTING) { server_disconnect(); } /* set title */ if (!ggz_gtk.embedded_protocol_version) { gtk_window_set_title(GTK_WINDOW(ggz_gtk.main_window), _("GGZ Gaming Zone")); } return GGZ_HOOK_OK; }
static GGZHookReturn game_negotiated(GGZGameEvent id, const void *event_data, const void *user_data) { ggz_debug("game", "Game module ready (creating channel)"); #ifndef HAVE_WINSOCK_H ggzcore_server_create_channel(ggz_gtk.server); #endif if(ggz_async_fd() != -1) { GIOChannel *channel; channel = g_io_channel_unix_new(ggz_async_fd()); ggz_gtk.resolv_tag = g_io_add_watch(channel, G_IO_IN, game_check_asyncfd, GINT_TO_POINTER(ggz_async_fd())); g_io_channel_unref(channel); } return GGZ_HOOK_OK; }
/* Draws a "splash screen" that is shown before the game is initialized. */ static void draw_splash_screen(void) { #if 0 card_t card = { ACE_HIGH, SPADES, 0 }; #endif ggz_debug(DBG_TABLE, "Drawing splash screen."); assert(!game_started && !table_ready); assert(table_buf); table_clear_table(FALSE); #if 0 /* This is temporarily disabled until I can figure out how to get it to work with the player list. */ draw_card(card, 0, (get_table_width() - CARDWIDTH) / 2, (get_table_height() - CARDHEIGHT) / 2, table_buf); #endif table_show_table(0, 0, get_table_width(), get_table_height()); }