]> git.localhorst.tv Git - blank.git/blob - src/client/client.cpp
4be9dbe88430cc8a77f677db8243af5520c6ee54
[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 "../app/TextureIndex.hpp"
8 #include "../model/CompositeModel.hpp"
9
10 #include <iostream>
11 #include <glm/gtx/io.hpp>
12
13 using namespace std;
14
15
16 namespace blank {
17 namespace client {
18
19 InitialState::InitialState(MasterState &master)
20 : master(master)
21 , message() {
22         message.Position(glm::vec3(0.0f), Gravity::CENTER);
23         message.Set(master.GetEnv().assets.large_ui_font, "logging in");
24 }
25
26 void InitialState::OnEnter() {
27
28 }
29
30 void InitialState::Handle(const SDL_Event &evt) {
31         if (evt.type == SDL_QUIT) {
32                 master.Quit();
33         }
34 }
35
36 void InitialState::Update(int dt) {
37         master.Update(dt);
38 }
39
40 void InitialState::Render(Viewport &viewport) {
41         message.Render(viewport);
42 }
43
44
45 // TODO: this clutter is a giant mess
46 InteractiveState::InteractiveState(MasterState &master, uint32_t player_id)
47 : master(master)
48 , block_types()
49 , save(master.GetEnv().config.GetWorldPath(master.GetWorldConf().name, master.GetClientConf().host))
50 , world(block_types, master.GetWorldConf())
51 , interface(
52         master.GetInterfaceConf(),
53         master.GetEnv(),
54         world,
55         world.AddPlayer(master.GetInterfaceConf().player_name, player_id)
56 )
57 , chunk_receiver(world.Chunks())
58 , chunk_renderer(*interface.GetPlayer().chunks)
59 , skeletons()
60 , loop_timer(16)
61 , player_hist() {
62         TextureIndex tex_index;
63         master.GetEnv().loader.LoadBlockTypes("default", block_types, tex_index);
64         chunk_renderer.LoadTextures(master.GetEnv().loader, tex_index);
65         chunk_renderer.FogDensity(master.GetWorldConf().fog_density);
66         skeletons.Load();
67         // TODO: better solution for initializing HUD
68         interface.SelectNext();
69         loop_timer.Start();
70 }
71
72 void InteractiveState::OnEnter() {
73         master.GetEnv().window.GrabMouse();
74 }
75
76 void InteractiveState::Handle(const SDL_Event &event) {
77         switch (event.type) {
78                 case SDL_KEYDOWN:
79                         interface.HandlePress(event.key);
80                         break;
81                 case SDL_KEYUP:
82                         interface.HandleRelease(event.key);
83                         break;
84                 case SDL_MOUSEBUTTONDOWN:
85                         interface.HandlePress(event.button);
86                         break;
87                 case SDL_MOUSEBUTTONUP:
88                         interface.HandleRelease(event.button);
89                         break;
90                 case SDL_MOUSEMOTION:
91                         interface.Handle(event.motion);
92                         break;
93                 case SDL_MOUSEWHEEL:
94                         interface.Handle(event.wheel);
95                         break;
96                 case SDL_QUIT:
97                         master.Quit();
98                         break;
99                 default:
100                         break;
101         }
102 }
103
104 void InteractiveState::Update(int dt) {
105         loop_timer.Update(dt);
106         master.Update(dt);
107         chunk_receiver.Update(dt);
108
109         interface.Update(dt);
110         int world_dt = 0;
111         while (loop_timer.HitOnce()) {
112                 world.Update(loop_timer.Interval());
113                 world_dt += loop_timer.Interval();
114                 loop_timer.PopIteration();
115         }
116         chunk_renderer.Update(dt);
117
118         Entity &player = *interface.GetPlayer().entity;
119
120         if (world_dt > 0) {
121                 PushPlayerUpdate(player, world_dt);
122         }
123
124         glm::mat4 trans = player.Transform(player.ChunkCoords());
125         glm::vec3 dir(trans * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f));
126         glm::vec3 up(trans * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
127         master.GetEnv().audio.Position(player.Position());
128         master.GetEnv().audio.Velocity(player.Velocity());
129         master.GetEnv().audio.Orientation(dir, up);
130 }
131
132 void InteractiveState::PushPlayerUpdate(const Entity &player, int dt) {
133         std::uint16_t packet = master.GetClient().SendPlayerUpdate(player);
134         if (player_hist.size() < 16) {
135                 player_hist.emplace_back(player.GetState(), dt, packet);
136         } else {
137                 auto entry = player_hist.begin();
138                 entry->state = player.GetState();
139                 entry->delta_t = dt;
140                 entry->packet = packet;
141                 player_hist.splice(player_hist.end(), player_hist, entry);
142         }
143 }
144
145 void InteractiveState::MergePlayerCorrection(uint16_t seq, const EntityState &corrected_state) {
146         if (player_hist.empty()) return;
147
148         auto entry = player_hist.begin();
149         auto end = player_hist.end();
150
151         // we may have received an older packet
152         int pack_diff = int16_t(seq) - int16_t(entry->packet);
153         if (pack_diff < 0) {
154                 // indeed we have, just ignore it
155                 return;
156         }
157
158         // drop anything older than the fix
159         while (entry != end) {
160                 pack_diff = int16_t(seq) - int16_t(entry->packet);
161                 if (pack_diff > 0) {
162                         entry = player_hist.erase(entry);
163                 } else {
164                         break;
165                 }
166         }
167
168         EntityState replay_state(corrected_state);
169         EntityState &player_state = interface.GetPlayer().entity->GetState();
170
171         if (entry != end) {
172                 entry->state.chunk_pos = replay_state.chunk_pos;
173                 entry->state.block_pos = replay_state.block_pos;
174                 ++entry;
175         }
176
177         while (entry != end) {
178                 replay_state.velocity = entry->state.velocity;
179                 replay_state.Update(entry->delta_t);
180                 entry->state.chunk_pos = replay_state.chunk_pos;
181                 entry->state.block_pos = replay_state.block_pos;
182                 ++entry;
183         }
184
185         glm::vec3 displacement(replay_state.Diff(player_state));
186         const float disp_squared = dot(displacement, displacement);
187
188         if (disp_squared < 16.0f * numeric_limits<float>::epsilon()) {
189                 return;
190         }
191
192         // if offset > 10cm, warp the player
193         // otherwise, move at most 1cm per frame towards
194         // the fixed position (160ms, so shouldn't be too noticeable)
195         constexpr float warp_thresh = 0.01f; // (1/10)^2
196         constexpr float max_disp = 0.0001f; // (1/100)^2
197
198         if (disp_squared > warp_thresh) {
199                 player_state.chunk_pos = replay_state.chunk_pos;
200                 player_state.block_pos = replay_state.block_pos;
201         } else if (disp_squared < max_disp) {
202                 player_state.block_pos += displacement;
203         } else {
204                 displacement *= 0.01f / sqrt(disp_squared);
205                 player_state.block_pos += displacement;
206         }
207 }
208
209 void InteractiveState::Render(Viewport &viewport) {
210         Entity &player = *interface.GetPlayer().entity;
211         viewport.WorldPosition(player.Transform(player.ChunkCoords()));
212         chunk_renderer.Render(viewport);
213         world.Render(viewport);
214         interface.Render(viewport);
215 }
216
217
218 MasterState::MasterState(
219         Environment &env,
220         const World::Config &wc,
221         const Interface::Config &ic,
222         const Client::Config &cc)
223 : env(env)
224 , world_conf(wc)
225 , intf_conf(ic)
226 , client_conf(cc)
227 , state()
228 , client(cc)
229 , init_state(*this)
230 , login_packet(-1)
231 , update_status()
232 , update_timer(16) {
233         client.GetConnection().SetHandler(this);
234         update_timer.Start();
235 }
236
237 void MasterState::Quit() {
238         if (!client.GetConnection().Closed()) {
239                 client.SendPart();
240         }
241         env.state.PopUntil(this);
242 }
243
244
245 void MasterState::OnEnter() {
246         login_packet = client.SendLogin(intf_conf.player_name);
247         env.state.Push(&init_state);
248 }
249
250
251 void MasterState::Handle(const SDL_Event &event) {
252
253 }
254
255
256 void MasterState::Update(int dt) {
257         update_timer.Update(dt);
258         client.Handle();
259         client.Update(dt);
260 }
261
262
263 void MasterState::Render(Viewport &) {
264
265 }
266
267
268 void MasterState::OnPacketLost(uint16_t id) {
269         if (id == login_packet) {
270                 login_packet = client.SendLogin(intf_conf.player_name);
271         }
272 }
273
274 void MasterState::OnTimeout() {
275         if (client.GetConnection().Closed()) {
276                 // TODO: push disconnected message
277                 cout << "connection timed out" << endl;
278                 Quit();
279         }
280 }
281
282 void MasterState::On(const Packet::Join &pack) {
283         pack.ReadWorldName(world_conf.name);
284
285         if (state) {
286                 // changing worlds
287                 cout << "server changing worlds to \"" << world_conf.name << '"' << endl;
288         } else {
289                 // joining game
290                 cout << "joined game \"" << world_conf.name << '"' << endl;
291                 // server received our login
292                 login_packet = -1;
293         }
294
295         uint32_t player_id;
296         pack.ReadPlayerID(player_id);
297         state.reset(new InteractiveState(*this, player_id));
298
299         pack.ReadPlayerState(state->GetInterface().GetPlayer().entity->GetState());
300
301         env.state.PopAfter(this);
302         env.state.Push(state.get());
303 }
304
305 void MasterState::On(const Packet::Part &pack) {
306         if (state) {
307                 // kicked
308                 cout << "kicked by server" << endl;
309         } else {
310                 // join refused
311                 cout << "login refused by server" << endl;
312         }
313         Quit();
314 }
315
316 void MasterState::On(const Packet::SpawnEntity &pack) {
317         if (!state) {
318                 cout << "got entity spawn before world was created" << endl;
319                 Quit();
320                 return;
321         }
322         uint32_t entity_id;
323         pack.ReadEntityID(entity_id);
324         Entity &entity = state->GetWorld().ForceAddEntity(entity_id);
325         UpdateEntity(entity_id, pack.Seq());
326         pack.ReadEntity(entity);
327         uint32_t skel_id;
328         pack.ReadSkeletonID(skel_id);
329         CompositeModel *skel = state->GetSkeletons().ByID(skel_id);
330         if (skel) {
331                 skel->Instantiate(entity.GetModel());
332         }
333         cout << "spawned entity " << entity.Name() << " at " << entity.AbsolutePosition() << endl;
334 }
335
336 void MasterState::On(const Packet::DespawnEntity &pack) {
337         if (!state) {
338                 cout << "got entity despawn before world was created" << endl;
339                 Quit();
340                 return;
341         }
342         uint32_t entity_id;
343         pack.ReadEntityID(entity_id);
344         ClearEntity(entity_id);
345         for (Entity &entity : state->GetWorld().Entities()) {
346                 if (entity.ID() == entity_id) {
347                         entity.Kill();
348                         cout << "despawned entity " << entity.Name() << " at " << entity.AbsolutePosition() << endl;
349                         return;
350                 }
351         }
352 }
353
354 void MasterState::On(const Packet::EntityUpdate &pack) {
355         if (!state) {
356                 cout << "got entity update before world was created" << endl;
357                 Quit();
358                 return;
359         }
360
361         auto world_iter = state->GetWorld().Entities().begin();
362         auto world_end = state->GetWorld().Entities().end();
363
364         uint32_t count = 0;
365         pack.ReadEntityCount(count);
366
367         for (uint32_t i = 0; i < count; ++i) {
368                 uint32_t entity_id = 0;
369                 pack.ReadEntityID(entity_id, i);
370
371                 while (world_iter != world_end && world_iter->ID() < entity_id) {
372                         ++world_iter;
373                 }
374                 if (world_iter == world_end) {
375                         // nothing can be done from here
376                         return;
377                 }
378                 if (world_iter->ID() == entity_id) {
379                         if (UpdateEntity(entity_id, pack.Seq())) {
380                                 pack.ReadEntityState(world_iter->GetState(), i);
381                         }
382                 }
383         }
384 }
385
386 bool MasterState::UpdateEntity(uint32_t entity_id, uint16_t seq) {
387         auto entry = update_status.find(entity_id);
388         if (entry == update_status.end()) {
389                 update_status.emplace(entity_id, UpdateStatus{ seq, update_timer.Elapsed() });
390                 return true;
391         }
392
393         int pack_diff = int16_t(seq) - int16_t(entry->second.last_packet);
394         int time_diff = update_timer.Elapsed() - entry->second.last_update;
395         entry->second.last_update = update_timer.Elapsed();
396
397         if (pack_diff > 0 || time_diff > 1500) {
398                 entry->second.last_packet = seq;
399                 return true;
400         } else {
401                 return false;
402         }
403 }
404
405 void MasterState::ClearEntity(uint32_t entity_id) {
406         update_status.erase(entity_id);
407 }
408
409 void MasterState::On(const Packet::PlayerCorrection &pack) {
410         if (!state) {
411                 cout << "got player correction without a player :S" << endl;
412                 Quit();
413                 return;
414         }
415         uint16_t pack_seq;
416         EntityState corrected_state;
417         pack.ReadPacketSeq(pack_seq);
418         pack.ReadPlayerState(corrected_state);
419         state->MergePlayerCorrection(pack_seq, corrected_state);
420 }
421
422 void MasterState::On(const Packet::ChunkBegin &pack) {
423         if (!state) {
424                 cout << "got chunk data, but the world has not been created yet" << endl;
425                 cout << "great, this will totally screw up everything :(" << endl;
426                 return;
427         }
428         state->GetChunkReceiver().Handle(pack);
429 }
430
431 void MasterState::On(const Packet::ChunkData &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 }
441 }