]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/server/server_player.c
Use dragonnet
[dragonblocks_alpha.git] / src / server / server_player.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4 #include <dragonstd/list.h>
5 #include "server/database.h"
6 #include "server/server_config.h"
7 #include "server/server_map.h"
8 #include "server/server_player.h"
9 #include "perlin.h"
10 #include "day.h"
11 #include "util.h"
12
13 static bool shutting_down = false;
14 static pthread_rwlock_t shutting_down_lock;
15
16 static List players;
17 static pthread_rwlock_t players_lock;
18
19 static List names;
20 static pthread_rwlock_t names_lock;
21
22 static u64 next_id = 1;
23
24 static bool list_compare_u64(u64 *p1, u64 *p2)
25 {
26         return *p1 == *p2;
27 }
28
29 static bool get_lock(pthread_rwlock_t *lock, bool write)
30 {
31         pthread_rwlock_rdlock(&shutting_down_lock);
32         if (shutting_down) {
33                 pthread_rwlock_unlock(&shutting_down_lock);
34                 return false;
35         }
36
37         if (write)
38                 pthread_rwlock_wrlock(lock);
39         else
40                 pthread_rwlock_rdlock(lock);
41
42         pthread_rwlock_unlock(&shutting_down_lock);
43         return true;
44 }
45
46 void server_player_init()
47 {
48         pthread_rwlock_init(&shutting_down_lock, NULL);
49
50         players = list_create((void *) &list_compare_u64);
51         pthread_rwlock_init(&players_lock, NULL);
52
53         names = list_create(&list_compare_string);
54         pthread_rwlock_init(&names_lock, NULL);
55 }
56
57 // list_clear_func callback used on server shutdown to disconnect all clients properly
58 static void list_disconnect_player(void *key, unused void *value, unused void *arg)
59 {
60         ServerPlayer *player = key;
61
62         pthread_t recv_thread = player->peer->recv_thread;
63         server_player_disconnect(player);
64         pthread_join(recv_thread, NULL);
65 }
66
67 void server_player_deinit()
68 {
69         pthread_rwlock_wrlock(&shutting_down_lock);
70         shutting_down = true;
71
72         pthread_rwlock_wrlock(&players_lock);
73         pthread_rwlock_wrlock(&names_lock);
74         pthread_rwlock_unlock(&shutting_down_lock);
75
76         list_clear_func(&players, &list_disconnect_player, NULL);
77         list_clear(&names);
78
79         pthread_rwlock_destroy(&players_lock);
80         pthread_rwlock_destroy(&names_lock);
81         pthread_rwlock_destroy(&shutting_down_lock);
82 }
83
84 void server_player_add(DragonnetPeer *peer)
85 {
86         ServerPlayer *player = malloc(sizeof *player);
87
88         player->id = next_id++;
89         player->peer = peer;
90         pthread_rwlock_init(&player->ref, NULL);
91         player->auth = false;
92         player->name = dragonnet_addr_str(peer->raddr);
93         pthread_rwlock_init(&player->auth_lock, NULL);
94         player->pos = (v3f64) {0.0f, 0.0f, 0.0f};
95         pthread_rwlock_init(&player->pos_lock, NULL);
96
97         printf("Connected %s\n", player->name);
98
99         // accept thread is joined before shutdown, we are guaranteed to obtain the lock
100         pthread_rwlock_wrlock(&players_lock);
101
102         list_put(&players, &player->id, player);
103         peer->extra = player;
104
105         pthread_rwlock_unlock(&players_lock);
106 }
107
108 void server_player_remove(DragonnetPeer *peer)
109 {
110         ServerPlayer *player = peer->extra;
111
112         // only (this) recv thread will modify the auth or name fields, no rdlocks needed
113
114         if (get_lock(&players_lock, true)) {
115                 list_delete(&players, &player->id);
116                 pthread_rwlock_unlock(&players_lock);
117
118                 printf("Disconnected %s\n", player->name);
119         }
120
121         if (player->auth && get_lock(&names_lock, true)) {
122                 list_delete(&names, player->name);
123                 pthread_rwlock_unlock(&names_lock);
124         }
125
126         pthread_rwlock_wrlock(&player->ref);
127
128         free(player->name);
129
130         pthread_rwlock_destroy(&player->ref);
131         pthread_rwlock_destroy(&player->auth_lock);
132         pthread_rwlock_destroy(&player->pos_lock);
133
134         free(player);
135 }
136
137 u64 server_player_find(char *name)
138 {
139         if (! get_lock(&names_lock, false))
140                 return 0;
141
142         u64 *id = list_get(&names, name);
143         return id ? *id : 0;
144 }
145
146 ServerPlayer *server_player_grab(u64 id)
147 {
148         if (! id)
149                 return NULL;
150
151         if (! get_lock(&players_lock, false))
152                 return NULL;
153
154         ServerPlayer *player = list_get(&players, &id);
155         if (player)
156                 pthread_rwlock_rdlock(&player->ref);
157
158         pthread_rwlock_unlock(&players_lock);
159
160         return player;
161 }
162
163 void server_player_drop(ServerPlayer *player)
164 {
165         pthread_rwlock_unlock(&player->ref);
166 }
167
168 bool server_player_auth(ServerPlayer *player, char *name)
169 {
170         if (! get_lock(&names_lock, true))
171                 return false;
172
173         pthread_rwlock_wrlock(&player->auth_lock);
174         pthread_rwlock_wrlock(&player->pos_lock);
175
176         bool success = list_put(&names, name, &player->id);
177
178         dragonnet_peer_send_ToClientAuth(player->peer, &(ToClientAuth) {
179                 .success = success,
180         });
181
182         if (success) {
183                 printf("Authentication %s: %s -> %s\n", success ? "success" : "failure", player->name, name);
184
185                 free(player->name);
186                 player->name = name;
187                 player->auth = true;
188
189                 if (! database_load_player(player->name, &player->pos)) {
190                         player->pos = (v3f64) {0.0, server_map.spawn_height + 0.5, 0.0};
191                         database_create_player(player->name, player->pos);
192                 }
193
194                 dragonnet_peer_send_ToClientInfo(player->peer, &(ToClientInfo) {
195                         .seed = seed,
196                         .simulation_distance = server_config.simulation_distance,
197                 });
198
199                 dragonnet_peer_send_ToClientTimeOfDay(player->peer, &(ToClientTimeOfDay) {
200                         .time_of_day = get_time_of_day(),
201                 });
202
203                 server_player_send_pos(player);
204         }
205
206         pthread_rwlock_unlock(&player->pos_lock);
207         pthread_rwlock_unlock(&player->auth_lock);
208         pthread_rwlock_unlock(&names_lock);
209
210         return success;
211 }
212
213 void server_player_disconnect(ServerPlayer *player)
214 {
215         dragonnet_peer_shutdown(player->peer);
216 }
217
218 void server_player_send_pos(ServerPlayer *player)
219 {
220         dragonnet_peer_send_ToClientPos(player->peer, & (ToClientPos) {
221                 .pos = player->pos,
222         });
223 }
224
225 void server_player_iterate(void (cb)(ServerPlayer *, void *), void *arg)
226 {
227         if (! get_lock(&players_lock, false))
228                 return;
229
230         ITERATE_LIST(&players, pair) {
231                 ServerPlayer *player = pair->value;
232
233                 pthread_rwlock_rdlock(&player->auth_lock);
234                 if (player->auth)
235                         cb(player, arg);
236                 pthread_rwlock_unlock(&player->auth_lock);
237         }
238
239         pthread_rwlock_unlock(&players_lock);
240 }
241
242 /*
243 229779
244 373875
245 374193
246 110738
247 390402
248 357272
249 390480
250
251 (these are only the wholesome ones)
252 */