]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/server/server.c
3f9a584040204780574e7f407da7ac3ced5da189
[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.config.simulation_distance = 10;
72
73         server.sockfd = fd;
74         pthread_rwlock_init(&server.clients_rwlck, NULL);
75         server.clients = list_create(NULL);
76         pthread_rwlock_init(&server.players_rwlck, NULL);
77         server.players = list_create(&list_compare_string);
78
79         database_init();
80         server_map_init(&server);
81         server_map_prepare_spawn();
82
83         while (! interrupted)
84                 accept_client();
85
86         printf("Shutting down\n");
87
88         pthread_rwlock_wrlock(&server.clients_rwlck);
89         list_clear_func(&server.clients, &list_disconnect_client, NULL);
90         pthread_rwlock_unlock(&server.clients_rwlck);
91
92         pthread_rwlock_wrlock(&server.players_rwlck);
93         list_clear(&server.players);
94         pthread_rwlock_unlock(&server.players_rwlck);
95
96         pthread_rwlock_destroy(&server.clients_rwlck);
97         pthread_rwlock_destroy(&server.players_rwlck);
98
99         shutdown(server.sockfd, SHUT_RDWR);
100         close(server.sockfd);
101
102         server_map_deinit();
103         database_deinit();
104
105         exit(EXIT_SUCCESS);
106 }
107
108 // public functions
109
110 // disconnect a client with various options an an optional detail message (flags: DiscoFlag bitmask)
111 void server_disconnect_client(Client *client, int flags, const char *detail)
112 {
113         client->state = CS_DISCONNECTED;
114
115         if (! (flags & DISCO_NO_REMOVE)) {
116                 if (client->name) {
117                         pthread_rwlock_wrlock(&server.players_rwlck);
118                         list_delete(&server.players, client->name);
119                         pthread_rwlock_unlock(&server.players_rwlck);
120                 }
121                 pthread_rwlock_wrlock(&server.clients_rwlck);
122                 list_delete(&server.clients, client);
123                 pthread_rwlock_unlock(&server.clients_rwlck);
124         }
125
126         if (! (flags & DISCO_NO_MESSAGE))
127                 printf("Disconnected %s %s%s%s\n", client->name, INBRACES(detail));
128
129         if (! (flags & DISCO_NO_SEND))
130                 send_command(client, CC_DISCONNECT);
131
132         pthread_mutex_lock(&client->mtx);
133         close(client->fd);
134         pthread_mutex_unlock(&client->mtx);
135
136         if (! (flags & DISCO_NO_JOIN))
137                 pthread_join(client->net_thread, NULL);
138
139         if (client->name != client->address)
140                 free(client->name);
141         free(client->address);
142
143         pthread_mutex_destroy(&client->mtx);
144         free(client);
145 }
146
147 // server entry point
148 int main(int argc, char **argv)
149 {
150         program_name = argv[0];
151
152         if (argc < 2)
153                 internal_error("missing port");
154
155         struct addrinfo hints = {
156                 .ai_family = AF_INET6,
157                 .ai_socktype = SOCK_STREAM,
158                 .ai_protocol = 0,
159                 .ai_flags = AI_NUMERICSERV | AI_PASSIVE,
160         };
161
162         struct addrinfo *info = NULL;
163
164         int gai_state = getaddrinfo(NULL, argv[1], &hints, &info);
165
166         if (gai_state != 0)
167                 internal_error(gai_strerror(gai_state));
168
169         int fd = socket(info->ai_family, info->ai_socktype, 0);
170
171         if (fd == -1)
172                 syscall_error("socket");
173
174         int flag = 1;
175
176         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1)
177                 syscall_error("setsockopt");
178
179         if (bind(fd, info->ai_addr, info->ai_addrlen) == -1)
180                 syscall_error("bind");
181
182         if (listen(fd, 3) == -1)
183                 syscall_error("listen");
184
185         char *addrstr = address_string((struct sockaddr_in6 *) info->ai_addr);
186         printf("Listening on %s\n", addrstr);
187         free(addrstr);
188
189         freeaddrinfo(info);
190
191         signal_handlers_init();
192         server_run(fd);
193
194         return EXIT_SUCCESS;
195 }