]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/client/client.c
refactoring
[dragonblocks_alpha.git] / src / client / client.c
1 #define _GNU_SOURCE // don't worry, GNU extensions are only used when available
2 #include <dragonstd/flag.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <pthread.h>
7 #include <unistd.h>
8 #include "client/client.h"
9 #include "client/client_auth.h"
10 #include "client/client_player.h"
11 #include "client/client_terrain.h"
12 #include "client/debug_menu.h"
13 #include "client/game.h"
14 #include "client/input.h"
15 #include "day.h"
16 #include "interrupt.h"
17 #include "perlin.h"
18 #include "types.h"
19
20 DragonnetPeer *client;
21
22 static Flag finish;
23 static Flag gfx_init;
24
25 static bool on_recv(__attribute__((unused)) DragonnetPeer *peer, DragonnetTypeId type, __attribute__((unused)) void *pkt)
26 {
27         bool allowed = false;
28         pthread_mutex_lock(&client_auth.mtx);
29
30         // this code exists to stop malicious or malfunctioning packets
31         switch (client_auth.state) {
32                 // the server shouldn't send anything during auth preparation, drop it
33                 case AUTH_INIT:
34                         allowed = false;
35                         break;
36
37                 // only the auth packet is allowed before auth is finished
38                 case AUTH_WAIT:
39                         allowed = type == DRAGONNET_TYPE_ToClientAuth;
40                         break;
41
42                 // don't process auth packets when auth is already finished
43                 case AUTH_SUCCESS:
44                         allowed = type != DRAGONNET_TYPE_ToClientAuth;
45                         break;
46         }
47
48         /*
49                 It is important that the auth state does not change to until the packet is
50                         processed.
51
52                 However, the only state change done by other threads is AUTH_INIT -> AUTH_WAIT,
53                         which is not problematic since packets that are received during AUTH_INIT
54                         are not processed, they are always dropped.
55
56                 Therefore the mutex can be unlocked at this point.
57         */
58         pthread_mutex_unlock(&client_auth.mtx);
59         return allowed;
60 }
61
62 static void on_disconnect(__attribute__((unused)) DragonnetPeer *peer)
63 {
64         flag_set(&interrupt);
65         // don't free the connection before all other client components have shut down
66         flag_slp(&finish);
67 }
68
69 static void on_ToClientAuth(__attribute__((unused)) DragonnetPeer *peer, ToClientAuth *pkt)
70 {
71         pthread_mutex_lock(&client_auth.mtx);
72         if (pkt->success) {
73                 client_auth.state = AUTH_SUCCESS;
74                 printf("[access] authenticated successfully\n");
75         } else {
76                 client_auth.state = AUTH_INIT;
77                 printf("[access] authentication failed, please try again\n");
78         }
79         pthread_cond_signal(&client_auth.cv);
80         pthread_mutex_unlock(&client_auth.mtx);
81
82         // yield the connection until the game is fully initialized
83         if (pkt->success)
84                 flag_slp(&gfx_init);
85 }
86
87 static void on_ToClientChunk(__attribute__((unused)) DragonnetPeer *peer, ToClientChunk *pkt)
88 {
89         TerrainChunk *chunk = terrain_get_chunk(client_terrain, pkt->pos, true);
90
91         terrain_deserialize_chunk(chunk, pkt->data);
92         ((TerrainChunkMeta *) chunk->extra)->empty = (pkt->data.siz == 0);
93         client_terrain_chunk_received(chunk);
94 }
95
96 static void on_ToClientInfo(__attribute__((unused)) DragonnetPeer *peer, ToClientInfo *pkt)
97 {
98         client_terrain_set_load_distance(pkt->load_distance);
99         seed = pkt->seed;
100 }
101
102 static void on_ToClientTimeOfDay(__attribute__((unused)) DragonnetPeer *peer, ToClientTimeOfDay *pkt)
103 {
104         set_time_of_day(pkt->time_of_day);
105 }
106
107 static void on_ToClientMovement(__attribute__((unused)) DragonnetPeer *peer, ToClientMovement *pkt)
108 {
109         pthread_rwlock_wrlock(&client_player.lock_movement);
110         client_player.movement = *pkt;
111         pthread_rwlock_unlock(&client_player.lock_movement);
112
113         debug_menu_changed(ENTRY_FLIGHT);
114         debug_menu_changed(ENTRY_COLLISION);
115 }
116
117 int main(int argc, char **argv)
118 {
119 #ifdef __GLIBC__ // check whether bloat is enabled
120         pthread_setname_np(pthread_self(), "main");
121 #endif // __GLIBC__
122
123         if (argc < 2) {
124                 fprintf(stderr, "[error] missing address\n");
125                 return EXIT_FAILURE;
126         }
127
128         if (!(client = dragonnet_connect(argv[1]))) {
129                 fprintf(stderr, "[error] failed to connect to server\n");
130                 return EXIT_FAILURE;
131         }
132
133         char *address = dragonnet_addr_str(client->raddr);
134         printf("[access] connected to %s\n", address);
135         free(address);
136
137         client->on_disconnect = &on_disconnect;
138         client->on_recv                                                  = (void *) &on_recv;
139         client->on_recv_type[DRAGONNET_TYPE_ToClientAuth               ] = (void *) &on_ToClientAuth;
140         client->on_recv_type[DRAGONNET_TYPE_ToClientChunk              ] = (void *) &on_ToClientChunk;
141         client->on_recv_type[DRAGONNET_TYPE_ToClientInfo               ] = (void *) &on_ToClientInfo;
142         client->on_recv_type[DRAGONNET_TYPE_ToClientTimeOfDay          ] = (void *) &on_ToClientTimeOfDay;
143         client->on_recv_type[DRAGONNET_TYPE_ToClientMovement           ] = (void *) &on_ToClientMovement;
144         client->on_recv_type[DRAGONNET_TYPE_ToClientEntityAdd          ] = (void *) &client_entity_add;
145         client->on_recv_type[DRAGONNET_TYPE_ToClientEntityRemove       ] = (void *) &client_entity_remove;
146         client->on_recv_type[DRAGONNET_TYPE_ToClientEntityUpdatePosRot ] = (void *) &client_entity_update_pos_rot;
147         client->on_recv_type[DRAGONNET_TYPE_ToClientEntityUpdateBoxEye ] = (void *) &client_entity_update_box_eye;
148         client->on_recv_type[DRAGONNET_TYPE_ToClientEntityUpdateNametag] = (void *) &client_entity_update_nametag;
149
150         flag_ini(&finish);
151         flag_ini(&gfx_init);
152
153         interrupt_init();
154         client_terrain_init();
155         client_player_init();
156         client_entity_init();
157         dragonnet_peer_run(client);
158
159         if (!client_auth_init())
160                 return EXIT_FAILURE;
161
162         if (!game(&gfx_init))
163                 return EXIT_FAILURE;
164
165         dragonnet_peer_shutdown(client);
166         client_auth_deinit();
167         client_entity_deinit();
168         client_player_deinit();
169         client_terrain_deinit();
170         interrupt_deinit();
171
172         pthread_t recv_thread = client->recv_thread;
173
174         flag_set(&finish);
175         pthread_join(recv_thread, NULL);
176
177         flag_dst(&finish);
178         flag_dst(&gfx_init);
179
180         return EXIT_SUCCESS;
181 }