1 /* $Header: /home/jcb/MahJong/newmj/RCS/client.c,v 12.0 2009/06/28 20:43:12 jcb Rel $
2  * client.c
3  * Provides generic client support. It connects to the controller,
4  * and maintains a game data structure in response to the Controller
5  * messages. After updating the game structure, it (will) invokes a callback
6  * installed by its user.
7  */
8 /****************** COPYRIGHT STATEMENT **********************
9  * This file is Copyright (c) 2000 by J. C. Bradfield.       *
10  * Distribution and use is governed by the LICENCE file that *
11  * accompanies this file.                                    *
12  * The moral rights of the author are asserted.              *
13  *                                                           *
14  ***************** DISCLAIMER OF WARRANTY ********************
15  * This code is not warranted fit for any purpose. See the   *
16  * LICENCE file for further information.                     *
17  *                                                           *
18  *************************************************************/
19 
20 static const char rcs_id[] = "$Header: /home/jcb/MahJong/newmj/RCS/client.c,v 12.0 2009/06/28 20:43:12 jcb Rel $";
21 
22 #include "sysdep.h"
23 #include <assert.h>
24 #include "client.h"
25 
26 /* local for below two fns */
_client_init(char * address,int reinit,int oldfd)27 static Game *_client_init(char *address, int reinit, int oldfd) {
28   int fd;
29   int i;
30   Game *g;
31 
32   if ( reinit ) {
33     fd = oldfd;
34   } else {
35     if ( strcmp("-",address) == 0 ) {
36       fd = STDOUT_FILENO;
37     } else {
38       fd = connect_to_host(address);
39       if ( fd == (int)INVALID_SOCKET ) {
40 	perror("client_init: connect_to_host failed");
41 	return 0;
42       }
43     }
44   }
45 
46   /* alloc the structures */
47 
48   g = (Game *) malloc(sizeof(Game));
49   if ( g == NULL ) {
50     warn("Couldn't malloc game structure");
51     exit(1);
52   }
53   memset((void *)g,0,sizeof(Game));
54 
55   for ( i=0 ; i < NUM_SEATS ; i++ ) {
56     if ( (g->players[i]
57 	  = (PlayerP) malloc(sizeof(Player)))
58 	 == NULL ) {
59       warn("couldn't malloc player structure");
60       exit(1);
61     }
62     memset((void *)g->players[i],0,sizeof(Player));
63   }
64 
65   g->fd = fd;
66   if ( ! reinit ) g->cseqno = 0;
67   return g;
68 }
69 
70 /* client_init: establish a connection to a controller */
client_init(char * address)71 Game *client_init(char *address) {
72   return _client_init(address,0,0);
73 }
74 
75 /* client_reinit: as above, but use the existing fd or handle passed
76    as an argument */
client_reinit(int fd)77 Game *client_reinit(int fd) {
78   return _client_init(NULL,1,fd);
79 }
80 
81 /* client_connect: take an id and a name, and send a connect message.
82    Return 1 on success, or 0 on failure. */
client_connect(Game * g,int id,char * name)83 int client_connect(Game *g, int id, char *name) {
84   PMsgConnectMsg cm;
85 
86   cm.type = PMsgConnect;
87   cm.pvers = PROTOCOL_VERSION;
88   cm.last_id = id;
89   cm.name = name;
90 
91   client_send_packet(g,(PMsgMsg *)&cm);
92   /* stash info in our own player record */
93   assert(g->players[0]);
94   initialize_player(g->players[0]);
95   set_player_id(g->players[0],id);
96   set_player_name(g->players[0],name);
97   return 1;
98 }
99 
100 /* internal for below */
_client_close(Game * g,int close)101 static Game *_client_close(Game *g,int close) {
102   int i;
103 
104   if ( close ) close_socket(g->fd);
105   for ( i = 0; i < NUM_SEATS; i++ ) {
106     set_player_name(g->players[i],NULL);
107     free((void *)(g->players[i]));
108   }
109   free(g);
110   return NULL;
111 }
112 
113 /* close connection and free storage */
client_close(Game * g)114 Game *client_close(Game *g) {
115   return _client_close(g,1);
116 }
117 
118 /* free storage, but don't actually close connection */
client_close_keepconnection(Game * g)119 Game *client_close_keepconnection(Game *g) {
120   return _client_close(g,0);
121 }
122 
123 
124 /* client_send_packet: send the given packet out on the game fd.
125    Return sequence number of packet. */
client_send_packet(Game * g,PMsgMsg * m)126 int client_send_packet(Game *g, PMsgMsg *m) {
127   char *l;
128 
129   if ( ! g ) {
130     warn("client_send_packet: null game");
131     return 0;
132   }
133   l = encode_pmsg(m);
134   if ( l == NULL ) {
135     /* this shouldn't happen */
136     warn("client_send_packet: protocol conversion failed");
137     return 0;
138   }
139   if ( put_line(g->fd,l) < 0 ) {
140     warn("client_send_packet: write failed");
141     /* maybe we should shutdown the descriptor here? */
142     return 0;
143   }
144   return ++g->cseqno;
145 }
146 
147 /* see client.h for spec. */
148 
client_find_sets(PlayerP p,Tile d,int mj,PlayerP * pcopies,MJSpecialHandFlags flags)149 TileSet *client_find_sets(PlayerP p, Tile d, int mj, PlayerP *pcopies,
150 			  MJSpecialHandFlags flags) {
151   static TileSet ts[14]; /* should be safe, but FIXME */
152   static Player copies[14];
153   TileSet *tsp = ts;
154   Tile t;
155   PlayerP cp = copies;
156   char seen[MaxTile]; /* to note tile denominations already done */
157   int j;
158 
159   /* check the discard */
160   if ( d != HiddenTile ) {
161     /* Can we kong? */
162     if ( !mj && player_can_kong(p,d) ) {
163       tsp->type = Kong;
164       tsp->tile = d;
165       tsp++; cp++;
166     }
167 
168     /* Can we pung it? */
169     copy_player(cp,p);
170     if ( player_pungs(cp,d) && (!mj || player_can_mah_jong(cp,HiddenTile,flags)) ) {
171       tsp->type = Pung;
172       tsp->tile = d;
173       tsp++; cp++;
174     }
175 
176 
177     /* Can we pair it? */
178     copy_player(cp,p);
179     if ( player_pairs(cp,d) && (!mj || player_can_mah_jong(cp,HiddenTile,flags)) ) {
180       tsp->type = Pair;
181       tsp->tile = d;
182       tsp++; cp++;
183     }
184 
185     /* Can we chow it? */
186     for ( j = Lower; j <= Upper; j++ ) {
187       copy_player(cp,p);
188       if ( player_chows(cp,d,j) && (!mj || player_can_mah_jong(cp,HiddenTile,flags))) {
189 	tsp->type = Chow;
190 	tsp->tile = make_tile(suit_of(d),value_of(d)-j);
191 	tsp++; cp++;
192       }
193     }
194   } else {
195     /* no discard. Look for closed sets */
196     /* Because we should return pungs, then chows, then pairs,
197        we have to go through the tiles three times, which is tedious. */
198     for (j=0; j < MaxTile; j++) seen[j] = 0;
199     for (j=0; j < p->num_concealed; j++) {
200       t = p->concealed[j];
201       if ( seen[t]++ ) continue; /* don't enter sets twice */
202       copy_player(cp,p);
203       if ( player_forms_closed_pung(cp,t) && (!mj || player_can_mah_jong(cp,HiddenTile,flags))) {
204 	tsp->type = ClosedPung;
205 	tsp->tile = t;
206 	tsp++; cp++;
207       }
208     }
209     for (j=0; j < MaxTile; j++) seen[j] = 0;
210     for (j=0; j < p->num_concealed; j++) {
211       t = p->concealed[j];
212       if ( seen[t]++ ) continue; /* don't enter sets twice */
213       copy_player(cp,p);
214       if ( player_forms_closed_chow(cp,t,Lower) && (!mj || player_can_mah_jong(cp,HiddenTile,flags))) {
215 	tsp->type = ClosedChow;
216 	tsp->tile = t;
217 	tsp++; cp++;
218       }
219     }
220     for (j=0; j < MaxTile; j++) seen[j] = 0;
221     for (j=0; j < p->num_concealed; j++) {
222       t = p->concealed[j];
223       if ( seen[t]++ ) continue; /* don't enter sets twice */
224       copy_player(cp,p);
225       if ( player_forms_closed_pair(cp,t) && (!mj || player_can_mah_jong(cp,HiddenTile,flags))) {
226 	tsp->type = ClosedPair;
227 	tsp->tile = t;
228 	tsp++; cp++;
229       }
230     }
231   }
232 
233   /* have we found anything? */
234   if ( tsp == ts ) return (TileSet *)0;
235 
236   tsp->type = Empty;
237   if ( pcopies ) *pcopies = copies;
238   return ts;
239 }
240