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