home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 January
/
usenetsourcesnewsgroupsinfomagicjanuary1994.iso
/
sources
/
games
/
volume14
/
okbridge2
/
part07
/
network.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-01-27
|
33KB
|
1,300 lines
/* network.c
*
! Copyright (C) 1990-1992 by Matthew Clegg. All Rights Reserved
!
! OKbridge is made available as a free service to the Internet.
! Accordingly, the following restrictions are placed on its use:
!
! 1. OKbridge may not be modified in any way without the explicit
! permission of Matthew Clegg.
!
! 2. OKbridge may not be used in any way for commercial advantage.
! It may not be placed on for-profit networks or on for-profit
! computer systems. It may not be bundled as part of a package
! or service provided by a for-profit organization.
!
! If you have questions about restrictions on the use of OKbridge,
! write to mclegg@cs.ucsd.edu.
!
! DISCLAIMER: The user of OKbridge accepts full responsibility for any
! damage which may be caused by OKbridge.
*
* This is the implementation of the network module. The network module
* provides two functions. First it abstracts the socket communication
* into messages which are handled on the message queues, and second
* it provides a branch point between the server and client sides of
* the code.
*
*/
/* The NO_TM_ZONE definition is for machines which do not have the
field tm_zone defined in the struct tm returned by localtime.
In this case, we use the struct timezone returned by gettimeofday
to determine an offset from GMT.
*/
#ifdef AIX
#define NO_TM_ZONE 1
#define NO_PWCOMMENT 1
#endif
#ifdef HARRIS
#define NO_TM_ZONE 1
#endif
/* The NO_PWCOMMENT definition is for machines which do not have the
pw_comment field defined in struct passwd returned by getpwuid ().
*/
#ifdef AIX
#define NO_PWCOMMENT 1
#endif
#ifdef SGI
#define NO_TM_ZONE 1
#endif
#include <ctype.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
#ifdef AIX
#include <sys/select.h>
#include <time.h>
#endif
#define _NETWORK_
#include "state.h"
#include "socket.h"
#include "display.h"
#include "gps.h"
#include "fds.h"
#ifdef GCC
extern bzero (), fprintf (), fflush (), accept ();
extern time_t time ();
#endif
extern int errno;
extern char *sys_errlist [];
extern char *getlogin(), *getenv();
extern char *strdup ();
extern char *malloc ();
extern void close ();
extern int select ();
extern int gethostname ();
extern uid_t getuid ();
extern int Terminate_Program ();
extern void restore_cursor ();
extern void Broadcast_Comment ();
extern void Generate_reset ();
extern void Vacate_seat ();
#ifndef index
extern char *index();
#endif
#ifdef LOGFILE
FILE *net_log = NULL;
#endif
#define DISCARD (void)
#define RETRY_LIMIT 60
static Message message_free_list = NULL;
static Connection connection_freelist = NULL;
static int listen_port = -1;
/* The socket we have opened for listening for new connections. */
static int server_port = -1;
/* The socket opened by the client for listening to the server. */
extern char socket_error[];
/* An error message buffer for recording socket errors. */
/* Procedures for manipulating command queues: */
static message_queue Allocate_message_queue ()
{
message_queue m;
m = (message_queue) malloc (sizeof(struct Message_queue_struct));
m->head = m->tail = NULL;
return (m);
}
int message_available (queue)
message_queue queue;
/* Returns true if the given queue is nonempty. */
{
return (queue->head != NULL);
}
Message dequeue_message (queue)
message_queue queue;
/* Deletes the first message from the given queue and returns a pointer
to that message.
*/
{
Message m;
if (!message_available(queue))
return (NULL);
m = queue->head;
queue->head = m->next;
if (queue->head == NULL)
queue->tail = NULL;
return (m);
}
void enqueue_message (queue, m)
message_queue queue; Message m;
/* Appends m to the list of messages stored in the given message queue. */
{
m->next = NULL;
if (queue->tail == NULL)
queue->head = queue->tail = m;
else {
queue->tail->next = m;
queue->tail = m;
}
}
Message allocate_message ()
/* Allocates a message structure and returns a pointer to the structure. */
{
Message m;
if (message_free_list == NULL)
m = (Message) malloc (sizeof(struct Message_struct));
else {
m = message_free_list;
message_free_list = m->next;
}
m->p.command_text[0] = '\0';
m->p.player_no = 0;
m->private = 0;
m->loopback = 0;
return (m);
}
void deallocate_message (m)
Message m;
/* Returns the message structure m to the pool of free message structs. */
{
m->next = message_free_list;
message_free_list = m;
m->p.player_no = 0;
}
void clear_message_queue (queue)
message_queue queue;
/* Deletes all messages from the named queue. */
{
Message m;
while (message_available(queue)) {
m = dequeue_message (queue);
deallocate_message (m);
}
}
void clear_all_message_queues (t)
Table t;
/* Clears all of the message queues. */
{
clear_message_queue (t->protocol_queue);
clear_message_queue (t->conversation_queue);
clear_message_queue (t->game_queue);
}
static Connection Allocate_Dummy_Connection ()
/* Allocates a new connection structure and fills in the fields with
appropriate initial values. Does not link the connection into any list. */
{
Connection c;
if (connection_freelist == NULL)
c = (Connection) malloc (sizeof(struct Connection_struct));
else {
c = connection_freelist;
connection_freelist = c->inext;
}
c->channel = 0;
c->local = 1;
c->state = CSTATE_CONNECTED;
c->player_name[0] = '\0';
c->fullname[0] = '\0';
c->email[0] = '\0';
c->registry[0] = '\0';
c->seat = PLAYER_OBS;
c->table = NULL;
c->iprev = c->inext = c->oprev = c->onext = NULL;
if (Connections == NULL)
Connections = c;
return (c);
}
static Connection Allocate_Connection ()
/* Allocates a new connection structure, and fills in the pointer fields
appropriately. Links the connection into the Connection list. */
{
Connection c = Allocate_Dummy_Connection ();
c->inext = Connections->inext;
c->iprev = Connections;
if (c->inext != NULL)
c->inext->iprev = c;
Connections->inext = c;
return (c);
}
static void Link_Output_Connection (p, c)
Connection p, c;
/* Links the connection c into the list pointed to by p. Since we
always store dummy header elements in connection lists, p is guaranteed
to be non-NULL, even in the case of an empty list.*/
{
c->oprev = p;
c->onext = p->onext;
if (c->onext != NULL)
c->onext->oprev = c;
p->onext = c;
}
static void Unlink_Output_Connection (c)
Connection c;
/* Unlinks the connection c from the list containing it. */
{
if (c->onext != NULL)
c->onext->oprev = c->oprev;
if (c->oprev != NULL)
c->oprev->onext = c->onext;
c->oprev = c->onext = NULL;
}
static void Deallocate_Connection (c)
Connection c;
/* Unlinks the connection c from any lists which may contain it and
then returns the connection to the free store. */
{
Unlink_Output_Connection (c);
if (c->inext != NULL)
c->inext->iprev = c->iprev;
c->iprev->inext = c->inext;
c->inext = connection_freelist;
connection_freelist = c;
}
void close_connection (c)
Connection c;
/* void close_connection (Connection c) */
/* Closes the connection to c. */
{
Table t = c->table;
#ifdef DEBUG
char error_buf[100];
#endif
if (c->local)
return;
#ifdef DEBUG
sprintf (error_buf, "CLOSED CONNECTION ON FD %d.", c->channel);
Network_Comment (error_buf);
#endif
Vacate_seat (c->table, c->seat);
if (c->channel > 0)
close (c->channel);
Deallocate_Connection (c);
}
int No_connections (t)
Table t;
/* Returns the number of connections to the table t. */
{
int n;
Connection c;
n = 0;
FOREACH_PLAYER (c, t)
n++;
return (n);
}
Message loopback_message_unformatted (t, c, message)
Table t; Connection c; char *message;
/* void loopback_message_unformatted (Table t, char *message); */
/* Constructs a message structure using the the given message.
Assumes that the message is in the format which is used by the
client in transmitting messages. Appends the message structure
to the protocol queue for the table t.
*/
{
Message m = allocate_message ();
strcpy (m->p.command_text, message);
Parse_Command_for_Client (&(m->p));
m->source = c;
m->loopback = 1;
enqueue_message (t->protocol_queue, m);
return (m);
}
Message loopback_message (t, c, message)
Table t; Connection c; char *message;
/* void loopback_message (Table t, Connection c, char *message); */
/* Constructs a message structure using the given source, source name
and message text. Appends the message structure to the protocol
queue for the table t.
*/
{
Message m = allocate_message ();
sprintf (m->p.command_text, "%s %s %s", seat_names[c->seat],
c->player_name, message);
m->source = c;
m->loopback = 1;
Parse_Command_for_Client (&(m->p));
enqueue_message (t->protocol_queue, m);
return (m);
}
int server_send_unformatted (c, message)
Connection c; char *message;
/* Transmits the message to the given player. Returns 0 if successful or 1
if an error. If an error occurs, then the connection is closed.
*/
{
int status; /* return code from Select call. */
int log; /* number of bytes transmitted. */
int n; /* number of bytes to transmit. */
struct fd_set wait_set; /* A set representing the connections that
have been established. */
struct timeval tm; /* A timelimit of zero for polling for new
connections. */
char buf [100];
if (c->local) {
/* loopback_message_unformatted (c->table, c, message); */
return (1);
}
FD_ZERO (&wait_set);
FD_SET (c->channel, &wait_set);
tm.tv_sec = 0;
tm.tv_usec = 0;
status = select (FD_SETSIZE, (fd_set *) 0, &wait_set, (fd_set *) 0, &tm);
if (status < 0) {
sprintf (buf, "WRITE ERROR TO %s: %s", c->player_name, sys_errlist[errno]);
Network_Comment (buf);
shutdown (c->channel, 2);
c->channel = 0;
close_connection (c);
return (1);
} else if (status == 0) {
sprintf (buf, "WRITE BLOCK FOR %s", c->player_name);
Network_Comment (buf);
shutdown (c->channel, 2);
c->channel = 0;
close_connection (c);
return (1);
}
n = strlen (message) + 1;
do {
log = fd_writeln (c->channel, message);
} while ((log < 0) && (errno == EINTR));
if (log < n) {
sprintf (buf, "WRITE BLOCK FOR %s", c->player_name);
Network_Comment (buf);
close_connection (c);
return (1);
}
#ifdef LOGFILE
fprintf (net_log, "%2d > %s\n", c->channel, message);
fflush (net_log);
#endif
return (0);
}
int server_send (c, source, player_name, message)
Connection c; int source; char *player_name, *message;
/* Formats a message and sends it across the given connection.
Returns 0 if successful or 1 if an error. If an error occurs,
then the connection is closed. */
{
char buf [120];
sprintf (buf, "%s %s %s", seat_names[source], player_name, message);
return (server_send_unformatted (c, buf));
}
int May_receive (c, msg_type)
Connection c; int msg_type;
/* Returns true if a message of type msg_type can be sent to channel c. */
{
if (c == NULL)
return (0);
else if (c->state == CSTATE_PLAYING)
return (1);
else
return (0);
}
void server_broadcast_unformatted (t, msg_type, message)
Table t; int msg_type; char *message;
/* Broadcasts the message to all of the people sitting at table t who are
entitled to receive the message. */
{
Connection c, d;
int local = 0;
c = t->Players->onext;
while (c != NULL) {
d = c->onext;
if (May_receive (c, msg_type)) {
if (c->local)
local = 1;
else
server_send_unformatted (c, message);
}
c = d;
}
if (local && (msg_type != CMD_BOARD) && (msg_type != CMD_RECORD))
loopback_message_unformatted (t, MODERATOR(t), message);
}
void server_broadcast (t, source, player_name, msg_type, message)
Table t; int source, msg_type; char *player_name, *message;
/* Formats and broadcasts the message to all of the people sitting at table t
who are entitled to receive the message. */
{
char buf [120];
sprintf (buf, "%s %s %s", seat_names[source], player_name, message);
server_broadcast_unformatted (t, msg_type, buf);
}
void Relay_message (t, c, msg_type, message)
Table t; Connection c; int msg_type; char *message;
/* Relays the message from c to all of the other players at table t who
are allowed to receive the message. */
{
Connection p, q;
p = t->Players->onext;
while (p != NULL) {
q = p->onext;
if ((p->channel != c->channel) && May_receive (p, msg_type))
server_send_unformatted (p, message);
p = q;
}
}
Message send_message (t, msg_type, message)
Table t; int msg_type; char *message;
/* If in server mode, sends a message to each of the clients.
If in client mode, sends a message to the server. */
{
char message_buf [120];
Message m;
m = loopback_message (t, Local_Player_Connection, message);
if (client_mode) {
sprintf (message_buf, "%s %s", local_player_name, message);
fd_writeln (server_port, message_buf);
#ifdef LOGFILE
fprintf (net_log, " > %s\n", message_buf);
fflush (net_log);
#endif
}
return (m);
}
void send_server_message (t, msg_type, message)
Table t; int msg_type; char *message;
/* Sends a message which originates from the 'SERVER'.
Should only be used if in server mode.
*/
{
server_broadcast (t, PLAYER_SERVER, seat_names[PLAYER_SERVER],
msg_type, message);
}
void send_private_message (c, message)
Connection c; char *message;
/* static void send_private_message (Connection c, char *message) */
/* Only to be used in server mode. Sends a message from the 'server'
to the connection c. If the connection c is the local player,
then appends the message to the protocol queue of the table of
the local player.
*/
{
Message m;
if (c->local) {
m = loopback_message (c->table, MODERATOR(c->table), message);
m->private = 1;
} else
server_send (c, PLAYER_SERVER, seat_names[PLAYER_SERVER], message);
}
static void Broadcast_Inline_Data (t, msg_type, buf)
Table t;
int msg_type;
char *buf;
/* Broadcasts the contents of buf to each of the connections at table t. */
{
Connection c, d;
if (server_mode) {
c = t->Players->onext;
while (c != NULL) {
d = c->onext;
if (May_receive (c, msg_type)) {
if (!c->local)
server_send_unformatted (c, buf);
}
c = d;
}
} else
fd_writeln (server_port, buf);
}
static void Relay_Inline_Data (t, f, msg_type, buf)
Table t;
Connection f;
int msg_type;
char *buf;
/* Broadcasts the contents of buf to each of the connections at table
t EXCEPT f. */
{
Connection c, d;
c = t->Players->onext;
while (c != NULL) {
d = c->onext;
if (May_receive (c, msg_type)) {
if ((c != f) && !c->local)
server_send_unformatted (c, buf);
}
c = d;
}
}
void Transmit_board (t, b)
Table t;
Board *b;
/* If we are the server, then transmits the board b to each of the players
at the table t. If we are a client, then transmits the board b to
the server. */
{
char buf1[100], buf2[100];
Encode_board (b, buf1, buf2);
Broadcast_Inline_Data (t, CMD_BOARD, buf1);
Broadcast_Inline_Data (t, CMD_BOARD, buf2);
}
void Transmit_play_record (t, p)
Table t;
Play_record *p;
/* If we are the server, then transmits the play record p to each of the
players at the table t. If we are a client, then transmits the play
record p to the server. */
{
char buf1[100], buf2[100], buf3[100];
Encode_play_record (p, buf1, buf2, buf3);
Broadcast_Inline_Data (t, CMD_RECORD, buf1);
Broadcast_Inline_Data (t, CMD_RECORD, buf2);
Broadcast_Inline_Data (t, CMD_RECORD, buf3);
}
void Relay_board (t, c, b)
Table t;
Connection c;
Board *b;
/* Relays the in-line board data from the connection c to the other players
at the table t.
*/
{
char buf1[100], buf2[100];
Encode_board (b, buf1, buf2);
Relay_Inline_Data (t, c, CMD_BOARD, buf1);
Relay_Inline_Data (t, c, CMD_BOARD, buf2);
}
void Relay_play_record (t, c, p)
Table t;
Connection c;
Play_record *p;
/* Relays the in-line play record from the connection c to the other players
at the table t.
*/
{
char buf1[100], buf2[100], buf3[100];
Encode_play_record (p, buf1, buf2, buf3);
Relay_Inline_Data (t, c, CMD_RECORD, buf1);
Relay_Inline_Data (t, c, CMD_RECORD, buf2);
Relay_Inline_Data (t, c, CMD_RECORD, buf3);
}
Board *Receive_board (c)
Connection c;
/* Receives a board as in-line data from the connection c. */
{
char buf1[100], buf2[100];
int status;
Board *b;
int chan;
if (client_mode)
chan = server_port;
else
chan = c->channel;
status = fd_readln (chan, buf1, 100);
if (status <= 0)
return (NULL);
status = fd_readln(chan, buf2, 100);
if (status <= 0)
return (NULL);
b = Decode_board (buf1, buf2);
return (b);
}
Play_record *Receive_play_record (c)
Connection c;
/* Receives a play record as in-line data from the connection c. */
{
char buf1[100], buf2[100], buf3[100];
int status;
Play_record *p;
int chan;
if (client_mode)
chan = server_port;
else
chan = c->channel;
status = fd_readln (chan, buf1, 100);
if (status < 0)
return (NULL);
status = fd_readln (chan, buf2, 100);
if (status < 0)
return (NULL);
status = fd_readln (chan, buf3, 100);
if (status < 0)
return (NULL);
p = Decode_play_record (buf1, buf2, buf3);
return (p);
}
static Table Allocate_Table ()
/* Allocates a table structure and initializes it. */
{
Table t;
int i;
t = (Table) malloc(sizeof(struct Table_struct));
for (i = 0; i < PLAYER_TYPES; i++) {
/* t->Seats[i].player_name[0] = '\0'; */
sprintf (t->Seats[i].player_name, "%s", seat_names[i]);
t->Seats[i].occupied = 0;
t->Seats[i].connection = NULL;
}
t->protocol_queue = Allocate_message_queue ();
t->conversation_queue = Allocate_message_queue ();
t->game_queue = Allocate_message_queue ();
/* The first element of the connections list is used to represent
the server for the table. */
t->Players = Allocate_Dummy_Connection ();
t->Players->local = 1;
t->Players->seat = PLAYER_SERVER;
t->Players->table = t;
sprintf (t->Players->player_name, "MOD");
t->table_no = 1;
t->game_mode = STARTUP_MODE;
t->playing_mode = CLUB_PLAYING_MODE;
t->board = NULL;
t->play_record = NULL;
t->above_line[0] = t->above_line[1] = 0;
t->below_line[0] = t->below_line[1] = 0;
if (Table_List == NULL) {
Table_List = t;
t->prev = t->next = NULL;
} else {
t->next = Table_List->next;
if (t->next != NULL)
t->next->prev = t;
t->prev = Table_List;
t->prev->next = t;
}
return (t);
}
void Initialize_Table_List ()
/* Initializes the array of tables. */
{
Table_List = Local_table = Allocate_Table ();
Local_Player_Connection = Allocate_Connection ();
Local_Player_Connection->channel = 0;
Local_Player_Connection->local = 1;
Local_Player_Connection->spectator = 0;
Local_Player_Connection->seat = local_player;
Local_Player_Connection->table = Local_table;
sprintf (Local_Player_Connection->player_name, "%s", local_player_name);
if (IS_PLAYER(local_player)) {
Local_table->Seats[local_player].connection = Local_Player_Connection;
Local_table->Seats[local_player].occupied = 1;
sprintf (Local_table->Seats[local_player].player_name, "%s",
local_player_name);
}
Link_Output_Connection (Local_table->Players, Local_Player_Connection);
}
void Initialize_Network_Logfile ()
{
#ifdef LOGFILE
char buf[80];
sprintf (buf, "%s.log", local_player_name);
net_log = fopen (buf, "w");
if (net_log == NULL) {
sprintf (buf, "Error opening net_log file %s: %s", buf,
sys_errlist[errno]);
fflush (net_log);
Terminate_Program (buf);
}
#endif
}
void Initialize_Network ()
{
struct hostent *he;
struct passwd *pw_entry;
char *my_addr, *user, *name, *p;
char buf[100];
#ifdef NO_TM_ZONE
struct timeval tp;
struct timezone tzp;
#else
time_t current_time;
struct tm *decoded_time;
#endif
gethostname (Host_name, 100);
if (!Host_IP_is_known) {
he = gethostbyname (Host_name);
if (he != NULL) {
sprintf (Host_name, "%s", he->h_name);
/* in = (struct in_addr *) he->h_addr_list[0];
my_addr = inet_ntoa(*in);
*/
my_addr = inet_ntoa(**((struct in_addr **) he->h_addr_list));
if (my_addr == NULL) my_addr = "--";
sprintf (Host_IP, "%s", my_addr);
Host_IP_is_known = 1;
} else
sprintf (Host_IP, "--");
}
pw_entry = getpwuid (getuid());
if (pw_entry != NULL) {
sprintf (User_name, "%s", pw_entry->pw_name);
#ifndef NO_PWCOMMENT
if (pw_entry->pw_comment != NULL) {
sprintf (User_fullname, "%s", pw_entry->pw_gecos);
if ((p = index(User_fullname, ',')) != NULL)
*p = '\0';
} else
#endif
if ((name = getenv("NAME")) != NULL)
sprintf (User_fullname, "%s", name);
else
sprintf (User_fullname, "%s", User_name);
} else {
user = getlogin ();
if (user == NULL)
user = getenv("USER");
if (user == NULL)
user = "unknown";
sprintf (User_name, "%s", user);
name = getenv("NAME");
if (name != NULL)
sprintf (User_fullname, "%s", name);
else if (local_player_full_name != NULL)
sprintf (User_fullname, "%s", local_player_full_name);
else
sprintf (User_fullname, "%s", User_name);
}
if (local_player_email == NULL) {
sprintf (buf, "%s@%s", pw_entry->pw_name, Host_name);
local_player_email = strdup (buf);
}
if (Timezone_name == NULL) {
#ifdef NO_TM_ZONE
gettimeofday (&tp, &tzp);
if (tzp.tz_minuteswest < 0)
sprintf (buf, "GMT+%d:%02d",
(-tzp.tz_minuteswest)/60, (-tzp.tz_minuteswest)%60);
else
sprintf (buf, "GMT-%d:%02d",
tzp.tz_minuteswest/60, tzp.tz_minuteswest%60);
Timezone_name = strdup (buf);
#else
time (¤t_time);
decoded_time = localtime (¤t_time);
Timezone_name = strdup(decoded_time->tm_zone);
#endif
}
if (Registration == NULL)
Registration = strdup (local_player_email);
/* Allocate a dummy head for the connections list. */
Connections = Allocate_Dummy_Connection ();
Initialize_Network_Logfile ();
Initialize_Table_List ();
client_mode = server_mode = 0;
server_port = listen_port = 0;
}
void Close_all_connections ()
{
Connection c, d;
Table t;
int i;
if (server_mode) {
if (listen_port > 0) {
close (listen_port);
listen_port = 0;
}
for (c = Connections->inext; c != NULL; ) {
d = c->inext;
if (!c->local) {
close_connection (c);
Vacate_seat (c->table, c->seat);
}
c = d;
}
GPS_End_Server_Mode ();
} else if (client_mode) {
for (t = Table_List; t != NULL; t = t->next)
for (i = 0; i < 4; i++)
if ((t != Local_table) || (i != local_player))
Vacate_seat (t, i);
if (server_port > 0)
close(server_port);
}
/* Initialize_Table_List (); */
server_mode = client_mode = 0;
server_port = listen_port = 0;
}
void Switch_Table (c, new_table)
Connection c;
Table new_table;
/* Unlinks the connection c from the current table and links it into
the new table.
*/
{
Unlink_Output_Connection (c);
Link_Output_Connection (new_table->Players, c);
}
void Setup_server ()
/* Initializes the data structures for the server. */
{
char comment_buf [80];
int retries;
time_t current_time;
struct tm *decoded_time;
retries = 0;
do {
listen_port = open_port (network_port);
retries = retries + 1;
if (listen_port <= 0)
network_port++;
} while ((listen_port <= 0) && (retries < 10));
if (listen_port <= 0) {
Network_Comment (socket_error);
Network_Comment ("ERROR IN SETTING UP SERVER.");
return;
}
sprintf (comment_buf, "ENTERING SERVER MODE ON PORT %d", network_port);
Network_Comment (comment_buf);
sprintf (PLAYER_NAME(Local_table, PLAYER_SERVER), "%s", local_player_name);
server_mode = 1;
time (¤t_time);
decoded_time = localtime (¤t_time);
sprintf (Server_Start_time, "%2d:%02d %s", decoded_time->tm_hour,
decoded_time->tm_min, Timezone_name);
if (local_player_full_name != NULL)
sprintf (Local_Player_Connection->fullname, "%s", local_player_full_name);
else if (strlen(User_fullname) > 0)
sprintf (Local_Player_Connection->fullname, "%s", User_fullname);
if (local_player_email != NULL)
sprintf (Local_Player_Connection->email, "%s", local_player_email);
sprintf (Local_Player_Connection->registry, "%s", Registration);
GPS_Broadcast_Server ();
Local_Player_Connection->state = CSTATE_PLAYING;
}
void Assign_seat ();
void Attempt_to_connect (seat_requested)
int seat_requested;
/* Attempts to connect to the server named as server_name. If the
* connection is successful, then initializes the Table data structure
* appropriately and sends an initial handshaking message.
*/
{
int status; /* Return code from client_init call. */
char message_buf [80];
sprintf (message_buf, "CONNECTING TO SERVER AT %s, PORT %d.", server_name,
network_port);
Network_Comment (message_buf);
restore_cursor ();
status = client_init (server_name, network_port, 3);
if (status < 0) {
Network_Comment (socket_error);
return;
}
Network_Comment ("CONNECTION ESTABLISHED.");
server_port = status;
client_mode = 1;
/* Now we construct the initial handshake message that we send
to the server. This message is of the form:
HELLO <ver> <player-name>
where
<ver> is the current version of the program,
<player-name> is the name of the local player.
*/
sprintf (message_buf, "%s HELLO %s %s %s", local_player_name,
major_revision_level, local_player_name,
seat_names[seat_requested]);
fd_writeln (server_port, message_buf);
#ifdef LOGFILE
fprintf (net_log, " > %s\n", message_buf);
fflush (net_log);
#endif
local_player = PLAYER_OBS;
(void) Assign_seat (Local_table, Local_Player_Connection, PLAYER_OBS);
Local_Player_Connection->state = CSTATE_CONNECTED;
Display_Player_Position();
if (local_player_full_name != NULL)
Send_fullname (Local_table, local_player_full_name);
else if (strlen(User_fullname) > 0)
Send_fullname (Local_table, User_fullname);
if (local_player_email != NULL)
Send_email (Local_table, local_player_email);
/* Send_registry (Local_table, Registration); */
}
void Accept_new_connection ()
/* Given that a new connection is waiting to be accepted, makes
the connection and begins processing for it.
*/
{
Connection c;
int fd_conn; /* file descriptor for the connection. */
struct sockaddr net_addr;
int addrlen;
#ifdef DEBUG
char error_buf[80];
#endif
addrlen = sizeof(struct sockaddr);
fd_conn = accept (listen_port, &net_addr, &addrlen);
if (fd_conn < 0) {
sprintf (socket_error, "Connection Error: %s", sys_errlist[errno]);
Network_Comment (socket_error);
return;
}
#ifdef LOGFILE
fprintf (net_log, "Connection established. fd %d.\n", fd_conn);
fflush (net_log);
#endif
c = Allocate_Connection ();
c->local = 0;
c->channel = fd_conn;
c->spectator = 0;
c->state = CSTATE_CONNECTED;
c->seat = PLAYER_OBS;
c->table = Local_table;
Link_Output_Connection (Local_table->Players, c);
#ifdef DEBUG
sprintf (error_buf, "ACCEPTED A NEW CONNECTION FOR FD %d (%d).",
c->channel, listen_port);
Network_Comment (error_buf);
#endif
}
static void Get_network_message (c, channel)
Connection c; int channel;
/* static void Get_network_message (Connection c) */
/* Gets a message from connection c, parses it, and processes it. */
{
int buflen; /* return code from fd_readln */
Message m; /* The message which was read from the
network. */
char msg_buf[100];
m = allocate_message ();
buflen = fd_readln (channel, m->p.command_text, BUFFER_LENGTH);
if (buflen <= 0) {
deallocate_message (m);
if (server_mode) {
sprintf (msg_buf, "THE NETWORK CONNECTION WITH %s HAS BEEN LOST.",
c->player_name);
close_connection (c);
Broadcast_Comment (msg_buf);
} else {
server_port = 0;
Network_Comment
("THE NETWORK CONNECTION WITH THE SERVER HAS BEEN LOST.");
Generate_reset (RESET_DISCONNECT);
}
} else {
m->source = c;
if (server_mode) {
Parse_Command_for_Server (&(m->p));
m->p.player_no = c->seat;
} else {
Parse_Command_for_Client (&(m->p));
}
/* If we are receiving a BOARD or a RECORD message, then we must also
recieve the associated in-line data. */
if (m->p.command == CMD_BOARD)
m->p.data.board.record = Receive_board (c);
else if (m->p.command == CMD_RECORD)
m->p.data.record.play = Receive_play_record (c);
enqueue_message (c->table->protocol_queue, m);
#ifdef LOGFILE
fprintf (net_log, "%2d < %s\n", channel, m->p.command_text);
fflush (net_log);
#endif
}
}
static void Wait_for_keyboard_event ()
{
int status; /* return code from Select call. */
struct fd_set wait_set; /* A set representing the connections that
have been established. */
do {
FD_ZERO (&wait_set);
FD_SET (fileno(stdin), &wait_set);
if (GPS_request_in_progress && (GPS_socket > 0))
FD_SET (GPS_socket, &wait_set);
status = select (FD_SETSIZE, &wait_set, (fd_set *) 0, (fd_set *) 0,
(struct timeval *) 0);
if ((status < 0) && (errno != EINTR)) {
sprintf (socket_error, "Error in select: %s", sys_errlist[errno]);
Network_Comment (socket_error);
}
} while (status < 0);
}
static void Client_wait_for_network_event ()
{
int status; /* return code from Select call. */
struct fd_set wait_set; /* A set representing the connections that
have been established. */
do {
FD_ZERO (&wait_set);
FD_SET (fileno(stdin), &wait_set);
FD_SET (server_port, &wait_set);
if (GPS_request_in_progress && (GPS_socket > 0))
FD_SET (GPS_socket, &wait_set);
status = select (FD_SETSIZE, &wait_set, (fd_set *) 0, (fd_set *) 0,
(struct timeval *) 0);
if ((status < 0) && (errno != EINTR)) {
sprintf (socket_error, "Error in select: %s", sys_errlist[errno]);
Network_Comment (socket_error);
}
} while (status < 0);
if (FD_ISSET(server_port, &wait_set))
Get_network_message (Local_Player_Connection, server_port);
}
static void Server_wait_for_network_event ()
{
Connection c, d;
int status; /* return code from Select call. */
struct fd_set wait_set; /* A set representing the connections that
have been established. */
int connection_available, data_available;
data_available = 0;
do {
FD_ZERO (&wait_set);
/* Set bits in the wait_set corresponding to the open socket
descriptors which we have at the moment. */
FOREACH_CONNECTION (c) {
if (!c->local)
FD_SET (c->channel, &wait_set);
}
FD_SET (fileno(stdin), &wait_set);
if (listen_port > 0)
FD_SET (listen_port, &wait_set);
if (GPS_request_in_progress && (GPS_socket > 0))
FD_SET (GPS_socket, &wait_set);
status = select (FD_SETSIZE, &wait_set, (fd_set *) 0, (fd_set *) 0,
(struct timeval *) 0);
connection_available = 0;
if (status < 0) {
if (errno != EINTR) {
sprintf (socket_error, "Error in select: %s", sys_errlist[errno]);
Network_Comment (socket_error);
}
data_available = 0;
} else {
connection_available = ((listen_port > 0)
&& FD_ISSET(listen_port, &wait_set));
if (connection_available)
data_available = status - 1;
else
data_available = 1;
}
if (connection_available)
Accept_new_connection ();
} while (!data_available);
c = Connections->inext;
while (c != NULL) {
d = c->inext;
if (!c->local && FD_ISSET(c->channel, &wait_set)) {
FD_CLR(c->channel, &wait_set);
Get_network_message (c, c->channel);
}
c = d;
}
}
void Wait_for_network_event ()
/* Waits for an input event to occur. If a network event occurs, then
parses the message and places it onto the proper player queue.
*/
{
if (server_mode)
Server_wait_for_network_event ();
else if (client_mode)
Client_wait_for_network_event ();
else
Wait_for_keyboard_event ();
#ifdef MDEBUG
if (!malloc_verify ()) {
Moderator_Comment ("A MEMORY ALLOCATION ERROR HAS BEEN DETECTED!");
access_error_routine ();
}
#endif
}