2005-12-01 Richard D. Worth <richard@theworths.org>
[ttt] / src / ttt-server.c
1 /* ttt-server.c - tic-tac-toe game server
2  *
3  * Copyright © 2005 Carl Worth
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * Author: Carl Worth <cworth@cworth.org>
20  */
21
22 #include "ttt-server.h"
23
24 #include "ttt-args.h"
25 #include "ttt-client.h"
26 #include "ttt-error.h"
27 #include "ttt-socket.h"
28
29 struct _ttt_server {
30     pthread_mutex_t mutex;
31
32     const char *host;
33     const char *port;
34
35     ttt_client_t **clients;
36     int clients_size;
37     int num_clients;
38 };
39
40 static void
41 ttt_server_init (ttt_server_t *server, const char *host, const char *port)
42 {
43     pthread_mutex_init (&server->mutex, NULL);
44
45     server->host = host;
46     server->port = port;
47
48     server->clients = NULL;
49     server->clients_size = 0;
50     server->num_clients = 0;
51 }
52
53 /* Exported: See ttt-server.h for documentation. */
54 ttt_error_t
55 ttt_server_register_client (ttt_server_t *server, ttt_client_t *client)
56 {
57     int i;
58     ttt_error_t error = TTT_ERROR_NONE;
59     const char *name;
60
61     pthread_mutex_lock (&server->mutex);
62
63     name = ttt_client_get_name (client);
64
65     assert (name != NULL);
66
67     if (name[0] == '\0')
68         return TTT_ERROR_INVALID_NAME;
69
70     for (i = 0; i < server->num_clients; i++) {
71         if (strcmp (ttt_client_get_name (server->clients[i]), name) == 0) {
72             error = TTT_ERROR_INVALID_NAME;
73             goto CLEANUP_LOCK;
74         }
75     }
76
77     printf ("Client %s has joined.\r\n", name);
78
79     server->num_clients++;
80
81     if (server->num_clients > server->clients_size) {
82         if (server->clients_size == 0)
83             server->clients_size = 1;
84         else
85             server->clients_size *= 2;
86
87         server->clients = xrealloc (server->clients,
88                                     server->clients_size * sizeof (ttt_client_t *));
89     }
90
91     server->clients [server->num_clients - 1] = client;
92
93  CLEANUP_LOCK:
94     pthread_mutex_unlock (&server->mutex);
95
96     return error;
97 }
98
99 /* Exported: See ttt-server.h for documentation. */
100 void
101 ttt_server_unregister_client (ttt_server_t *server, ttt_client_t *client)
102 {
103     int i;
104
105     pthread_mutex_lock (&server->mutex);
106
107     for (i = 0; i < server->num_clients; i++)
108         if (server->clients[i] == client)
109             break;
110
111     assert (i < server->num_clients);
112
113     printf ("Client %s has left.\r\n", ttt_client_get_name (client));
114
115     memmove (&server->clients[i], &server->clients[i+1],
116              (server->num_clients - i - 1) * sizeof (ttt_client_t *));
117
118     server->num_clients--;
119
120     pthread_mutex_unlock (&server->mutex);
121 }
122
123 /* Exported: See ttt-server.h for documentation. */
124 void
125 ttt_server_broadcast (ttt_server_t *server, const char *message)
126 {
127     int i;
128
129     pthread_mutex_lock (&server->mutex);
130
131     for (i = 0; i < server->num_clients; i++)
132         ttt_client_send (server->clients[i], message);
133
134     pthread_mutex_unlock (&server->mutex);
135 }
136
137 /* Exported: See ttt-server.h for documentation. */
138 const char*
139 ttt_server_who (ttt_server_t *server)
140 {
141     int i;
142     char *response;
143
144     pthread_mutex_lock (&server->mutex);
145
146     xasprintf (&response, "WHO");
147
148     for (i = 0; i < server->num_clients; i++)
149         xasprintf (&response, "%s %s %d",
150                    response,
151                    ttt_client_get_name (server->clients[i]),
152                    ttt_client_get_num_wins (server->clients[i]));
153
154     xasprintf (&response, "%s\r\n", response);
155
156     pthread_mutex_unlock (&server->mutex);
157
158     return response;
159 }
160
161 /* Exported: See ttt-server.h for documentation. */
162 const char*
163 ttt_server_get_host (ttt_server_t *server)
164 {
165     return server->host;
166 }
167
168 /* Exported: See ttt-server.h for documentation. */
169 const char*
170 ttt_server_get_port (ttt_server_t *server)
171 {
172     return server->port;
173 }
174
175 static const char *WELCOME_MESSAGE = 
176 "Welcome to ttt-server. So far, this program is still a demonstration\r\n"
177 "TCP/IP server, acting something like a rather braindead chat server.\r\n"
178 "The server is currently listening on:\r\n"
179 "\r\n   %s:%s\r\n"
180 "\r\nTo test this, simply connect one or more clients to that host and port.\r\n"
181 "For example:\r\n"
182 "\r\n   telnet %s %s\r\n"
183 "\r\nOnce you have connected a client, the server will send each line of text\r\n"
184 "it receives to all connected clients. The server reports client joins and\r\n"
185 "departures on stdout.\r\n"
186 "\r\nNote that to terminate the telnet client you type Control-], then\r\n"
187 "<Enter>, then \"close\" (and <Enter>) at the \"telnet> \" prompt.\r\n"
188 "\r\nHave fun!\r\n"
189 "-Carl\r\n"
190 "\r\nPS. At this point we're ready to leave the demonstration phase and to\r\n"
191 "begin implementing TTTP (tic-tac-toe protocol) as well as fixing the\r\n"
192 "protocol specifcation. We don't need a custom client to move forward on\r\n"
193 "the server (that is one of the ideas behind using a telnet-compatible\r\n"
194 "protocol), but a custom client would still be a fine project for a\r\n"
195 "motivated beginning programmer.\r\n\r\n";
196
197 static void
198 _ttt_server_accept (void *closure, int client_socket)
199 {
200     ttt_server_t *server = closure;
201
202     ttt_client_new (server, client_socket);
203 }
204
205 int 
206 main (int argc, char **argv)
207 {
208     ttt_args_t args;
209     ttt_server_t server;
210     int socket;
211
212     ttt_args_parse (&args, argc, argv);
213
214     if (args.log_file)
215         xfreopen (args.log_file, "a", stderr);
216
217     socket = ttt_socket_create_server (args.host, args.port);
218
219     printf (WELCOME_MESSAGE, args.host, args.port, args.host, args.port);
220
221     ttt_server_init (&server, args.host, args.port);
222
223     while (1)
224         ttt_socket_accept (socket, _ttt_server_accept, &server);
225
226     /* We only reach here if something bad happened. */
227     return 1;
228 }