summaryrefslogtreecommitdiff
path: root/src/server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server.cpp')
-rw-r--r--src/server.cpp483
1 files changed, 279 insertions, 204 deletions
diff --git a/src/server.cpp b/src/server.cpp
index 172bb4744..4aa8375c8 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -220,6 +220,7 @@ Server::Server(
m_itemdef(createItemDefManager()),
m_nodedef(createNodeDefManager()),
m_craftdef(createCraftDefManager()),
+ m_thread(new ServerThread(this)),
m_uptime(0),
m_clients(m_con),
m_admin_chat(iface),
@@ -305,6 +306,11 @@ Server::~Server()
for (auto &detached_inventory : m_detached_inventories) {
delete detached_inventory.second;
}
+
+ while (!m_unsent_map_edit_queue.empty()) {
+ delete m_unsent_map_edit_queue.front();
+ m_unsent_map_edit_queue.pop();
+ }
}
void Server::init()
@@ -321,9 +327,6 @@ void Server::init()
if (!loadGameConfAndInitWorld(m_path_world, m_gamespec))
throw ServerError("Failed to initialize world");
- // Create server thread
- m_thread = new ServerThread(this);
-
// Create emerge manager
m_emerge = new EmergeManager(this);
@@ -472,7 +475,6 @@ void Server::step(float dtime)
void Server::AsyncRunStep(bool initial_step)
{
- g_profiler->add("Server::AsyncRunStep (num)", 1);
float dtime;
{
@@ -488,10 +490,7 @@ void Server::AsyncRunStep(bool initial_step)
if((dtime < 0.001) && !initial_step)
return;
- g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
-
- //infostream<<"Server steps "<<dtime<<std::endl;
- //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
+ ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
{
MutexAutoLock lock1(m_step_dtime_mutex);
@@ -537,8 +536,6 @@ void Server::AsyncRunStep(bool initial_step)
}
m_env->reportMaxLagEstimate(max_lag);
// Step environment
- ScopeProfiler sp(g_profiler, "SEnv step");
- ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
m_env->step(dtime);
}
@@ -628,124 +625,27 @@ void Server::AsyncRunStep(bool initial_step)
m_clients.lock();
const RemoteClientMap &clients = m_clients.getClientList();
- ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
-
- // 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;
- 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<u16> removed_objects;
- std::queue<u16> 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;
@@ -764,7 +664,7 @@ void Server::AsyncRunStep(bool initial_step)
*/
{
MutexAutoLock envlock(m_env_mutex);
- ScopeProfiler sp(g_profiler, "Server: sending object messages");
+ ScopeProfiler sp(g_profiler, "Server: send SAO messages");
// Key = object id
// Value = data sent by object
@@ -794,19 +694,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<ActiveObjectMessage>* 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
@@ -974,7 +888,7 @@ void Server::AsyncRunStep(bool initial_step)
counter = 0.0;
MutexAutoLock lock(m_env_mutex);
- ScopeProfiler sp(g_profiler, "Server: saving stuff");
+ ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
// Save ban file
if (m_banmanager->isModified()) {
@@ -1065,7 +979,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())
@@ -1097,9 +1011,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);
}
@@ -1108,7 +1022,7 @@ void Server::ProcessData(NetworkPacket *pkt)
// Environment is locked first.
MutexAutoLock envlock(m_env_mutex);
- ScopeProfiler sp(g_profiler, "Server::ProcessData");
+ ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
u32 peer_id = pkt->getPeerId();
try {
@@ -1195,12 +1109,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)
@@ -1242,26 +1156,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:
@@ -1269,12 +1179,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:
@@ -1428,10 +1338,10 @@ void Server::SendMovement(session_t peer_id)
void Server::SendPlayerHPOrDie(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
{
- if (!g_settings->getBool("enable_damage"))
+ if (playersao->isImmortal())
return;
- session_t peer_id = playersao->getPeerID();
+ session_t peer_id = playersao->getPeerID();
bool is_alive = playersao->getHP() > 0;
if (is_alive)
@@ -1535,21 +1445,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());
-
- std::ostringstream os;
- playerSAO->getInventory()->serialize(os);
+ NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
- 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);
}
@@ -1573,9 +1489,9 @@ 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)
+ 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
@@ -1586,7 +1502,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;
@@ -1868,6 +1784,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)
{
@@ -1915,7 +1841,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);
}
@@ -1927,16 +1854,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<u16> 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,
@@ -1960,6 +1978,13 @@ void Server::SendCSMRestrictionFlags(session_t peer_id)
Send(&pkt);
}
+void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
+{
+ NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
+ pkt << added_vel;
+ Send(&pkt);
+}
+
s32 Server::playSound(const SimpleSoundSpec &spec,
const ServerSoundParams &params)
{
@@ -2253,14 +2278,12 @@ void Server::SendBlocks(float dtime)
MutexAutoLock envlock(m_env_mutex);
//TODO check if one big lock could be faster then multiple small ones
- ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
-
std::vector<PrioritySortedBlockTransfer> queue;
u32 total_sending = 0;
{
- ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
+ ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
std::vector<session_t> clients = m_clients.getClientIDs();
@@ -2289,16 +2312,16 @@ void Server::SendBlocks(float dtime)
u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
+ ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
+ Map &map = m_env->getMap();
+
for (const PrioritySortedBlockTransfer &block_to_send : queue) {
if (total_sending >= max_blocks_to_send)
break;
- MapBlock *block = nullptr;
- try {
- block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
- } catch (const InvalidPositionException &e) {
+ MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
+ if (!block)
continue;
- }
RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
CS_Active);
@@ -2314,6 +2337,25 @@ void Server::SendBlocks(float dtime)
m_clients.unlock();
}
+bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
+{
+ MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
+ if (!block)
+ return false;
+
+ m_clients.lock();
+ RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
+ if (!client || client->isBlockSent(blockpos)) {
+ m_clients.unlock();
+ return false;
+ }
+ SendBlockNoLock(peer_id, block, client->serialization_version,
+ client->net_proto_version);
+ m_clients.unlock();
+
+ return true;
+}
+
void Server::fillMediaCache()
{
infostream<<"Server: Calculating media file checksums"<<std::endl;
@@ -2555,6 +2597,9 @@ void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
player_it->second.empty()) {
// OK. Send to everyone
} else {
+ if (!m_env)
+ return; // Mods are not done loading
+
RemotePlayer *p = m_env->getPlayer(player_it->second.c_str());
if (!p)
return; // Player is offline
@@ -2576,8 +2621,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<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
pkt.putRawString(os_str);
}
@@ -2588,11 +2634,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);
}
}
@@ -2803,6 +2854,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;
@@ -2843,28 +2899,28 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna
{
// If something goes wrong, this player is to blame
RollbackScopeActor rollback_scope(m_rollback,
- std::string("player:") + name);
+ std::string("player:") + name);
if (g_settings->getBool("strip_color_codes"))
wmessage = unescape_enriched(wmessage);
if (player) {
switch (player->canSendChatMessage()) {
- case RPLAYER_CHATRESULT_FLOODING: {
- std::wstringstream ws;
- ws << L"You cannot send more messages. You are limited to "
- << g_settings->getFloat("chat_message_limit_per_10sec")
- << L" messages per 10 seconds.";
- return ws.str();
- }
- case RPLAYER_CHATRESULT_KICK:
- DenyAccess_Legacy(player->getPeerId(),
- L"You have been kicked due to message flooding.");
- return L"";
- case RPLAYER_CHATRESULT_OK:
- break;
- default:
- FATAL_ERROR("Unhandled chat filtering result found.");
+ case RPLAYER_CHATRESULT_FLOODING: {
+ std::wstringstream ws;
+ ws << L"You cannot send more messages. You are limited to "
+ << g_settings->getFloat("chat_message_limit_per_10sec")
+ << L" messages per 10 seconds.";
+ return ws.str();
+ }
+ case RPLAYER_CHATRESULT_KICK:
+ DenyAccess_Legacy(player->getPeerId(),
+ L"You have been kicked due to message flooding.");
+ return L"";
+ case RPLAYER_CHATRESULT_OK:
+ break;
+ default:
+ FATAL_ERROR("Unhandled chat filtering result found.");
}
}
@@ -2892,10 +2948,8 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna
line += L"-!- You don't have permission to shout.";
broadcast_line = false;
} else {
- line += L"<";
- line += wname;
- line += L"> ";
- line += wmessage;
+ line += narrow_to_wide(m_script->formatChatMessage(name,
+ wide_to_narrow(wmessage)));
}
/*
@@ -3187,7 +3241,7 @@ bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
return true;
}
-void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
+void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
{
if (!player)
return;
@@ -3196,7 +3250,7 @@ void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
}
-void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
+void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
{
if (!player)
return;
@@ -3352,7 +3406,9 @@ Inventory* Server::createDetachedInventory(const std::string &name, const std::s
Inventory *inv = new Inventory(m_itemdef);
sanity_check(inv);
m_detached_inventories[name] = inv;
- m_detached_inventories_player[name] = player;
+ if (!player.empty())
+ m_detached_inventories_player[name] = player;
+
//TODO find a better way to do this
sendDetachedInventory(name,PEER_ID_INEXISTENT);
return inv;
@@ -3367,6 +3423,9 @@ bool Server::removeDetachedInventory(const std::string &name)
delete inv_it->second;
m_detached_inventories.erase(inv_it);
+ if (!m_env) // Mods are not done loading
+ return true;
+
const auto &player_it = m_detached_inventories_player.find(name);
if (player_it != m_detached_inventories_player.end()) {
RemotePlayer *player = m_env->getPlayer(player_it->second.c_str());
@@ -3492,52 +3551,71 @@ v3f Server::findSpawnPos()
{
ServerMap &map = m_env->getServerMap();
v3f nodeposf;
- if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
+ if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
return nodeposf * BS;
- }
bool is_good = false;
// Limit spawn range to mapgen edges (determined by 'mapgen_limit')
s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
// Try to find a good place a few times
- for(s32 i = 0; i < 4000 && !is_good; i++) {
+ for (s32 i = 0; i < 4000 && !is_good; i++) {
s32 range = MYMIN(1 + i, range_max);
// We're going to try to throw the player to this position
v2s16 nodepos2d = v2s16(
-range + (myrand() % (range * 2)),
-range + (myrand() % (range * 2)));
-
// Get spawn level at point
s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
- // Continue if MAX_MAP_GENERATION_LIMIT was returned by
- // the mapgen to signify an unsuitable spawn position
- if (spawn_level == MAX_MAP_GENERATION_LIMIT)
+ // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
+ // signify an unsuitable spawn position, or if outside limits.
+ if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
+ spawn_level <= -MAX_MAP_GENERATION_LIMIT)
continue;
v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
-
+ // Consecutive empty nodes
s32 air_count = 0;
- for (s32 i = 0; i < 10; i++) {
+
+ // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
+ // avoid obstructions in already-generated mapblocks.
+ // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
+ // no obstructions, but mapgen decorations are generated after spawn so
+ // the player may end up inside one.
+ for (s32 i = 0; i < 8; i++) {
v3s16 blockpos = getNodeBlockPos(nodepos);
map.emergeBlock(blockpos, true);
- content_t c = map.getNodeNoEx(nodepos).getContent();
- if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
+ content_t c = map.getNode(nodepos).getContent();
+
+ // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
+ // In ungenerated mapblocks allow spawn in 'ignore' nodes.
+ if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
air_count++;
if (air_count >= 2) {
+ // Spawn in lower empty node
+ nodepos.Y--;
nodeposf = intToFloat(nodepos, BS);
// Don't spawn the player outside map boundaries
if (objectpos_over_limit(nodeposf))
- continue;
+ // Exit this loop, positions above are probably over limit
+ break;
+
+ // Good position found, cause an exit from main loop
is_good = true;
break;
}
+ } else {
+ air_count = 0;
}
nodepos.Y++;
}
}
- return nodeposf;
+ if (is_good)
+ return nodeposf;
+
+ // No suitable spawn point found, return fallback 0,0,0
+ return v3f(0.0f, 0.0f, 0.0f);
}
void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
@@ -3653,10 +3731,7 @@ void dedicated_server_loop(Server &server, bool &kill)
for(;;) {
// This is kind of a hack but can be done like this
// because server.step() is very light
- {
- ScopeProfiler sp(g_profiler, "dedicated server sleep");
- sleep_ms((int)(steplen*1000.0));
- }
+ sleep_ms((int)(steplen*1000.0));
server.step(steplen);
if (server.isShutdownRequested() || kill)