X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fserver.cpp;h=c1f27149004ee33d7852955d365f0f06312dd44b;hb=0c08f948d7014e66d5a79d7584c03164af2edd93;hp=871612e60c478e811877eb0ceb550ceea1a1652c;hpb=b8131c3415ee8811ab98b537c080d203d8bee774;p=dragonfireclient.git diff --git a/src/server.cpp b/src/server.cpp index 871612e60..c1f271490 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -93,6 +93,15 @@ void *ServerThread::run() { BEGIN_DEBUG_EXCEPTION_HANDLER + /* + * The real business of the server happens on the ServerThread. + * How this works: + * AsyncRunStep() runs an actual server step as soon as enough time has + * passed (dedicated_server_loop keeps track of that). + * Receive() blocks at least(!) 30ms waiting for a packet (so this loop + * doesn't busy wait) and will process any remaining packets. + */ + m_server->AsyncRunStep(true); while (!stopRequested()) { @@ -101,7 +110,6 @@ void *ServerThread::run() m_server->Receive(); - } catch (con::NoIncomingDataException &e) { } catch (con::PeerNotFoundException &e) { infostream<<"Server: PeerNotFoundException"<getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE; - - // Radius inside which players are active - static thread_local const bool is_transfer_limited = - g_settings->exists("unlimited_player_transfer_distance") && - !g_settings->getBool("unlimited_player_transfer_distance"); - static thread_local const s16 player_transfer_dist = - g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE; - s16 player_radius = player_transfer_dist; - if (player_radius == 0 && is_transfer_limited) - player_radius = radius; + ScopeProfiler sp(g_profiler, "Server: update objects within range"); for (const auto &client_it : clients) { RemoteClient *client = client_it.second; - // If definitions and textures have not been sent, don't - // send objects either if (client->getState() < CS_DefinitionsSent) continue; - RemotePlayer *player = m_env->getPlayer(client->peer_id); - if (!player) { - // This can happen if the client timeouts somehow + // This can happen if the client times out somehow + if (!m_env->getPlayer(client->peer_id)) continue; - } - PlayerSAO *playersao = player->getPlayerSAO(); + PlayerSAO *playersao = getPlayerSAO(client->peer_id); if (!playersao) continue; - s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE); - if (my_radius <= 0) my_radius = radius; - //infostream << "Server: Active Radius " << my_radius << std::endl; - - std::queue removed_objects; - std::queue added_objects; - m_env->getRemovedActiveObjects(playersao, my_radius, player_radius, - client->m_known_objects, removed_objects); - m_env->getAddedActiveObjects(playersao, my_radius, player_radius, - client->m_known_objects, added_objects); - - // Ignore if nothing happened - if (removed_objects.empty() && added_objects.empty()) { - continue; - } - - std::string data_buffer; - - char buf[4]; - - // Handle removed objects - writeU16((u8*)buf, removed_objects.size()); - data_buffer.append(buf, 2); - while (!removed_objects.empty()) { - // Get object - u16 id = removed_objects.front(); - ServerActiveObject* obj = m_env->getActiveObject(id); - - // Add to data buffer for sending - writeU16((u8*)buf, id); - data_buffer.append(buf, 2); - - // Remove from known objects - client->m_known_objects.erase(id); - - if(obj && obj->m_known_by_count > 0) - obj->m_known_by_count--; - removed_objects.pop(); - } - - // Handle added objects - writeU16((u8*)buf, added_objects.size()); - data_buffer.append(buf, 2); - while (!added_objects.empty()) { - // Get object - u16 id = added_objects.front(); - ServerActiveObject* obj = m_env->getActiveObject(id); - - // Get object type - u8 type = ACTIVEOBJECT_TYPE_INVALID; - if (!obj) - warningstream << FUNCTION_NAME << ": NULL object" << std::endl; - else - type = obj->getSendType(); - - // Add to data buffer for sending - writeU16((u8*)buf, id); - data_buffer.append(buf, 2); - writeU8((u8*)buf, type); - data_buffer.append(buf, 1); - - if(obj) - data_buffer.append(serializeLongString( - obj->getClientInitializationData(client->net_proto_version))); - else - data_buffer.append(serializeLongString("")); - - // Add to known objects - client->m_known_objects.insert(id); - - if(obj) - obj->m_known_by_count++; - - added_objects.pop(); - } - - u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer); - verbosestream << "Server: Sent object remove/add: " - << removed_objects.size() << " removed, " - << added_objects.size() << " added, " - << "packet size is " << pktSize << std::endl; + SendActiveObjectRemoveAdd(client, playersao); } m_clients.unlock(); + // Save mod storages if modified m_mod_storage_save_timer -= dtime; if (m_mod_storage_save_timer <= 0.0f) { infostream << "Saving registered mod storages." << std::endl; @@ -786,19 +702,33 @@ void Server::AsyncRunStep(bool initial_step) // Route data to every client for (const auto &client_it : clients) { RemoteClient *client = client_it.second; + PlayerSAO *player = getPlayerSAO(client->peer_id); std::string reliable_data; std::string unreliable_data; // Go through all objects in message buffer for (const auto &buffered_message : buffered_messages) { - // If object is not known by client, skip it + // If object does not exist or is not known by client, skip it u16 id = buffered_message.first; - if (client->m_known_objects.find(id) == client->m_known_objects.end()) + ServerActiveObject *sao = m_env->getActiveObject(id); + if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end()) continue; // Get message list of object std::vector* list = buffered_message.second; // Go through every message for (const ActiveObjectMessage &aom : *list) { + // Send position updates to players who do not see the attachment + if (aom.datastring[0] == GENERIC_CMD_UPDATE_POSITION) { + if (sao->getId() == player->getId()) + continue; + + // Do not send position updates for attached players + // as long the parent is known to the client + ServerActiveObject *parent = sao->getParent(); + if (parent && client->m_known_objects.find(parent->getId()) != + client->m_known_objects.end()) + continue; + } // Compose the full new data with header std::string new_data; // Add object id @@ -989,24 +919,43 @@ void Server::AsyncRunStep(bool initial_step) void Server::Receive() { - session_t peer_id = 0; - try { - NetworkPacket pkt; - m_con->Receive(&pkt); - peer_id = pkt.getPeerId(); - ProcessData(&pkt); - } catch (const con::InvalidIncomingDataException &e) { - infostream << "Server::Receive(): InvalidIncomingDataException: what()=" - << e.what() << std::endl; - } catch (const SerializationError &e) { - infostream << "Server::Receive(): SerializationError: what()=" - << e.what() << std::endl; - } catch (const ClientStateError &e) { - errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl; - DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect." - L"Try reconnecting or updating your client"); - } catch (const con::PeerNotFoundException &e) { - // Do nothing + NetworkPacket pkt; + session_t peer_id; + bool first = true; + for (;;) { + pkt.clear(); + peer_id = 0; + try { + /* + In the first iteration *wait* for a packet, afterwards process + all packets that are immediately available (no waiting). + */ + if (first) { + m_con->Receive(&pkt); + first = false; + } else { + if (!m_con->TryReceive(&pkt)) + return; + } + + peer_id = pkt.getPeerId(); + ProcessData(&pkt); + } catch (const con::InvalidIncomingDataException &e) { + infostream << "Server::Receive(): InvalidIncomingDataException: what()=" + << e.what() << std::endl; + } catch (const SerializationError &e) { + infostream << "Server::Receive(): SerializationError: what()=" + << e.what() << std::endl; + } catch (const ClientStateError &e) { + errorstream << "ProcessData: peer=" << peer_id << " what()=" + << e.what() << std::endl; + DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect." + L"Try reconnecting or updating your client"); + } catch (const con::PeerNotFoundException &e) { + // Do nothing + } catch (const con::NoIncomingDataException &e) { + return; + } } } @@ -1057,7 +1006,7 @@ PlayerSAO* Server::StageTwoClientInit(session_t peer_id) SendPlayerInventoryFormspec(peer_id); // Send inventory - SendInventory(playersao); + SendInventory(playersao, false); // Send HP or death screen if (playersao->isDead()) @@ -1089,9 +1038,9 @@ PlayerSAO* Server::StageTwoClientInit(session_t peer_id) return playersao; } -inline void Server::handleCommand(NetworkPacket* pkt) +inline void Server::handleCommand(NetworkPacket *pkt) { - const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()]; + const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()]; (this->*opHandle.handler)(pkt); } @@ -1187,12 +1136,12 @@ void Server::setTimeOfDay(u32 time) m_time_of_day_send_timer = 0; } -void Server::onMapEditEvent(MapEditEvent *event) +void Server::onMapEditEvent(const MapEditEvent &event) { - if (m_ignore_map_edit_events_area.contains(event->getArea())) + if (m_ignore_map_edit_events_area.contains(event.getArea())) return; - MapEditEvent *e = event->clone(); - m_unsent_map_edit_queue.push(e); + + m_unsent_map_edit_queue.push(new MapEditEvent(event)); } Inventory* Server::getInventory(const InventoryLocation &loc) @@ -1234,26 +1183,22 @@ Inventory* Server::getInventory(const InventoryLocation &loc) return NULL; } -void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend) +void Server::setInventoryModified(const InventoryLocation &loc) { switch(loc.type){ case InventoryLocation::UNDEFINED: break; case InventoryLocation::PLAYER: { - if (!playerSend) - return; RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); if (!player) return; - PlayerSAO *playersao = player->getPlayerSAO(); - if(!playersao) - return; - - SendInventory(playersao); + player->setModified(true); + player->inventory.setModified(true); + // Updates are sent in ServerEnvironment::step() } break; case InventoryLocation::NODEMETA: @@ -1261,12 +1206,12 @@ void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend) MapEditEvent event; event.type = MEET_BLOCK_NODE_METADATA_CHANGED; event.p = loc.p; - m_env->getMap().dispatchEvent(&event); + m_env->getMap().dispatchEvent(event); } break; case InventoryLocation::DETACHED: { - sendDetachedInventory(loc.name,PEER_ID_INEXISTENT); + // Updates are sent in ServerEnvironment::step() } break; default: @@ -1338,7 +1283,7 @@ bool Server::getClientInfo( *major = client->getMajor(); *minor = client->getMinor(); *patch = client->getPatch(); - *vers_string = client->getPatch(); + *vers_string = client->getFull(); m_clients.unlock(); @@ -1527,21 +1472,27 @@ void Server::SendNodeDef(session_t peer_id, Non-static send methods */ -void Server::SendInventory(PlayerSAO* playerSAO) +void Server::SendInventory(PlayerSAO *sao, bool incremental) { - UpdateCrafting(playerSAO->getPlayer()); + RemotePlayer *player = sao->getPlayer(); + + // Do not send new format to old clients + incremental &= player->protocol_version >= 38; + + UpdateCrafting(player); /* Serialize it */ - NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID()); + NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID()); - std::ostringstream os; - playerSAO->getInventory()->serialize(os); - - std::string s = os.str(); + std::ostringstream os(std::ios::binary); + sao->getInventory()->serialize(os, incremental); + sao->getInventory()->setModified(false); + player->setModified(true); + const std::string &s = os.str(); pkt.putRawString(s.c_str(), s.size()); Send(&pkt); } @@ -1567,7 +1518,7 @@ void Server::SendChatMessage(session_t peer_id, const ChatMessage &message) void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec, const std::string &formname) { - NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id); + NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id); if (formspec.empty()){ //the client should close the formspec //but make sure there wasn't another one open in meantime @@ -1578,7 +1529,7 @@ void Server::SendShowFormspecMessage(session_t peer_id, const std::string &forms pkt.putLongString(""); } else { m_formspec_state_data[peer_id] = formname; - pkt.putLongString(FORMSPEC_VERSION_STRING + formspec); + pkt.putLongString(formspec); } pkt << formname; @@ -1703,7 +1654,8 @@ void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form) pkt << id << (u8) form->type << form->pos << form->name << form->scale << form->text << form->number << form->item << form->dir - << form->align << form->offset << form->world_pos << form->size; + << form->align << form->offset << form->world_pos << form->size + << form->z_index; Send(&pkt); } @@ -1816,10 +1768,7 @@ void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed) void Server::SendPlayerHP(session_t peer_id) { PlayerSAO *playersao = getPlayerSAO(peer_id); - // In some rare case if the player is disconnected - // while Lua call l_punch, for example, this can be NULL - if (!playersao) - return; + assert(playersao); SendHP(peer_id, playersao->getHP()); m_script->player_event(playersao,"health_changed"); @@ -1860,6 +1809,16 @@ void Server::SendMovePlayer(session_t peer_id) Send(&pkt); } +void Server::SendPlayerFov(session_t peer_id) +{ + NetworkPacket pkt(TOCLIENT_FOV, 4 + 1, peer_id); + + PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov(); + pkt << fov_spec.fov << fov_spec.is_multiplier; + + Send(&pkt); +} + void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4], f32 animation_speed) { @@ -1907,7 +1866,8 @@ void Server::SendPlayerInventoryFormspec(session_t peer_id) return; NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id); - pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec); + pkt.putLongString(player->inventory_formspec); + Send(&pkt); } @@ -1919,16 +1879,107 @@ void Server::SendPlayerFormspecPrepend(session_t peer_id) return; NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id); - pkt << FORMSPEC_VERSION_STRING + player->formspec_prepend; + pkt << player->formspec_prepend; Send(&pkt); } -u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas) +void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao) { - NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id); - pkt.putRawString(datas.c_str(), datas.size()); + // Radius inside which objects are active + static thread_local const s16 radius = + g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE; + + // Radius inside which players are active + static thread_local const bool is_transfer_limited = + g_settings->exists("unlimited_player_transfer_distance") && + !g_settings->getBool("unlimited_player_transfer_distance"); + + static thread_local const s16 player_transfer_dist = + g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE; + + s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ? + radius : player_transfer_dist; + + s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE); + if (my_radius <= 0) + my_radius = radius; + + std::queue removed_objects, added_objects; + m_env->getRemovedActiveObjects(playersao, my_radius, player_radius, + client->m_known_objects, removed_objects); + m_env->getAddedActiveObjects(playersao, my_radius, player_radius, + client->m_known_objects, added_objects); + + int removed_count = removed_objects.size(); + int added_count = added_objects.size(); + + if (removed_objects.empty() && added_objects.empty()) + return; + + char buf[4]; + std::string data; + + // Handle removed objects + writeU16((u8*)buf, removed_objects.size()); + data.append(buf, 2); + while (!removed_objects.empty()) { + // Get object + u16 id = removed_objects.front(); + ServerActiveObject* obj = m_env->getActiveObject(id); + + // Add to data buffer for sending + writeU16((u8*)buf, id); + data.append(buf, 2); + + // Remove from known objects + client->m_known_objects.erase(id); + + if (obj && obj->m_known_by_count > 0) + obj->m_known_by_count--; + + removed_objects.pop(); + } + + // Handle added objects + writeU16((u8*)buf, added_objects.size()); + data.append(buf, 2); + while (!added_objects.empty()) { + // Get object + u16 id = added_objects.front(); + ServerActiveObject *obj = m_env->getActiveObject(id); + added_objects.pop(); + + if (!obj) { + warningstream << FUNCTION_NAME << ": NULL object id=" + << (int)id << std::endl; + continue; + } + + // Get object type + u8 type = obj->getSendType(); + + // Add to data buffer for sending + writeU16((u8*)buf, id); + data.append(buf, 2); + writeU8((u8*)buf, type); + data.append(buf, 1); + + data.append(serializeLongString( + obj->getClientInitializationData(client->net_proto_version))); + + // Add to known objects + client->m_known_objects.insert(id); + + obj->m_known_by_count++; + } + + NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id); + pkt.putRawString(data.c_str(), data.size()); Send(&pkt); - return pkt.getSize(); + + verbosestream << "Server::SendActiveObjectRemoveAdd: " + << removed_count << " removed, " << added_count << " added, " + << "packet size is " << pkt.getSize() << std::endl; } void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas, @@ -1959,8 +2010,18 @@ void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel) Send(&pkt); } +inline s32 Server::nextSoundId() +{ + s32 ret = m_next_sound_id; + if (m_next_sound_id == INT32_MAX) + m_next_sound_id = 0; // signed overflow is undefined + else + m_next_sound_id++; + return ret; +} + s32 Server::playSound(const SimpleSoundSpec &spec, - const ServerSoundParams ¶ms) + const ServerSoundParams ¶ms, bool ephemeral) { // Find out initial position of sound bool pos_exists = false; @@ -1971,7 +2032,7 @@ s32 Server::playSound(const SimpleSoundSpec &spec, // Filter destination clients std::vector dst_clients; - if(!params.to_player.empty()) { + if (!params.to_player.empty()) { RemotePlayer *player = m_env->getPlayer(params.to_player.c_str()); if(!player){ infostream<<"Server::playSound: Player \""<getPlayer(client_id); if (!player) continue; + if (!params.exclude_player.empty() && + params.exclude_player == player->getName()) + continue; PlayerSAO *sao = player->getPlayerSAO(); if (!sao) @@ -2009,27 +2073,32 @@ s32 Server::playSound(const SimpleSoundSpec &spec, return -1; // Create the sound - s32 id = m_next_sound_id++; - // The sound will exist as a reference in m_playing_sounds - m_playing_sounds[id] = ServerPlayingSound(); - ServerPlayingSound &psound = m_playing_sounds[id]; - psound.params = params; - psound.spec = spec; + s32 id; + ServerPlayingSound *psound = nullptr; + if (ephemeral) { + id = -1; // old clients will still use this, so pick a reserved ID + } else { + id = nextSoundId(); + // The sound will exist as a reference in m_playing_sounds + m_playing_sounds[id] = ServerPlayingSound(); + psound = &m_playing_sounds[id]; + psound->params = params; + psound->spec = spec; + } float gain = params.gain * spec.gain; NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0); pkt << id << spec.name << gain << (u8) params.type << pos << params.object - << params.loop << params.fade << params.pitch; + << params.loop << params.fade << params.pitch + << ephemeral; - // Backwards compability - bool play_sound = gain > 0; + bool as_reliable = !ephemeral; for (const u16 dst_client : dst_clients) { - if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) { - psound.clients.insert(dst_client); - m_clients.send(dst_client, 0, &pkt, true); - } + if (psound) + psound->clients.insert(dst_client); + m_clients.send(dst_client, 0, &pkt, as_reliable); } return id; } @@ -2595,8 +2664,9 @@ void Server::sendDetachedInventory(const std::string &name, session_t peer_id) // Serialization & NetworkPacket isn't a love story std::ostringstream os(std::ios_base::binary); inv_it->second->serialize(os); + inv_it->second->setModified(false); - std::string os_str = os.str(); + const std::string &os_str = os.str(); pkt << static_cast(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients pkt.putRawString(os_str); } @@ -2607,11 +2677,16 @@ void Server::sendDetachedInventory(const std::string &name, session_t peer_id) Send(&pkt); } -void Server::sendDetachedInventories(session_t peer_id) +void Server::sendDetachedInventories(session_t peer_id, bool incremental) { for (const auto &detached_inventory : m_detached_inventories) { const std::string &name = detached_inventory.first; - //Inventory *inv = i->second; + if (incremental) { + Inventory *inv = detached_inventory.second; + if (!inv || !inv->checkModified()) + continue; + } + sendDetachedInventory(name, peer_id); } } @@ -2623,10 +2698,7 @@ void Server::sendDetachedInventories(session_t peer_id) void Server::DiePlayer(session_t peer_id, const PlayerHPChangeReason &reason) { PlayerSAO *playersao = getPlayerSAO(peer_id); - // In some rare cases this can be NULL -- if the player is disconnected - // when a Lua function modifies l_punch, for example - if (!playersao) - return; + assert(playersao); infostream << "Server::DiePlayer(): Player " << playersao->getPlayer()->getName() @@ -2822,6 +2894,11 @@ void Server::UpdateCrafting(RemotePlayer *player) if (!clist || clist->getSize() == 0) return; + if (!clist->checkModified()) { + verbosestream << "Skip Server::UpdateCrafting(): list unmodified" << std::endl; + return; + } + // Get a preview for crafting ItemStack preview; InventoryLocation loc; @@ -3691,6 +3768,11 @@ void dedicated_server_loop(Server &server, bool &kill) static thread_local const float profiler_print_interval = g_settings->getFloat("profiler_print_interval"); + /* + * The dedicated server loop only does time-keeping (in Server::step) and + * provides a way to main.cpp to kill the server externally (bool &kill). + */ + for(;;) { // This is kind of a hack but can be done like this // because server.step() is very light