4 #include <dragonstd/list.h>
5 #include "server/database.h"
6 #include "server/server_config.h"
7 #include "server/server_map.h"
8 #include "server/server_player.h"
13 static bool shutting_down = false;
14 static pthread_rwlock_t shutting_down_lock;
17 static pthread_rwlock_t players_lock;
20 static pthread_rwlock_t names_lock;
22 static u64 next_id = 1;
24 static bool list_compare_u64(u64 *p1, u64 *p2)
29 static bool get_lock(pthread_rwlock_t *lock, bool write)
31 pthread_rwlock_rdlock(&shutting_down_lock);
33 pthread_rwlock_unlock(&shutting_down_lock);
38 pthread_rwlock_wrlock(lock);
40 pthread_rwlock_rdlock(lock);
42 pthread_rwlock_unlock(&shutting_down_lock);
46 void server_player_init()
48 pthread_rwlock_init(&shutting_down_lock, NULL);
50 players = list_create((void *) &list_compare_u64);
51 pthread_rwlock_init(&players_lock, NULL);
53 names = list_create(&list_compare_string);
54 pthread_rwlock_init(&names_lock, NULL);
57 // list_clear_func callback used on server shutdown to disconnect all clients properly
58 static void list_disconnect_player(void *key, unused void *value, unused void *arg)
60 ServerPlayer *player = key;
62 pthread_t recv_thread = player->peer->recv_thread;
63 server_player_disconnect(player);
64 pthread_join(recv_thread, NULL);
67 void server_player_deinit()
69 pthread_rwlock_wrlock(&shutting_down_lock);
72 pthread_rwlock_wrlock(&players_lock);
73 pthread_rwlock_wrlock(&names_lock);
74 pthread_rwlock_unlock(&shutting_down_lock);
76 list_clear_func(&players, &list_disconnect_player, NULL);
79 pthread_rwlock_destroy(&players_lock);
80 pthread_rwlock_destroy(&names_lock);
81 pthread_rwlock_destroy(&shutting_down_lock);
84 void server_player_add(DragonnetPeer *peer)
86 ServerPlayer *player = malloc(sizeof *player);
88 player->id = next_id++;
90 pthread_rwlock_init(&player->ref, NULL);
92 player->name = dragonnet_addr_str(peer->raddr);
93 pthread_rwlock_init(&player->auth_lock, NULL);
94 player->pos = (v3f64) {0.0f, 0.0f, 0.0f};
95 pthread_rwlock_init(&player->pos_lock, NULL);
97 printf("Connected %s\n", player->name);
99 // accept thread is joined before shutdown, we are guaranteed to obtain the lock
100 pthread_rwlock_wrlock(&players_lock);
102 list_put(&players, &player->id, player);
103 peer->extra = player;
105 pthread_rwlock_unlock(&players_lock);
108 void server_player_remove(DragonnetPeer *peer)
110 ServerPlayer *player = peer->extra;
112 // only (this) recv thread will modify the auth or name fields, no rdlocks needed
114 if (get_lock(&players_lock, true)) {
115 list_delete(&players, &player->id);
116 pthread_rwlock_unlock(&players_lock);
118 printf("Disconnected %s\n", player->name);
121 if (player->auth && get_lock(&names_lock, true)) {
122 list_delete(&names, player->name);
123 pthread_rwlock_unlock(&names_lock);
126 pthread_rwlock_wrlock(&player->ref);
130 pthread_rwlock_destroy(&player->ref);
131 pthread_rwlock_destroy(&player->auth_lock);
132 pthread_rwlock_destroy(&player->pos_lock);
137 u64 server_player_find(char *name)
139 if (! get_lock(&names_lock, false))
142 u64 *id = list_get(&names, name);
146 ServerPlayer *server_player_grab(u64 id)
151 if (! get_lock(&players_lock, false))
154 ServerPlayer *player = list_get(&players, &id);
156 pthread_rwlock_rdlock(&player->ref);
158 pthread_rwlock_unlock(&players_lock);
163 void server_player_drop(ServerPlayer *player)
165 pthread_rwlock_unlock(&player->ref);
168 bool server_player_auth(ServerPlayer *player, char *name)
170 if (! get_lock(&names_lock, true))
173 pthread_rwlock_wrlock(&player->auth_lock);
174 pthread_rwlock_wrlock(&player->pos_lock);
176 bool success = list_put(&names, name, &player->id);
178 dragonnet_peer_send_ToClientAuth(player->peer, &(ToClientAuth) {
183 printf("Authentication %s: %s -> %s\n", success ? "success" : "failure", player->name, name);
189 if (! database_load_player(player->name, &player->pos)) {
190 player->pos = (v3f64) {0.0, server_map.spawn_height + 0.5, 0.0};
191 database_create_player(player->name, player->pos);
194 dragonnet_peer_send_ToClientInfo(player->peer, &(ToClientInfo) {
196 .simulation_distance = server_config.simulation_distance,
199 dragonnet_peer_send_ToClientTimeOfDay(player->peer, &(ToClientTimeOfDay) {
200 .time_of_day = get_time_of_day(),
203 server_player_send_pos(player);
206 pthread_rwlock_unlock(&player->pos_lock);
207 pthread_rwlock_unlock(&player->auth_lock);
208 pthread_rwlock_unlock(&names_lock);
213 void server_player_disconnect(ServerPlayer *player)
215 dragonnet_peer_shutdown(player->peer);
218 void server_player_send_pos(ServerPlayer *player)
220 dragonnet_peer_send_ToClientPos(player->peer, & (ToClientPos) {
225 void server_player_iterate(void (cb)(ServerPlayer *, void *), void *arg)
227 if (! get_lock(&players_lock, false))
230 ITERATE_LIST(&players, pair) {
231 ServerPlayer *player = pair->value;
233 pthread_rwlock_rdlock(&player->auth_lock);
236 pthread_rwlock_unlock(&player->auth_lock);
239 pthread_rwlock_unlock(&players_lock);
251 (these are only the wholesome ones)