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;
18 // called on server shutdown
19 static void player_drop(ServerPlayer *player)
21 pthread_rwlock_rdlock(&player->lock_peer);
22 pthread_t recv_thread = player->peer ? player->peer->recv_thread : 0;
23 pthread_rwlock_unlock(&player->lock_peer);
25 server_player_disconnect(player);
27 pthread_join(recv_thread, NULL);
29 refcount_drp(&player->rc); // map no longer has a reference to player
33 // called when all refs have been dropped
34 static void player_delete(ServerPlayer *player)
36 refcount_dst(&player->rc);
38 pthread_rwlock_destroy(&player->lock_peer);
41 pthread_rwlock_destroy(&player->lock_auth);
43 pthread_rwlock_destroy(&player->lock_pos);
49 // called when auth was successful
50 static void player_spawn(ServerPlayer *player)
52 // lock_pos has already been wrlocked by caller
53 if (!database_load_player(player->name, &player->pos, &player->rot)) {
54 player->pos = (v3f64) {0.0, server_terrain_spawn_height() + 0.5, 0.0};
55 player->rot = (v3f32) {0.0f, 0.0f, 0.0f};
56 database_create_player(player->name, player->pos, player->rot);
59 // since this is recv thread, we don't need lock_peer
60 dragonnet_peer_send_ToClientInfo(player->peer, &(ToClientInfo) {
62 .load_distance = server_config.load_distance,
64 dragonnet_peer_send_ToClientTimeOfDay(player->peer, &(ToClientTimeOfDay) {
65 .time_of_day = get_time_of_day(),
67 dragonnet_peer_send_ToClientMovement(player->peer, &(ToClientMovement) {
70 .speed = server_config.movement.speed_normal,
71 .gravity = server_config.movement.gravity,
72 .jump = server_config.movement.jump,
74 dragonnet_peer_send_ToClientEntityAdd(player->peer, &(ToClientEntityAdd) {
75 .type = ENTITY_LOCALPLAYER,
80 .box = {{-0.3f, 0.0f, -0.3f}, {0.3f, 1.75f, 0.3f}},
81 .eye = {0.0f, 1.75f, 0.0f},
88 // called when adding, getting or removing a player from the map
89 static int cmp_player_id(const Refcount *player, const u64 *id)
91 return u64_cmp(&((ServerPlayer *) player->obj)->id, id);
95 // called when adding, getting or removing a player from the players_named Map
96 static int cmp_player_name(const Refcount *player, const char *name)
98 // names of players in players_named Map don't change, no lock_auth needed
99 return strcmp(((ServerPlayer *) player->obj)->name, name);
103 // called on server startup
104 void server_player_init()
107 map_ini(&players_named);
111 // called on server shutdown
112 void server_player_deinit()
114 // just forget about name -> player mapping
115 map_cnl(&players_named, &refcount_drp, NULL, NULL, 0);
116 // disconnect players and forget about them
117 map_cnl(&players, &player_drop, NULL, &refcount_obj, 0);
121 // called on new connection
122 void server_player_add(DragonnetPeer *peer)
124 ServerPlayer *player = malloc(sizeof *player);
126 // ID is allocated later in this function
128 refcount_ini(&player->rc, player, &player_delete);
131 pthread_rwlock_init(&player->lock_peer, NULL);
133 player->auth = false;
134 // use address as name until auth is done
135 player->name = dragonnet_addr_str(peer->raddr);
136 pthread_rwlock_init(&player->lock_auth, NULL);
138 player->pos = (v3f64) {0.0f, 0.0f, 0.0f};
139 player->rot = (v3f32) {0.0f, 0.0f, 0.0f};
140 pthread_rwlock_init(&player->lock_pos, NULL);
142 printf("[access] connected %s\n", player->name);
143 peer->extra = refcount_grb(&player->rc);
145 // keep the search tree somewhat balanced by using random IDs
146 // duplicate IDs are very unlikely, but it doesn't hurt to check
147 // make sure to avoid 0 since it's not a valid ID
148 while (!player->id || !map_add(&players, &player->id, &player->rc, &cmp_player_id, &refcount_inc))
149 player->id = random();
151 // player has been grabbed by Map and peer
152 refcount_drp(&player->rc);
156 // called on connection close
157 void server_player_remove(DragonnetPeer *peer)
159 ServerPlayer *player = peer->extra;
160 peer->extra = NULL; // technically not necessary, but just in case
162 // peer will be deleted - forget about it!
163 pthread_rwlock_wrlock(&player->lock_peer);
165 pthread_rwlock_unlock(&player->lock_peer);
167 // only (this) recv thread will modify the auth or name fields, no lock_auth needed
168 // map_del returns false if it was canceled
169 // (we don't want disconnect messages for every player on server shutdown)
170 if (map_del(&players, &player->id, &cmp_player_id, &refcount_drp, NULL, NULL))
171 printf("[access] disconnected %s\n", player->name);
173 map_del(&players_named, player->name, &cmp_player_name, &refcount_drp, NULL, NULL);
175 // peer no longer has a reference to player
176 refcount_drp(&player->rc);
180 ServerPlayer *server_player_grab(u64 id)
182 // 0 is an invalid ID
183 return id ? map_get(&players, &id, &cmp_player_id, &refcount_grb) : NULL;
187 ServerPlayer *server_player_grab_named(char *name)
189 // NULL is an invalid name
190 return name ? map_get(&players, name, &cmp_player_name, &refcount_grb) : NULL;
194 // called on recv auth packet
195 bool server_player_auth(ServerPlayer *player, char *name)
197 pthread_rwlock_wrlock(&player->lock_auth);
198 pthread_rwlock_wrlock(&player->lock_pos);
200 // temporary change name, save old name to either free or reset it if auth fails
201 char *old_name = player->name;
204 bool success = map_add(&players_named, player->name, &player->rc, &cmp_player_name, &refcount_inc);
206 printf("[access] authentication %s: %s -> %s\n", success ? "success" : "failure", old_name, player->name);
208 // since this is recv thread, we don't need lock_peer
209 dragonnet_peer_send_ToClientAuth(player->peer, &(ToClientAuth) {
216 // load player from database and send some initial info
217 player_spawn(player);
219 player->name = old_name;
222 pthread_rwlock_unlock(&player->lock_pos);
223 pthread_rwlock_unlock(&player->lock_auth);
228 void server_player_disconnect(ServerPlayer *player)
230 pthread_rwlock_rdlock(&player->lock_peer);
231 // the recv thread will call server_player_remove when the connection was shut down
233 dragonnet_peer_shutdown(player->peer);
234 pthread_rwlock_unlock(&player->lock_peer);
238 void server_player_iterate(void *func, void *arg)
240 map_trv(&players_named, func, arg, &refcount_obj, TRAVERSION_INORDER);
252 (these are only the wholesome ones)