6
* Desc: GGZ game module functions, GGZ side
7
* $Id: ggzmod-ggz.c 7790 2006-01-17 21:26:35Z jdorje $
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.
14
* Copyright (C) 2001-2002 GGZ Development Team.
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.
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.
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
32
# include <config.h> /* Site-specific config */
42
#ifdef HAVE_SYS_SELECT_H
43
# include <sys/select.h>
45
#ifdef HAVE_SYS_SOCKET_H
46
# include <sys/socket.h>
48
#ifdef HAVE_WINSOCK2_H
49
# include <winsock2.h>
52
#include <sys/types.h>
53
#ifdef HAVE_SYS_WAIT_H
54
# include <sys/wait.h>
60
#include "ggzmod-ggz.h"
67
* internal function prototypes
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);
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);
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);
91
* Creating/destroying a ggzmod object
94
static int stats_compare(const void *p, const void *q)
96
const GGZStat *s_p = p, *s_q = q;
98
return s_p->number - s_q->number;
101
static int infos_compare(const void *p, const void *q)
103
const GGZPlayerInfo *s_p = p, *s_q = q;
105
return s_p->num - s_q->num;
109
* How a game is launched (incomplete): see ggzmod.c.
112
/* Creates a new ggzmod object. */
113
GGZMod *ggzmod_ggz_new(GGZModType type)
118
/* verify parameter */
119
if (type != GGZMOD_GGZ && type != GGZMOD_GAME)
123
ggzmod = ggz_malloc(sizeof(*ggzmod));
127
ggzmod->state = GGZMOD_STATE_CREATED;
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;
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;
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);
154
ggzmod->infos = ggz_list_create(infos_compare, NULL, NULL,
155
GGZ_LIST_ALLOW_DUPS);
160
ggzmod->process = INVALID_HANDLE_VALUE;
163
for (i = 0; i < GGZMOD_NUM_TRANSACTIONS; i++)
164
ggzmod->thandlers[i] = NULL;
170
/* Frees (deletes) a ggzmod object */
171
void ggzmod_ggz_free(GGZMod * ggzmod)
179
if (ggzmod->fd != -1)
180
(void)ggzmod_ggz_disconnect(ggzmod);
182
if (ggzmod->server_host) ggz_free(ggzmod->server_host);
183
if (ggzmod->server_handle) ggz_free(ggzmod->server_handle);
188
ggz_free(ggzmod->my_name);
191
ggz_free(ggzmod->pwd);
194
for (i = 0; ggzmod->argv[i]; i++)
196
ggz_free(ggzmod->argv[i]);
197
ggz_free(ggzmod->argv);
200
/* Free the object */
206
* Accesor functions for GGZMod
209
/* The ggzmod FD is the main ggz<->game server communications socket. */
210
int ggzmod_ggz_get_fd(GGZMod * ggzmod)
219
GGZModState ggzmod_ggz_get_state(GGZMod * ggzmod)
222
return -1; /* not very useful */
224
return ggzmod->state;
228
void* ggzmod_ggz_get_gamedata(GGZMod * ggzmod)
233
return ggzmod->gamedata;
237
void ggzmod_ggz_set_module(GGZMod * ggzmod, const char *pwd, char **argv)
241
ggz_debug("GGZMOD", "Setting arguments");
246
if (ggzmod->type != GGZMOD_GGZ) {
247
_ggzmod_ggz_error(ggzmod, "Cannot set module args from module");
251
/* Check parameters */
252
if (!argv || !argv[0]) {
253
_ggzmod_ggz_error(ggzmod, "Bad module arguments");
257
/* Count the number of args so we know how much to allocate */
258
for (i = 0; argv[i]; i++) {}
260
ggz_debug("GGZMOD", "Set %d arguments", i);
262
ggzmod->argv = ggz_malloc(sizeof(char*)*(i+1));
263
ggzmod->pwd = ggz_strdup(pwd);
265
for (i = 0; argv[i]; i++)
266
ggzmod->argv[i] = ggz_strdup(argv[i]);
270
void ggzmod_ggz_set_gamedata(GGZMod * ggzmod, void * data)
273
ggzmod->gamedata = data;
277
void ggzmod_ggz_set_server_host(GGZMod * ggzmod,
278
const char *host, unsigned int port,
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);
292
void ggzmod_ggz_set_server_fd(GGZMod * ggzmod, int fd)
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);
302
void ggzmod_ggz_set_handler(GGZMod * ggzmod, GGZModEvent e,
305
if (!ggzmod || e < 0 || e >= GGZMOD_NUM_HANDLERS) {
306
ggz_error_msg("ggzmod_ggz_set_handler: "
308
return; /* not very useful */
311
ggzmod->handlers[e] = func;
315
void ggzmod_ggz_set_transaction_handler(GGZMod * ggzmod,
317
GGZModTransactionHandler func)
320
|| t < 0 || t >= GGZMOD_NUM_TRANSACTIONS
321
|| ggzmod->type != GGZMOD_GGZ) {
322
ggz_error_msg("ggzmod_ggz_set_transaction_handler: "
327
ggzmod->thandlers[t] = func;
330
static void _ggzmod_ggz_set_player(GGZMod *ggzmod,
332
int is_spectator, int seat_num)
335
ggz_free(ggzmod->my_name);
336
ggzmod->my_name = ggz_strdup(name);
338
ggzmod->i_am_spectator = is_spectator;
339
ggzmod->my_seat_num = seat_num;
342
int ggzmod_ggz_set_player(GGZMod *ggzmod, const char *name,
343
int is_spectator, int seat_num)
346
|| ggzmod->type != GGZMOD_GGZ)
349
_ggzmod_ggz_set_player(ggzmod, name, is_spectator, seat_num);
351
if (ggzmod->state != GGZMOD_STATE_CREATED)
352
_io_ggz_send_player(ggzmod->fd, name, is_spectator, seat_num);
358
* Seats and spectator seats.
361
static GGZSeat *seat_copy(GGZSeat *orig)
365
seat = ggz_malloc(sizeof(*seat));
367
seat->num = orig->num;
368
seat->type = orig->type;
369
seat->name = ggz_strdup(orig->name);
374
static int seat_compare(GGZSeat *a, GGZSeat *b)
376
return a->num - b->num;
379
static void seat_free(GGZSeat *seat)
382
ggz_free(seat->name);
386
static GGZSpectatorSeat* spectator_seat_copy(GGZSpectatorSeat *orig)
388
GGZSpectatorSeat *seat;
390
seat = ggz_malloc(sizeof(*seat));
392
seat->num = orig->num;
393
seat->name = ggz_strdup(orig->name);
398
static int spectator_seat_compare(GGZSpectatorSeat *a, GGZSpectatorSeat *b)
400
return a->num - b->num;
403
static void spectator_seat_free(GGZSpectatorSeat *seat)
406
ggz_free(seat->name);
411
static void _ggzmod_ggz_set_seat(GGZMod *ggzmod, GGZSeat *seat)
413
if (seat->num >= ggzmod->num_seats)
414
ggzmod->num_seats = seat->num + 1;
415
ggz_list_insert(ggzmod->seats, seat);
418
static GGZSeat _ggzmod_ggz_get_seat(GGZMod *ggzmod, int num)
420
GGZSeat seat = {num: num,
424
if (num >= 0 && num < ggzmod->num_seats) {
426
entry = ggz_list_search(ggzmod->seats, &seat);
428
seat = *(GGZSeat*)ggz_list_get_data(entry);
434
int ggzmod_ggz_set_seat(GGZMod * ggzmod, GGZSeat *seat)
438
if (ggzmod->type == GGZMOD_GAME)
441
if (!seat || seat->num < 0) {
445
/* If there is no such seat, return error */
446
oldseat = _ggzmod_ggz_get_seat(ggzmod, seat->num);
448
if (oldseat.type == seat->type
449
&& ggz_strcmp(oldseat.name, seat->name) == 0) {
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");
459
_ggzmod_ggz_set_seat(ggzmod, seat);
464
static void _ggzmod_ggz_set_spectator_seat(GGZMod * ggzmod, GGZSpectatorSeat *seat)
467
if (seat->num >= ggzmod->num_spectator_seats)
468
ggzmod->num_spectator_seats = seat->num + 1;
469
ggz_list_insert(ggzmod->spectator_seats, seat);
471
/* Non-occupied seats are just empty entries in the list. */
472
GGZListEntry *entry = ggz_list_search(ggzmod->spectator_seats,
474
ggz_list_delete_entry(ggzmod->spectator_seats, entry);
476
/* FIXME: reduce num_spectator_seats */
480
static GGZSpectatorSeat _ggzmod_ggz_get_spectator_seat(GGZMod * ggzmod, int num)
482
GGZSpectatorSeat seat = {num: num, name: NULL};
484
if (num >= 0 && num < ggzmod->num_spectator_seats) {
486
entry = ggz_list_search(ggzmod->spectator_seats, &seat);
488
seat = *(GGZSpectatorSeat*)ggz_list_get_data(entry);
494
int ggzmod_ggz_set_spectator_seat(GGZMod * ggzmod, GGZSpectatorSeat *seat)
496
if (!seat) return -1;
497
if (ggzmod->type == GGZMOD_GAME) return -2;
498
if (seat->num < 0) return -3;
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");
510
_ggzmod_ggz_set_spectator_seat(ggzmod, seat);
515
int ggzmod_ggz_inform_chat(GGZMod * ggzmod, const char *player, const char *msg)
517
if (_io_ggz_send_msg_chat(ggzmod->fd, player, msg) < 0) {
528
int ggzmod_ggz_connect(GGZMod * ggzmod)
533
if (ggzmod->type == GGZMOD_GGZ) {
534
/* For the ggz side, we fork the game and then send the launch message */
537
if (game_fork(ggzmod) < 0) {
538
_ggzmod_ggz_error(ggzmod, "Error: table fork failed");
542
ggz_debug("GGZMOD", "Running embedded game (no fork)");
543
if (game_embedded(ggzmod) < 0) {
544
_ggzmod_ggz_error(ggzmod, "Error: embedded table failed");
549
if (send_game_launch(ggzmod) < 0) {
550
_ggzmod_ggz_error(ggzmod, "Error sending launch to game");
559
int ggzmod_ggz_dispatch(GGZMod * ggzmod)
561
struct timeval timeout;
571
FD_ZERO(&read_fd_set);
572
FD_SET(ggzmod->fd, &read_fd_set);
574
timeout.tv_sec = timeout.tv_usec = 0; /* is this really portable? */
576
status = select(ggzmod->fd + 1, &read_fd_set, NULL, NULL, &timeout);
579
/* Nothing to read. */
581
} else if (status < 0) {
587
return _ggzmod_ggz_handle_event(ggzmod, read_fd_set);
590
int ggzmod_ggz_disconnect(GGZMod * ggzmod)
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)
603
if (ggzmod->type == GGZMOD_GGZ) {
604
/* For the ggz side, we kill the game server and close the socket */
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);
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;
626
_ggzmod_ggz_set_state(ggzmod, GGZMOD_STATE_DONE);
627
/* FIXME: what other cleanups should we do? */
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. */
633
/* Clean up the ggzmod object. In theory it could now reconnect for
635
#ifdef HAVE_WINSOCK2_H
636
closesocket(ggzmod->fd);
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)
652
if (FD_ISSET(ggzmod->fd, &read_fds)) {
653
status = _io_ggz_read_data(ggzmod);
655
_ggzmod_ggz_error(ggzmod, "Error reading data");
656
/* FIXME: should be disconnect? */
657
_ggzmod_ggz_set_state(ggzmod, GGZMOD_STATE_DONE);
665
static void _ggzmod_ggz_set_state(GGZMod * ggzmod, GGZModState state)
667
GGZModState old_state = ggzmod->state;
668
if (state == ggzmod->state)
669
return; /* Is this an error? */
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);
681
* ggz specific actions
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)
690
if (_io_ggz_send_player(ggzmod->fd,
692
ggzmod->i_am_spectator,
693
ggzmod->my_seat_num) < 0)
696
for (entry = ggz_list_head(ggzmod->seats);
698
entry = ggz_list_next(entry)) {
699
GGZSeat *seat = ggz_list_get_data(entry);
700
if (_io_ggz_send_seat(ggzmod->fd, seat) < 0)
703
for (entry = ggz_list_head(ggzmod->spectator_seats);
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)
711
if (_io_ggz_send_launch(ggzmod->fd) < 0)
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)
719
if (ggzmod->server_host)
720
if (_io_ggz_send_server(ggzmod->fd, ggzmod->server_host,
722
ggzmod->server_handle) < 0)
729
/* Common setup for normal mode and embedded mode */
730
static int game_prepare(int fd_pair[2], int *sock)
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);
741
/* Winsock implementation: see ggzmod_ggz_connect. */
745
*sock = ggz_make_socket(GGZ_SOCK_SERVER, port, NULL);
746
} while (*sock < 0 && port < 7000);
748
ggz_error_msg("Could not bind socket.");
751
if (listen(*sock, 1) < 0) {
752
ggz_error_msg("Could not listen on socket.");
755
snprintf(buf, sizeof(buf), "%d", port);
757
setenv("GGZSOCKET", buf, 1);
758
setenv("GGZMODE", "true", 1);
760
SetEnvironmentVariable("GGZSOCKET", buf);
761
SetEnvironmentVariable("GGZMODE", "true");
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)
773
int fd_pair[2]; /* socketpair, always needs to be declared */
774
#ifndef HAVE_SOCKETPAIR
780
char cmdline[1024] = "";
782
PROCESS_INFORMATION pi;
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");
792
if(game_prepare(fd_pair, &sock) < 0)
796
if ((pid = fork()) < 0)
797
ggz_error_sys_exit("fork failed");
800
#ifdef HAVE_SOCKETPAIR
803
/* debugging message??? */
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");
816
/* FIXME: Close all other fd's? */
817
/* FIXME: Not necessary to close other fd's if we use
820
/* Set working directory */
822
&& chdir(ggzmod->pwd) < 0) {
823
/* FIXME: what to do? */
826
/* FIXME: can we call ggzmod_ggz_log() from here? */
827
execv(ggzmod->argv[0], ggzmod->argv); /* run game */
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]);
834
#ifdef HAVE_SOCKETPAIR
837
ggzmod->fd = fd_pair[0];
841
/* FIXME: should we delete the argv arguments? */
846
for (i = 0; ggzmod->argv[i]; i++) {
847
snprintf(cmdline + strlen(cmdline),
848
sizeof(cmdline) - strlen(cmdline),
849
"%s ", ggzmod->argv[i]);
852
ZeroMemory(&si, sizeof(si));
853
if (!CreateProcess(NULL, cmdline, NULL, NULL, TRUE,
854
DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
855
NULL, NULL, &si, &pi)) {
858
CloseHandle(pi.hThread);
859
ggzmod->process = pi.hProcess;
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
865
sock2 = accept(sock, NULL, NULL);
867
ggz_error_sys("Listening to socket failed.");
870
#ifdef HAVE_WINSOCK2_H
881
/* Similar to game_fork(), but runs the game embedded */
882
static int game_embedded(GGZMod * ggzmod)
885
int fd_pair[2]; /* socketpair, always needs to be declared */
886
#ifndef HAVE_SOCKETPAIR
890
if(game_prepare(fd_pair, &sock) < 0)
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");
901
ggzmod->fd = fd_pair[0];
903
/* FIXME: we need to select, with a maximum timeout. */
904
/* FIXME: this is insecure; it should be restricted to local
906
sock2 = accept(sock, NULL, NULL);
908
ggz_error_sys("Listening to socket failed.");
911
#ifdef HAVE_WINSOCK2_H
919
ggzmod->pid = -1; /* FIXME: use -1 for embedded ggzcore? getpid()? */
921
ggzmod->process = INVALID_HANDLE_VALUE;
928
/**** Internal library functions ****/
930
/* Invokes handlers for the specified event */
931
static void call_handler(GGZMod *ggzmod, GGZModEvent event, void *data)
933
if (ggzmod->handlers[event])
934
(*ggzmod->handlers[event]) (ggzmod, event, data);
938
static void call_transaction(GGZMod * ggzmod, GGZModTransaction t, void *data)
940
if (!ggzmod->thandlers[t]) {
941
ggz_error_msg("Unhandled transaction %d.", t);
945
if (ggzmod->type != GGZMOD_GGZ) {
946
ggz_error_msg("The game can't handle transactions!");
950
(*ggzmod->thandlers[t])(ggzmod, t, data);
954
void _ggzmod_ggz_error(GGZMod *ggzmod, char* error)
956
call_handler(ggzmod, GGZMOD_EVENT_ERROR, error);
960
void _ggzmod_ggz_handle_state(GGZMod * ggzmod, GGZModState state)
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. */
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
983
_ggzmod_ggz_set_state(ggzmod, state);
986
_ggzmod_ggz_error(ggzmod,
987
"Game requested incorrect state value");
989
/* Is this right? has the gameover happened yet? */
992
void _ggzmod_ggz_handle_stand_request(GGZMod *ggzmod)
994
call_transaction(ggzmod, GGZMOD_TRANSACTION_STAND, NULL);
997
void _ggzmod_ggz_handle_sit_request(GGZMod *ggzmod, int seat_num)
999
call_transaction(ggzmod, GGZMOD_TRANSACTION_SIT, &seat_num);
1002
void _ggzmod_ggz_handle_boot_request(GGZMod *ggzmod, char *name)
1004
call_transaction(ggzmod, GGZMOD_TRANSACTION_BOOT, name);
1007
void _ggzmod_ggz_handle_bot_request(GGZMod *ggzmod, int seat_num)
1009
call_transaction(ggzmod, GGZMOD_TRANSACTION_BOT, &seat_num);
1012
void _ggzmod_ggz_handle_open_request(GGZMod *ggzmod, int seat_num)
1014
call_transaction(ggzmod, GGZMOD_TRANSACTION_OPEN, &seat_num);
1017
void _ggzmod_ggz_handle_chat_request(GGZMod *ggzmod, char *chat_msg)
1019
call_transaction(ggzmod, GGZMOD_TRANSACTION_CHAT, chat_msg);
1022
void _ggzmod_ggz_handle_info_request(GGZMod *ggzmod, int seat_num)
1024
call_transaction(ggzmod, GGZMOD_TRANSACTION_INFO, &seat_num);
1027
int ggzmod_ggz_set_stats(GGZMod *ggzmod, GGZStat *player_stats,
1028
GGZStat *spectator_stats)
1032
|| (!spectator_stats && ggzmod->num_spectator_seats > 0)
1033
|| ggzmod->type != GGZMOD_GGZ
1034
|| ggzmod->state == GGZMOD_STATE_CREATED) {
1038
return _io_ggz_send_stats(ggzmod->fd, ggzmod->num_seats, player_stats,
1039
ggzmod->num_spectator_seats, spectator_stats);
1042
int ggzmod_ggz_set_info(GGZMod *ggzmod, int num,
1049
return _io_ggz_send_msg_info(ggzmod->fd, num, infos);