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