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"
39 #include "client/renderingengine.h"
42 CAOShaderConstantSetter
45 //! Shader constant setter for passing material emissive color to the CAO object_shader
46 class CAOShaderConstantSetter : public IShaderConstantSetter
49 CAOShaderConstantSetter():
50 m_emissive_color_setting("emissiveColor")
53 ~CAOShaderConstantSetter() override = default;
55 void onSetConstants(video::IMaterialRendererServices *services) override
58 video::SColorf emissive_color(m_emissive_color);
66 m_emissive_color_setting.set(as_array, services);
69 void onSetMaterial(const video::SMaterial& material) override
71 m_emissive_color = material.EmissiveColor;
75 video::SColor m_emissive_color;
76 CachedPixelShaderSetting<float, 4> m_emissive_color_setting;
79 class CAOShaderConstantSetterFactory : public IShaderConstantSetterFactory
82 CAOShaderConstantSetterFactory()
85 virtual IShaderConstantSetter* create()
87 return new CAOShaderConstantSetter();
95 ClientEnvironment::ClientEnvironment(ClientMap *map,
96 ITextureSource *texturesource, Client *client):
99 m_texturesource(texturesource),
102 auto *shdrsrc = m_client->getShaderSource();
103 shdrsrc->addShaderConstantSetterFactory(new CAOShaderConstantSetterFactory());
106 ClientEnvironment::~ClientEnvironment()
108 m_ao_manager.clear();
110 for (auto &simple_object : m_simple_objects) {
111 delete simple_object;
117 delete m_local_player;
120 Map & ClientEnvironment::getMap()
125 ClientMap & ClientEnvironment::getClientMap()
130 void ClientEnvironment::setLocalPlayer(LocalPlayer *player)
133 It is a failure if already is a local player
135 FATAL_ERROR_IF(m_local_player != NULL,
136 "Local player already allocated");
138 m_local_player = player;
141 void ClientEnvironment::step(float dtime)
143 /* Step time of day */
144 stepTimeOfDay(dtime);
147 bool fly_allowed = m_client->checkLocalPrivilege("fly");
148 bool free_move = fly_allowed && g_settings->getBool("free_move");
151 LocalPlayer *lplayer = getLocalPlayer();
153 // collision info queue
154 std::vector<CollisionInfo> player_collisions;
157 Get the speed the player is going
159 bool is_climbing = lplayer->is_climbing;
161 f32 player_speed = lplayer->getSpeed().getLength();
164 Maximum position increment
166 //f32 position_max_increment = 0.05*BS;
167 f32 position_max_increment = 0.1*BS;
169 // Maximum time increment (for collision detection etc)
170 // time = distance / speed
171 f32 dtime_max_increment = 1;
172 if(player_speed > 0.001)
173 dtime_max_increment = position_max_increment / player_speed;
175 // Maximum time increment is 10ms or lower
176 if(dtime_max_increment > 0.01)
177 dtime_max_increment = 0.01;
179 // Don't allow overly huge dtime
184 Stuff that has a maximum time increment
187 u32 steps = ceil(dtime / dtime_max_increment);
188 f32 dtime_part = dtime / steps;
189 for (; steps > 0; --steps) {
191 Local player handling
194 // Control local player
195 lplayer->applyControl(dtime_part, this);
198 lplayer->gravity = 0;
201 if (!is_climbing && !lplayer->in_liquid)
202 // HACK the factor 2 for gravity is arbitrary and should be removed eventually
203 lplayer->gravity = 2 * lplayer->movement_gravity * lplayer->physics_override.gravity;
205 // Liquid floating / sinking
206 if (!is_climbing && lplayer->in_liquid &&
207 !lplayer->swimming_vertical &&
208 !lplayer->swimming_pitch)
209 // HACK the factor 2 for gravity is arbitrary and should be removed eventually
210 lplayer->gravity = 2 * lplayer->movement_liquid_sink;
212 // Movement resistance
213 if (lplayer->move_resistance > 0) {
214 v3f speed = lplayer->getSpeed();
216 // How much the node's move_resistance blocks movement, ranges
217 // between 0 and 1. Should match the scale at which liquid_viscosity
218 // increase affects other liquid attributes.
219 static const f32 resistance_factor = 0.3f;
222 bool in_liquid_stable = lplayer->in_liquid_stable || lplayer->in_liquid;
223 if (in_liquid_stable) {
224 d_wanted = -speed / lplayer->movement_liquid_fluidity;
226 d_wanted = -speed / BS;
228 f32 dl = d_wanted.getLength();
229 if (in_liquid_stable) {
230 if (dl > lplayer->movement_liquid_fluidity_smooth)
231 dl = lplayer->movement_liquid_fluidity_smooth;
234 dl *= (lplayer->move_resistance * resistance_factor) +
235 (1 - resistance_factor);
236 v3f d = d_wanted.normalize() * (dl * dtime_part * 100.0f);
239 lplayer->setSpeed(speed);
245 This also does collision detection.
248 lplayer->move(dtime_part, this, position_max_increment,
252 bool player_immortal = false;
253 f32 player_fall_factor = 1.0f;
254 GenericCAO *playercao = lplayer->getCAO();
256 player_immortal = playercao->isImmortal();
257 int addp_p = itemgroup_get(playercao->getGroups(),
258 "fall_damage_add_percent");
259 // convert armor group into an usable fall damage factor
260 player_fall_factor = 1.0f + (float)addp_p / 100.0f;
263 for (const CollisionInfo &info : player_collisions) {
264 v3f speed_diff = info.new_speed - info.old_speed;;
265 // Handle only fall damage
266 // (because otherwise walking against something in fast_move kills you)
267 if (speed_diff.Y < 0 || info.old_speed.Y >= 0)
269 // Get rid of other components
272 f32 pre_factor = 1; // 1 hp per node/s
273 f32 tolerance = BS*14; // 5 without damage
274 if (info.type == COLLISION_NODE) {
275 const ContentFeatures &f = m_client->ndef()->
276 get(m_map->getNode(info.node_p));
277 // Determine fall damage modifier
278 int addp_n = itemgroup_get(f.groups, "fall_damage_add_percent");
279 // convert node group to an usable fall damage factor
280 f32 node_fall_factor = 1.0f + (float)addp_n / 100.0f;
281 // combine both player fall damage modifiers
282 pre_factor = node_fall_factor * player_fall_factor;
284 float speed = pre_factor * speed_diff.getLength();
286 if (speed > tolerance && !player_immortal && pre_factor > 0.0f) {
287 f32 damage_f = (speed - tolerance) / BS;
288 u16 damage = (u16)MYMIN(damage_f + 0.5, U16_MAX);
290 damageLocalPlayer(damage, true);
291 m_client->getEventManager()->put(
292 new SimpleTriggerEvent(MtEvent::PLAYER_FALLING_DAMAGE));
297 if (m_client->modsLoaded())
298 m_script->environment_step(dtime);
300 // Update lighting on local player (used for wield item)
301 u32 day_night_ratio = getDayNightRatio();
305 // On InvalidPositionException, use this as default
306 // (day: LIGHT_SUN, night: 0)
307 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
309 v3s16 p = lplayer->getLightPosition();
310 node_at_lplayer = m_map->getNode(p);
312 u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef());
313 lplayer->light_color = encode_light(light, 0); // this transfers light.alpha
314 final_color_blend(&lplayer->light_color, light, day_night_ratio);
318 Step active objects and update lighting of them
321 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
322 auto cb_state = [this, dtime, update_lighting, day_night_ratio] (ClientActiveObject *cao) {
324 cao->step(dtime, this);
327 cao->updateLight(day_night_ratio);
330 m_ao_manager.step(dtime, cb_state);
333 Step and handle simple objects
335 g_profiler->avg("ClientEnv: CSO count [#]", m_simple_objects.size());
336 for (auto i = m_simple_objects.begin(); i != m_simple_objects.end();) {
337 ClientSimpleObject *simple = *i;
340 if(simple->m_to_be_removed) {
342 i = m_simple_objects.erase(i);
350 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
352 m_simple_objects.push_back(simple);
355 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
357 ClientActiveObject *obj = getActiveObject(id);
358 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
359 return (GenericCAO*) obj;
364 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
366 // Register object. If failed return zero id
367 if (!m_ao_manager.registerObject(object))
370 object->addToScene(m_texturesource, m_client->getSceneManager());
372 // Update lighting immediately
373 object->updateLight(getDayNightRatio());
374 return object->getId();
377 void ClientEnvironment::addActiveObject(u16 id, u8 type,
378 const std::string &init_data)
380 ClientActiveObject* obj =
381 ClientActiveObject::create((ActiveObjectType) type, m_client, this);
384 infostream<<"ClientEnvironment::addActiveObject(): "
385 <<"id="<<id<<" type="<<type<<": Couldn't create object"
394 obj->initialize(init_data);
396 catch(SerializationError &e)
398 errorstream<<"ClientEnvironment::addActiveObject():"
399 <<" id="<<id<<" type="<<type
400 <<": SerializationError in initialize(): "
402 <<": init_data="<<serializeJsonString(init_data)
406 u16 new_id = addActiveObject(obj);
407 // Object initialized:
408 if ((obj = getActiveObject(new_id))) {
409 // Final step is to update all children which are already known
410 // Data provided by AO_CMD_SPAWN_INFANT
411 const auto &children = obj->getAttachmentChildIds();
412 for (auto c_id : children) {
413 if (auto *o = getActiveObject(c_id))
414 o->updateAttachments();
420 void ClientEnvironment::removeActiveObject(u16 id)
422 // Get current attachment childs to detach them visually
423 std::unordered_set<int> attachment_childs;
424 if (auto *obj = getActiveObject(id))
425 attachment_childs = obj->getAttachmentChildIds();
427 m_ao_manager.removeObject(id);
429 // Perform a proper detach in Irrlicht
430 for (auto c_id : attachment_childs) {
431 if (ClientActiveObject *child = getActiveObject(c_id))
432 child->updateAttachments();
436 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
438 ClientActiveObject *obj = getActiveObject(id);
440 infostream << "ClientEnvironment::processActiveObjectMessage():"
441 << " got message for id=" << id << ", which doesn't exist."
447 obj->processMessage(data);
448 } catch (SerializationError &e) {
449 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
450 << " id=" << id << " type=" << obj->getType()
451 << " SerializationError in processMessage(): " << e.what()
457 Callbacks for activeobjects
460 void ClientEnvironment::damageLocalPlayer(u16 damage, bool handle_hp)
462 LocalPlayer *lplayer = getLocalPlayer();
466 if (lplayer->hp > damage)
467 lplayer->hp -= damage;
472 ClientEnvEvent event;
473 event.type = CEE_PLAYER_DAMAGE;
474 event.player_damage.amount = damage;
475 event.player_damage.send_to_server = handle_hp;
476 m_client_event_queue.push(event);
480 Client likes to call these
483 ClientEnvEvent ClientEnvironment::getClientEnvEvent()
485 FATAL_ERROR_IF(m_client_event_queue.empty(),
486 "ClientEnvironment::getClientEnvEvent(): queue is empty");
488 ClientEnvEvent event = m_client_event_queue.front();
489 m_client_event_queue.pop();
493 void ClientEnvironment::getSelectedActiveObjects(
494 const core::line3d<f32> &shootline_on_map,
495 std::vector<PointedThing> &objects)
497 std::vector<DistanceSortedActiveObject> allObjects;
498 m_ao_manager.getActiveSelectableObjects(shootline_on_map, allObjects);
499 const v3f line_vector = shootline_on_map.getVector();
501 for (const auto &allObject : allObjects) {
502 ClientActiveObject *obj = allObject.obj;
503 aabb3f selection_box;
504 if (!obj->getSelectionBox(&selection_box))
507 v3f current_intersection;
508 v3f current_normal, current_raw_normal;
509 const v3f rel_pos = shootline_on_map.start - obj->getPosition();
511 GenericCAO* gcao = dynamic_cast<GenericCAO*>(obj);
512 if (gcao != nullptr && gcao->getProperties().rotate_selectionbox) {
513 gcao->getSceneNode()->updateAbsolutePosition();
514 const v3f deg = obj->getSceneNode()->getAbsoluteTransformation().getRotationDegrees();
515 collision = boxLineCollision(selection_box, deg,
516 rel_pos, line_vector, ¤t_intersection, ¤t_normal, ¤t_raw_normal);
518 collision = boxLineCollision(selection_box, rel_pos, line_vector,
519 ¤t_intersection, ¤t_normal);
520 current_raw_normal = current_normal;
523 current_intersection += obj->getPosition();
524 objects.emplace_back(obj->getId(), current_intersection, current_normal, current_raw_normal,
525 (current_intersection - shootline_on_map.start).getLengthSQ());
530 void ClientEnvironment::updateFrameTime(bool is_paused)
532 // if paused, m_frame_time_pause_accumulator increases by dtime,
533 // otherwise, m_frame_time increases by dtime
536 m_frame_time_pause_accumulator = porting::getTimeMs() - m_frame_time;
539 auto new_frame_time = porting::getTimeMs() - m_frame_time_pause_accumulator;
540 m_frame_dtime = new_frame_time - MYMAX(m_frame_time, m_frame_time_pause_accumulator);
541 m_frame_time = new_frame_time;