]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/client/client_player.c
d6c9cc00c65a683ffd06e16b36bac22c6e9faa20
[dragonblocks_alpha.git] / src / client / client_player.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include "client/camera.h"
4 #include "client/client.h"
5 #include "client/client_inventory.h"
6 #include "client/client_player.h"
7 #include "client/client_terrain.h"
8 #include "client/cube.h"
9 #include "client/debug_menu.h"
10 #include "client/texture.h"
11 #include "common/environment.h"
12 #include "common/physics.h"
13
14 struct ClientPlayer client_player;
15
16 static ClientEntity *player_entity;
17 static pthread_rwlock_t lock_player_entity;
18
19 static Model *player_model;
20
21 // updat epos/rot box/eye functions
22
23 static void update_camera()
24 {
25         vec4 dst, src = {0.0f, 0.0f, 0.0f, 1.0f};
26
27         ClientPlayerData *data = player_entity->extra;
28
29         if (data->bones.eyes)
30                 mat4x4_mul_vec4(dst, data->bones.eyes->abs, src);
31         else
32                 vec4_dup(dst, src);
33
34         camera_set_position((v3f32) {dst[0], dst[1], dst[2]});
35 }
36
37 static void update_pos()
38 {
39         debug_menu_changed(ENTRY_POS);
40         debug_menu_changed(ENTRY_HUMIDITY);
41         debug_menu_changed(ENTRY_TEMPERATURE);
42 }
43
44 static void update_rot()
45 {
46         camera_set_angle(M_PI / 2 - player_entity->data.rot.y, -player_entity->data.rot.x);
47         debug_menu_changed(ENTRY_YAW);
48         debug_menu_changed(ENTRY_PITCH);
49 }
50
51 static void update_transform()
52 {
53         client_entity_transform(player_entity);
54         update_camera();
55 }
56
57 static void send_pos_rot()
58 {
59         update_transform();
60
61         dragonnet_peer_send_ToServerPosRot(client, &(ToServerPosRot) {
62                 .pos = player_entity->data.pos,
63                 .rot = player_entity->data.rot,
64         });
65 }
66
67 static void recv_pos_rot()
68 {
69         update_transform();
70
71         update_pos();
72         update_rot();
73 }
74
75 // entity callbacks
76
77 static void local_on_model_before_render(__attribute__((unused)) Model *model)
78 {
79         client_entity_depth_offset(0.2f);
80         client_inventory_depth_offset(0.2f);
81 }
82
83 static void local_on_model_after_render(__attribute__((unused)) Model *model)
84 {
85         client_entity_depth_offset(0.0f);
86         client_inventory_depth_offset(0.0f);}
87
88 static void on_add(ClientEntity *entity)
89 {
90         entity->model = model_clone(player_model);
91         entity->model->extra = refcount_grb(&entity->rc);
92
93         ClientPlayerData *data = entity->extra = malloc(sizeof *data);
94         data->bones = (struct ClientPlayerBones) {NULL};
95
96         model_get_bones(entity->model, (ModelBoneMapping[9]) {
97                 {"nametag",        &data->bones.nametag   },
98                 {"neck",           &data->bones.neck      },
99                 {"neck.head.eyes", &data->bones.eyes      },
100                 {"arm_left",       &data->bones.arm_left  },
101                 {"arm_right",      &data->bones.arm_right },
102                 {"arm_left.hand",  &data->bones.hand_left },
103                 {"arm_right.hand", &data->bones.hand_right},
104                 {"leg_left",       &data->bones.leg_left  },
105                 {"leg_right",      &data->bones.leg_right },
106         }, 9);
107
108         entity->nametag_offset = data->bones.nametag ? &data->bones.nametag->abs : NULL;
109         entity->box_collision = (aabb3f32) {{-0.45f, 0.0f, -0.45f}, {0.45f, 1.8f, 0.45f}};
110
111         client_inventory_init_player(entity);
112
113         model_scene_add(entity->model);
114         client_entity_transform(entity);
115 }
116
117 static void on_remove(ClientEntity *entity)
118 {
119         entity->model->flags.delete = 1;
120         entity->model = NULL;
121 }
122
123 static void on_free(ClientEntity *entity)
124 {
125         client_inventory_init_player(entity);
126         free(entity->extra);
127 }
128
129 static void on_transform(ClientEntity *entity)
130 {
131         ClientPlayerData *data = entity->extra;
132
133         entity->model->root->rot.x = entity->model->root->rot.z = 0.0f;
134
135         if (data->bones.neck) {
136                 data->bones.neck->rot.x = entity->data.rot.x;
137                 model_node_transform(data->bones.neck);
138         }
139 }
140
141 static void local_on_add(ClientEntity *entity)
142 {
143         pthread_rwlock_wrlock(&lock_player_entity);
144
145         if (player_entity) {
146                 fprintf(stderr, "[error] attempt to re-add localplayer entity\n");
147                 abort();
148         }
149
150         on_add(entity);
151         entity->model->callbacks.before_render = &local_on_model_before_render;
152         entity->model->callbacks.after_render = &local_on_model_after_render;
153
154         player_entity = refcount_grb(&entity->rc);
155         recv_pos_rot();
156
157         entity->type->update_nametag(entity);
158
159         pthread_rwlock_unlock(&lock_player_entity);
160 }
161
162 static void local_on_remove(ClientEntity *entity)
163 {
164         pthread_rwlock_wrlock(&lock_player_entity);
165         refcount_drp(&entity->rc);
166         player_entity = NULL;
167         pthread_rwlock_unlock(&lock_player_entity);
168
169         on_remove(entity);
170 }
171
172 static void local_on_update_pos_rot(__attribute__((unused)) ClientEntity *entity)
173 {
174         recv_pos_rot();
175 }
176
177 static void local_on_update_nametag(ClientEntity *entity)
178 {
179         if (entity->data.nametag) {
180                 free(entity->data.nametag);
181                 entity->data.nametag = NULL;
182         }
183 }
184
185 static void on_model_delete(Model *model)
186 {
187         if (model->extra)
188                 refcount_drp(&((ClientEntity *) model->extra)->rc);
189 }
190
191 // called on startup
192 void client_player_init()
193 {
194         client_player.movement = (ToClientMovement) {
195                 .flight = false,
196                 .collision = true,
197                 .speed = 0.0f,
198                 .jump = 0.0f,
199                 .gravity = 0.0f,
200         };
201
202         client_entity_types[ENTITY_PLAYER] = (ClientEntityType) {
203                 .add = &on_add,
204                 .remove = &on_remove,
205                 .free = &on_free,
206                 .update_pos_rot = NULL,
207                 .update_nametag = NULL,
208                 .transform = &on_transform,
209         };
210
211         client_entity_types[ENTITY_LOCALPLAYER] = (ClientEntityType) {
212                 .add = &local_on_add,
213                 .remove = &local_on_remove,
214                 .free = &on_free,
215                 .update_pos_rot = &local_on_update_pos_rot,
216                 .update_nametag = &local_on_update_nametag,
217                 .transform = &on_transform,
218         };
219
220         pthread_rwlock_init(&client_player.lock_movement, NULL);
221
222         player_entity = NULL;
223         pthread_rwlock_init(&lock_player_entity, NULL);
224 }
225
226 // called on shutdown
227 void client_player_deinit()
228 {
229         pthread_rwlock_destroy(&client_player.lock_movement);
230         pthread_rwlock_destroy(&lock_player_entity);
231 }
232
233 void client_player_gfx_init()
234 {
235         player_model = model_load(
236                 ASSET_PATH "models/player.txt", ASSET_PATH "textures/models/player",
237                 &client_entity_cube, &client_entity_shader);
238
239         player_model->callbacks.delete = &on_model_delete;
240 }
241
242 void client_player_gfx_deinit()
243 {
244         model_delete(player_model);
245 }
246
247 ClientEntity *client_player_entity(u64 id)
248 {
249         ClientEntity *entity = client_entity_grab(id);
250
251         if (entity->type == &client_entity_types[ENTITY_LOCALPLAYER]
252                         || entity->type == &client_entity_types[ENTITY_PLAYER])
253                 return entity;
254
255         refcount_drp(&entity->rc);
256         return NULL;
257 }
258
259 ClientEntity *client_player_entity_local()
260 {
261         ClientEntity *entity = NULL;
262
263         pthread_rwlock_rdlock(&lock_player_entity);
264         if (player_entity)
265                 entity = refcount_grb(&player_entity->rc);
266         pthread_rwlock_unlock(&lock_player_entity);
267
268         return entity;
269 }
270
271 void client_player_update_pos(ClientEntity *entity)
272 {
273         pthread_rwlock_rdlock(&lock_player_entity);
274
275         if (entity == player_entity) {
276                 update_pos();
277                 send_pos_rot();
278         }
279
280         pthread_rwlock_unlock(&lock_player_entity);
281 }
282
283 void client_player_update_rot(ClientEntity *entity)
284 {
285         pthread_rwlock_rdlock(&lock_player_entity);
286
287         if (entity == player_entity) {
288                 update_rot();
289                 send_pos_rot();
290         }
291
292         pthread_rwlock_unlock(&lock_player_entity);
293 }
294
295 // jump if possible
296 void client_player_jump()
297 {
298         ClientEntity *entity = client_player_entity_local();
299         if (!entity)
300                 return;
301
302         pthread_rwlock_rdlock(&entity->lock_pos_rot);
303         pthread_rwlock_rdlock(&entity->lock_box_off);
304
305         if (physics_ground(
306                 client_terrain,
307                 client_player.movement.collision,
308                 entity->box_collision,
309                 &entity->data.pos,
310                 &client_player.velocity
311         ))
312                 client_player.velocity.y += client_player.movement.jump;
313
314         pthread_rwlock_unlock(&entity->lock_box_off);
315         pthread_rwlock_unlock(&entity->lock_pos_rot);
316
317         refcount_drp(&entity->rc);
318 }
319
320 // to be called every frame
321 void client_player_tick(f64 dtime)
322 {
323         ClientEntity *entity = client_player_entity_local();
324         if (!entity)
325                 return;
326
327         pthread_rwlock_rdlock(&client_player.lock_movement);
328         pthread_rwlock_wrlock(&entity->lock_pos_rot);
329         pthread_rwlock_rdlock(&entity->lock_box_off);
330
331         if (physics_step(
332                 client_terrain,
333                 client_player.movement.collision,
334                 entity->box_collision,
335                 &entity->data.pos,
336                 &client_player.velocity,
337                 &(v3f64) {
338                         0.0,
339                         client_player.movement.flight ? 0.0 : -client_player.movement.gravity,
340                         0.0,
341                 },
342                 dtime
343         ))
344                 client_player_update_pos(entity);
345
346         pthread_rwlock_unlock(&entity->lock_box_off);
347         pthread_rwlock_unlock(&entity->lock_pos_rot);
348         pthread_rwlock_unlock(&client_player.lock_movement);
349
350         refcount_drp(&entity->rc);
351 }