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