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