]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/server/server.c
Add configuration files for client and server
[dragonblocks_alpha.git] / src / server / server.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <errno.h>
5 #include <netdb.h>
6 #include "server/database.h"
7 #include "server/server.h"
8 #include "server/server_map.h"
9 #include "signal_handlers.h"
10 #include "util.h"
11
12 static Server server;
13
14 // include handle_packets implementation
15
16 #include "network.c"
17
18 // utility functions
19
20 // pthread start routine for reciever thread
21 static void *reciever_thread(void *arg)
22 {
23         Client *client = arg;
24
25         if (! handle_packets(client))
26                 server_disconnect_client(client, DISCO_NO_SEND | DISCO_NO_JOIN, "network error");
27
28         return NULL;
29 }
30
31 // accept a new connection, initialize client and start reciever thread
32 static void accept_client()
33 {
34         struct sockaddr_storage client_address = {0};
35         socklen_t client_addrlen = sizeof(client_address);
36
37         int fd = accept(server.sockfd, (struct sockaddr *) &client_address, &client_addrlen);
38
39         if (fd == -1) {
40                 if (errno != EINTR)
41                         perror("accept");
42                 return;
43         }
44
45         Client *client = malloc(sizeof(Client));
46         client->fd = fd;
47         pthread_mutex_init(&client->mtx, NULL);
48         client->state = CS_CREATED;
49         client->address = address_string((struct sockaddr_in6 *) &client_address);
50         client->name = client->address;
51         client->server = &server;
52         client->pos = (v3f64) {0.0f, 0.0f, 0.0f};
53         pthread_create(&client->net_thread, NULL, &reciever_thread, client);
54
55         pthread_rwlock_wrlock(&server.clients_rwlck);
56         list_put(&server.clients, client, NULL);
57         pthread_rwlock_unlock(&server.clients_rwlck);
58
59         printf("Connected %s\n", client->address);
60 }
61
62 // list_clear_func callback used on server shutdown to disconnect all clients properly
63 static void list_disconnect_client(void *key, unused void *value, unused void *arg)
64 {
65         server_disconnect_client(key, DISCO_NO_REMOVE | DISCO_NO_MESSAGE, "");
66 }
67
68 // start up the server after socket was created, then accept connections until interrupted, then shutdown server
69 static void server_run(int fd)
70 {
71         server.sockfd = fd;
72         pthread_rwlock_init(&server.clients_rwlck, NULL);
73         server.clients = list_create(NULL);
74         pthread_rwlock_init(&server.players_rwlck, NULL);
75         server.players = list_create(&list_compare_string);
76
77         database_init();
78         server_map_init(&server);
79         server_map_prepare_spawn();
80
81         while (! interrupted)
82                 accept_client();
83
84         printf("Shutting down\n");
85
86         pthread_rwlock_wrlock(&server.clients_rwlck);
87         list_clear_func(&server.clients, &list_disconnect_client, NULL);
88         pthread_rwlock_unlock(&server.clients_rwlck);
89
90         pthread_rwlock_wrlock(&server.players_rwlck);
91         list_clear(&server.players);
92         pthread_rwlock_unlock(&server.players_rwlck);
93
94         pthread_rwlock_destroy(&server.clients_rwlck);
95         pthread_rwlock_destroy(&server.players_rwlck);
96
97         shutdown(server.sockfd, SHUT_RDWR);
98         close(server.sockfd);
99
100         server_map_deinit();
101         database_deinit();
102
103         exit(EXIT_SUCCESS);
104 }
105
106 // public functions
107
108 // disconnect a client with various options an an optional detail message (flags: DiscoFlag bitmask)
109 void server_disconnect_client(Client *client, int flags, const char *detail)
110 {
111         client->state = CS_DISCONNECTED;
112
113         if (! (flags & DISCO_NO_REMOVE)) {
114                 if (client->name) {
115                         pthread_rwlock_wrlock(&server.players_rwlck);
116                         list_delete(&server.players, client->name);
117                         pthread_rwlock_unlock(&server.players_rwlck);
118                 }
119                 pthread_rwlock_wrlock(&server.clients_rwlck);
120                 list_delete(&server.clients, client);
121                 pthread_rwlock_unlock(&server.clients_rwlck);
122         }
123
124         if (! (flags & DISCO_NO_MESSAGE))
125                 printf("Disconnected %s %s%s%s\n", client->name, INBRACES(detail));
126
127         if (! (flags & DISCO_NO_SEND))
128                 send_command(client, CC_DISCONNECT);
129
130         pthread_mutex_lock(&client->mtx);
131         close(client->fd);
132         pthread_mutex_unlock(&client->mtx);
133
134         if (! (flags & DISCO_NO_JOIN))
135                 pthread_join(client->net_thread, NULL);
136
137         if (client->name != client->address)
138                 free(client->name);
139         free(client->address);
140
141         pthread_mutex_destroy(&client->mtx);
142         free(client);
143 }
144
145 // server entry point
146 int main(int argc, char **argv)
147 {
148         program_name = argv[0];
149
150         if (argc < 2)
151                 internal_error("missing port");
152
153         struct addrinfo hints = {
154                 .ai_family = AF_INET6,
155                 .ai_socktype = SOCK_STREAM,
156                 .ai_protocol = 0,
157                 .ai_flags = AI_NUMERICSERV | AI_PASSIVE,
158         };
159
160         struct addrinfo *info = NULL;
161
162         int gai_state = getaddrinfo(NULL, argv[1], &hints, &info);
163
164         if (gai_state != 0)
165                 internal_error(gai_strerror(gai_state));
166
167         int fd = socket(info->ai_family, info->ai_socktype, 0);
168
169         if (fd == -1)
170                 syscall_error("socket");
171
172         int flag = 1;
173
174         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1)
175                 syscall_error("setsockopt");
176
177         if (bind(fd, info->ai_addr, info->ai_addrlen) == -1)
178                 syscall_error("bind");
179
180         if (listen(fd, 3) == -1)
181                 syscall_error("listen");
182
183         char *addrstr = address_string((struct sockaddr_in6 *) info->ai_addr);
184         printf("Listening on %s\n", addrstr);
185         free(addrstr);
186
187         freeaddrinfo(info);
188
189         signal_handlers_init();
190         server_run(fd);
191
192         return EXIT_SUCCESS;
193 }