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