]> git.localhorst.tv Git - blank.git/blob - src/client/client.cpp
use eye transform for client rendering
[blank.git] / src / client / client.cpp
1 #include "InitialState.hpp"
2 #include "InteractiveState.hpp"
3 #include "MasterState.hpp"
4
5 #include "../app/Environment.hpp"
6 #include "../app/init.hpp"
7 #include "../model/Model.hpp"
8 #include "../io/WorldSave.hpp"
9 #include "../world/ChunkIndex.hpp"
10 #include "../world/ChunkStore.hpp"
11
12 #include <iostream>
13 #include <glm/gtx/io.hpp>
14
15 using namespace std;
16
17
18 namespace blank {
19 namespace client {
20
21 InitialState::InitialState(MasterState &master)
22 : master(master)
23 , message() {
24         message.Position(glm::vec3(0.0f), Gravity::CENTER);
25         message.Set(master.GetEnv().assets.large_ui_font, "logging in");
26 }
27
28 void InitialState::OnEnter() {
29
30 }
31
32 void InitialState::Handle(const SDL_Event &evt) {
33         if (evt.type == SDL_QUIT) {
34                 master.Quit();
35         }
36 }
37
38 void InitialState::Update(int dt) {
39         master.Update(dt);
40 }
41
42 void InitialState::Render(Viewport &viewport) {
43         message.Render(viewport);
44 }
45
46
47 // TODO: this clutter is a giant mess
48 InteractiveState::InteractiveState(MasterState &master, uint32_t player_id)
49 : master(master)
50 , res()
51 , sounds()
52 , save(master.GetEnv().config.GetWorldPath(master.GetWorldConf().name, master.GetConfig().net.host))
53 , world(res.block_types, master.GetWorldConf())
54 , player(*world.AddPlayer(master.GetConfig().player.name))
55 , hud(master.GetEnv(), master.GetConfig(), player)
56 , manip(master.GetEnv().audio, sounds, player.GetEntity())
57 , input(world, player, master.GetClient())
58 , interface(master.GetConfig(), master.GetEnv().keymap, input, *this)
59 , chunk_receiver(world.Chunks(), save)
60 , chunk_renderer(player.GetChunks())
61 , loop_timer(16)
62 , sky(master.GetEnv().loader.LoadCubeMap("skybox"))
63 , update_status() {
64         if (!save.Exists()) {
65                 save.Write(master.GetWorldConf());
66         }
67         res.Load(master.GetEnv().loader, "default");
68         if (res.models.size() < 1) {
69                 throw std::runtime_error("need at least one model to run");
70         }
71         res.models[0].Instantiate(player.GetEntity().GetModel());
72         sounds.Load(master.GetEnv().loader, res.snd_index);
73         interface.SetInventorySlots(res.block_types.size() - 1);
74         chunk_renderer.LoadTextures(master.GetEnv().loader, res.tex_index);
75         chunk_renderer.FogDensity(master.GetWorldConf().fog_density);
76         loop_timer.Start();
77         if (save.Exists(player)) {
78                 save.Read(player);
79         }
80 }
81
82 void InteractiveState::OnEnter() {
83         master.GetEnv().window.GrabMouse();
84 }
85
86 void InteractiveState::Handle(const SDL_Event &event) {
87         switch (event.type) {
88                 case SDL_KEYDOWN:
89                         interface.HandlePress(event.key);
90                         break;
91                 case SDL_KEYUP:
92                         interface.HandleRelease(event.key);
93                         break;
94                 case SDL_MOUSEBUTTONDOWN:
95                         interface.HandlePress(event.button);
96                         break;
97                 case SDL_MOUSEBUTTONUP:
98                         interface.HandleRelease(event.button);
99                         break;
100                 case SDL_MOUSEMOTION:
101                         interface.Handle(event.motion);
102                         break;
103                 case SDL_MOUSEWHEEL:
104                         interface.Handle(event.wheel);
105                         break;
106                 case SDL_QUIT:
107                         Exit();
108                         break;
109                 default:
110                         break;
111         }
112 }
113
114 void InteractiveState::Update(int dt) {
115         input.Update(dt);
116         if (input.BlockFocus()) {
117                 hud.FocusBlock(input.BlockFocus().GetChunk(), input.BlockFocus().block);
118         } else if (input.EntityFocus()) {
119                 hud.FocusEntity(*input.EntityFocus().entity);
120         } else {
121                 hud.FocusNone();
122         }
123         hud.Display(res.block_types[player.GetInventorySlot() + 1]);
124         loop_timer.Update(dt);
125         master.Update(dt);
126         chunk_receiver.Update(dt);
127
128         hud.Update(dt);
129         int world_dt = 0;
130         while (loop_timer.HitOnce()) {
131                 world.Update(loop_timer.Interval());
132                 world_dt += loop_timer.Interval();
133                 loop_timer.PopIteration();
134         }
135         chunk_renderer.Update(dt);
136
137         if (world_dt > 0) {
138                 input.PushPlayerUpdate(world_dt);
139         }
140
141         glm::mat4 trans = player.GetEntity().Transform(player.GetEntity().ChunkCoords());
142         glm::vec3 dir(trans * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f));
143         glm::vec3 up(trans * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
144         master.GetEnv().audio.Position(player.GetEntity().Position());
145         master.GetEnv().audio.Velocity(player.GetEntity().Velocity());
146         master.GetEnv().audio.Orientation(dir, up);
147 }
148
149 void InteractiveState::Render(Viewport &viewport) {
150         viewport.WorldPosition(
151                 player.GetEntity().Transform(player.GetEntity().ChunkCoords())
152                 * player.GetEntity().GetModel().EyesTransform());
153         if (master.GetConfig().video.world) {
154                 chunk_renderer.Render(viewport);
155                 world.Render(viewport);
156                 sky.Render(viewport);
157         }
158         hud.Render(viewport);
159 }
160
161 void InteractiveState::Handle(const Packet::SpawnEntity &pack) {
162         uint32_t entity_id;
163         pack.ReadEntityID(entity_id);
164         Entity &entity = world.ForceAddEntity(entity_id);
165         UpdateEntity(entity_id, pack.Seq());
166         pack.ReadEntity(entity);
167         uint32_t skel_id;
168         pack.ReadSkeletonID(skel_id);
169         if (skel_id > 0 && skel_id <= res.models.size()) {
170                 Model &skel = res.models.Get(skel_id);
171                 skel.Instantiate(entity.GetModel());
172         }
173         cout << "spawned entity #" << entity_id << "  (" << entity.Name()
174                 << ") at " << entity.AbsolutePosition() << endl;
175 }
176
177 void InteractiveState::Handle(const Packet::DespawnEntity &pack) {
178         uint32_t entity_id;
179         pack.ReadEntityID(entity_id);
180         ClearEntity(entity_id);
181         for (Entity &entity : world.Entities()) {
182                 if (entity.ID() == entity_id) {
183                         entity.Kill();
184                         cout << "despawned entity #" << entity_id << " (" << entity.Name() << ") at " << entity.AbsolutePosition() << endl;
185                         return;
186                 }
187         }
188 }
189
190 void InteractiveState::Handle(const Packet::EntityUpdate &pack) {
191         auto world_iter = world.Entities().begin();
192         auto world_end = world.Entities().end();
193
194         uint32_t count = 0;
195         pack.ReadEntityCount(count);
196
197         for (uint32_t i = 0; i < count; ++i) {
198                 uint32_t entity_id = 0;
199                 pack.ReadEntityID(entity_id, i);
200
201                 while (world_iter != world_end && world_iter->ID() < entity_id) {
202                         ++world_iter;
203                 }
204                 if (world_iter == world_end) {
205                         // nothing can be done from here
206                         return;
207                 }
208                 if (world_iter->ID() == entity_id) {
209                         if (UpdateEntity(entity_id, pack.Seq())) {
210                                 pack.ReadEntityState(world_iter->GetState(), i);
211                         }
212                 }
213         }
214 }
215
216 bool InteractiveState::UpdateEntity(uint32_t entity_id, uint16_t seq) {
217         auto entry = update_status.find(entity_id);
218         if (entry == update_status.end()) {
219                 update_status.emplace(entity_id, UpdateStatus{ seq, loop_timer.Elapsed() });
220                 return true;
221         }
222
223         int16_t pack_diff = int16_t(seq) - int16_t(entry->second.last_packet);
224         int time_diff = loop_timer.Elapsed() - entry->second.last_update;
225         entry->second.last_update = loop_timer.Elapsed();
226
227         if (pack_diff > 0 || time_diff > 1500) {
228                 entry->second.last_packet = seq;
229                 return true;
230         } else {
231                 return false;
232         }
233 }
234
235 void InteractiveState::ClearEntity(uint32_t entity_id) {
236         update_status.erase(entity_id);
237 }
238
239 void InteractiveState::Handle(const Packet::PlayerCorrection &pack) {
240         uint16_t pack_seq;
241         EntityState corrected_state;
242         pack.ReadPacketSeq(pack_seq);
243         pack.ReadPlayerState(corrected_state);
244         input.MergePlayerCorrection(pack_seq, corrected_state);
245 }
246
247 void InteractiveState::Handle(const Packet::BlockUpdate &pack) {
248         glm::ivec3 pos;
249         pack.ReadChunkCoords(pos);
250         Chunk *chunk = player.GetChunks().Get(pos);
251         if (!chunk) {
252                 // this change doesn't concern us
253                 return;
254         }
255         uint32_t count = 0;
256         pack.ReadBlockCount(count);
257         for (uint32_t i = 0; i < count; ++i) {
258                 uint16_t index;
259                 Block block;
260                 pack.ReadIndex(index, i);
261                 pack.ReadBlock(block, i);
262                 if (index < Chunk::size && block.type < res.block_types.size()) {
263                         manip.SetBlock(*chunk, index, block);
264                 }
265         }
266 }
267
268 void InteractiveState::SetAudio(bool b) {
269         master.GetConfig().audio.enabled = b;
270         if (b) {
271                 hud.PostMessage("Audio enabled");
272         } else {
273                 hud.PostMessage("Audio disabled");
274         }
275 }
276
277 void InteractiveState::SetVideo(bool b) {
278         master.GetConfig().video.world = b;
279         if (b) {
280                 hud.PostMessage("World rendering enabled");
281         } else {
282                 hud.PostMessage("World rendering disabled");
283         }
284 }
285
286 void InteractiveState::SetHUD(bool b) {
287         master.GetConfig().video.hud = b;
288         if (b) {
289                 hud.PostMessage("HUD rendering enabled");
290         } else {
291                 hud.PostMessage("HUD rendering disabled");
292         }
293 }
294
295 void InteractiveState::SetDebug(bool b) {
296         master.GetConfig().video.debug = b;
297         if (b) {
298                 hud.PostMessage("Debug rendering enabled");
299         } else {
300                 hud.PostMessage("Debug rendering disabled");
301         }
302 }
303
304 void InteractiveState::Exit() {
305         save.Write(player);
306         master.Quit();
307 }
308
309
310 MasterState::MasterState(
311         Environment &env,
312         Config &config,
313         const World::Config &wc)
314 : env(env)
315 , config(config)
316 , world_conf(wc)
317 , state()
318 , client(config.net)
319 , init_state(*this)
320 , login_packet(-1) {
321         client.GetConnection().SetHandler(this);
322 }
323
324 void MasterState::Quit() {
325         if (!client.GetConnection().Closed()) {
326                 client.SendPart();
327         }
328         env.state.PopUntil(this);
329 }
330
331
332 void MasterState::OnEnter() {
333         login_packet = client.SendLogin(config.player.name);
334         env.state.Push(&init_state);
335 }
336
337
338 void MasterState::Handle(const SDL_Event &event) {
339
340 }
341
342
343 void MasterState::Update(int dt) {
344         client.Handle();
345         client.Update(dt);
346 }
347
348
349 void MasterState::Render(Viewport &) {
350
351 }
352
353
354 void MasterState::OnPacketLost(uint16_t id) {
355         if (id == login_packet) {
356                 login_packet = client.SendLogin(config.player.name);
357         }
358 }
359
360 void MasterState::OnTimeout() {
361         if (client.GetConnection().Closed()) {
362                 Quit();
363                 env.ShowMessage("connection timed out");
364         }
365 }
366
367 void MasterState::On(const Packet::Join &pack) {
368         pack.ReadWorldName(world_conf.name);
369
370         if (state) {
371                 // changing worlds
372                 cout << "server changing worlds to \"" << world_conf.name << '"' << endl;
373         } else {
374                 // joining game
375                 cout << "joined game \"" << world_conf.name << '"' << endl;
376                 // server received our login
377                 login_packet = -1;
378         }
379
380         uint32_t player_id;
381         pack.ReadPlayerID(player_id);
382         state.reset(new InteractiveState(*this, player_id));
383
384         pack.ReadPlayerState(state->GetPlayer().GetEntity().GetState());
385         glm::vec3 orient(glm::eulerAngles(state->GetPlayer().GetEntity().Orientation()));
386         state->GetPlayerController().TurnHead(orient.x, orient.y);
387
388         env.state.PopAfter(this);
389         env.state.Push(state.get());
390 }
391
392 void MasterState::On(const Packet::Part &pack) {
393         Quit();
394         if (state) {
395                 // kicked
396                 env.ShowMessage("kicked by server");
397         } else {
398                 // join refused
399                 env.ShowMessage("login refused by server");
400         }
401 }
402
403 void MasterState::On(const Packet::SpawnEntity &pack) {
404         if (!state) {
405                 cout << "got entity spawn before world was created" << endl;
406                 return;
407         }
408         state->Handle(pack);
409 }
410
411 void MasterState::On(const Packet::DespawnEntity &pack) {
412         if (!state) {
413                 cout << "got entity despawn before world was created" << endl;
414                 return;
415         }
416         state->Handle(pack);
417 }
418
419 void MasterState::On(const Packet::EntityUpdate &pack) {
420         if (!state) {
421                 cout << "got entity update before world was created" << endl;
422                 return;
423         }
424         state->Handle(pack);
425 }
426
427 void MasterState::On(const Packet::PlayerCorrection &pack) {
428         if (!state) {
429                 cout << "got player correction without a player :S" << endl;
430                 return;
431         }
432         state->Handle(pack);
433 }
434
435 void MasterState::On(const Packet::ChunkBegin &pack) {
436         if (!state) {
437                 cout << "got chunk data, but the world has not been created yet" << endl;
438                 cout << "great, this will totally screw up everything :(" << endl;
439                 return;
440         }
441         state->GetChunkReceiver().Handle(pack);
442 }
443
444 void MasterState::On(const Packet::ChunkData &pack) {
445         if (!state) {
446                 cout << "got chunk data, but the world has not been created yet" << endl;
447                 cout << "great, this will totally screw up everything :(" << endl;
448                 return;
449         }
450         state->GetChunkReceiver().Handle(pack);
451 }
452
453 void MasterState::On(const Packet::BlockUpdate &pack) {
454         if (!state) {
455                 cout << "received block update, but the world has not been created yet" << endl;
456                 return;
457         }
458         state->Handle(pack);
459 }
460
461 }
462 }