]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/server/server_player.c
c024915aefdabdb5ad5683b1568d94b8a157fea3
[dragonblocks_alpha.git] / src / server / server_player.c
1 #include <dragonstd/map.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <pthread.h>
6 #include "day.h"
7 #include "entity.h"
8 #include "perlin.h"
9 #include "server/database.h"
10 #include "server/server_config.h"
11 #include "server/server_player.h"
12 #include "server/server_terrain.h"
13
14 #ifdef _WIN32
15 #define random rand
16 #endif
17
18 static Map players;
19 static Map players_named;
20
21 static void send_entity_add(ServerPlayer *player, ServerPlayer *entity)
22 {
23         dragonnet_peer_send_ToClientEntityAdd(player->peer, &(ToClientEntityAdd) {
24                 .type = player == entity ? ENTITY_LOCALPLAYER : ENTITY_PLAYER,
25                 .data = {
26                         .id = entity->id,
27                         .pos = entity->pos,
28                         .rot = entity->rot,
29                         .nametag = entity->name,
30                 },
31         });
32 }
33
34 static void send_entity_remove(ServerPlayer *client, ServerPlayer *entity)
35 {
36         dragonnet_peer_send_ToClientEntityRemove(client->peer, &(ToClientEntityRemove) {
37                 .id = entity->id,
38         });
39 }
40
41 static void send_entity_update_pos_rot(ServerPlayer *client, ServerPlayer *entity)
42 {
43         if (client != entity)
44                 dragonnet_peer_send_ToClientEntityUpdatePosRot(client->peer, &(ToClientEntityUpdatePosRot) {
45                         .id = entity->id,
46                         .pos = entity->pos,
47                         .rot = entity->rot,
48                 });
49 }
50
51 static void send_entity_add_existing(ServerPlayer *entity, ServerPlayer *client)
52 {
53         if (client != entity) {
54                 pthread_rwlock_rdlock(&entity->lock_pos);
55                 send_entity_add(client, entity);
56                 pthread_rwlock_unlock(&entity->lock_pos);
57         }
58 }
59
60 static void send_player_inventory(ServerPlayer *client, ServerPlayer *player)
61 {
62         ToClientPlayerInventory pkt;
63         pkt.id = player->id;
64         item_stack_serialize(&player->inventory.left, &pkt.left);
65         item_stack_serialize(&player->inventory.right, &pkt.right);
66         dragonnet_peer_send_ToClientPlayerInventory(client->peer, &pkt);
67 }
68
69 static void send_player_inventory_existing(ServerPlayer *player, ServerPlayer *client)
70 {
71         if (client != player) {
72                 pthread_mutex_lock(&player->mtx_inv);
73                 send_player_inventory(client, player);
74                 pthread_mutex_unlock(&player->mtx_inv);
75         }
76 }
77
78 // main thread
79 // called on server shutdown
80 static void player_drop(ServerPlayer *player)
81 {
82         pthread_rwlock_rdlock(&player->lock_peer);
83         pthread_t recv_thread = player->peer ? player->peer->recv_thread : 0;
84         pthread_rwlock_unlock(&player->lock_peer);
85
86         server_player_disconnect(player);
87         if (recv_thread)
88                 pthread_join(recv_thread, NULL);
89
90         refcount_drp(&player->rc); // map no longer has a reference to player
91 }
92
93 // any thread
94 // called when all refs have been dropped
95 static void player_delete(ServerPlayer *player)
96 {
97         refcount_dst(&player->rc);
98
99         pthread_rwlock_destroy(&player->lock_peer);
100
101         free(player->name);
102         pthread_rwlock_destroy(&player->lock_auth);
103
104         pthread_rwlock_destroy(&player->lock_pos);
105
106         item_stack_destroy(&player->inventory.left);
107         item_stack_destroy(&player->inventory.right);
108         pthread_mutex_destroy(&player->mtx_inv);
109
110         free(player);
111 }
112
113 // recv thread
114 // called when auth was successful
115 static void player_spawn(ServerPlayer *player)
116 {
117         // lock_pos has already been wrlocked by caller
118         if (!database_load_player(player->name, &player->pos, &player->rot)) {
119                 player->pos = (v3f64) {0.0, server_terrain_spawn_height() + 0.5, 0.0};
120                 player->rot = (v3f32) {0.0f, 0.0f, 0.0f};
121                 database_create_player(player->name, player->pos, player->rot);
122         }
123
124         item_stack_set(&player->inventory.left, ITEM_NONE + rand() % (COUNT_ITEM - ITEM_NONE), 1, (Blob) {0, NULL});
125         item_stack_set(&player->inventory.right, ITEM_NONE + rand() % (COUNT_ITEM - ITEM_NONE), 1, (Blob) {0, NULL});
126
127         // since this is recv thread, we don't need lock_peer
128         dragonnet_peer_send_ToClientInfo(player->peer, &(ToClientInfo) {
129                 .seed = seed,
130                 .load_distance = server_config.load_distance,
131         });
132         dragonnet_peer_send_ToClientTimeOfDay(player->peer, &(ToClientTimeOfDay) {
133                 .time_of_day = get_time_of_day(),
134         });
135         dragonnet_peer_send_ToClientMovement(player->peer, &(ToClientMovement) {
136                 .flight = false,
137                 .collision = true,
138                 .speed = server_config.movement.speed_normal,
139                 .gravity = server_config.movement.gravity,
140                 .jump = server_config.movement.jump,
141         });
142
143         server_player_iterate(&send_entity_add, player);
144         server_player_iterate(&send_entity_add_existing, player);
145
146         server_player_iterate(&send_player_inventory, player);
147         server_player_iterate(&send_player_inventory_existing, player);
148 }
149
150 // any thread
151 // called when adding, getting or removing a player from the map
152 static int cmp_player_id(const Refcount *player, const u64 *id)
153 {
154         return u64_cmp(&((ServerPlayer *) player->obj)->id, id);
155 }
156
157 // any thread
158 // called when adding, getting or removing a player from the players_named Map
159 static int cmp_player_name(const Refcount *player, const char *name)
160 {
161         // names of players in players_named Map don't change, no lock_auth needed
162         return strcmp(((ServerPlayer *) player->obj)->name, name);
163 }
164
165 // main thread
166 // called on server startup
167 void server_player_init()
168 {
169         map_ini(&players);
170         map_ini(&players_named);
171 }
172
173 // main thread
174 // called on server shutdown
175 void server_player_deinit()
176 {
177         // just forget about name -> player mapping
178         map_cnl(&players_named, &refcount_drp, NULL, NULL,          0);
179         // disconnect players and forget about them
180         map_cnl(&players,       &player_drop,  NULL, &refcount_obj, 0);
181 }
182
183 // accept thread
184 // called on new connection
185 void server_player_add(DragonnetPeer *peer)
186 {
187         ServerPlayer *player = malloc(sizeof *player);
188
189         // ID is allocated later in this function
190         player->id = 0;
191         refcount_ini(&player->rc, player, &player_delete);
192
193         player->peer = peer;
194         pthread_rwlock_init(&player->lock_peer, NULL);
195
196         player->auth = false;
197         // use address as name until auth is done
198         player->name = strdup(peer->address);
199         pthread_rwlock_init(&player->lock_auth, NULL);
200
201         player->pos = (v3f64) {0.0f, 0.0f, 0.0f};
202         player->rot = (v3f32) {0.0f, 0.0f, 0.0f};
203         pthread_rwlock_init(&player->lock_pos, NULL);
204
205         item_stack_initialize(&player->inventory.left);
206         item_stack_initialize(&player->inventory.right);
207         pthread_mutex_init(&player->mtx_inv, NULL);
208
209         printf("[access] connected %s\n", player->name);
210         peer->extra = refcount_grb(&player->rc);
211
212         // keep the search tree somewhat balanced by using random IDs
213         // duplicate IDs are very unlikely, but it doesn't hurt to check
214         // make sure to avoid 0 since it's not a valid ID
215         while (!player->id || !map_add(&players, &player->id, &player->rc, &cmp_player_id, &refcount_inc))
216                 player->id = random();
217
218         // player has been grabbed by Map and peer
219         refcount_drp(&player->rc);
220 }
221
222 // recv thread
223 // called on connection close
224 void server_player_remove(DragonnetPeer *peer)
225 {
226         ServerPlayer *player = peer->extra;
227         peer->extra = NULL; // technically not necessary, but just in case
228
229         // peer will be deleted - forget about it!
230         pthread_rwlock_wrlock(&player->lock_peer);
231         player->peer = NULL;
232         pthread_rwlock_unlock(&player->lock_peer);
233
234         // only (this) recv thread will modify the auth or name fields, no lock_auth needed
235         // map_del returns false if it was canceled
236         // (we don't want disconnect messages for every player on server shutdown)
237         if (map_del(&players, &player->id, &cmp_player_id, &refcount_drp, NULL, NULL))
238                 printf("[access] disconnected %s\n", player->name);
239
240         if (player->auth && map_del(&players_named, player->name, &cmp_player_name, &refcount_drp, NULL, NULL)) {
241                 pthread_rwlock_rdlock(&player->lock_pos);
242                 server_player_iterate(&send_entity_remove, player);
243                 pthread_rwlock_unlock(&player->lock_pos);
244         }
245
246         // peer no longer has a reference to player
247         refcount_drp(&player->rc);
248 }
249
250 // any thread
251 ServerPlayer *server_player_grab(u64 id)
252 {
253         // 0 is an invalid ID
254         return id ? map_get(&players, &id, &cmp_player_id, &refcount_grb) : NULL;
255 }
256
257 // any thread
258 ServerPlayer *server_player_grab_named(char *name)
259 {
260         // NULL is an invalid name
261         return name ? map_get(&players, name, &cmp_player_name, &refcount_grb) : NULL;
262 }
263
264 // recv thread
265 // called on recv auth packet
266 bool server_player_auth(ServerPlayer *player, char *name)
267 {
268         pthread_rwlock_wrlock(&player->lock_auth);
269         pthread_rwlock_wrlock(&player->lock_pos);
270
271         // temporary change name, save old name to either free or reset it if auth fails
272         char *old_name = player->name;
273         player->name = name;
274
275         bool success = map_add(&players_named, player->name, &player->rc, &cmp_player_name, &refcount_inc);
276
277         printf("[access] authentication %s: %s -> %s\n", success ? "success" : "failure", old_name, player->name);
278
279         // since this is recv thread, we don't need lock_peer
280         dragonnet_peer_send_ToClientAuth(player->peer, &(ToClientAuth) {
281                 .success = success,
282         });
283
284         if (success) {
285                 free(old_name);
286                 player->auth = true;
287                 // load player from database and send some initial info
288                 player_spawn(player);
289         } else {
290                 player->name = old_name;
291         }
292
293         pthread_rwlock_unlock(&player->lock_pos);
294         pthread_rwlock_unlock(&player->lock_auth);
295         return success;
296 }
297
298 // recv thread
299 void server_player_move(ServerPlayer *player, v3f64 pos, v3f32 rot)
300 {
301         pthread_rwlock_wrlock(&player->lock_pos);
302         // this is recv thread, no lock_auth needed
303         database_update_player_pos_rot(player->name, player->pos = pos, player->rot = rot);
304         server_player_iterate(&send_entity_update_pos_rot, player);
305         pthread_rwlock_unlock(&player->lock_pos);
306 }
307
308 // any thread
309 void server_player_disconnect(ServerPlayer *player)
310 {
311         pthread_rwlock_rdlock(&player->lock_peer);
312         // the recv thread will call server_player_remove when the connection was shut down
313         if (player->peer)
314                 dragonnet_peer_shutdown(player->peer);
315         pthread_rwlock_unlock(&player->lock_peer);
316 }
317
318 // any thread
319 void server_player_iterate(void *func, void *arg)
320 {
321         map_trv(&players_named, func, arg, &refcount_obj, TRAVERSION_INORDER);
322 }
323
324 void server_player_inventory_changed(ServerPlayer *player)
325 {
326         server_player_iterate(&send_player_inventory, player);
327 }
328
329 /*
330 229779
331 373875
332 374193
333 110738
334 390402
335 357272
336 390480
337
338 (these are only the wholesome ones)
339 */