3 Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "util/serialize.h"
21 #include "util/pointedthing.h"
23 #include "clientenvironment.h"
24 #include "clientsimpleobject.h"
25 #include "clientmap.h"
26 #include "scripting_client.h"
27 #include "mapblock_mesh.h"
29 #include "collision.h"
33 #include "voxelalgorithms.h"
36 #include "content_cao.h"
38 #include "client/renderingengine.h"
41 CAOShaderConstantSetter
44 //! Shader constant setter for passing material emissive color to the CAO object_shader
45 class CAOShaderConstantSetter
: public IShaderConstantSetter
48 CAOShaderConstantSetter():
49 m_emissive_color_setting("emissiveColor")
52 ~CAOShaderConstantSetter() override
= default;
54 void onSetConstants(video::IMaterialRendererServices
*services
,
55 bool is_highlevel
) override
61 video::SColorf
emissive_color(m_emissive_color
);
69 m_emissive_color_setting
.set(as_array
, services
);
72 void onSetMaterial(const video::SMaterial
& material
) override
74 m_emissive_color
= material
.EmissiveColor
;
78 video::SColor m_emissive_color
;
79 CachedPixelShaderSetting
<float, 4> m_emissive_color_setting
;
82 class CAOShaderConstantSetterFactory
: public IShaderConstantSetterFactory
85 CAOShaderConstantSetterFactory()
88 virtual IShaderConstantSetter
* create()
90 return new CAOShaderConstantSetter();
98 ClientEnvironment::ClientEnvironment(ClientMap
*map
,
99 ITextureSource
*texturesource
, Client
*client
):
102 m_texturesource(texturesource
),
105 auto *shdrsrc
= m_client
->getShaderSource();
106 shdrsrc
->addShaderConstantSetterFactory(new CAOShaderConstantSetterFactory());
109 ClientEnvironment::~ClientEnvironment()
111 m_ao_manager
.clear();
113 for (auto &simple_object
: m_simple_objects
) {
114 delete simple_object
;
120 delete m_local_player
;
123 Map
& ClientEnvironment::getMap()
128 ClientMap
& ClientEnvironment::getClientMap()
133 void ClientEnvironment::setLocalPlayer(LocalPlayer
*player
)
136 It is a failure if already is a local player
138 FATAL_ERROR_IF(m_local_player
!= NULL
,
139 "Local player already allocated");
141 m_local_player
= player
;
144 void ClientEnvironment::step(float dtime
)
146 /* Step time of day */
147 stepTimeOfDay(dtime
);
150 bool fly_allowed
= m_client
->checkLocalPrivilege("fly") || g_settings
->getBool("freecam");
151 bool free_move
= (fly_allowed
&& g_settings
->getBool("free_move")) || g_settings
->getBool("freecam");
154 LocalPlayer
*lplayer
= getLocalPlayer();
156 // collision info queue
157 std::vector
<CollisionInfo
> player_collisions
;
160 Get the speed the player is going
162 bool is_climbing
= lplayer
->is_climbing
;
164 f32 player_speed
= lplayer
->getSpeed().getLength();
167 Maximum position increment
169 //f32 position_max_increment = 0.05*BS;
170 f32 position_max_increment
= 0.1*BS
;
172 // Maximum time increment (for collision detection etc)
173 // time = distance / speed
174 f32 dtime_max_increment
= 1;
175 if(player_speed
> 0.001)
176 dtime_max_increment
= position_max_increment
/ player_speed
;
178 // Maximum time increment is 10ms or lower
179 if(dtime_max_increment
> 0.01)
180 dtime_max_increment
= 0.01;
182 // Don't allow overly huge dtime
187 Stuff that has a maximum time increment
190 u32 steps
= ceil(dtime
/ dtime_max_increment
);
191 f32 dtime_part
= dtime
/ steps
;
192 for (; steps
> 0; --steps
) {
194 Local player handling
197 // Control local player
198 lplayer
->applyControl(dtime_part
, this);
201 if (!free_move
&& !is_climbing
&& !g_settings
->getBool("freecam")) {
203 v3f speed
= lplayer
->getSpeed();
204 if (!lplayer
->in_liquid
)
205 speed
.Y
-= lplayer
->movement_gravity
*
206 lplayer
->physics_override_gravity
* dtime_part
* 2.0f
;
208 // Liquid floating / sinking
209 if (lplayer
->in_liquid
&& !lplayer
->swimming_vertical
&&
210 !lplayer
->swimming_pitch
)
211 speed
.Y
-= lplayer
->movement_liquid_sink
* dtime_part
* 2.0f
;
214 if (lplayer
->in_liquid_stable
|| lplayer
->in_liquid
) {
215 // How much the node's viscosity blocks movement, ranges
216 // between 0 and 1. Should match the scale at which viscosity
217 // increase affects other liquid attributes.
218 static const f32 viscosity_factor
= 0.3f
;
220 v3f d_wanted
= -speed
/ lplayer
->movement_liquid_fluidity
;
221 f32 dl
= d_wanted
.getLength();
222 if (dl
> lplayer
->movement_liquid_fluidity_smooth
)
223 dl
= lplayer
->movement_liquid_fluidity_smooth
;
225 dl
*= (lplayer
->liquid_viscosity
* viscosity_factor
) +
226 (1 - viscosity_factor
);
227 v3f d
= d_wanted
.normalize() * (dl
* dtime_part
* 100.0f
);
231 lplayer
->setSpeed(speed
);
236 This also does collision detection.
238 lplayer
->move(dtime_part
, this, position_max_increment
,
242 bool player_immortal
= lplayer
->getCAO() && lplayer
->getCAO()->isImmortal();
244 for (const CollisionInfo
&info
: player_collisions
) {
245 v3f speed_diff
= info
.new_speed
- info
.old_speed
;;
246 // Handle only fall damage
247 // (because otherwise walking against something in fast_move kills you)
248 if (speed_diff
.Y
< 0 || info
.old_speed
.Y
>= 0)
250 // Get rid of other components
253 f32 pre_factor
= 1; // 1 hp per node/s
254 f32 tolerance
= BS
*14; // 5 without damage
255 f32 post_factor
= 1; // 1 hp per node/s
256 if (info
.type
== COLLISION_NODE
) {
257 const ContentFeatures
&f
= m_client
->ndef()->
258 get(m_map
->getNode(info
.node_p
));
259 // Determine fall damage multiplier
260 int addp
= itemgroup_get(f
.groups
, "fall_damage_add_percent");
261 pre_factor
= 1.0f
+ (float)addp
/ 100.0f
;
263 float speed
= pre_factor
* speed_diff
.getLength();
264 if (speed
> tolerance
&& !player_immortal
) {
265 f32 damage_f
= (speed
- tolerance
) / BS
* post_factor
;
266 u16 damage
= (u16
)MYMIN(damage_f
+ 0.5, U16_MAX
);
267 if (!g_settings
->getBool("prevent_natural_damage") && damage
!= 0) {
268 damageLocalPlayer(damage
, true);
269 m_client
->getEventManager()->put(
270 new SimpleTriggerEvent(MtEvent::PLAYER_FALLING_DAMAGE
));
275 if (m_client
->modsLoaded())
276 m_script
->environment_step(dtime
);
278 // Update lighting on local player (used for wield item)
279 u32 day_night_ratio
= getDayNightRatio();
283 // On InvalidPositionException, use this as default
284 // (day: LIGHT_SUN, night: 0)
285 MapNode
node_at_lplayer(CONTENT_AIR
, 0x0f, 0);
287 v3s16 p
= lplayer
->getLightPosition();
288 node_at_lplayer
= m_map
->getNode(p
);
290 u16 light
= getInteriorLight(node_at_lplayer
, 0, m_client
->ndef());
291 final_color_blend(&lplayer
->light_color
, light
, day_night_ratio
);
295 Step active objects and update lighting of them
298 bool update_lighting
= m_active_object_light_update_interval
.step(dtime
, 0.21);
299 auto cb_state
= [this, dtime
, update_lighting
, day_night_ratio
] (ClientActiveObject
*cao
) {
301 cao
->step(dtime
, this);
304 cao
->updateLight(day_night_ratio
);
307 m_ao_manager
.step(dtime
, cb_state
);
310 Step and handle simple objects
312 g_profiler
->avg("ClientEnv: CSO count [#]", m_simple_objects
.size());
313 for (auto i
= m_simple_objects
.begin(); i
!= m_simple_objects
.end();) {
314 ClientSimpleObject
*simple
= *i
;
317 if(simple
->m_to_be_removed
) {
319 i
= m_simple_objects
.erase(i
);
327 void ClientEnvironment::addSimpleObject(ClientSimpleObject
*simple
)
329 m_simple_objects
.push_back(simple
);
332 GenericCAO
* ClientEnvironment::getGenericCAO(u16 id
)
334 ClientActiveObject
*obj
= getActiveObject(id
);
335 if (obj
&& obj
->getType() == ACTIVEOBJECT_TYPE_GENERIC
)
336 return (GenericCAO
*) obj
;
341 bool isFreeClientActiveObjectId(const u16 id
,
342 ClientActiveObjectMap
&objects
)
344 return id
!= 0 && objects
.find(id
) == objects
.end();
348 u16
ClientEnvironment::addActiveObject(ClientActiveObject
*object
)
350 // Register object. If failed return zero id
351 if (!m_ao_manager
.registerObject(object
))
354 object
->addToScene(m_texturesource
);
356 // Update lighting immediately
357 object
->updateLight(getDayNightRatio());
358 return object
->getId();
361 void ClientEnvironment::addActiveObject(u16 id
, u8 type
,
362 const std::string
&init_data
)
364 ClientActiveObject
* obj
=
365 ClientActiveObject::create((ActiveObjectType
) type
, m_client
, this);
368 infostream
<<"ClientEnvironment::addActiveObject(): "
369 <<"id="<<id
<<" type="<<type
<<": Couldn't create object"
378 obj
->initialize(init_data
);
380 catch(SerializationError
&e
)
382 errorstream
<<"ClientEnvironment::addActiveObject():"
383 <<" id="<<id
<<" type="<<type
384 <<": SerializationError in initialize(): "
386 <<": init_data="<<serializeJsonString(init_data
)
390 u16 new_id
= addActiveObject(obj
);
391 // Object initialized:
392 if ((obj
= getActiveObject(new_id
))) {
393 // Final step is to update all children which are already known
394 // Data provided by AO_CMD_SPAWN_INFANT
395 const auto &children
= obj
->getAttachmentChildIds();
396 for (auto c_id
: children
) {
397 if (auto *o
= getActiveObject(c_id
))
398 o
->updateAttachments();
404 void ClientEnvironment::removeActiveObject(u16 id
)
406 // Get current attachment childs to detach them visually
407 std::unordered_set
<int> attachment_childs
;
408 if (auto *obj
= getActiveObject(id
))
409 attachment_childs
= obj
->getAttachmentChildIds();
411 m_ao_manager
.removeObject(id
);
413 // Perform a proper detach in Irrlicht
414 for (auto c_id
: attachment_childs
) {
415 if (ClientActiveObject
*child
= getActiveObject(c_id
))
416 child
->updateAttachments();
420 void ClientEnvironment::processActiveObjectMessage(u16 id
, const std::string
&data
)
422 ClientActiveObject
*obj
= getActiveObject(id
);
424 infostream
<< "ClientEnvironment::processActiveObjectMessage():"
425 << " got message for id=" << id
<< ", which doesn't exist."
431 obj
->processMessage(data
);
432 } catch (SerializationError
&e
) {
433 errorstream
<<"ClientEnvironment::processActiveObjectMessage():"
434 << " id=" << id
<< " type=" << obj
->getType()
435 << " SerializationError in processMessage(): " << e
.what()
441 Callbacks for activeobjects
444 void ClientEnvironment::damageLocalPlayer(u16 damage
, bool handle_hp
)
446 LocalPlayer
*lplayer
= getLocalPlayer();
450 if (lplayer
->hp
> damage
)
451 lplayer
->hp
-= damage
;
456 ClientEnvEvent event
;
457 event
.type
= CEE_PLAYER_DAMAGE
;
458 event
.player_damage
.amount
= damage
;
459 event
.player_damage
.send_to_server
= handle_hp
;
460 m_client_event_queue
.push(event
);
464 Client likes to call these
467 ClientEnvEvent
ClientEnvironment::getClientEnvEvent()
469 FATAL_ERROR_IF(m_client_event_queue
.empty(),
470 "ClientEnvironment::getClientEnvEvent(): queue is empty");
472 ClientEnvEvent event
= m_client_event_queue
.front();
473 m_client_event_queue
.pop();
477 void ClientEnvironment::getSelectedActiveObjects(
478 const core::line3d
<f32
> &shootline_on_map
,
479 std::vector
<PointedThing
> &objects
)
481 std::vector
<DistanceSortedActiveObject
> allObjects
;
482 getActiveObjects(shootline_on_map
.start
,
483 shootline_on_map
.getLength() + 10.0f
, allObjects
);
484 const v3f line_vector
= shootline_on_map
.getVector();
486 for (const auto &allObject
: allObjects
) {
487 ClientActiveObject
*obj
= allObject
.obj
;
488 aabb3f selection_box
;
489 if (!obj
->getSelectionBox(&selection_box
))
492 const v3f
&pos
= obj
->getPosition();
493 aabb3f
offsetted_box(selection_box
.MinEdge
+ pos
,
494 selection_box
.MaxEdge
+ pos
);
496 v3f current_intersection
;
497 v3s16 current_normal
;
498 if (boxLineCollision(offsetted_box
, shootline_on_map
.start
, line_vector
,
499 ¤t_intersection
, ¤t_normal
)) {
500 objects
.emplace_back((s16
) obj
->getId(), current_intersection
, current_normal
,
501 (current_intersection
- shootline_on_map
.start
).getLengthSQ());