]> git.localhorst.tv Git - blank.git/blob - src/client/client.cpp
use entity's eyes to aim
[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(player.GetEntity().Transform(player.GetEntity().ChunkCoords()));
151         if (master.GetConfig().video.world) {
152                 chunk_renderer.Render(viewport);
153                 world.Render(viewport);
154                 sky.Render(viewport);
155         }
156         hud.Render(viewport);
157 }
158
159 void InteractiveState::Handle(const Packet::SpawnEntity &pack) {
160         uint32_t entity_id;
161         pack.ReadEntityID(entity_id);
162         Entity &entity = world.ForceAddEntity(entity_id);
163         UpdateEntity(entity_id, pack.Seq());
164         pack.ReadEntity(entity);
165         uint32_t skel_id;
166         pack.ReadSkeletonID(skel_id);
167         if (skel_id > 0 && skel_id <= res.models.size()) {
168                 Model &skel = res.models.Get(skel_id);
169                 skel.Instantiate(entity.GetModel());
170         }
171         cout << "spawned entity #" << entity_id << "  (" << entity.Name()
172                 << ") at " << entity.AbsolutePosition() << endl;
173 }
174
175 void InteractiveState::Handle(const Packet::DespawnEntity &pack) {
176         uint32_t entity_id;
177         pack.ReadEntityID(entity_id);
178         ClearEntity(entity_id);
179         for (Entity &entity : world.Entities()) {
180                 if (entity.ID() == entity_id) {
181                         entity.Kill();
182                         cout << "despawned entity #" << entity_id << " (" << entity.Name() << ") at " << entity.AbsolutePosition() << endl;
183                         return;
184                 }
185         }
186 }
187
188 void InteractiveState::Handle(const Packet::EntityUpdate &pack) {
189         auto world_iter = world.Entities().begin();
190         auto world_end = world.Entities().end();
191
192         uint32_t count = 0;
193         pack.ReadEntityCount(count);
194
195         for (uint32_t i = 0; i < count; ++i) {
196                 uint32_t entity_id = 0;
197                 pack.ReadEntityID(entity_id, i);
198
199                 while (world_iter != world_end && world_iter->ID() < entity_id) {
200                         ++world_iter;
201                 }
202                 if (world_iter == world_end) {
203                         // nothing can be done from here
204                         return;
205                 }
206                 if (world_iter->ID() == entity_id) {
207                         if (UpdateEntity(entity_id, pack.Seq())) {
208                                 pack.ReadEntityState(world_iter->GetState(), i);
209                         }
210                 }
211         }
212 }
213
214 bool InteractiveState::UpdateEntity(uint32_t entity_id, uint16_t seq) {
215         auto entry = update_status.find(entity_id);
216         if (entry == update_status.end()) {
217                 update_status.emplace(entity_id, UpdateStatus{ seq, loop_timer.Elapsed() });
218                 return true;
219         }
220
221         int16_t pack_diff = int16_t(seq) - int16_t(entry->second.last_packet);
222         int time_diff = loop_timer.Elapsed() - entry->second.last_update;
223         entry->second.last_update = loop_timer.Elapsed();
224
225         if (pack_diff > 0 || time_diff > 1500) {
226                 entry->second.last_packet = seq;
227                 return true;
228         } else {
229                 return false;
230         }
231 }
232
233 void InteractiveState::ClearEntity(uint32_t entity_id) {
234         update_status.erase(entity_id);
235 }
236
237 void InteractiveState::Handle(const Packet::PlayerCorrection &pack) {
238         uint16_t pack_seq;
239         EntityState corrected_state;
240         pack.ReadPacketSeq(pack_seq);
241         pack.ReadPlayerState(corrected_state);
242         input.MergePlayerCorrection(pack_seq, corrected_state);
243 }
244
245 void InteractiveState::Handle(const Packet::BlockUpdate &pack) {
246         glm::ivec3 pos;
247         pack.ReadChunkCoords(pos);
248         Chunk *chunk = player.GetChunks().Get(pos);
249         if (!chunk) {
250                 // this change doesn't concern us
251                 return;
252         }
253         uint32_t count = 0;
254         pack.ReadBlockCount(count);
255         for (uint32_t i = 0; i < count; ++i) {
256                 uint16_t index;
257                 Block block;
258                 pack.ReadIndex(index, i);
259                 pack.ReadBlock(block, i);
260                 if (index < Chunk::size && block.type < res.block_types.size()) {
261                         manip.SetBlock(*chunk, index, block);
262                 }
263         }
264 }
265
266 void InteractiveState::SetAudio(bool b) {
267         master.GetConfig().audio.enabled = b;
268         if (b) {
269                 hud.PostMessage("Audio enabled");
270         } else {
271                 hud.PostMessage("Audio disabled");
272         }
273 }
274
275 void InteractiveState::SetVideo(bool b) {
276         master.GetConfig().video.world = b;
277         if (b) {
278                 hud.PostMessage("World rendering enabled");
279         } else {
280                 hud.PostMessage("World rendering disabled");
281         }
282 }
283
284 void InteractiveState::SetHUD(bool b) {
285         master.GetConfig().video.hud = b;
286         if (b) {
287                 hud.PostMessage("HUD rendering enabled");
288         } else {
289                 hud.PostMessage("HUD rendering disabled");
290         }
291 }
292
293 void InteractiveState::SetDebug(bool b) {
294         master.GetConfig().video.debug = b;
295         if (b) {
296                 hud.PostMessage("Debug rendering enabled");
297         } else {
298                 hud.PostMessage("Debug rendering disabled");
299         }
300 }
301
302 void InteractiveState::Exit() {
303         save.Write(player);
304         master.Quit();
305 }
306
307
308 MasterState::MasterState(
309         Environment &env,
310         Config &config,
311         const World::Config &wc)
312 : env(env)
313 , config(config)
314 , world_conf(wc)
315 , state()
316 , client(config.net)
317 , init_state(*this)
318 , login_packet(-1) {
319         client.GetConnection().SetHandler(this);
320 }
321
322 void MasterState::Quit() {
323         if (!client.GetConnection().Closed()) {
324                 client.SendPart();
325         }
326         env.state.PopUntil(this);
327 }
328
329
330 void MasterState::OnEnter() {
331         login_packet = client.SendLogin(config.player.name);
332         env.state.Push(&init_state);
333 }
334
335
336 void MasterState::Handle(const SDL_Event &event) {
337
338 }
339
340
341 void MasterState::Update(int dt) {
342         client.Handle();
343         client.Update(dt);
344 }
345
346
347 void MasterState::Render(Viewport &) {
348
349 }
350
351
352 void MasterState::OnPacketLost(uint16_t id) {
353         if (id == login_packet) {
354                 login_packet = client.SendLogin(config.player.name);
355         }
356 }
357
358 void MasterState::OnTimeout() {
359         if (client.GetConnection().Closed()) {
360                 Quit();
361                 env.ShowMessage("connection timed out");
362         }
363 }
364
365 void MasterState::On(const Packet::Join &pack) {
366         pack.ReadWorldName(world_conf.name);
367
368         if (state) {
369                 // changing worlds
370                 cout << "server changing worlds to \"" << world_conf.name << '"' << endl;
371         } else {
372                 // joining game
373                 cout << "joined game \"" << world_conf.name << '"' << endl;
374                 // server received our login
375                 login_packet = -1;
376         }
377
378         uint32_t player_id;
379         pack.ReadPlayerID(player_id);
380         state.reset(new InteractiveState(*this, player_id));
381
382         pack.ReadPlayerState(state->GetPlayer().GetEntity().GetState());
383
384         env.state.PopAfter(this);
385         env.state.Push(state.get());
386 }
387
388 void MasterState::On(const Packet::Part &pack) {
389         Quit();
390         if (state) {
391                 // kicked
392                 env.ShowMessage("kicked by server");
393         } else {
394                 // join refused
395                 env.ShowMessage("login refused by server");
396         }
397 }
398
399 void MasterState::On(const Packet::SpawnEntity &pack) {
400         if (!state) {
401                 cout << "got entity spawn before world was created" << endl;
402                 return;
403         }
404         state->Handle(pack);
405 }
406
407 void MasterState::On(const Packet::DespawnEntity &pack) {
408         if (!state) {
409                 cout << "got entity despawn before world was created" << endl;
410                 return;
411         }
412         state->Handle(pack);
413 }
414
415 void MasterState::On(const Packet::EntityUpdate &pack) {
416         if (!state) {
417                 cout << "got entity update before world was created" << endl;
418                 return;
419         }
420         state->Handle(pack);
421 }
422
423 void MasterState::On(const Packet::PlayerCorrection &pack) {
424         if (!state) {
425                 cout << "got player correction without a player :S" << endl;
426                 return;
427         }
428         state->Handle(pack);
429 }
430
431 void MasterState::On(const Packet::ChunkBegin &pack) {
432         if (!state) {
433                 cout << "got chunk data, but the world has not been created yet" << endl;
434                 cout << "great, this will totally screw up everything :(" << endl;
435                 return;
436         }
437         state->GetChunkReceiver().Handle(pack);
438 }
439
440 void MasterState::On(const Packet::ChunkData &pack) {
441         if (!state) {
442                 cout << "got chunk data, but the world has not been created yet" << endl;
443                 cout << "great, this will totally screw up everything :(" << endl;
444                 return;
445         }
446         state->GetChunkReceiver().Handle(pack);
447 }
448
449 void MasterState::On(const Packet::BlockUpdate &pack) {
450         if (!state) {
451                 cout << "received block update, but the world has not been created yet" << endl;
452                 return;
453         }
454         state->Handle(pack);
455 }
456
457 }
458 }