]> git.lizzy.rs Git - minetest.git/blob - src/clientenvironment.cpp
c98b5fc9020a24eaa764bed022b1816e66fe361d
[minetest.git] / src / clientenvironment.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "util/serialize.h"
21 #include "util/pointedthing.h"
22 #include "client.h"
23 #include "clientenvironment.h"
24 #include "clientsimpleobject.h"
25 #include "clientmap.h"
26 #include "scripting_client.h"
27 #include "mapblock_mesh.h"
28 #include "event.h"
29 #include "collision.h"
30 #include "profiler.h"
31 #include "raycast.h"
32 #include "voxelalgorithms.h"
33 #include "settings.h"
34 #include <algorithm>
35 #include "client/renderingengine.h"
36
37 /*
38         ClientEnvironment
39 */
40
41 ClientEnvironment::ClientEnvironment(ClientMap *map,
42         ITextureSource *texturesource, Client *client):
43         Environment(client),
44         m_map(map),
45         m_texturesource(texturesource),
46         m_client(client)
47 {
48         char zero = 0;
49         memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
50 }
51
52 ClientEnvironment::~ClientEnvironment()
53 {
54         // delete active objects
55         for (ClientActiveObjectMap::iterator i = m_active_objects.begin();
56                         i != m_active_objects.end(); ++i) {
57                 delete i->second;
58         }
59
60         for(std::vector<ClientSimpleObject*>::iterator
61                 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
62                 delete *i;
63         }
64
65         // Drop/delete map
66         m_map->drop();
67
68         delete m_local_player;
69 }
70
71 Map & ClientEnvironment::getMap()
72 {
73         return *m_map;
74 }
75
76 ClientMap & ClientEnvironment::getClientMap()
77 {
78         return *m_map;
79 }
80
81 void ClientEnvironment::setLocalPlayer(LocalPlayer *player)
82 {
83         DSTACK(FUNCTION_NAME);
84         /*
85                 It is a failure if already is a local player
86         */
87         FATAL_ERROR_IF(m_local_player != NULL,
88                 "Local player already allocated");
89
90         m_local_player = player;
91 }
92
93 void ClientEnvironment::step(float dtime)
94 {
95         DSTACK(FUNCTION_NAME);
96
97         /* Step time of day */
98         stepTimeOfDay(dtime);
99
100         // Get some settings
101         bool fly_allowed = m_client->checkLocalPrivilege("fly");
102         bool free_move = fly_allowed && g_settings->getBool("free_move");
103
104         // Get local player
105         LocalPlayer *lplayer = getLocalPlayer();
106         assert(lplayer);
107         // collision info queue
108         std::vector<CollisionInfo> player_collisions;
109
110         /*
111                 Get the speed the player is going
112         */
113         bool is_climbing = lplayer->is_climbing;
114
115         f32 player_speed = lplayer->getSpeed().getLength();
116
117         /*
118                 Maximum position increment
119         */
120         //f32 position_max_increment = 0.05*BS;
121         f32 position_max_increment = 0.1*BS;
122
123         // Maximum time increment (for collision detection etc)
124         // time = distance / speed
125         f32 dtime_max_increment = 1;
126         if(player_speed > 0.001)
127                 dtime_max_increment = position_max_increment / player_speed;
128
129         // Maximum time increment is 10ms or lower
130         if(dtime_max_increment > 0.01)
131                 dtime_max_increment = 0.01;
132
133         // Don't allow overly huge dtime
134         if(dtime > 0.5)
135                 dtime = 0.5;
136
137         f32 dtime_downcount = dtime;
138
139         /*
140                 Stuff that has a maximum time increment
141         */
142
143         u32 loopcount = 0;
144         do
145         {
146                 loopcount++;
147
148                 f32 dtime_part;
149                 if(dtime_downcount > dtime_max_increment)
150                 {
151                         dtime_part = dtime_max_increment;
152                         dtime_downcount -= dtime_part;
153                 }
154                 else
155                 {
156                         dtime_part = dtime_downcount;
157                         /*
158                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
159                                 when dtime_part is so small that dtime_downcount -= dtime_part
160                                 does nothing
161                         */
162                         dtime_downcount = 0;
163                 }
164
165                 /*
166                         Handle local player
167                 */
168
169                 {
170                         // Apply physics
171                         if(!free_move && !is_climbing)
172                         {
173                                 // Gravity
174                                 v3f speed = lplayer->getSpeed();
175                                 if(!lplayer->in_liquid)
176                                         speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
177
178                                 // Liquid floating / sinking
179                                 if(lplayer->in_liquid && !lplayer->swimming_vertical)
180                                         speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
181
182                                 // Liquid resistance
183                                 if(lplayer->in_liquid_stable || lplayer->in_liquid)
184                                 {
185                                         // How much the node's viscosity blocks movement, ranges between 0 and 1
186                                         // Should match the scale at which viscosity increase affects other liquid attributes
187                                         const f32 viscosity_factor = 0.3;
188
189                                         v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
190                                         f32 dl = d_wanted.getLength();
191                                         if(dl > lplayer->movement_liquid_fluidity_smooth)
192                                                 dl = lplayer->movement_liquid_fluidity_smooth;
193                                         dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
194
195                                         v3f d = d_wanted.normalize() * dl;
196                                         speed += d;
197                                 }
198
199                                 lplayer->setSpeed(speed);
200                         }
201
202                         /*
203                                 Move the lplayer.
204                                 This also does collision detection.
205                         */
206                         lplayer->move(dtime_part, this, position_max_increment,
207                                 &player_collisions);
208                 }
209         }
210         while(dtime_downcount > 0.001);
211
212         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
213
214         for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
215                 i != player_collisions.end(); ++i) {
216                 CollisionInfo &info = *i;
217                 v3f speed_diff = info.new_speed - info.old_speed;;
218                 // Handle only fall damage
219                 // (because otherwise walking against something in fast_move kills you)
220                 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
221                         continue;
222                 // Get rid of other components
223                 speed_diff.X = 0;
224                 speed_diff.Z = 0;
225                 f32 pre_factor = 1; // 1 hp per node/s
226                 f32 tolerance = BS*14; // 5 without damage
227                 f32 post_factor = 1; // 1 hp per node/s
228                 if(info.type == COLLISION_NODE)
229                 {
230                         const ContentFeatures &f = m_client->ndef()->
231                                 get(m_map->getNodeNoEx(info.node_p));
232                         // Determine fall damage multiplier
233                         int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
234                         pre_factor = 1.0 + (float)addp/100.0;
235                 }
236                 float speed = pre_factor * speed_diff.getLength();
237                 if (speed > tolerance) {
238                         f32 damage_f = (speed - tolerance) / BS * post_factor;
239                         u8 damage = (u8)MYMIN(damage_f + 0.5, 255);
240                         if (damage != 0) {
241                                 damageLocalPlayer(damage, true);
242                                 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
243                                 m_client->event()->put(e);
244                         }
245                 }
246         }
247
248         if (m_client->moddingEnabled()) {
249                 m_script->environment_step(dtime);
250         }
251
252         // Protocol v29 make this behaviour obsolete
253         if (getGameDef()->getProtoVersion() < 29) {
254                 if (m_lava_hurt_interval.step(dtime, 1.0)) {
255                         v3f pf = lplayer->getPosition();
256
257                         // Feet, middle and head
258                         v3s16 p1 = floatToInt(pf + v3f(0, BS * 0.1, 0), BS);
259                         MapNode n1 = m_map->getNodeNoEx(p1);
260                         v3s16 p2 = floatToInt(pf + v3f(0, BS * 0.8, 0), BS);
261                         MapNode n2 = m_map->getNodeNoEx(p2);
262                         v3s16 p3 = floatToInt(pf + v3f(0, BS * 1.6, 0), BS);
263                         MapNode n3 = m_map->getNodeNoEx(p3);
264
265                         u32 damage_per_second = 0;
266                         damage_per_second = MYMAX(damage_per_second,
267                                 m_client->ndef()->get(n1).damage_per_second);
268                         damage_per_second = MYMAX(damage_per_second,
269                                 m_client->ndef()->get(n2).damage_per_second);
270                         damage_per_second = MYMAX(damage_per_second,
271                                 m_client->ndef()->get(n3).damage_per_second);
272
273                         if (damage_per_second != 0)
274                                 damageLocalPlayer(damage_per_second, true);
275                 }
276
277                 /*
278                         Drowning
279                 */
280                 if (m_drowning_interval.step(dtime, 2.0)) {
281                         v3f pf = lplayer->getPosition();
282
283                         // head
284                         v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS);
285                         MapNode n = m_map->getNodeNoEx(p);
286                         ContentFeatures c = m_client->ndef()->get(n);
287                         u8 drowning_damage = c.drowning;
288                         if (drowning_damage > 0 && lplayer->hp > 0) {
289                                 u16 breath = lplayer->getBreath();
290                                 if (breath > 10) {
291                                         breath = 11;
292                                 }
293                                 if (breath > 0) {
294                                         breath -= 1;
295                                 }
296                                 lplayer->setBreath(breath);
297                                 updateLocalPlayerBreath(breath);
298                         }
299
300                         if (lplayer->getBreath() == 0 && drowning_damage > 0) {
301                                 damageLocalPlayer(drowning_damage, true);
302                         }
303                 }
304                 if (m_breathing_interval.step(dtime, 0.5)) {
305                         v3f pf = lplayer->getPosition();
306
307                         // head
308                         v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS);
309                         MapNode n = m_map->getNodeNoEx(p);
310                         ContentFeatures c = m_client->ndef()->get(n);
311                         if (!lplayer->hp) {
312                                 lplayer->setBreath(11);
313                         } else if (c.drowning == 0) {
314                                 u16 breath = lplayer->getBreath();
315                                 if (breath <= 10) {
316                                         breath += 1;
317                                         lplayer->setBreath(breath);
318                                         updateLocalPlayerBreath(breath);
319                                 }
320                         }
321                 }
322         }
323
324         // Update lighting on local player (used for wield item)
325         u32 day_night_ratio = getDayNightRatio();
326         {
327                 // Get node at head
328
329                 // On InvalidPositionException, use this as default
330                 // (day: LIGHT_SUN, night: 0)
331                 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
332
333                 v3s16 p = lplayer->getLightPosition();
334                 node_at_lplayer = m_map->getNodeNoEx(p);
335
336                 u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef());
337                 final_color_blend(&lplayer->light_color, light, day_night_ratio);
338         }
339
340         /*
341                 Step active objects and update lighting of them
342         */
343
344         g_profiler->avg("CEnv: num of objects", m_active_objects.size());
345         bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
346         for (ClientActiveObjectMap::iterator i = m_active_objects.begin();
347                         i != m_active_objects.end(); ++i) {
348                 ClientActiveObject* obj = i->second;
349                 // Step object
350                 obj->step(dtime, this);
351
352                 if(update_lighting)
353                 {
354                         // Update lighting
355                         u8 light = 0;
356                         bool pos_ok;
357
358                         // Get node at head
359                         v3s16 p = obj->getLightPosition();
360                         MapNode n = m_map->getNodeNoEx(p, &pos_ok);
361                         if (pos_ok)
362                                 light = n.getLightBlend(day_night_ratio, m_client->ndef());
363                         else
364                                 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
365
366                         obj->updateLight(light);
367                 }
368         }
369
370         /*
371                 Step and handle simple objects
372         */
373         g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
374         for(std::vector<ClientSimpleObject*>::iterator
375                 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
376                 std::vector<ClientSimpleObject*>::iterator cur = i;
377                 ClientSimpleObject *simple = *cur;
378
379                 simple->step(dtime);
380                 if(simple->m_to_be_removed) {
381                         delete simple;
382                         i = m_simple_objects.erase(cur);
383                 }
384                 else {
385                         ++i;
386                 }
387         }
388 }
389
390 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
391 {
392         m_simple_objects.push_back(simple);
393 }
394
395 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
396 {
397         ClientActiveObject *obj = getActiveObject(id);
398         if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
399                 return (GenericCAO*) obj;
400         else
401                 return NULL;
402 }
403
404 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
405 {
406         ClientActiveObjectMap::iterator n = m_active_objects.find(id);
407         if (n == m_active_objects.end())
408                 return NULL;
409         return n->second;
410 }
411
412 bool isFreeClientActiveObjectId(const u16 id,
413         ClientActiveObjectMap &objects)
414 {
415         if(id == 0)
416                 return false;
417
418         return objects.find(id) == objects.end();
419 }
420
421 u16 getFreeClientActiveObjectId(ClientActiveObjectMap &objects)
422 {
423         //try to reuse id's as late as possible
424         static u16 last_used_id = 0;
425         u16 startid = last_used_id;
426         for(;;) {
427                 last_used_id ++;
428                 if (isFreeClientActiveObjectId(last_used_id, objects))
429                         return last_used_id;
430
431                 if (last_used_id == startid)
432                         return 0;
433         }
434 }
435
436 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
437 {
438         assert(object); // Pre-condition
439         if(object->getId() == 0)
440         {
441                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
442                 if(new_id == 0)
443                 {
444                         infostream<<"ClientEnvironment::addActiveObject(): "
445                                 <<"no free ids available"<<std::endl;
446                         delete object;
447                         return 0;
448                 }
449                 object->setId(new_id);
450         }
451         if (!isFreeClientActiveObjectId(object->getId(), m_active_objects)) {
452                 infostream<<"ClientEnvironment::addActiveObject(): "
453                         <<"id is not free ("<<object->getId()<<")"<<std::endl;
454                 delete object;
455                 return 0;
456         }
457         infostream<<"ClientEnvironment::addActiveObject(): "
458                 <<"added (id="<<object->getId()<<")"<<std::endl;
459         m_active_objects[object->getId()] = object;
460         object->addToScene(m_texturesource);
461         { // Update lighting immediately
462                 u8 light = 0;
463                 bool pos_ok;
464
465                 // Get node at head
466                 v3s16 p = object->getLightPosition();
467                 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
468                 if (pos_ok)
469                         light = n.getLightBlend(getDayNightRatio(), m_client->ndef());
470                 else
471                         light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
472
473                 object->updateLight(light);
474         }
475         return object->getId();
476 }
477
478 void ClientEnvironment::addActiveObject(u16 id, u8 type,
479         const std::string &init_data)
480 {
481         ClientActiveObject* obj =
482                 ClientActiveObject::create((ActiveObjectType) type, m_client, this);
483         if(obj == NULL)
484         {
485                 infostream<<"ClientEnvironment::addActiveObject(): "
486                         <<"id="<<id<<" type="<<type<<": Couldn't create object"
487                         <<std::endl;
488                 return;
489         }
490
491         obj->setId(id);
492
493         try
494         {
495                 obj->initialize(init_data);
496         }
497         catch(SerializationError &e)
498         {
499                 errorstream<<"ClientEnvironment::addActiveObject():"
500                         <<" id="<<id<<" type="<<type
501                         <<": SerializationError in initialize(): "
502                         <<e.what()
503                         <<": init_data="<<serializeJsonString(init_data)
504                         <<std::endl;
505         }
506
507         addActiveObject(obj);
508 }
509
510 void ClientEnvironment::removeActiveObject(u16 id)
511 {
512         verbosestream<<"ClientEnvironment::removeActiveObject(): "
513                 <<"id="<<id<<std::endl;
514         ClientActiveObject* obj = getActiveObject(id);
515         if (obj == NULL) {
516                 infostream<<"ClientEnvironment::removeActiveObject(): "
517                         <<"id="<<id<<" not found"<<std::endl;
518                 return;
519         }
520         obj->removeFromScene(true);
521         delete obj;
522         m_active_objects.erase(id);
523 }
524
525 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
526 {
527         ClientActiveObject *obj = getActiveObject(id);
528         if (obj == NULL) {
529                 infostream << "ClientEnvironment::processActiveObjectMessage():"
530                         << " got message for id=" << id << ", which doesn't exist."
531                         << std::endl;
532                 return;
533         }
534
535         try {
536                 obj->processMessage(data);
537         } catch (SerializationError &e) {
538                 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
539                         << " id=" << id << " type=" << obj->getType()
540                         << " SerializationError in processMessage(): " << e.what()
541                         << std::endl;
542         }
543 }
544
545 /*
546         Callbacks for activeobjects
547 */
548
549 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
550 {
551         LocalPlayer *lplayer = getLocalPlayer();
552         assert(lplayer);
553
554         if (handle_hp) {
555                 if (lplayer->hp > damage)
556                         lplayer->hp -= damage;
557                 else
558                         lplayer->hp = 0;
559         }
560
561         ClientEnvEvent event;
562         event.type = CEE_PLAYER_DAMAGE;
563         event.player_damage.amount = damage;
564         event.player_damage.send_to_server = handle_hp;
565         m_client_event_queue.push(event);
566 }
567
568 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
569 {
570         ClientEnvEvent event;
571         event.type = CEE_PLAYER_BREATH;
572         event.player_breath.amount = breath;
573         m_client_event_queue.push(event);
574 }
575
576 /*
577         Client likes to call these
578 */
579
580 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
581         std::vector<DistanceSortedActiveObject> &dest)
582 {
583         for (ClientActiveObjectMap::iterator i = m_active_objects.begin();
584                         i != m_active_objects.end(); ++i) {
585                 ClientActiveObject* obj = i->second;
586
587                 f32 d = (obj->getPosition() - origin).getLength();
588
589                 if(d > max_d)
590                         continue;
591
592                 DistanceSortedActiveObject dso(obj, d);
593
594                 dest.push_back(dso);
595         }
596 }
597
598 ClientEnvEvent ClientEnvironment::getClientEnvEvent()
599 {
600         FATAL_ERROR_IF(m_client_event_queue.empty(),
601                         "ClientEnvironment::getClientEnvEvent(): queue is empty");
602
603         ClientEnvEvent event = m_client_event_queue.front();
604         m_client_event_queue.pop();
605         return event;
606 }
607
608 void ClientEnvironment::getSelectedActiveObjects(
609         const core::line3d<f32> &shootline_on_map,
610         std::vector<PointedThing> &objects)
611 {
612         std::vector<DistanceSortedActiveObject> allObjects;
613         getActiveObjects(shootline_on_map.start,
614                 shootline_on_map.getLength() + 10.0f, allObjects);
615         const v3f line_vector = shootline_on_map.getVector();
616
617         for (u32 i = 0; i < allObjects.size(); i++) {
618                 ClientActiveObject *obj = allObjects[i].obj;
619                 aabb3f selection_box;
620                 if (!obj->getSelectionBox(&selection_box))
621                         continue;
622                 v3f pos = obj->getPosition();
623                 aabb3f offsetted_box(selection_box.MinEdge + pos,
624                         selection_box.MaxEdge + pos);
625
626                 v3f current_intersection;
627                 v3s16 current_normal;
628                 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
629                                 &current_intersection, &current_normal)) {
630                         objects.push_back(PointedThing(
631                                 (s16) obj->getId(), current_intersection, current_normal,
632                                 (current_intersection - shootline_on_map.start).getLengthSQ()));
633                 }
634         }
635 }