void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data) { ClientActiveObject* obj = getActiveObject(id); if(obj == NULL) { dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():" <<" got message for id="<<id<<", which doesn't exist." <<std::endl; return; } obj->processMessage(data); }
void ClientEnvironment::removeActiveObject(u16 id) { verbosestream<<"ClientEnvironment::removeActiveObject(): " <<"id="<<id<<std::endl; ClientActiveObject* obj = getActiveObject(id); if (obj == NULL) { infostream<<"ClientEnvironment::removeActiveObject(): " <<"id="<<id<<" not found"<<std::endl; return; } obj->removeFromScene(true); delete obj; m_active_objects.erase(id); }
// clang-format on void ActiveObjectMgr::getActiveObjects(const v3f &origin, f32 max_d, std::vector<DistanceSortedActiveObject> &dest) { for (auto &ao_it : m_active_objects) { ClientActiveObject *obj = ao_it.second; f32 d = (obj->getPosition() - origin).getLength(); if (d > max_d) continue; dest.emplace_back(obj, d); } }
void ClientEnvironment::removeActiveObject(u16 id) { dstream<<"ClientEnvironment::removeActiveObject(): " <<"id="<<id<<std::endl; ClientActiveObject* obj = getActiveObject(id); if(obj == NULL) { dstream<<"WARNING: ClientEnvironment::removeActiveObject(): " <<"id="<<id<<" not found"<<std::endl; return; } obj->removeFromScene(); delete obj; m_active_objects.remove(id); }
void ActiveObjectMgr::removeObject(u16 id) { verbosestream << "Client::ActiveObjectMgr::removeObject(): " << "id=" << id << std::endl; ClientActiveObject *obj = getActiveObject(id); if (!obj) { infostream << "Client::ActiveObjectMgr::removeObject(): " << "id=" << id << " not found" << std::endl; return; } m_active_objects.erase(id); obj->removeFromScene(true); delete obj; }
void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, std::vector<DistanceSortedActiveObject> &dest) { for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin(); i != m_active_objects.end(); ++i) { ClientActiveObject* obj = i->second; f32 d = (obj->getPosition() - origin).getLength(); if(d > max_d) continue; DistanceSortedActiveObject dso(obj, d); dest.push_back(dso); } }
void ClientEnvironment::addActiveObject(u16 id, u8 type, const std::string &init_data) { ClientActiveObject* obj = ClientActiveObject::create(type); if(obj == NULL) { dstream<<"WARNING: ClientEnvironment::addActiveObject(): " <<"id="<<id<<" type="<<type<<": Couldn't create object" <<std::endl; return; } obj->setId(id); addActiveObject(obj); obj->initialize(init_data); }
void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, core::array<DistanceSortedActiveObject> &dest) { for(core::map<u16, ClientActiveObject*>::Iterator i = m_active_objects.getIterator(); i.atEnd()==false; i++) { ClientActiveObject* obj = i.getNode()->getValue(); f32 d = (obj->getPosition() - origin).getLength(); if(d > max_d) continue; DistanceSortedActiveObject dso(obj, d); dest.push_back(dso); } }
void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data) { ClientActiveObject *obj = getActiveObject(id); if (obj == NULL) { infostream << "ClientEnvironment::processActiveObjectMessage():" << " got message for id=" << id << ", which doesn't exist." << std::endl; return; } try { obj->processMessage(data); } catch (SerializationError &e) { errorstream<<"ClientEnvironment::processActiveObjectMessage():" << " id=" << id << " type=" << obj->getType() << " SerializationError in processMessage(): " << e.what() << std::endl; } }
ClientActiveObject * Client::getSelectedActiveObject( f32 max_d, v3f from_pos_f_on_map, core::line3d<f32> shootline_on_map ) { core::array<DistanceSortedActiveObject> objects; m_env.getActiveObjects(from_pos_f_on_map, max_d, objects); //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl; // Sort them. // After this, the closest object is the first in the array. objects.sort(); for(u32 i=0; i<objects.size(); i++) { ClientActiveObject *obj = objects[i].obj; core::aabbox3d<f32> *selection_box = obj->getSelectionBox(); if(selection_box == NULL) continue; v3f pos = obj->getPosition(); core::aabbox3d<f32> offsetted_box( selection_box->MinEdge + pos, selection_box->MaxEdge + pos ); if(offsetted_box.intersectsWithLine(shootline_on_map)) { //infostream<<"Returning selected object"<<std::endl; return obj; } } //infostream<<"No object selected; returning NULL."<<std::endl; return NULL; }
ClientActiveObject * Client::getSelectedActiveObject( f32 max_d, v3f from_pos_f_on_map, core::line3d<f32> shootline_on_map ) { std::vector<DistanceSortedActiveObject> objects; m_env.getActiveObjects(from_pos_f_on_map, max_d, objects); // Sort them. // After this, the closest object is the first in the array. std::sort(objects.begin(), objects.end()); for(unsigned int i=0; i<objects.size(); i++) { ClientActiveObject *obj = objects[i].obj; core::aabbox3d<f32> *selection_box = obj->getSelectionBox(); if(selection_box == NULL) continue; v3f pos = obj->getPosition(); core::aabbox3d<f32> offsetted_box( selection_box->MinEdge + pos, selection_box->MaxEdge + pos ); if(offsetted_box.intersectsWithLine(shootline_on_map)) { return obj; } } return NULL; }
void Client::step(float dtime) { DSTACK(FUNCTION_NAME); // Limit a bit if(dtime > 2.0) dtime = 2.0; if(m_ignore_damage_timer > dtime) m_ignore_damage_timer -= dtime; else m_ignore_damage_timer = 0.0; m_animation_time += dtime; if(m_animation_time > 60.0) m_animation_time -= 60.0; m_time_of_day_update_timer += dtime; ReceiveAll(); /* Packet counter */ { float &counter = m_packetcounter_timer; counter -= dtime; if(counter <= 0.0) { counter = 20.0; infostream << "Client packetcounter (" << m_packetcounter_timer << "):"<<std::endl; m_packetcounter.print(infostream); m_packetcounter.clear(); } } // UGLY hack to fix 2 second startup delay caused by non existent // server client startup synchronization in local server or singleplayer mode static bool initial_step = true; if (initial_step) { initial_step = false; } else if(m_state == LC_Created) { float &counter = m_connection_reinit_timer; counter -= dtime; if(counter <= 0.0) { counter = 2.0; Player *myplayer = m_env.getLocalPlayer(); FATAL_ERROR_IF(myplayer == NULL, "Local player not found in environment."); // Send TOSERVER_INIT_LEGACY // [0] u16 TOSERVER_INIT_LEGACY // [2] u8 SER_FMT_VER_HIGHEST_READ // [3] u8[20] player_name // [23] u8[28] password (new in some version) // [51] u16 minimum supported network protocol version (added sometime) // [53] u16 maximum supported network protocol version (added later than the previous one) char pName[PLAYERNAME_SIZE]; char pPassword[PASSWORD_SIZE]; memset(pName, 0, PLAYERNAME_SIZE * sizeof(char)); memset(pPassword, 0, PASSWORD_SIZE * sizeof(char)); std::string hashed_password = translatePassword(myplayer->getName(), m_password); snprintf(pName, PLAYERNAME_SIZE, "%s", myplayer->getName()); snprintf(pPassword, PASSWORD_SIZE, "%s", hashed_password.c_str()); sendLegacyInit(pName, pPassword); if (LATEST_PROTOCOL_VERSION >= 25) sendInit(myplayer->getName()); } // Not connected, return return; } /* Do stuff if connected */ /* Run Map's timers and unload unused data */ const float map_timer_and_unload_dtime = 5.25; if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) { ScopeProfiler sp(g_profiler, "Client: map timer and unload"); std::vector<v3s16> deleted_blocks; m_env.getMap().timerUpdate(map_timer_and_unload_dtime, g_settings->getFloat("client_unload_unused_data_timeout"), g_settings->getS32("client_mapblock_limit"), &deleted_blocks); /* Send info to server NOTE: This loop is intentionally iterated the way it is. */ std::vector<v3s16>::iterator i = deleted_blocks.begin(); std::vector<v3s16> sendlist; for(;;) { if(sendlist.size() == 255 || i == deleted_blocks.end()) { if(sendlist.empty()) break; /* [0] u16 command [2] u8 count [3] v3s16 pos_0 [3+6] v3s16 pos_1 ... */ sendDeletedBlocks(sendlist); if(i == deleted_blocks.end()) break; sendlist.clear(); } sendlist.push_back(*i); ++i; } } /* Handle environment */ // Control local player (0ms) LocalPlayer *player = m_env.getLocalPlayer(); assert(player != NULL); player->applyControl(dtime); // Step environment m_env.step(dtime); /* Get events */ for(;;) { ClientEnvEvent event = m_env.getClientEvent(); if(event.type == CEE_NONE) { break; } else if(event.type == CEE_PLAYER_DAMAGE) { if(m_ignore_damage_timer <= 0) { u8 damage = event.player_damage.amount; if(event.player_damage.send_to_server) sendDamage(damage); // Add to ClientEvent queue ClientEvent event; event.type = CE_PLAYER_DAMAGE; event.player_damage.amount = damage; m_client_event_queue.push(event); } } else if(event.type == CEE_PLAYER_BREATH) { u16 breath = event.player_breath.amount; sendBreath(breath); } } /* Print some info */ float &counter = m_avg_rtt_timer; counter += dtime; if(counter >= 10) { counter = 0.0; // connectedAndInitialized() is true, peer exists. float avg_rtt = getRTT(); infostream << "Client: avg_rtt=" << avg_rtt << std::endl; } /* Send player position to server */ { float &counter = m_playerpos_send_timer; counter += dtime; if((m_state == LC_Ready) && (counter >= m_recommended_send_interval)) { counter = 0.0; sendPlayerPos(); } } /* Replace updated meshes */ { int num_processed_meshes = 0; while (!m_mesh_update_thread.m_queue_out.empty()) { num_processed_meshes++; MinimapMapblock *minimap_mapblock = NULL; bool do_mapper_update = true; MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx(); MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p); if (block) { // Delete the old mesh if (block->mesh != NULL) { delete block->mesh; block->mesh = NULL; } if (r.mesh) { minimap_mapblock = r.mesh->moveMinimapMapblock(); if (minimap_mapblock == NULL) do_mapper_update = false; } if (r.mesh && r.mesh->getMesh()->getMeshBufferCount() == 0) { delete r.mesh; } else { // Replace with the new mesh block->mesh = r.mesh; } } else { delete r.mesh; } if (do_mapper_update) m_mapper->addBlock(r.p, minimap_mapblock); if (r.ack_block_to_server) { /* Acknowledge block [0] u8 count [1] v3s16 pos_0 */ sendGotBlocks(r.p); } } if (num_processed_meshes > 0) g_profiler->graphAdd("num_processed_meshes", num_processed_meshes); } /* Load fetched media */ if (m_media_downloader && m_media_downloader->isStarted()) { m_media_downloader->step(this); if (m_media_downloader->isDone()) { received_media(); delete m_media_downloader; m_media_downloader = NULL; } } /* If the server didn't update the inventory in a while, revert the local inventory (so the player notices the lag problem and knows something is wrong). */ if(m_inventory_from_server) { float interval = 10.0; float count_before = floor(m_inventory_from_server_age / interval); m_inventory_from_server_age += dtime; float count_after = floor(m_inventory_from_server_age / interval); if(count_after != count_before) { // Do this every <interval> seconds after TOCLIENT_INVENTORY // Reset the locally changed inventory to the authoritative inventory Player *player = m_env.getLocalPlayer(); player->inventory = *m_inventory_from_server; m_inventory_updated = true; } } /* Update positions of sounds attached to objects */ { for(std::map<int, u16>::iterator i = m_sounds_to_objects.begin(); i != m_sounds_to_objects.end(); ++i) { int client_id = i->first; u16 object_id = i->second; ClientActiveObject *cao = m_env.getActiveObject(object_id); if(!cao) continue; v3f pos = cao->getPosition(); m_sound->updateSoundPosition(client_id, pos); } } /* Handle removed remotely initiated sounds */ m_removed_sounds_check_timer += dtime; if(m_removed_sounds_check_timer >= 2.32) { m_removed_sounds_check_timer = 0; // Find removed sounds and clear references to them std::vector<s32> removed_server_ids; for(std::map<s32, int>::iterator i = m_sounds_server_to_client.begin(); i != m_sounds_server_to_client.end();) { s32 server_id = i->first; int client_id = i->second; ++i; if(!m_sound->soundExists(client_id)) { m_sounds_server_to_client.erase(server_id); m_sounds_client_to_server.erase(client_id); m_sounds_to_objects.erase(client_id); removed_server_ids.push_back(server_id); } } // Sync to server if(!removed_server_ids.empty()) { sendRemovedSounds(removed_server_ids); } } // Write server map if (m_localdb && m_localdb_save_interval.step(dtime, m_cache_save_interval)) { m_localdb->endSave(); m_localdb->beginSave(); } }
void Client::step(float dtime) { // Limit a bit if(dtime > 2.0) dtime = 2.0; if(m_ignore_damage_timer > dtime) m_ignore_damage_timer -= dtime; else m_ignore_damage_timer = 0.0; m_animation_time += dtime; if(m_animation_time > 60.0) m_animation_time -= 60.0; m_time_of_day_update_timer += dtime; ReceiveAll(); /* Packet counter */ { float &counter = m_packetcounter_timer; counter -= dtime; if(counter <= 0.0) { counter = 20.0; infostream << "Client packetcounter (" << m_packetcounter_timer << "):"<<std::endl; m_packetcounter.print(infostream); m_packetcounter.clear(); } } // UGLY hack to fix 2 second startup delay caused by non existent // server client startup synchronization in local server or singleplayer mode static bool initial_step = true; if (initial_step) { initial_step = false; } else if(m_state == LC_Created) { float &counter = m_connection_reinit_timer; counter -= dtime; if(counter <= 0.0) { counter = 2.0; LocalPlayer *myplayer = m_env.getLocalPlayer(); FATAL_ERROR_IF(myplayer == NULL, "Local player not found in environment."); sendInit(myplayer->getName()); } // Not connected, return return; } /* Do stuff if connected */ /* Run Map's timers and unload unused data */ const float map_timer_and_unload_dtime = 5.25; if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) { ScopeProfiler sp(g_profiler, "Client: map timer and unload"); std::vector<v3s16> deleted_blocks; m_env.getMap().timerUpdate(map_timer_and_unload_dtime, g_settings->getFloat("client_unload_unused_data_timeout"), g_settings->getS32("client_mapblock_limit"), &deleted_blocks); /* Send info to server NOTE: This loop is intentionally iterated the way it is. */ std::vector<v3s16>::iterator i = deleted_blocks.begin(); std::vector<v3s16> sendlist; for(;;) { if(sendlist.size() == 255 || i == deleted_blocks.end()) { if(sendlist.empty()) break; /* [0] u16 command [2] u8 count [3] v3s16 pos_0 [3+6] v3s16 pos_1 ... */ sendDeletedBlocks(sendlist); if(i == deleted_blocks.end()) break; sendlist.clear(); } sendlist.push_back(*i); ++i; } } /* Send pending messages on out chat queue */ if (!m_out_chat_queue.empty() && canSendChatMessage()) { sendChatMessage(m_out_chat_queue.front()); m_out_chat_queue.pop(); } /* Handle environment */ // Control local player (0ms) LocalPlayer *player = m_env.getLocalPlayer(); assert(player); player->applyControl(dtime, &m_env); // Step environment m_env.step(dtime); m_sound->step(dtime); /* Get events */ while (m_env.hasClientEnvEvents()) { ClientEnvEvent envEvent = m_env.getClientEnvEvent(); if (envEvent.type == CEE_PLAYER_DAMAGE) { if (m_ignore_damage_timer <= 0) { u8 damage = envEvent.player_damage.amount; if (envEvent.player_damage.send_to_server) sendDamage(damage); // Add to ClientEvent queue ClientEvent *event = new ClientEvent(); event->type = CE_PLAYER_DAMAGE; event->player_damage.amount = damage; m_client_event_queue.push(event); } } } /* Print some info */ float &counter = m_avg_rtt_timer; counter += dtime; if(counter >= 10) { counter = 0.0; // connectedAndInitialized() is true, peer exists. float avg_rtt = getRTT(); infostream << "Client: avg_rtt=" << avg_rtt << std::endl; } /* Send player position to server */ { float &counter = m_playerpos_send_timer; counter += dtime; if((m_state == LC_Ready) && (counter >= m_recommended_send_interval)) { counter = 0.0; sendPlayerPos(); } } /* Replace updated meshes */ { int num_processed_meshes = 0; while (!m_mesh_update_thread.m_queue_out.empty()) { num_processed_meshes++; MinimapMapblock *minimap_mapblock = NULL; bool do_mapper_update = true; MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx(); MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p); if (block) { // Delete the old mesh delete block->mesh; block->mesh = nullptr; if (r.mesh) { minimap_mapblock = r.mesh->moveMinimapMapblock(); if (minimap_mapblock == NULL) do_mapper_update = false; bool is_empty = true; for (int l = 0; l < MAX_TILE_LAYERS; l++) if (r.mesh->getMesh(l)->getMeshBufferCount() != 0) is_empty = false; if (is_empty) delete r.mesh; else // Replace with the new mesh block->mesh = r.mesh; } } else { delete r.mesh; } if (m_minimap && do_mapper_update) m_minimap->addBlock(r.p, minimap_mapblock); if (r.ack_block_to_server) { /* Acknowledge block [0] u8 count [1] v3s16 pos_0 */ sendGotBlocks(r.p); } } if (num_processed_meshes > 0) g_profiler->graphAdd("num_processed_meshes", num_processed_meshes); } /* Load fetched media */ if (m_media_downloader && m_media_downloader->isStarted()) { m_media_downloader->step(this); if (m_media_downloader->isDone()) { delete m_media_downloader; m_media_downloader = NULL; } } /* If the server didn't update the inventory in a while, revert the local inventory (so the player notices the lag problem and knows something is wrong). */ if(m_inventory_from_server) { float interval = 10.0; float count_before = floor(m_inventory_from_server_age / interval); m_inventory_from_server_age += dtime; float count_after = floor(m_inventory_from_server_age / interval); if(count_after != count_before) { // Do this every <interval> seconds after TOCLIENT_INVENTORY // Reset the locally changed inventory to the authoritative inventory LocalPlayer *player = m_env.getLocalPlayer(); player->inventory = *m_inventory_from_server; m_inventory_updated = true; } } /* Update positions of sounds attached to objects */ { for (auto &m_sounds_to_object : m_sounds_to_objects) { int client_id = m_sounds_to_object.first; u16 object_id = m_sounds_to_object.second; ClientActiveObject *cao = m_env.getActiveObject(object_id); if (!cao) continue; m_sound->updateSoundPosition(client_id, cao->getPosition()); } } /* Handle removed remotely initiated sounds */ m_removed_sounds_check_timer += dtime; if(m_removed_sounds_check_timer >= 2.32) { m_removed_sounds_check_timer = 0; // Find removed sounds and clear references to them std::vector<s32> removed_server_ids; for (std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.begin(); i != m_sounds_server_to_client.end();) { s32 server_id = i->first; int client_id = i->second; ++i; if(!m_sound->soundExists(client_id)) { m_sounds_server_to_client.erase(server_id); m_sounds_client_to_server.erase(client_id); m_sounds_to_objects.erase(client_id); removed_server_ids.push_back(server_id); } } // Sync to server if(!removed_server_ids.empty()) { sendRemovedSounds(removed_server_ids); } } m_mod_storage_save_timer -= dtime; if (m_mod_storage_save_timer <= 0.0f) { verbosestream << "Saving registered mod storages." << std::endl; m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval"); for (std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) { if (it->second->isModified()) { it->second->save(getModStoragePath()); } } } // Write server map if (m_localdb && m_localdb_save_interval.step(dtime, m_cache_save_interval)) { m_localdb->endSave(); m_localdb->beginSave(); } }
void ClientEnvironment::step(float dtime) { DSTACK(__FUNCTION_NAME); // Get some settings bool free_move = g_settings.getBool("free_move"); bool footprints = g_settings.getBool("footprints"); // Get local player LocalPlayer *lplayer = getLocalPlayer(); assert(lplayer); // collision info queue core::list<CollisionInfo> player_collisions; /* Get the speed the player is going */ bool is_climbing = lplayer->is_climbing; f32 player_speed = 0.001; // just some small value player_speed = lplayer->getSpeed().getLength(); /* Maximum position increment */ //f32 position_max_increment = 0.05*BS; f32 position_max_increment = 0.1*BS; // Maximum time increment (for collision detection etc) // time = distance / speed f32 dtime_max_increment = position_max_increment / player_speed; // Maximum time increment is 10ms or lower if(dtime_max_increment > 0.01) dtime_max_increment = 0.01; // Don't allow overly huge dtime if(dtime > 0.5) dtime = 0.5; f32 dtime_downcount = dtime; /* Stuff that has a maximum time increment */ u32 loopcount = 0; do { loopcount++; f32 dtime_part; if(dtime_downcount > dtime_max_increment) { dtime_part = dtime_max_increment; dtime_downcount -= dtime_part; } else { dtime_part = dtime_downcount; /* Setting this to 0 (no -=dtime_part) disables an infinite loop when dtime_part is so small that dtime_downcount -= dtime_part does nothing */ dtime_downcount = 0; } /* Handle local player */ { v3f lplayerpos = lplayer->getPosition(); // Apply physics if(free_move == false && is_climbing == false) { // Gravity v3f speed = lplayer->getSpeed(); if(lplayer->swimming_up == false) speed.Y -= 9.81 * BS * dtime_part * 2; // Water resistance if(lplayer->in_water_stable || lplayer->in_water) { f32 max_down = 2.0*BS; if(speed.Y < -max_down) speed.Y = -max_down; f32 max = 2.5*BS; if(speed.getLength() > max) { speed = speed / speed.getLength() * max; } } lplayer->setSpeed(speed); } /* Move the lplayer. This also does collision detection. */ lplayer->move(dtime_part, *m_map, position_max_increment, &player_collisions); } } while(dtime_downcount > 0.001); //std::cout<<"Looped "<<loopcount<<" times."<<std::endl; for(core::list<CollisionInfo>::Iterator i = player_collisions.begin(); i != player_collisions.end(); i++) { CollisionInfo &info = *i; if(info.t == COLLISION_FALL) { //f32 tolerance = BS*10; // 2 without damage f32 tolerance = BS*12; // 3 without damage f32 factor = 1; if(info.speed > tolerance) { f32 damage_f = (info.speed - tolerance)/BS*factor; u16 damage = (u16)(damage_f+0.5); if(lplayer->hp > damage) lplayer->hp -= damage; else lplayer->hp = 0; ClientEnvEvent event; event.type = CEE_PLAYER_DAMAGE; event.player_damage.amount = damage; m_client_event_queue.push_back(event); } } } /* Stuff that can be done in an arbitarily large dtime */ for(core::list<Player*>::Iterator i = m_players.begin(); i != m_players.end(); i++) { Player *player = *i; v3f playerpos = player->getPosition(); /* Handle non-local players */ if(player->isLocal() == false) { // Move player->move(dtime, *m_map, 100*BS); // Update lighting on remote players on client u8 light = LIGHT_MAX; try{ // Get node at head v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS); MapNode n = m_map->getNode(p); light = n.getLightBlend(getDayNightRatio()); } catch(InvalidPositionException &e) {} player->updateLight(light); } /* Add footsteps to grass */ if(footprints) { // Get node that is at BS/4 under player v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS); try{ MapNode n = m_map->getNode(bottompos); if(n.getContent() == CONTENT_GRASS) { n.setContent(CONTENT_GRASS_FOOTSTEPS); m_map->setNode(bottompos, n); // Update mesh on client if(m_map->mapType() == MAPTYPE_CLIENT) { v3s16 p_blocks = getNodeBlockPos(bottompos); MapBlock *b = m_map->getBlockNoCreate(p_blocks); //b->updateMesh(getDayNightRatio()); b->setMeshExpired(true); } } } catch(InvalidPositionException &e) { } } } /* Step active objects and update lighting of them */ for(core::map<u16, ClientActiveObject*>::Iterator i = m_active_objects.getIterator(); i.atEnd()==false; i++) { ClientActiveObject* obj = i.getNode()->getValue(); // Step object obj->step(dtime, this); if(m_active_object_light_update_interval.step(dtime, 0.21)) { // Update lighting //u8 light = LIGHT_MAX; u8 light = 0; try{ // Get node at head v3s16 p = obj->getLightPosition(); MapNode n = m_map->getNode(p); light = n.getLightBlend(getDayNightRatio()); } catch(InvalidPositionException &e) {} obj->updateLight(light); } } }
/* sender_peer_id given to this shall be quaranteed to be a valid peer */ void Client::ProcessData(NetworkPacket *pkt) { DSTACK(__FUNCTION_NAME); ScopeProfiler sp(g_profiler, "Client::ProcessData"); auto datasize = pkt->getSize(); auto sender_peer_id = pkt->getPeerId(); int command; MsgpackPacket packet; msgpack::unpacked msg; if (!con::parse_msgpack_packet(pkt->getString(0), datasize, &packet, &command, &msg)) { // invalid packet return; } //infostream<<"Client: received command="<<command<<std::endl; m_packetcounter.add((u16)command); /* If this check is removed, be sure to change the queue system to know the ids */ if(sender_peer_id != PEER_ID_SERVER) { infostream<<"Client::ProcessData(): Discarding data not " "coming from server: peer_id="<<sender_peer_id <<std::endl; return; } u8 ser_version = m_server_ser_ver; //infostream<<"Client received command="<<(int)command<<std::endl; if(command == TOCLIENT_INIT) { u8 deployed; packet[TOCLIENT_INIT_DEPLOYED].convert(&deployed); infostream<<"Client: TOCLIENT_INIT received with " "deployed="<<((int)deployed&0xff)<<std::endl; if(!ser_ver_supported(deployed)) { infostream<<"Client: TOCLIENT_INIT: Server sent " <<"unsupported ser_fmt_ver"<<std::endl; return; } m_server_ser_ver = deployed; // Set player position Player *player = m_env.getLocalPlayer(); if(!player) return; packet[TOCLIENT_INIT_SEED].convert(&m_map_seed); infostream<<"Client: received map seed: "<<m_map_seed<<std::endl; packet[TOCLIENT_INIT_STEP].convert(&m_recommended_send_interval); infostream<<"Client: received recommended send interval " <<m_recommended_send_interval<<std::endl; // TOCLIENT_INIT_POS if (m_localserver) { Settings settings; packet[TOCLIENT_INIT_MAP_PARAMS].convert(&settings); m_localserver->getEmergeManager()->params.load(settings); } // Reply to server MSGPACK_PACKET_INIT(TOSERVER_INIT2, 0); m_con.Send(PEER_ID_SERVER, 1, buffer, true); m_state = LC_Init; return; } if(command == TOCLIENT_ACCESS_DENIED_LEGACY) { // The server didn't like our password. Note, this needs // to be processed even if the serialisation format has // not been agreed yet, the same as TOCLIENT_INIT. m_access_denied = true; packet[TOCLIENT_ACCESS_DENIED_CUSTOM_STRING].convert(&m_access_denied_reason); return; } if(ser_version == SER_FMT_VER_INVALID) { infostream<<"Client: Server serialization" " format invalid or not initialized." " Skipping incoming command="<<command<<std::endl; return; } /* Handle runtime commands */ // there's no sane reason why we shouldn't have a player and // almost everyone needs a player reference Player *player = m_env.getLocalPlayer(); if(!player) return; if(command == TOCLIENT_REMOVENODE) { v3s16 p = packet[TOCLIENT_REMOVENODE_POS].as<v3s16>(); removeNode(p, 2); //use light from top node } else if(command == TOCLIENT_ADDNODE) { v3s16 p = packet[TOCLIENT_ADDNODE_POS].as<v3s16>(); MapNode n = packet[TOCLIENT_ADDNODE_NODE].as<MapNode>(); bool remove_metadata = packet[TOCLIENT_ADDNODE_REMOVE_METADATA].as<bool>(); addNode(p, n, remove_metadata, 2); //fast add } else if(command == TOCLIENT_BLOCKDATA) { v3s16 p = packet[TOCLIENT_BLOCKDATA_POS].as<v3s16>(); s8 step = 1; packet[TOCLIENT_BLOCKDATA_STEP].convert(&step); if (step == 1) { std::istringstream istr(packet[TOCLIENT_BLOCKDATA_DATA].as<std::string>(), std::ios_base::binary); MapBlock *block; block = m_env.getMap().getBlockNoCreateNoEx(p); bool new_block = !block; if (new_block) block = new MapBlock(&m_env.getMap(), p, this); block->deSerialize(istr, ser_version, false); s32 h; // for convert to atomic packet[TOCLIENT_BLOCKDATA_HEAT].convert(&h); block->heat = h; packet[TOCLIENT_BLOCKDATA_HUMIDITY].convert(&h); block->humidity = h; if (packet.count(TOCLIENT_BLOCKDATA_CONTENT_ONLY)) block->content_only = packet[TOCLIENT_BLOCKDATA_CONTENT_ONLY].as<content_t>(); if (m_localserver != NULL) { m_localserver->getMap().saveBlock(block); } if (new_block) if (!m_env.getMap().insertBlock(block)) delete block; /* //Add it to mesh update queue and set it to be acknowledged after update. */ //infostream<<"Adding mesh update task for received block "<<p<<std::endl; updateMeshTimestampWithEdge(p); if (block->content_only != CONTENT_IGNORE && block->content_only != CONTENT_AIR) { if (getNodeBlockPos(floatToInt(m_env.getLocalPlayer()->getPosition(), BS)).getDistanceFrom(p) <= 1) addUpdateMeshTaskWithEdge(p); } /* #if !defined(NDEBUG) if (m_env.getClientMap().m_block_boundary.size() > 150) m_env.getClientMap().m_block_boundary.clear(); m_env.getClientMap().m_block_boundary[p] = block; #endif */ }//step } else if(command == TOCLIENT_INVENTORY) { std::string datastring = packet[TOCLIENT_INVENTORY_DATA].as<std::string>(); std::istringstream is(datastring, std::ios_base::binary); Player *player = m_env.getLocalPlayer(); if(!player) return; player->inventory.deSerialize(is); m_inventory_updated = true; delete m_inventory_from_server; m_inventory_from_server = new Inventory(player->inventory); m_inventory_from_server_age = 0.0; } else if(command == TOCLIENT_TIME_OF_DAY) { u16 time_of_day = packet[TOCLIENT_TIME_OF_DAY_TIME].as<u16>(); time_of_day = time_of_day % 24000; f32 time_speed = packet[TOCLIENT_TIME_OF_DAY_TIME_SPEED].as<f32>(); // Update environment m_env.setTimeOfDay(time_of_day); m_env.setTimeOfDaySpeed(time_speed); m_time_of_day_set = true; u32 dr = m_env.getDayNightRatio(); verbosestream<<"Client: time_of_day="<<time_of_day <<" time_speed="<<time_speed <<" dr="<<dr<<std::endl; } else if(command == TOCLIENT_CHAT_MESSAGE) { std::string message = packet[TOCLIENT_CHAT_MESSAGE_DATA].as<std::string>(); m_chat_queue.push(message); } else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD) { std::vector<u16> removed_objects; packet[TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD_REMOVE].convert(&removed_objects); for (size_t i = 0; i < removed_objects.size(); ++i) m_env.removeActiveObject(removed_objects[i]); std::vector<ActiveObjectAddData> added_objects; packet[TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD_ADD].convert(&added_objects); for (size_t i = 0; i < added_objects.size(); ++i) m_env.addActiveObject(added_objects[i].id, added_objects[i].type, added_objects[i].data); } else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES) { ActiveObjectMessages messages; packet[TOCLIENT_ACTIVE_OBJECT_MESSAGES_MESSAGES].convert(&messages); for (size_t i = 0; i < messages.size(); ++i) m_env.processActiveObjectMessage(messages[i].first, messages[i].second); } else if(command == TOCLIENT_MOVEMENT) { Player *player = m_env.getLocalPlayer(); packet[TOCLIENT_MOVEMENT_ACCELERATION_DEFAULT].convert(&player->movement_acceleration_default); packet[TOCLIENT_MOVEMENT_ACCELERATION_AIR].convert(&player->movement_acceleration_air); packet[TOCLIENT_MOVEMENT_ACCELERATION_FAST].convert(&player->movement_acceleration_fast); packet[TOCLIENT_MOVEMENT_SPEED_WALK].convert(&player->movement_speed_walk); packet[TOCLIENT_MOVEMENT_SPEED_CROUCH].convert(&player->movement_speed_crouch); packet[TOCLIENT_MOVEMENT_SPEED_FAST].convert(&player->movement_speed_fast); packet[TOCLIENT_MOVEMENT_SPEED_CLIMB].convert(&player->movement_speed_climb); packet[TOCLIENT_MOVEMENT_SPEED_JUMP].convert(&player->movement_speed_jump); packet[TOCLIENT_MOVEMENT_LIQUID_FLUIDITY].convert(&player->movement_liquid_fluidity); packet[TOCLIENT_MOVEMENT_LIQUID_FLUIDITY_SMOOTH].convert(&player->movement_liquid_fluidity_smooth); packet[TOCLIENT_MOVEMENT_LIQUID_SINK].convert(&player->movement_liquid_sink); packet[TOCLIENT_MOVEMENT_GRAVITY].convert(&player->movement_gravity); } else if(command == TOCLIENT_HP) { Player *player = m_env.getLocalPlayer(); if(!player) return; u8 oldhp = player->hp; u8 hp = packet[TOCLIENT_HP_HP].as<u8>(); player->hp = hp; if(hp < oldhp) { // Add to ClientEvent queue ClientEvent event; event.type = CE_PLAYER_DAMAGE; event.player_damage.amount = oldhp - hp; m_client_event_queue.push(event); } } else if(command == TOCLIENT_BREATH) { Player *player = m_env.getLocalPlayer(); player->setBreath(packet[TOCLIENT_BREATH_BREATH].as<u16>()) ; } else if(command == TOCLIENT_MOVE_PLAYER) { Player *player = m_env.getLocalPlayer(); if(!player) return; v3f pos = packet[TOCLIENT_MOVE_PLAYER_POS].as<v3f>(); f32 pitch = packet[TOCLIENT_MOVE_PLAYER_PITCH].as<f32>(); f32 yaw = packet[TOCLIENT_MOVE_PLAYER_YAW].as<f32>(); player->setPosition(pos); infostream<<"Client got TOCLIENT_MOVE_PLAYER" <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")" <<" pitch="<<pitch <<" yaw="<<yaw <<std::endl; /* Add to ClientEvent queue. This has to be sent to the main program because otherwise it would just force the pitch and yaw values to whatever the camera points to. */ ClientEvent event; event.type = CE_PLAYER_FORCE_MOVE; event.player_force_move.pitch = pitch; event.player_force_move.yaw = yaw; m_client_event_queue.push(event); // Ignore damage for a few seconds, so that the player doesn't // get damage from falling on ground m_ignore_damage_timer = 3.0; } else if(command == TOCLIENT_DEATHSCREEN) { bool set_camera_point_target = packet[TOCLIENT_DEATHSCREEN_SET_CAMERA].as<bool>(); v3f camera_point_target = packet[TOCLIENT_DEATHSCREEN_CAMERA_POINT].as<v3f>(); ClientEvent event; event.type = CE_DEATHSCREEN; event.deathscreen.set_camera_point_target = set_camera_point_target; event.deathscreen.camera_point_target_x = camera_point_target.X; event.deathscreen.camera_point_target_y = camera_point_target.Y; event.deathscreen.camera_point_target_z = camera_point_target.Z; m_client_event_queue.push(event); } else if(command == TOCLIENT_ANNOUNCE_MEDIA) { if (m_media_downloader == NULL || m_media_downloader->isStarted()) { const char *problem = m_media_downloader ? "we already saw another announcement" : "all media has been received already"; errorstream<<"Client: Received media announcement but " <<problem<<"!" <<std::endl; return; } // Mesh update thread must be stopped while // updating content definitions //assert(!m_mesh_update_thread.IsRunning()); MediaAnnounceList announce_list; packet[TOCLIENT_ANNOUNCE_MEDIA_LIST].convert(&announce_list); for (size_t i = 0; i < announce_list.size(); ++i) m_media_downloader->addFile(announce_list[i].first, base64_decode(announce_list[i].second)); std::vector<std::string> remote_media; std::string remote_media_string = packet[TOCLIENT_ANNOUNCE_MEDIA_REMOTE_SERVER].as<std::string>(); Strfnd sf(remote_media_string); while(!sf.atend()) { std::string baseurl = trim(sf.next(",")); if(baseurl != "") m_media_downloader->addRemoteServer(baseurl); } m_media_downloader->step(this); } else if(command == TOCLIENT_MEDIA) { MediaData media_data; packet[TOCLIENT_MEDIA_MEDIA].convert(&media_data); // Mesh update thread must be stopped while // updating content definitions //assert(!m_mesh_update_thread.IsRunning()); for(size_t i = 0; i < media_data.size(); ++i) m_media_downloader->conventionalTransferDone( media_data[i].first, media_data[i].second, this); } else if(command == TOCLIENT_NODEDEF) { infostream<<"Client: Received node definitions: packet size: " <<datasize<<std::endl; // Mesh update thread must be stopped while // updating content definitions //assert(!m_mesh_update_thread.IsRunning()); packet[TOCLIENT_NODEDEF_DEFINITIONS].convert(m_nodedef); m_nodedef_received = true; } else if(command == TOCLIENT_ITEMDEF) { infostream<<"Client: Received item definitions: packet size: " <<datasize<<std::endl; // Mesh update thread must be stopped while // updating content definitions //assert(!m_mesh_update_thread.IsRunning()); packet[TOCLIENT_ITEMDEF_DEFINITIONS].convert(m_itemdef); m_itemdef_received = true; } else if(command == TOCLIENT_PLAY_SOUND) { s32 server_id = packet[TOCLIENT_PLAY_SOUND_ID].as<s32>(); std::string name = packet[TOCLIENT_PLAY_SOUND_NAME].as<std::string>(); float gain = packet[TOCLIENT_PLAY_SOUND_GAIN].as<f32>(); int type = packet[TOCLIENT_PLAY_SOUND_TYPE].as<u8>(); // 0=local, 1=positional, 2=object v3f pos = packet[TOCLIENT_PLAY_SOUND_POS].as<v3f>(); u16 object_id = packet[TOCLIENT_PLAY_SOUND_OBJECT_ID].as<u16>(); bool loop = packet[TOCLIENT_PLAY_SOUND_LOOP].as<bool>(); // Start playing int client_id = -1; switch(type){ case 0: // local client_id = m_sound->playSound(name, loop, gain); break; case 1: // positional client_id = m_sound->playSoundAt(name, loop, gain, pos); break; case 2: { // object ClientActiveObject *cao = m_env.getActiveObject(object_id); if(cao) pos = cao->getPosition(); client_id = m_sound->playSoundAt(name, loop, gain, pos); // TODO: Set up sound to move with object break; } default: break; } if(client_id != -1){ m_sounds_server_to_client[server_id] = client_id; m_sounds_client_to_server[client_id] = server_id; if(object_id != 0) m_sounds_to_objects[client_id] = object_id; } } else if(command == TOCLIENT_STOP_SOUND) { s32 server_id = packet[TOCLIENT_STOP_SOUND_ID].as<s32>(); std::map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id); if(i != m_sounds_server_to_client.end()){ int client_id = i->second; m_sound->stopSound(client_id); } } else if(command == TOCLIENT_PRIVILEGES) { packet[TOCLIENT_PRIVILEGES_PRIVILEGES].convert(&m_privileges); } else if(command == TOCLIENT_INVENTORY_FORMSPEC) { // Store formspec in LocalPlayer player->inventory_formspec = packet[TOCLIENT_INVENTORY_FORMSPEC_DATA].as<std::string>(); } else if(command == TOCLIENT_DETACHED_INVENTORY) { std::string name = packet[TOCLIENT_DETACHED_INVENTORY_NAME].as<std::string>(); std::string datastring = packet[TOCLIENT_DETACHED_INVENTORY_DATA].as<std::string>(); std::istringstream is(datastring, std::ios_base::binary); infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl; Inventory *inv = NULL; if(m_detached_inventories.count(name) > 0) inv = m_detached_inventories[name]; else{ inv = new Inventory(m_itemdef); m_detached_inventories[name] = inv; } inv->deSerialize(is); } else if(command == TOCLIENT_SHOW_FORMSPEC) { std::string formspec = packet[TOCLIENT_SHOW_FORMSPEC_DATA].as<std::string>(); std::string formname = packet[TOCLIENT_SHOW_FORMSPEC_NAME].as<std::string>(); ClientEvent event; event.type = CE_SHOW_FORMSPEC; // pointer is required as event is a struct only! // adding a std:string to a struct isn't possible event.show_formspec.formspec = new std::string(formspec); event.show_formspec.formname = new std::string(formname); m_client_event_queue.push(event); } else if(command == TOCLIENT_SPAWN_PARTICLE) { v3f pos = packet[TOCLIENT_SPAWN_PARTICLE_POS].as<v3f>(); v3f vel = packet[TOCLIENT_SPAWN_PARTICLE_VELOCITY].as<v3f>(); v3f acc = packet[TOCLIENT_SPAWN_PARTICLE_ACCELERATION].as<v3f>(); float expirationtime = packet[TOCLIENT_SPAWN_PARTICLE_EXPIRATIONTIME].as<float>(); float size = packet[TOCLIENT_SPAWN_PARTICLE_SIZE].as<float>(); bool collisiondetection = packet[TOCLIENT_SPAWN_PARTICLE_COLLISIONDETECTION].as<bool>(); std::string texture = packet[TOCLIENT_SPAWN_PARTICLE_TEXTURE].as<std::string>(); bool vertical = packet[TOCLIENT_SPAWN_PARTICLE_VERTICAL].as<bool>(); ClientEvent event; event.type = CE_SPAWN_PARTICLE; event.spawn_particle.pos = new v3f (pos); event.spawn_particle.vel = new v3f (vel); event.spawn_particle.acc = new v3f (acc); event.spawn_particle.expirationtime = expirationtime; event.spawn_particle.size = size; event.spawn_particle.collisiondetection = collisiondetection; event.spawn_particle.vertical = vertical; event.spawn_particle.texture = new std::string(texture); m_client_event_queue.push(event); } else if(command == TOCLIENT_ADD_PARTICLESPAWNER) { u16 amount; float spawntime, minexptime, maxexptime, minsize, maxsize; v3f minpos, maxpos, minvel, maxvel, minacc, maxacc; bool collisiondetection, vertical; u32 id; std::string texture; packet[TOCLIENT_ADD_PARTICLESPAWNER_AMOUNT].convert(&amount); packet[TOCLIENT_ADD_PARTICLESPAWNER_SPAWNTIME].convert(&spawntime); packet[TOCLIENT_ADD_PARTICLESPAWNER_MINPOS].convert(&minpos); packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXPOS].convert(&maxpos); packet[TOCLIENT_ADD_PARTICLESPAWNER_MINVEL].convert(&minvel); packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXVEL].convert(&maxvel); packet[TOCLIENT_ADD_PARTICLESPAWNER_MINACC].convert(&minacc); packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXACC].convert(&maxacc); packet[TOCLIENT_ADD_PARTICLESPAWNER_MINEXPTIME].convert(&minexptime); packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXEXPTIME].convert(&maxexptime); packet[TOCLIENT_ADD_PARTICLESPAWNER_MINSIZE].convert(&minsize); packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXSIZE].convert(&maxsize); packet[TOCLIENT_ADD_PARTICLESPAWNER_COLLISIONDETECTION].convert(&collisiondetection); packet[TOCLIENT_ADD_PARTICLESPAWNER_TEXTURE].convert(&texture); packet[TOCLIENT_ADD_PARTICLESPAWNER_VERTICAL].convert(&vertical); packet[TOCLIENT_ADD_PARTICLESPAWNER_ID].convert(&id); ClientEvent event; event.type = CE_ADD_PARTICLESPAWNER; event.add_particlespawner.amount = amount; event.add_particlespawner.spawntime = spawntime; event.add_particlespawner.minpos = new v3f (minpos); event.add_particlespawner.maxpos = new v3f (maxpos); event.add_particlespawner.minvel = new v3f (minvel); event.add_particlespawner.maxvel = new v3f (maxvel); event.add_particlespawner.minacc = new v3f (minacc); event.add_particlespawner.maxacc = new v3f (maxacc); event.add_particlespawner.minexptime = minexptime; event.add_particlespawner.maxexptime = maxexptime; event.add_particlespawner.minsize = minsize; event.add_particlespawner.maxsize = maxsize; event.add_particlespawner.collisiondetection = collisiondetection; event.add_particlespawner.vertical = vertical; event.add_particlespawner.texture = new std::string(texture); event.add_particlespawner.id = id; m_client_event_queue.push(event); } else if(command == TOCLIENT_DELETE_PARTICLESPAWNER) { u32 id = packet[TOCLIENT_DELETE_PARTICLESPAWNER_ID].as<u32>(); ClientEvent event; event.type = CE_DELETE_PARTICLESPAWNER; event.delete_particlespawner.id = id; m_client_event_queue.push(event); } else if(command == TOCLIENT_HUDADD) { //std::string datastring((char *)&data[2], datasize - 2); //std::istringstream is(datastring, std::ios_base::binary); u32 id, number, item, dir; u8 type; v2f pos, scale, align, offset; std::string name, text; v3f world_pos; v2s32 size; packet[TOCLIENT_HUDADD_ID].convert(&id); packet[TOCLIENT_HUDADD_TYPE].convert(&type); packet[TOCLIENT_HUDADD_POS].convert(&pos); packet[TOCLIENT_HUDADD_NAME].convert(&name); packet[TOCLIENT_HUDADD_SCALE].convert(&scale); packet[TOCLIENT_HUDADD_TEXT].convert(&text); packet[TOCLIENT_HUDADD_NUMBER].convert(&number); packet[TOCLIENT_HUDADD_ITEM].convert(&item); packet[TOCLIENT_HUDADD_DIR].convert(&dir); packet[TOCLIENT_HUDADD_ALIGN].convert(&align); packet[TOCLIENT_HUDADD_OFFSET].convert(&offset); packet[TOCLIENT_HUDADD_WORLD_POS].convert(&world_pos); packet[TOCLIENT_HUDADD_SIZE].convert(&size); ClientEvent event; event.type = CE_HUDADD; event.hudadd.id = id; event.hudadd.type = type; event.hudadd.pos = new v2f(pos); event.hudadd.name = new std::string(name); event.hudadd.scale = new v2f(scale); event.hudadd.text = new std::string(text); event.hudadd.number = number; event.hudadd.item = item; event.hudadd.dir = dir; event.hudadd.align = new v2f(align); event.hudadd.offset = new v2f(offset); event.hudadd.world_pos = new v3f(world_pos); event.hudadd.size = new v2s32(size); m_client_event_queue.push(event); } else if(command == TOCLIENT_HUDRM) { u32 id = packet[TOCLIENT_HUDRM_ID].as<u32>(); ClientEvent event; event.type = CE_HUDRM; event.hudrm.id = id; m_client_event_queue.push(event); } else if(command == TOCLIENT_HUDCHANGE) { std::string sdata; v2f v2fdata; v3f v3fdata; v2s32 v2s32data; u32 intdata = 0; u32 id = packet[TOCLIENT_HUDCHANGE_ID].as<u32>(); u8 stat = packet[TOCLIENT_HUDCHANGE_STAT].as<int>(); if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE || stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET) packet[TOCLIENT_HUDCHANGE_V2F].convert(&v2fdata); else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT) packet[TOCLIENT_HUDCHANGE_STRING].convert(&sdata); else if (stat == HUD_STAT_WORLD_POS) packet[TOCLIENT_HUDCHANGE_V3F].convert(&v3fdata); else if (stat == HUD_STAT_SIZE) packet[TOCLIENT_HUDCHANGE_V2S32].convert(&v2s32data); else packet[TOCLIENT_HUDCHANGE_U32].convert(&intdata); ClientEvent event; event.type = CE_HUDCHANGE; event.hudchange.id = id; event.hudchange.stat = (HudElementStat)stat; event.hudchange.v2fdata = new v2f(v2fdata); event.hudchange.v3fdata = new v3f(v3fdata); event.hudchange.sdata = new std::string(sdata); event.hudchange.data = intdata; event.hudchange.v2s32data = new v2s32(v2s32data); m_client_event_queue.push(event); } else if(command == TOCLIENT_HUD_SET_FLAGS) { Player *player = m_env.getLocalPlayer(); if(!player) return; u32 flags = packet[TOCLIENT_HUD_SET_FLAGS_FLAGS].as<u32>(); u32 mask = packet[TOCLIENT_HUD_SET_FLAGS_MASK].as<u32>(); player->hud_flags &= ~mask; player->hud_flags |= flags; } else if(command == TOCLIENT_HUD_SET_PARAM) { u16 param = packet[TOCLIENT_HUD_SET_PARAM_ID].as<u16>(); std::string value = packet[TOCLIENT_HUD_SET_PARAM_VALUE].as<std::string>(); if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){ s32 hotbar_itemcount = readS32((u8*) value.c_str()); if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX) player->hud_hotbar_itemcount = hotbar_itemcount; } else if (param == HUD_PARAM_HOTBAR_IMAGE) { ((LocalPlayer *) player)->hotbar_image = value; } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) { ((LocalPlayer *) player)->hotbar_selected_image = value; } } /* else if(command == TOCLIENT_ANIMATIONS) { LocalPlayer *player = m_env.getLocalPlayer(); packet[TOCLIENT_ANIMATIONS_DEFAULT_START].convert(&player->animation_default_start); packet[TOCLIENT_ANIMATIONS_DEFAULT_STOP].convert(&player->animation_default_stop); packet[TOCLIENT_ANIMATIONS_WALK_START].convert(&player->animation_walk_start); packet[TOCLIENT_ANIMATIONS_WALK_STOP].convert(&player->animation_walk_stop); packet[TOCLIENT_ANIMATIONS_DIG_START].convert(&player->animation_dig_start); packet[TOCLIENT_ANIMATIONS_DIG_STOP].convert(&player->animation_dig_stop); packet[TOCLIENT_ANIMATIONS_WD_START].convert(&player->animation_wd_start); packet[TOCLIENT_ANIMATIONS_WD_STOP].convert(&player->animation_wd_stop); } */ else if(command == TOCLIENT_SET_SKY) { video::SColor *bgcolor = new video::SColor(packet[TOCLIENT_SET_SKY_COLOR].as<video::SColor>()); std::string *type = new std::string(packet[TOCLIENT_SET_SKY_TYPE].as<std::string>()); std::vector<std::string> *params = new std::vector<std::string>; packet[TOCLIENT_SET_SKY_PARAMS].convert(params); ClientEvent event; event.type = CE_SET_SKY; event.set_sky.bgcolor = bgcolor; event.set_sky.type = type; event.set_sky.params = params; m_client_event_queue.push(event); } else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO) { bool do_override; float day_night_ratio_f; packet[TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO_DO].convert(&do_override); packet[TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO_VALUE].convert(&day_night_ratio_f); ClientEvent event; event.type = CE_OVERRIDE_DAY_NIGHT_RATIO; event.override_day_night_ratio.do_override = do_override; event.override_day_night_ratio.ratio_f = day_night_ratio_f; m_client_event_queue.push(event); } else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS) { LocalPlayer *player = m_env.getLocalPlayer(); if(!player) return; packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_IDLE].convert(&player->local_animations[0]); packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_WALK].convert(&player->local_animations[1]); packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_DIG].convert(&player->local_animations[2]); packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_WALKDIG].convert(&player->local_animations[3]); packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_FRAME_SPEED].convert(&player->local_animation_speed); } else if(command == TOCLIENT_EYE_OFFSET) { LocalPlayer *player = m_env.getLocalPlayer(); if(!player) return; packet[TOCLIENT_EYE_OFFSET_FIRST].convert(&player->eye_offset_first); packet[TOCLIENT_EYE_OFFSET_THIRD].convert(&player->eye_offset_third); } else { infostream<<"Client: Ignoring unknown command " <<command<<std::endl; } }
void ClientEnvironment::step(float dtime) { DSTACK(FUNCTION_NAME); /* Step time of day */ stepTimeOfDay(dtime); // Get some settings bool fly_allowed = m_client->checkLocalPrivilege("fly"); bool free_move = fly_allowed && g_settings->getBool("free_move"); // Get local player LocalPlayer *lplayer = getLocalPlayer(); assert(lplayer); // collision info queue std::vector<CollisionInfo> player_collisions; /* Get the speed the player is going */ bool is_climbing = lplayer->is_climbing; f32 player_speed = lplayer->getSpeed().getLength(); /* Maximum position increment */ //f32 position_max_increment = 0.05*BS; f32 position_max_increment = 0.1*BS; // Maximum time increment (for collision detection etc) // time = distance / speed f32 dtime_max_increment = 1; if(player_speed > 0.001) dtime_max_increment = position_max_increment / player_speed; // Maximum time increment is 10ms or lower if(dtime_max_increment > 0.01) dtime_max_increment = 0.01; // Don't allow overly huge dtime if(dtime > 0.5) dtime = 0.5; f32 dtime_downcount = dtime; /* Stuff that has a maximum time increment */ u32 loopcount = 0; do { loopcount++; f32 dtime_part; if(dtime_downcount > dtime_max_increment) { dtime_part = dtime_max_increment; dtime_downcount -= dtime_part; } else { dtime_part = dtime_downcount; /* Setting this to 0 (no -=dtime_part) disables an infinite loop when dtime_part is so small that dtime_downcount -= dtime_part does nothing */ dtime_downcount = 0; } /* Handle local player */ { // Apply physics if(!free_move && !is_climbing) { // Gravity v3f speed = lplayer->getSpeed(); if(!lplayer->in_liquid) speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2; // Liquid floating / sinking if(lplayer->in_liquid && !lplayer->swimming_vertical) speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2; // Liquid resistance if(lplayer->in_liquid_stable || lplayer->in_liquid) { // How much the node's viscosity blocks movement, ranges between 0 and 1 // Should match the scale at which viscosity increase affects other liquid attributes const f32 viscosity_factor = 0.3; v3f d_wanted = -speed / lplayer->movement_liquid_fluidity; f32 dl = d_wanted.getLength(); if(dl > lplayer->movement_liquid_fluidity_smooth) dl = lplayer->movement_liquid_fluidity_smooth; dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor); v3f d = d_wanted.normalize() * dl; speed += d; } lplayer->setSpeed(speed); } /* Move the lplayer. This also does collision detection. */ lplayer->move(dtime_part, this, position_max_increment, &player_collisions); } } while(dtime_downcount > 0.001); //std::cout<<"Looped "<<loopcount<<" times."<<std::endl; for(std::vector<CollisionInfo>::iterator i = player_collisions.begin(); i != player_collisions.end(); ++i) { CollisionInfo &info = *i; v3f speed_diff = info.new_speed - info.old_speed;; // Handle only fall damage // (because otherwise walking against something in fast_move kills you) if(speed_diff.Y < 0 || info.old_speed.Y >= 0) continue; // Get rid of other components speed_diff.X = 0; speed_diff.Z = 0; f32 pre_factor = 1; // 1 hp per node/s f32 tolerance = BS*14; // 5 without damage f32 post_factor = 1; // 1 hp per node/s if(info.type == COLLISION_NODE) { const ContentFeatures &f = m_client->ndef()-> get(m_map->getNodeNoEx(info.node_p)); // Determine fall damage multiplier int addp = itemgroup_get(f.groups, "fall_damage_add_percent"); pre_factor = 1.0 + (float)addp/100.0; } float speed = pre_factor * speed_diff.getLength(); if(speed > tolerance) { f32 damage_f = (speed - tolerance)/BS * post_factor; u16 damage = (u16)(damage_f+0.5); if(damage != 0){ damageLocalPlayer(damage, true); MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage"); m_client->event()->put(e); } } } /* A quick draft of lava damage */ if(m_lava_hurt_interval.step(dtime, 1.0)) { v3f pf = lplayer->getPosition(); // Feet, middle and head v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS); MapNode n1 = m_map->getNodeNoEx(p1); v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS); MapNode n2 = m_map->getNodeNoEx(p2); v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS); MapNode n3 = m_map->getNodeNoEx(p3); u32 damage_per_second = 0; damage_per_second = MYMAX(damage_per_second, m_client->ndef()->get(n1).damage_per_second); damage_per_second = MYMAX(damage_per_second, m_client->ndef()->get(n2).damage_per_second); damage_per_second = MYMAX(damage_per_second, m_client->ndef()->get(n3).damage_per_second); if(damage_per_second != 0) { damageLocalPlayer(damage_per_second, true); } } // Protocol v29 make this behaviour obsolete if (getGameDef()->getProtoVersion() < 29) { /* Drowning */ if (m_drowning_interval.step(dtime, 2.0)) { v3f pf = lplayer->getPosition(); // head v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); ContentFeatures c = m_client->ndef()->get(n); u8 drowning_damage = c.drowning; if (drowning_damage > 0 && lplayer->hp > 0) { u16 breath = lplayer->getBreath(); if (breath > 10) { breath = 11; } if (breath > 0) { breath -= 1; } lplayer->setBreath(breath); updateLocalPlayerBreath(breath); } if (lplayer->getBreath() == 0 && drowning_damage > 0) { damageLocalPlayer(drowning_damage, true); } } if (m_breathing_interval.step(dtime, 0.5)) { v3f pf = lplayer->getPosition(); // head v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); ContentFeatures c = m_client->ndef()->get(n); if (!lplayer->hp) { lplayer->setBreath(11); } else if (c.drowning == 0) { u16 breath = lplayer->getBreath(); if (breath <= 10) { breath += 1; lplayer->setBreath(breath); updateLocalPlayerBreath(breath); } } } } // Update lighting on local player (used for wield item) u32 day_night_ratio = getDayNightRatio(); { // Get node at head // On InvalidPositionException, use this as default // (day: LIGHT_SUN, night: 0) MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0); v3s16 p = lplayer->getLightPosition(); node_at_lplayer = m_map->getNodeNoEx(p); u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef()); final_color_blend(&lplayer->light_color, light, day_night_ratio); } /* Step active objects and update lighting of them */ g_profiler->avg("CEnv: num of objects", m_active_objects.size()); bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21); for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin(); i != m_active_objects.end(); ++i) { ClientActiveObject* obj = i->second; // Step object obj->step(dtime, this); if(update_lighting) { // Update lighting u8 light = 0; bool pos_ok; // Get node at head v3s16 p = obj->getLightPosition(); MapNode n = m_map->getNodeNoEx(p, &pos_ok); if (pos_ok) light = n.getLightBlend(day_night_ratio, m_client->ndef()); else light = blend_light(day_night_ratio, LIGHT_SUN, 0); obj->updateLight(light); } } /* Step and handle simple objects */ g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size()); for(std::vector<ClientSimpleObject*>::iterator i = m_simple_objects.begin(); i != m_simple_objects.end();) { std::vector<ClientSimpleObject*>::iterator cur = i; ClientSimpleObject *simple = *cur; simple->step(dtime); if(simple->m_to_be_removed) { delete simple; i = m_simple_objects.erase(cur); } else { ++i; } } }