~ubuntu-branches/ubuntu/edgy/ggz-client-libs/edgy

« back to all changes in this revision

Viewing changes to ggzmod-ggz/ggzmod-ggz.c

  • Committer: Bazaar Package Importer
  • Author(s): Peter Eisentraut, Josef Spillner, Peter Eisentraut
  • Date: 2006-09-09 13:37:14 UTC
  • mfrom: (2.1.2 edgy)
  • Revision ID: james.westby@ubuntu.com-20060909133714-q49a9kvjfkc0wcc3
Tags: 0.0.13-3
[ Josef Spillner ]
* Change ggzcore-bin dependency from ggzmod to recommends from ggzcore
  (closes: #384671).

[ Peter Eisentraut ]
* Make package dependencies binNMU-safe through use of ${binary:Version}
  (closes: #386126)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
 * File: ggzmod-ggz.c
 
3
 * Author: GGZ Dev Team
 
4
 * Project: ggzmod
 
5
 * Date: 10/14/01
 
6
 * Desc: GGZ game module functions, GGZ side
 
7
 * $Id: ggzmod-ggz.c 7790 2006-01-17 21:26:35Z jdorje $
 
8
 *
 
9
 * This file contains the backend for the ggzmod library.  This
 
10
 * library facilitates the communication between the GGZ core client (ggz)
 
11
 * and game clients.  The file provides an interface similar to ggzmod.c,
 
12
 * but a different implementation.
 
13
 *
 
14
 * Copyright (C) 2001-2002 GGZ Development Team.
 
15
 *
 
16
 * This program is free software; you can redistribute it and/or modify
 
17
 * it under the terms of the GNU General Public License as published by
 
18
 * the Free Software Foundation; either version 2 of the License, or
 
19
 * (at your option) any later version.
 
20
 *
 
21
 * This program is distributed in the hope that it will be useful,
 
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
24
 * GNU General Public License for more details.
 
25
 *
 
26
 * You should have received a copy of the GNU General Public License
 
27
 * along with this program; if not, write to the Free Software
 
28
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
29
 */
 
30
 
 
31
#ifdef HAVE_CONFIG_H
 
32
#  include <config.h>                   /* Site-specific config */
 
33
#endif
 
34
 
 
35
#include <assert.h>
 
36
#include <errno.h>
 
37
#include <signal.h>
 
38
#include <stdarg.h>
 
39
#include <stdio.h>
 
40
#include <stdlib.h>
 
41
#include <string.h>
 
42
#ifdef HAVE_SYS_SELECT_H
 
43
# include <sys/select.h>
 
44
#endif
 
45
#ifdef HAVE_SYS_SOCKET_H
 
46
# include <sys/socket.h>
 
47
#endif
 
48
#ifdef HAVE_WINSOCK2_H
 
49
# include <winsock2.h>
 
50
#endif
 
51
#include <sys/time.h>
 
52
#include <sys/types.h>
 
53
#ifdef HAVE_SYS_WAIT_H
 
54
# include <sys/wait.h>
 
55
#endif
 
56
#include <unistd.h>
 
57
 
 
58
#include <ggz.h>
 
59
 
 
60
#include "ggzmod-ggz.h"
 
61
#include "mod-ggz.h"
 
62
#include "io-ggz.h"
 
63
#include "protocol.h"
 
64
 
 
65
 
 
66
/* 
 
67
 * internal function prototypes
 
68
 */
 
69
 
 
70
static void call_handler(GGZMod *ggzmod, GGZModEvent event, void *data);
 
71
static void call_transaction(GGZMod * ggzmod, GGZModTransaction t, void *data);
 
72
static int send_game_launch(GGZMod * ggzmod);
 
73
static int game_prepare(int fd_pair[2], int *sock);
 
74
static int game_fork(GGZMod * ggzmod);
 
75
static int game_embedded(GGZMod * ggzmod);
 
76
 
 
77
static GGZSeat _ggzmod_ggz_get_seat(GGZMod *ggzmod, int num);
 
78
static GGZSpectatorSeat _ggzmod_ggz_get_spectator_seat(GGZMod * ggzmod, int num);
 
79
static int _ggzmod_ggz_handle_event(GGZMod * ggzmod, fd_set read_fds);
 
80
static void _ggzmod_ggz_set_state(GGZMod * ggzmod, GGZModState state);
 
81
 
 
82
/* Functions for manipulating seats */
 
83
static GGZSeat* seat_copy(GGZSeat *orig);
 
84
static int seat_compare(GGZSeat *a, GGZSeat *b);
 
85
static void seat_free(GGZSeat *seat);
 
86
static GGZSpectatorSeat* spectator_seat_copy(GGZSpectatorSeat *orig);
 
87
static int spectator_seat_compare(GGZSpectatorSeat *a, GGZSpectatorSeat *b);
 
88
static void spectator_seat_free(GGZSpectatorSeat *seat);
 
89
 
 
90
/* 
 
91
 * Creating/destroying a ggzmod object
 
92
 */
 
93
 
 
94
static int stats_compare(const void *p, const void *q)
 
95
{
 
96
        const GGZStat *s_p = p, *s_q = q;
 
97
 
 
98
        return s_p->number - s_q->number;
 
99
}
 
100
 
 
101
static int infos_compare(const void *p, const void *q)
 
102
{
 
103
        const GGZPlayerInfo *s_p = p, *s_q = q;
 
104
 
 
105
        return s_p->num - s_q->num;
 
106
}
 
107
 
 
108
/*
 
109
 * How a game is launched (incomplete): see ggzmod.c.
 
110
 */
 
111
 
 
112
/* Creates a new ggzmod object. */
 
113
GGZMod *ggzmod_ggz_new(GGZModType type)
 
114
{
 
115
        int i;
 
116
        GGZMod *ggzmod;
 
117
 
 
118
        /* verify parameter */
 
119
        if (type != GGZMOD_GGZ && type != GGZMOD_GAME)
 
120
                return NULL;
 
121
 
 
122
        /* allocate */
 
123
        ggzmod = ggz_malloc(sizeof(*ggzmod));
 
124
 
 
125
        /* initialize */
 
126
        ggzmod->type = type;
 
127
        ggzmod->state = GGZMOD_STATE_CREATED;
 
128
        ggzmod->fd = -1;
 
129
        ggzmod->server_fd = -1;
 
130
        ggzmod->server_host = NULL;
 
131
        ggzmod->server_port = 0;
 
132
        ggzmod->server_handle = NULL;
 
133
        for (i = 0; i < GGZMOD_NUM_HANDLERS; i++)
 
134
                ggzmod->handlers[i] = NULL;
 
135
        ggzmod->gamedata = NULL;
 
136
        ggzmod->my_seat_num = -1;
 
137
 
 
138
        ggzmod->seats = ggz_list_create((ggzEntryCompare)seat_compare,
 
139
                                        (ggzEntryCreate)seat_copy,
 
140
                                        (ggzEntryDestroy)seat_free,
 
141
                                        GGZ_LIST_REPLACE_DUPS);
 
142
        ggzmod->spectator_seats =
 
143
                ggz_list_create((ggzEntryCompare)spectator_seat_compare,
 
144
                                (ggzEntryCreate)spectator_seat_copy,
 
145
                                (ggzEntryDestroy)spectator_seat_free,
 
146
                                GGZ_LIST_REPLACE_DUPS);
 
147
        ggzmod->num_seats = ggzmod->num_spectator_seats = 0;
 
148
 
 
149
        ggzmod->stats = ggz_list_create(stats_compare, NULL, NULL,
 
150
                                        GGZ_LIST_ALLOW_DUPS);
 
151
        ggzmod->spectator_stats = ggz_list_create(stats_compare, NULL, NULL,
 
152
                                                  GGZ_LIST_ALLOW_DUPS);
 
153
 
 
154
        ggzmod->infos = ggz_list_create(infos_compare, NULL, NULL,
 
155
                                        GGZ_LIST_ALLOW_DUPS);
 
156
 
 
157
#ifdef HAVE_FORK
 
158
        ggzmod->pid = -1;
 
159
#else
 
160
        ggzmod->process = INVALID_HANDLE_VALUE;
 
161
#endif
 
162
        ggzmod->argv = NULL;
 
163
        for (i = 0; i < GGZMOD_NUM_TRANSACTIONS; i++)
 
164
                ggzmod->thandlers[i] = NULL;
 
165
 
 
166
        return ggzmod;
 
167
}
 
168
 
 
169
 
 
170
/* Frees (deletes) a ggzmod object */
 
171
void ggzmod_ggz_free(GGZMod * ggzmod)
 
172
{
 
173
        int i;
 
174
 
 
175
        if (!ggzmod) {
 
176
                return;
 
177
        }
 
178
        
 
179
        if (ggzmod->fd != -1)
 
180
                (void)ggzmod_ggz_disconnect(ggzmod);
 
181
        
 
182
        if (ggzmod->server_host) ggz_free(ggzmod->server_host);
 
183
        if (ggzmod->server_handle) ggz_free(ggzmod->server_handle);
 
184
 
 
185
        ggzmod->type = -1;
 
186
 
 
187
        if (ggzmod->my_name)
 
188
                ggz_free(ggzmod->my_name);
 
189
 
 
190
        if (ggzmod->pwd)
 
191
                ggz_free(ggzmod->pwd);
 
192
        
 
193
        if (ggzmod->argv) {
 
194
                for (i = 0; ggzmod->argv[i]; i++)
 
195
                        if (ggzmod->argv[i])
 
196
                                ggz_free(ggzmod->argv[i]);
 
197
                ggz_free(ggzmod->argv);
 
198
        }
 
199
 
 
200
        /* Free the object */
 
201
        ggz_free(ggzmod);
 
202
}
 
203
 
 
204
 
 
205
/* 
 
206
 * Accesor functions for GGZMod
 
207
 */
 
208
 
 
209
/* The ggzmod FD is the main ggz<->game server communications socket. */
 
210
int ggzmod_ggz_get_fd(GGZMod * ggzmod)
 
211
{
 
212
        if (!ggzmod) {
 
213
                return -1;
 
214
        }
 
215
        return ggzmod->fd;
 
216
}
 
217
 
 
218
 
 
219
GGZModState ggzmod_ggz_get_state(GGZMod * ggzmod)
 
220
{
 
221
        if (!ggzmod) {
 
222
                return -1;      /* not very useful */
 
223
        }
 
224
        return ggzmod->state;
 
225
}
 
226
 
 
227
 
 
228
void* ggzmod_ggz_get_gamedata(GGZMod * ggzmod)
 
229
{
 
230
        if (!ggzmod) {
 
231
                return NULL;
 
232
        }
 
233
        return ggzmod->gamedata;
 
234
}
 
235
 
 
236
 
 
237
void ggzmod_ggz_set_module(GGZMod * ggzmod, const char *pwd, char **argv)
 
238
{
 
239
        int i;
 
240
 
 
241
        ggz_debug("GGZMOD", "Setting arguments");
 
242
        
 
243
        if (!ggzmod)
 
244
                return;
 
245
 
 
246
        if (ggzmod->type != GGZMOD_GGZ) {
 
247
                _ggzmod_ggz_error(ggzmod, "Cannot set module args from module");
 
248
                return;
 
249
        }
 
250
                
 
251
        /* Check parameters */
 
252
        if (!argv || !argv[0]) {
 
253
                _ggzmod_ggz_error(ggzmod, "Bad module arguments");
 
254
                return;
 
255
        }
 
256
 
 
257
        /* Count the number of args so we know how much to allocate */
 
258
        for (i = 0; argv[i]; i++) {}
 
259
 
 
260
        ggz_debug("GGZMOD", "Set %d arguments", i);
 
261
        
 
262
        ggzmod->argv = ggz_malloc(sizeof(char*)*(i+1));
 
263
        ggzmod->pwd = ggz_strdup(pwd);
 
264
        
 
265
        for (i = 0; argv[i]; i++) 
 
266
                ggzmod->argv[i] = ggz_strdup(argv[i]);
 
267
}
 
268
 
 
269
 
 
270
void ggzmod_ggz_set_gamedata(GGZMod * ggzmod, void * data)
 
271
{
 
272
        if (ggzmod)
 
273
                ggzmod->gamedata = data;
 
274
}
 
275
 
 
276
 
 
277
void ggzmod_ggz_set_server_host(GGZMod * ggzmod,
 
278
                            const char *host, unsigned int port,
 
279
                            const char *handle)
 
280
{
 
281
        if (ggzmod && ggzmod->type == GGZMOD_GGZ) {
 
282
                /* If we're already connected, send the fd */
 
283
                if (ggzmod->state == GGZMOD_STATE_CONNECTED)
 
284
                        _io_ggz_send_server(ggzmod->fd, host, port, handle);
 
285
                ggzmod->server_host = ggz_strdup(host);
 
286
                ggzmod->server_port = port;
 
287
                ggzmod->server_handle = ggz_strdup(handle);
 
288
        }
 
289
}
 
290
 
 
291
 
 
292
void ggzmod_ggz_set_server_fd(GGZMod * ggzmod, int fd)
 
293
{
 
294
        if (ggzmod && ggzmod->type == GGZMOD_GGZ) {
 
295
                /* If we're already connected, send the fd */
 
296
                if (ggzmod->state == GGZMOD_STATE_CONNECTED)
 
297
                        _io_ggz_send_server_fd(ggzmod->fd, fd);
 
298
        }
 
299
}
 
300
 
 
301
 
 
302
void ggzmod_ggz_set_handler(GGZMod * ggzmod, GGZModEvent e,
 
303
                         GGZModHandler func)
 
304
{
 
305
        if (!ggzmod || e < 0 || e >= GGZMOD_NUM_HANDLERS) {
 
306
                ggz_error_msg("ggzmod_ggz_set_handler: "
 
307
                              "invalid params");
 
308
                return;         /* not very useful */
 
309
        }
 
310
 
 
311
        ggzmod->handlers[e] = func;
 
312
}
 
313
 
 
314
 
 
315
void ggzmod_ggz_set_transaction_handler(GGZMod * ggzmod,
 
316
                                    GGZModTransaction t,
 
317
                                    GGZModTransactionHandler func)
 
318
{
 
319
        if (!ggzmod
 
320
            || t < 0 || t >= GGZMOD_NUM_TRANSACTIONS
 
321
            || ggzmod->type != GGZMOD_GGZ) {
 
322
                ggz_error_msg("ggzmod_ggz_set_transaction_handler: "
 
323
                              "invalid params");
 
324
                return;
 
325
        }
 
326
 
 
327
        ggzmod->thandlers[t] = func;
 
328
}
 
329
 
 
330
static void _ggzmod_ggz_set_player(GGZMod *ggzmod,
 
331
                               const char *name,
 
332
                               int is_spectator, int seat_num)
 
333
{
 
334
        if (ggzmod->my_name)
 
335
                ggz_free(ggzmod->my_name);
 
336
        ggzmod->my_name = ggz_strdup(name);
 
337
 
 
338
        ggzmod->i_am_spectator = is_spectator;
 
339
        ggzmod->my_seat_num = seat_num;
 
340
}
 
341
 
 
342
int ggzmod_ggz_set_player(GGZMod *ggzmod, const char *name,
 
343
                      int is_spectator, int seat_num)
 
344
{
 
345
        if (!ggzmod
 
346
            || ggzmod->type != GGZMOD_GGZ)
 
347
                return -1;
 
348
 
 
349
        _ggzmod_ggz_set_player(ggzmod, name, is_spectator, seat_num);
 
350
 
 
351
        if (ggzmod->state != GGZMOD_STATE_CREATED)
 
352
                _io_ggz_send_player(ggzmod->fd, name, is_spectator, seat_num);
 
353
 
 
354
        return 0;
 
355
}
 
356
 
 
357
/*
 
358
 * Seats and spectator seats.
 
359
 */
 
360
 
 
361
static GGZSeat *seat_copy(GGZSeat *orig)
 
362
{
 
363
        GGZSeat *seat;
 
364
 
 
365
        seat = ggz_malloc(sizeof(*seat));
 
366
 
 
367
        seat->num = orig->num;
 
368
        seat->type = orig->type;
 
369
        seat->name = ggz_strdup(orig->name);
 
370
 
 
371
        return seat;
 
372
}
 
373
 
 
374
static int seat_compare(GGZSeat *a, GGZSeat *b)
 
375
{
 
376
        return a->num - b->num;
 
377
}
 
378
 
 
379
static void seat_free(GGZSeat *seat)
 
380
{
 
381
        if (seat->name)
 
382
                ggz_free(seat->name);
 
383
        ggz_free(seat);
 
384
}
 
385
 
 
386
static GGZSpectatorSeat* spectator_seat_copy(GGZSpectatorSeat *orig)
 
387
{
 
388
        GGZSpectatorSeat *seat;
 
389
 
 
390
        seat = ggz_malloc(sizeof(*seat));
 
391
 
 
392
        seat->num = orig->num;
 
393
        seat->name = ggz_strdup(orig->name);
 
394
 
 
395
        return seat;
 
396
}
 
397
 
 
398
static int spectator_seat_compare(GGZSpectatorSeat *a, GGZSpectatorSeat *b)
 
399
{
 
400
        return a->num - b->num;
 
401
}
 
402
 
 
403
static void spectator_seat_free(GGZSpectatorSeat *seat)
 
404
{
 
405
        if (seat->name)
 
406
                ggz_free(seat->name);
 
407
 
 
408
        ggz_free(seat);
 
409
}
 
410
 
 
411
static void _ggzmod_ggz_set_seat(GGZMod *ggzmod, GGZSeat *seat)
 
412
{
 
413
        if (seat->num >= ggzmod->num_seats)
 
414
                ggzmod->num_seats = seat->num + 1;
 
415
        ggz_list_insert(ggzmod->seats, seat);
 
416
}
 
417
 
 
418
static GGZSeat _ggzmod_ggz_get_seat(GGZMod *ggzmod, int num)
 
419
{
 
420
        GGZSeat seat = {num: num,
 
421
                        type: GGZ_SEAT_NONE,
 
422
                        name: NULL};
 
423
 
 
424
        if (num >= 0 && num < ggzmod->num_seats) {
 
425
                GGZListEntry *entry;
 
426
                entry = ggz_list_search(ggzmod->seats, &seat);
 
427
                if (entry)
 
428
                        seat = *(GGZSeat*)ggz_list_get_data(entry);
 
429
        }
 
430
 
 
431
        return seat;
 
432
}
 
433
 
 
434
int ggzmod_ggz_set_seat(GGZMod * ggzmod, GGZSeat *seat)
 
435
{
 
436
        GGZSeat oldseat;
 
437
 
 
438
        if (ggzmod->type == GGZMOD_GAME)
 
439
                return -1;
 
440
 
 
441
        if (!seat || seat->num < 0) {
 
442
                return -2;
 
443
        }
 
444
 
 
445
        /* If there is no such seat, return error */
 
446
        oldseat = _ggzmod_ggz_get_seat(ggzmod, seat->num);
 
447
 
 
448
        if (oldseat.type == seat->type
 
449
            && ggz_strcmp(oldseat.name, seat->name) == 0) {
 
450
                /* No change. */
 
451
                return 0;
 
452
        }
 
453
 
 
454
        if (ggzmod->state != GGZMOD_STATE_CREATED) {
 
455
                if (_io_ggz_send_seat(ggzmod->fd, seat) < 0)
 
456
                        _ggzmod_ggz_error(ggzmod, "Error writing to game");
 
457
        }
 
458
 
 
459
        _ggzmod_ggz_set_seat(ggzmod, seat);
 
460
 
 
461
        return 0;
 
462
}
 
463
 
 
464
static void _ggzmod_ggz_set_spectator_seat(GGZMod * ggzmod, GGZSpectatorSeat *seat)
 
465
{
 
466
        if (seat->name) {
 
467
                if (seat->num >= ggzmod->num_spectator_seats)
 
468
                        ggzmod->num_spectator_seats = seat->num + 1;
 
469
                ggz_list_insert(ggzmod->spectator_seats, seat);
 
470
        } else {
 
471
                /* Non-occupied seats are just empty entries in the list. */
 
472
                GGZListEntry *entry = ggz_list_search(ggzmod->spectator_seats,
 
473
                                                      seat);
 
474
                ggz_list_delete_entry(ggzmod->spectator_seats, entry);
 
475
 
 
476
                /* FIXME: reduce num_spectator_seats */
 
477
        }
 
478
}
 
479
 
 
480
static GGZSpectatorSeat _ggzmod_ggz_get_spectator_seat(GGZMod * ggzmod, int num)
 
481
{
 
482
        GGZSpectatorSeat seat = {num: num, name: NULL};
 
483
 
 
484
        if (num >= 0 && num < ggzmod->num_spectator_seats) {
 
485
                GGZListEntry *entry;
 
486
                entry = ggz_list_search(ggzmod->spectator_seats, &seat);
 
487
                if (entry)
 
488
                  seat = *(GGZSpectatorSeat*)ggz_list_get_data(entry);
 
489
        }
 
490
 
 
491
        return seat;
 
492
}
 
493
 
 
494
int ggzmod_ggz_set_spectator_seat(GGZMod * ggzmod, GGZSpectatorSeat *seat)
 
495
{
 
496
        if (!seat) return -1;
 
497
        if (ggzmod->type == GGZMOD_GAME) return -2;
 
498
        if (seat->num < 0) return -3;
 
499
 
 
500
        if (ggzmod->state != GGZMOD_STATE_CREATED) {
 
501
                GGZSpectatorSeat old_seat;
 
502
                old_seat = _ggzmod_ggz_get_spectator_seat(ggzmod, seat->num);
 
503
                if (ggz_strcmp(seat->name, old_seat.name)
 
504
                    && _io_ggz_send_spectator_seat(ggzmod->fd, seat) < 0) {
 
505
                        _ggzmod_ggz_error(ggzmod, "Error writing to game");
 
506
                        return -4;
 
507
                }
 
508
        }
 
509
 
 
510
        _ggzmod_ggz_set_spectator_seat(ggzmod, seat);
 
511
 
 
512
        return 0;
 
513
}
 
514
 
 
515
int ggzmod_ggz_inform_chat(GGZMod * ggzmod, const char *player, const char *msg)
 
516
{
 
517
        if (_io_ggz_send_msg_chat(ggzmod->fd, player, msg) < 0) {
 
518
                return -1;
 
519
        }
 
520
        return 0;
 
521
}
 
522
 
 
523
 
 
524
/* 
 
525
 * GGZmod actions
 
526
 */
 
527
 
 
528
int ggzmod_ggz_connect(GGZMod * ggzmod)
 
529
{
 
530
        if (!ggzmod)
 
531
                return -1;
 
532
 
 
533
        if (ggzmod->type == GGZMOD_GGZ) {
 
534
                /* For the ggz side, we fork the game and then send the launch message */
 
535
                
 
536
                if (ggzmod->argv) {
 
537
                        if (game_fork(ggzmod) < 0) {
 
538
                                _ggzmod_ggz_error(ggzmod, "Error: table fork failed");
 
539
                                return -1;
 
540
                        }
 
541
                } else {
 
542
                        ggz_debug("GGZMOD", "Running embedded game (no fork)");
 
543
                        if (game_embedded(ggzmod) < 0) {
 
544
                                _ggzmod_ggz_error(ggzmod, "Error: embedded table failed");
 
545
                                return -1;
 
546
                        }
 
547
                }
 
548
                
 
549
                if (send_game_launch(ggzmod) < 0) {
 
550
                        _ggzmod_ggz_error(ggzmod, "Error sending launch to game");
 
551
                        return -1;
 
552
                }
 
553
        }
 
554
        
 
555
        return 0;
 
556
}
 
557
 
 
558
 
 
559
int ggzmod_ggz_dispatch(GGZMod * ggzmod)
 
560
{
 
561
        struct timeval timeout;
 
562
        fd_set read_fd_set;
 
563
        int status;
 
564
 
 
565
        if (!ggzmod)
 
566
                return -1;
 
567
 
 
568
        if (ggzmod->fd < 0)
 
569
                return -1;
 
570
 
 
571
        FD_ZERO(&read_fd_set);
 
572
        FD_SET(ggzmod->fd, &read_fd_set);
 
573
 
 
574
        timeout.tv_sec = timeout.tv_usec = 0;   /* is this really portable? */
 
575
        
 
576
        status = select(ggzmod->fd + 1, &read_fd_set, NULL, NULL, &timeout);
 
577
 
 
578
        if (status == 0) {
 
579
                /* Nothing to read. */
 
580
                return 0;
 
581
        } else if (status < 0) {
 
582
                if (errno == EINTR)
 
583
                        return 0;
 
584
                return -1;
 
585
        }
 
586
        
 
587
        return _ggzmod_ggz_handle_event(ggzmod, read_fd_set);
 
588
}
 
589
 
 
590
int ggzmod_ggz_disconnect(GGZMod * ggzmod)
 
591
{
 
592
        if (!ggzmod) {
 
593
                return -1;
 
594
        }
 
595
        if (ggzmod->fd < 0) {
 
596
                /* This isn't an error; it usually means
 
597
                   we already disconnected.  The invariant is that the
 
598
                   process (ggzmod->pid) exists iff the socket (ggzmod->fd)
 
599
                   exists. */
 
600
                return 0;
 
601
        }
 
602
 
 
603
        if (ggzmod->type == GGZMOD_GGZ) {
 
604
                /* For the ggz side, we kill the game server and close the socket */
 
605
                
 
606
#ifdef HAVE_KILL
 
607
                /* Make sure game server is dead */
 
608
                if (ggzmod->pid > 0) {
 
609
                        kill(ggzmod->pid, SIGINT);
 
610
                        /* This will block waiting for the child to exit.
 
611
                           This could be a problem if there is an error
 
612
                           (or if the child refuses to exit...). */
 
613
                        (void) waitpid(ggzmod->pid, NULL, 0);
 
614
                }
 
615
                ggzmod->pid = -1;
 
616
#else
 
617
#  ifdef HAVE_WINSOCK2_H
 
618
                if (ggzmod->process != INVALID_HANDLE_VALUE) {
 
619
                        TerminateProcess(ggzmod->process, 0);
 
620
                        CloseHandle(ggzmod->process);
 
621
                        ggzmod->process = INVALID_HANDLE_VALUE;
 
622
                }
 
623
#  endif
 
624
#endif
 
625
                
 
626
                _ggzmod_ggz_set_state(ggzmod, GGZMOD_STATE_DONE);
 
627
                /* FIXME: what other cleanups should we do? */
 
628
        }
 
629
        
 
630
        /* We no longer free the seat data here.  It will stick around until
 
631
           ggzmod_ggz_free is called or it is used again. */
 
632
 
 
633
        /* Clean up the ggzmod object.  In theory it could now reconnect for
 
634
           a new game. */
 
635
#ifdef HAVE_WINSOCK2_H
 
636
        closesocket(ggzmod->fd);
 
637
#else
 
638
        close(ggzmod->fd);
 
639
#endif
 
640
        ggzmod->fd = -1;
 
641
 
 
642
        return 0;
 
643
}
 
644
 
 
645
 
 
646
 
 
647
/* Returns -1 on error, the number of events handled on success. */
 
648
static int _ggzmod_ggz_handle_event(GGZMod * ggzmod, fd_set read_fds)
 
649
{
 
650
        int status = 0;
 
651
        
 
652
        if (FD_ISSET(ggzmod->fd, &read_fds)) {
 
653
                status = _io_ggz_read_data(ggzmod);
 
654
                if (status < 0) {
 
655
                        _ggzmod_ggz_error(ggzmod, "Error reading data");
 
656
                        /* FIXME: should be disconnect? */
 
657
                        _ggzmod_ggz_set_state(ggzmod, GGZMOD_STATE_DONE);
 
658
                }
 
659
        }
 
660
 
 
661
        return status;
 
662
}
 
663
 
 
664
 
 
665
static void _ggzmod_ggz_set_state(GGZMod * ggzmod, GGZModState state)
 
666
{
 
667
        GGZModState old_state = ggzmod->state;
 
668
        if (state == ggzmod->state)
 
669
                return;         /* Is this an error? */
 
670
 
 
671
        /* The callback function retrieves the state from ggzmod_ggz_get_state.
 
672
           It could instead be passed as an argument. */
 
673
        ggzmod->state = state;
 
674
        call_handler(ggzmod, GGZMOD_EVENT_STATE, &old_state);
 
675
}
 
676
 
 
677
 
 
678
 
 
679
 
 
680
/* 
 
681
 * ggz specific actions
 
682
 */
 
683
 
 
684
/* Sends a game launch packet to ggzmod-game. A negative return value
 
685
   indicates a serious (fatal) error. */
 
686
static int send_game_launch(GGZMod * ggzmod)
 
687
{
 
688
        GGZListEntry *entry;
 
689
 
 
690
        if (_io_ggz_send_player(ggzmod->fd,
 
691
                            ggzmod->my_name,
 
692
                            ggzmod->i_am_spectator,
 
693
                            ggzmod->my_seat_num) < 0)
 
694
                return -2;
 
695
 
 
696
        for (entry = ggz_list_head(ggzmod->seats);
 
697
             entry;
 
698
             entry = ggz_list_next(entry)) {
 
699
                GGZSeat *seat = ggz_list_get_data(entry);
 
700
                if (_io_ggz_send_seat(ggzmod->fd, seat) < 0)
 
701
                        return -3;
 
702
        }
 
703
        for (entry = ggz_list_head(ggzmod->spectator_seats);
 
704
             entry;
 
705
             entry = ggz_list_next(entry)) {
 
706
                GGZSpectatorSeat *seat = ggz_list_get_data(entry);
 
707
                if (_io_ggz_send_spectator_seat(ggzmod->fd, seat) < 0)
 
708
                        return -4;
 
709
        }
 
710
 
 
711
        if (_io_ggz_send_launch(ggzmod->fd) < 0)
 
712
                return -1;
 
713
 
 
714
        /* If the server fd has already been set, send that too */
 
715
        if (ggzmod->server_fd != -1)
 
716
                if (_io_ggz_send_server_fd(ggzmod->fd, ggzmod->server_fd) < 0)
 
717
                        return -5;
 
718
 
 
719
        if (ggzmod->server_host)
 
720
                if (_io_ggz_send_server(ggzmod->fd, ggzmod->server_host,
 
721
                                    ggzmod->server_port,
 
722
                                    ggzmod->server_handle) < 0)
 
723
                        return -5;
 
724
 
 
725
        return 0;
 
726
}
 
727
 
 
728
 
 
729
/* Common setup for normal mode and embedded mode */
 
730
static int game_prepare(int fd_pair[2], int *sock)
 
731
{
 
732
#ifdef HAVE_SOCKETPAIR
 
733
        if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fd_pair) < 0)
 
734
                ggz_error_sys_exit("socketpair failed");
 
735
        setenv("GGZSOCKET", "103", 1);
 
736
        setenv("GGZMODE", "true", 1);
 
737
#else
 
738
        int port;
 
739
        char buf[100];
 
740
 
 
741
        /* Winsock implementation: see ggzmod_ggz_connect. */
 
742
        port = 5898;
 
743
        do {
 
744
                port++;
 
745
                *sock = ggz_make_socket(GGZ_SOCK_SERVER, port, NULL);
 
746
        } while (*sock < 0 && port < 7000);
 
747
        if (*sock < 0) {
 
748
                ggz_error_msg("Could not bind socket.");
 
749
                return -1;
 
750
        }
 
751
        if (listen(*sock, 1) < 0) {
 
752
                ggz_error_msg("Could not listen on socket.");
 
753
                return -1;
 
754
        }
 
755
        snprintf(buf, sizeof(buf), "%d", port);
 
756
#ifdef HAVE_SETENV
 
757
        setenv("GGZSOCKET", buf, 1);
 
758
        setenv("GGZMODE", "true", 1);
 
759
#else
 
760
        SetEnvironmentVariable("GGZSOCKET", buf);
 
761
        SetEnvironmentVariable("GGZMODE", "true");
 
762
#endif
 
763
#endif
 
764
 
 
765
        return 0;
 
766
}
 
767
 
 
768
/* Forks the game.  A negative return value indicates a serious error. */
 
769
/* No locking should be necessary within this function. */
 
770
static int game_fork(GGZMod * ggzmod)
 
771
{
 
772
        int sock;
 
773
        int fd_pair[2];         /* socketpair, always needs to be declared */
 
774
#ifndef HAVE_SOCKETPAIR
 
775
        int sock2;
 
776
#endif
 
777
#ifdef HAVE_FORK
 
778
        int pid;
 
779
#else
 
780
        char cmdline[1024] = "";
 
781
        int i;
 
782
        PROCESS_INFORMATION pi;
 
783
        STARTUPINFO si;
 
784
#endif
 
785
 
 
786
        /* If there are no args, we don't know what to run! */
 
787
        if (ggzmod->argv == NULL || ggzmod->argv[0] == NULL) {
 
788
                _ggzmod_ggz_error(ggzmod, "No arguments");
 
789
                return -1;
 
790
        }
 
791
 
 
792
        if(game_prepare(fd_pair, &sock) < 0)
 
793
                return -1;
 
794
 
 
795
#ifdef HAVE_FORK
 
796
        if ((pid = fork()) < 0)
 
797
                ggz_error_sys_exit("fork failed");
 
798
        else if (pid == 0) {
 
799
                /* child */
 
800
#ifdef HAVE_SOCKETPAIR
 
801
                close(fd_pair[0]);
 
802
 
 
803
                /* debugging message??? */
 
804
 
 
805
                /* Now we copy one end of the socketpair to fd 103 */
 
806
                if (fd_pair[1] != 103) {
 
807
                        /* We'd like to send an error message if either of
 
808
                           these fail, but we can't.  --JDS */
 
809
                        if (dup2(fd_pair[1], 103) != 103 || close(fd_pair[1]) < 0)
 
810
                                ggz_error_sys_exit("dup failed");
 
811
                }
 
812
#else
 
813
                close(sock);
 
814
#endif
 
815
 
 
816
                /* FIXME: Close all other fd's? */
 
817
                /* FIXME: Not necessary to close other fd's if we use
 
818
                   CLOSE_ON_EXEC */
 
819
 
 
820
                /* Set working directory */
 
821
                if (ggzmod->pwd
 
822
                    && chdir(ggzmod->pwd) < 0) {
 
823
                        /* FIXME: what to do? */
 
824
                }
 
825
 
 
826
                /* FIXME: can we call ggzmod_ggz_log() from here? */
 
827
                execv(ggzmod->argv[0], ggzmod->argv);   /* run game */
 
828
 
 
829
                /* We should never get here.  If we do, it's an eror */
 
830
                /* we still can't send error messages... */
 
831
                ggz_error_sys_exit("exec of %s failed", ggzmod->argv[0]);
 
832
        } else {
 
833
                /* parent */
 
834
#ifdef HAVE_SOCKETPAIR
 
835
                close(fd_pair[1]);
 
836
 
 
837
                ggzmod->fd = fd_pair[0];
 
838
#endif
 
839
                ggzmod->pid = pid;
 
840
                
 
841
                /* FIXME: should we delete the argv arguments? */
 
842
                
 
843
                /* That's all! */
 
844
        }
 
845
#else
 
846
        for (i = 0; ggzmod->argv[i]; i++) {
 
847
                snprintf(cmdline + strlen(cmdline),
 
848
                         sizeof(cmdline) - strlen(cmdline),
 
849
                         "%s ", ggzmod->argv[i]);
 
850
        }
 
851
 
 
852
        ZeroMemory(&si, sizeof(si));
 
853
        if (!CreateProcess(NULL, cmdline, NULL, NULL, TRUE,
 
854
                           DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
 
855
                           NULL, NULL, &si, &pi)) {
 
856
                return -1;
 
857
        }
 
858
        CloseHandle(pi.hThread);
 
859
        ggzmod->process = pi.hProcess;
 
860
#endif
 
861
#ifndef HAVE_SOCKETPAIR
 
862
        /* FIXME: we need to select, with a maximum timeout. */
 
863
        /* FIXME: this is insecure; it should be restricted to local
 
864
         * connections. */
 
865
        sock2 = accept(sock, NULL, NULL);
 
866
        if (sock2 < 0) {
 
867
                ggz_error_sys("Listening to socket failed.");
 
868
                return -1;
 
869
        }
 
870
#ifdef HAVE_WINSOCK2_H
 
871
        closesocket(sock);
 
872
#else
 
873
        close(sock);
 
874
#endif
 
875
        ggzmod->fd = sock2;
 
876
#endif
 
877
        return 0;
 
878
}
 
879
 
 
880
 
 
881
/* Similar to game_fork(), but runs the game embedded */
 
882
static int game_embedded(GGZMod * ggzmod)
 
883
{
 
884
        int sock;
 
885
        int fd_pair[2];         /* socketpair, always needs to be declared */
 
886
#ifndef HAVE_SOCKETPAIR
 
887
        int sock2;
 
888
#endif
 
889
 
 
890
        if(game_prepare(fd_pair, &sock) < 0)
 
891
                return -1;
 
892
 
 
893
#ifdef HAVE_SOCKETPAIR
 
894
        if (fd_pair[1] != 103) {
 
895
                /* We'd like to send an error message if either of
 
896
                   these fail, but we can't.  --JDS */
 
897
                if (dup2(fd_pair[1], 103) != 103 || close(fd_pair[1]) < 0)
 
898
                        ggz_error_sys_exit("dup failed");
 
899
        }
 
900
 
 
901
        ggzmod->fd = fd_pair[0];
 
902
#else
 
903
        /* FIXME: we need to select, with a maximum timeout. */
 
904
        /* FIXME: this is insecure; it should be restricted to local
 
905
         * connections. */
 
906
        sock2 = accept(sock, NULL, NULL);
 
907
        if (sock2 < 0) {
 
908
                ggz_error_sys("Listening to socket failed.");
 
909
                return -1;
 
910
        }
 
911
#ifdef HAVE_WINSOCK2_H
 
912
        closesocket(sock);
 
913
#else
 
914
        close(sock);
 
915
#endif
 
916
        ggzmod->fd = sock2;
 
917
#endif
 
918
#ifdef HAVE_FORK
 
919
        ggzmod->pid = -1; /* FIXME: use -1 for embedded ggzcore? getpid()? */
 
920
#else
 
921
        ggzmod->process = INVALID_HANDLE_VALUE;
 
922
#endif
 
923
 
 
924
        return 0;
 
925
}
 
926
 
 
927
 
 
928
/**** Internal library functions ****/
 
929
 
 
930
/* Invokes handlers for the specified event */
 
931
static void call_handler(GGZMod *ggzmod, GGZModEvent event, void *data)
 
932
{
 
933
        if (ggzmod->handlers[event])
 
934
                (*ggzmod->handlers[event]) (ggzmod, event, data);
 
935
}
 
936
 
 
937
 
 
938
static void call_transaction(GGZMod * ggzmod, GGZModTransaction t, void *data)
 
939
{
 
940
        if (!ggzmod->thandlers[t]) {
 
941
                ggz_error_msg("Unhandled transaction %d.", t);
 
942
                return;
 
943
        }
 
944
 
 
945
        if (ggzmod->type != GGZMOD_GGZ) {
 
946
                ggz_error_msg("The game can't handle transactions!");
 
947
                return;
 
948
        }
 
949
 
 
950
        (*ggzmod->thandlers[t])(ggzmod, t, data);
 
951
}
 
952
 
 
953
 
 
954
void _ggzmod_ggz_error(GGZMod *ggzmod, char* error)
 
955
{
 
956
        call_handler(ggzmod, GGZMOD_EVENT_ERROR, error);
 
957
}
 
958
 
 
959
 
 
960
void _ggzmod_ggz_handle_state(GGZMod * ggzmod, GGZModState state)
 
961
{
 
962
        /* There's only certain ones the game is allowed to set it to,
 
963
           and they can only change it if the state is currently
 
964
           WAITING or PLAYING. */
 
965
        switch (state) {
 
966
        case GGZMOD_STATE_CREATED:
 
967
        case GGZMOD_STATE_CONNECTED:
 
968
        case GGZMOD_STATE_WAITING:
 
969
        case GGZMOD_STATE_PLAYING:
 
970
        case GGZMOD_STATE_DONE:
 
971
                /* In contradiction to what I say above, the game
 
972
                   actually _is_ allowed to change its state from
 
973
                   CREATED to WAITING.  When ggzmod-ggz sends a
 
974
                   launch packet to ggzmod-game, ggzmod-game
 
975
                   automatically changes the state from CREATED
 
976
                   to WAITING.  When this happens, it tells
 
977
                   ggzmod-ggz of this change and we end up back
 
978
                   here.  So, although it's a bit unsafe, we have
 
979
                   to allow this for now.  The alternative would
 
980
                   be to have ggzmod-ggz and ggzmod-game both
 
981
                   separately change states when the launch packet
 
982
                   is sent. */
 
983
                _ggzmod_ggz_set_state(ggzmod, state);
 
984
                return;
 
985
        }
 
986
        _ggzmod_ggz_error(ggzmod,
 
987
                      "Game requested incorrect state value");
 
988
 
 
989
        /* Is this right? has the gameover happened yet? */
 
990
}
 
991
 
 
992
void _ggzmod_ggz_handle_stand_request(GGZMod *ggzmod)
 
993
{
 
994
        call_transaction(ggzmod, GGZMOD_TRANSACTION_STAND, NULL);
 
995
}
 
996
 
 
997
void _ggzmod_ggz_handle_sit_request(GGZMod *ggzmod, int seat_num)
 
998
{
 
999
        call_transaction(ggzmod, GGZMOD_TRANSACTION_SIT, &seat_num);
 
1000
}
 
1001
 
 
1002
void _ggzmod_ggz_handle_boot_request(GGZMod *ggzmod, char *name)
 
1003
{
 
1004
        call_transaction(ggzmod, GGZMOD_TRANSACTION_BOOT, name);
 
1005
}
 
1006
 
 
1007
void _ggzmod_ggz_handle_bot_request(GGZMod *ggzmod, int seat_num)
 
1008
{
 
1009
        call_transaction(ggzmod, GGZMOD_TRANSACTION_BOT, &seat_num);
 
1010
}
 
1011
 
 
1012
void _ggzmod_ggz_handle_open_request(GGZMod *ggzmod, int seat_num)
 
1013
{
 
1014
        call_transaction(ggzmod, GGZMOD_TRANSACTION_OPEN, &seat_num);
 
1015
}
 
1016
 
 
1017
void _ggzmod_ggz_handle_chat_request(GGZMod *ggzmod, char *chat_msg)
 
1018
{
 
1019
        call_transaction(ggzmod, GGZMOD_TRANSACTION_CHAT, chat_msg);
 
1020
}
 
1021
 
 
1022
void _ggzmod_ggz_handle_info_request(GGZMod *ggzmod, int seat_num)
 
1023
{
 
1024
        call_transaction(ggzmod, GGZMOD_TRANSACTION_INFO, &seat_num);
 
1025
}
 
1026
 
 
1027
int ggzmod_ggz_set_stats(GGZMod *ggzmod, GGZStat *player_stats,
 
1028
                     GGZStat *spectator_stats)
 
1029
{
 
1030
        if (!player_stats
 
1031
            || !ggzmod
 
1032
            || (!spectator_stats && ggzmod->num_spectator_seats > 0)
 
1033
            || ggzmod->type != GGZMOD_GGZ
 
1034
            || ggzmod->state == GGZMOD_STATE_CREATED) {
 
1035
                return -1;
 
1036
        }
 
1037
 
 
1038
        return _io_ggz_send_stats(ggzmod->fd, ggzmod->num_seats, player_stats,
 
1039
                              ggzmod->num_spectator_seats, spectator_stats);
 
1040
}
 
1041
 
 
1042
int ggzmod_ggz_set_info(GGZMod *ggzmod, int num,
 
1043
                        GGZList *infos)
 
1044
{
 
1045
        if (!ggzmod) {
 
1046
                return -1;
 
1047
        }
 
1048
 
 
1049
        return _io_ggz_send_msg_info(ggzmod->fd, num, infos);
 
1050
}
 
1051