1 #include <dragonstd/map.h>
9 #include "server/database.h"
10 #include "server/server_config.h"
11 #include "server/server_player.h"
12 #include "server/server_terrain.h"
15 static Map players_named;
17 static void send_entity_add(ServerPlayer *player, ServerPlayer *entity)
19 dragonnet_peer_send_ToClientEntityAdd(player->peer, &(ToClientEntityAdd) {
20 .type = player == entity ? ENTITY_LOCALPLAYER : ENTITY_PLAYER,
25 .nametag = entity->name,
30 static void send_entity_remove(ServerPlayer *player, ServerPlayer *entity)
32 dragonnet_peer_send_ToClientEntityRemove(player->peer, &(ToClientEntityRemove) {
37 static void send_entity_update_pos_rot(ServerPlayer *player, ServerPlayer *entity)
40 dragonnet_peer_send_ToClientEntityUpdatePosRot(player->peer, &(ToClientEntityUpdatePosRot) {
47 static void send_entity_add_existing(ServerPlayer *entity, ServerPlayer *player)
49 if (player != entity) {
50 pthread_rwlock_rdlock(&entity->lock_pos);
51 send_entity_add(player, entity);
52 pthread_rwlock_unlock(&entity->lock_pos);
57 // called on server shutdown
58 static void player_drop(ServerPlayer *player)
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);
64 server_player_disconnect(player);
66 pthread_join(recv_thread, NULL);
68 refcount_drp(&player->rc); // map no longer has a reference to player
72 // called when all refs have been dropped
73 static void player_delete(ServerPlayer *player)
75 refcount_dst(&player->rc);
77 pthread_rwlock_destroy(&player->lock_peer);
80 pthread_rwlock_destroy(&player->lock_auth);
82 pthread_rwlock_destroy(&player->lock_pos);
88 // called when auth was successful
89 static void player_spawn(ServerPlayer *player)
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);
98 // since this is recv thread, we don't need lock_peer
99 dragonnet_peer_send_ToClientInfo(player->peer, &(ToClientInfo) {
101 .load_distance = server_config.load_distance,
103 dragonnet_peer_send_ToClientTimeOfDay(player->peer, &(ToClientTimeOfDay) {
104 .time_of_day = get_time_of_day(),
106 dragonnet_peer_send_ToClientMovement(player->peer, &(ToClientMovement) {
109 .speed = server_config.movement.speed_normal,
110 .gravity = server_config.movement.gravity,
111 .jump = server_config.movement.jump,
114 server_player_iterate(&send_entity_add, player);
115 server_player_iterate(&send_entity_add_existing, player);
119 // called when adding, getting or removing a player from the map
120 static int cmp_player_id(const Refcount *player, const u64 *id)
122 return u64_cmp(&((ServerPlayer *) player->obj)->id, id);
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)
129 // names of players in players_named Map don't change, no lock_auth needed
130 return strcmp(((ServerPlayer *) player->obj)->name, name);
134 // called on server startup
135 void server_player_init()
138 map_ini(&players_named);
142 // called on server shutdown
143 void server_player_deinit()
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);
152 // called on new connection
153 void server_player_add(DragonnetPeer *peer)
155 ServerPlayer *player = malloc(sizeof *player);
157 // ID is allocated later in this function
159 refcount_ini(&player->rc, player, &player_delete);
162 pthread_rwlock_init(&player->lock_peer, NULL);
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);
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);
173 printf("[access] connected %s\n", player->name);
174 peer->extra = refcount_grb(&player->rc);
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();
182 // player has been grabbed by Map and peer
183 refcount_drp(&player->rc);
187 // called on connection close
188 void server_player_remove(DragonnetPeer *peer)
190 ServerPlayer *player = peer->extra;
191 peer->extra = NULL; // technically not necessary, but just in case
193 // peer will be deleted - forget about it!
194 pthread_rwlock_wrlock(&player->lock_peer);
196 pthread_rwlock_unlock(&player->lock_peer);
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);
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);
210 // peer no longer has a reference to player
211 refcount_drp(&player->rc);
215 ServerPlayer *server_player_grab(u64 id)
217 // 0 is an invalid ID
218 return id ? map_get(&players, &id, &cmp_player_id, &refcount_grb) : NULL;
222 ServerPlayer *server_player_grab_named(char *name)
224 // NULL is an invalid name
225 return name ? map_get(&players, name, &cmp_player_name, &refcount_grb) : NULL;
229 // called on recv auth packet
230 bool server_player_auth(ServerPlayer *player, char *name)
232 pthread_rwlock_wrlock(&player->lock_auth);
233 pthread_rwlock_wrlock(&player->lock_pos);
235 // temporary change name, save old name to either free or reset it if auth fails
236 char *old_name = player->name;
239 bool success = map_add(&players_named, player->name, &player->rc, &cmp_player_name, &refcount_inc);
241 printf("[access] authentication %s: %s -> %s\n", success ? "success" : "failure", old_name, player->name);
243 // since this is recv thread, we don't need lock_peer
244 dragonnet_peer_send_ToClientAuth(player->peer, &(ToClientAuth) {
251 // load player from database and send some initial info
252 player_spawn(player);
254 player->name = old_name;
257 pthread_rwlock_unlock(&player->lock_pos);
258 pthread_rwlock_unlock(&player->lock_auth);
263 void server_player_move(ServerPlayer *player, v3f64 pos, v3f32 rot)
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);
273 void server_player_disconnect(ServerPlayer *player)
275 pthread_rwlock_rdlock(&player->lock_peer);
276 // the recv thread will call server_player_remove when the connection was shut down
278 dragonnet_peer_shutdown(player->peer);
279 pthread_rwlock_unlock(&player->lock_peer);
283 void server_player_iterate(void *func, void *arg)
285 map_trv(&players_named, func, arg, &refcount_obj, TRAVERSION_INORDER);
297 (these are only the wholesome ones)