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