aboutsummaryrefslogtreecommitdiff
path: root/src/server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server.cpp')
-rw-r--r--src/server.cpp578
1 files changed, 345 insertions, 233 deletions
diff --git a/src/server.cpp b/src/server.cpp
index dc7b101a6..a3b686c25 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "ban.h"
#include "environment.h"
#include "map.h"
-#include "jthread/jmutexautolock.h"
+#include "threading/mutex_auto_lock.h"
#include "constants.h"
#include "voxel.h"
#include "config.h"
@@ -71,35 +71,29 @@ public:
{}
};
-class ServerThread : public JThread
+class ServerThread : public Thread
{
- Server *m_server;
-
public:
ServerThread(Server *server):
- JThread(),
+ Thread("Server"),
m_server(server)
- {
- }
+ {}
+
+ void *run();
- void * Thread();
+private:
+ Server *m_server;
};
-void *ServerThread::Thread()
+void *ServerThread::run()
{
- log_register_thread("ServerThread");
-
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
BEGIN_DEBUG_EXCEPTION_HANDLER
m_server->AsyncRunStep(true);
- ThreadStarted();
-
- porting::setThreadName("ServerThread");
-
- while (!StopRequested()) {
+ while (!stopRequested()) {
try {
//TimeTaker timer("AsyncRunStep() + Receive()");
@@ -118,7 +112,7 @@ void *ServerThread::Thread()
}
}
- END_DEBUG_EXCEPTION_HANDLER(errorstream)
+ END_DEBUG_EXCEPTION_HANDLER
return NULL;
}
@@ -154,7 +148,8 @@ Server::Server(
const std::string &path_world,
const SubgameSpec &gamespec,
bool simple_singleplayer_mode,
- bool ipv6
+ bool ipv6,
+ ChatInterface *iface
):
m_path_world(path_world),
m_gamespec(gamespec),
@@ -181,6 +176,7 @@ Server::Server(
m_clients(&m_con),
m_shutdown_requested(false),
m_shutdown_ask_reconnect(false),
+ m_admin_chat(iface),
m_ignore_map_edit_events(false),
m_ignore_map_edit_events_peer_id(0),
m_next_sound_id(0)
@@ -267,8 +263,8 @@ Server::Server(
errorstream << std::endl;
}
- // Lock environment
- JMutexAutoLock envlock(m_env_mutex);
+ //lock environment
+ MutexAutoLock envlock(m_env_mutex);
// Load mapgen params from Settings
m_emerge->loadMapgenParams();
@@ -282,41 +278,30 @@ Server::Server(
m_script = new GameScripting(this);
std::string script_path = getBuiltinLuaPath() + DIR_DELIM "init.lua";
- std::string error_msg;
- if (!m_script->loadMod(script_path, BUILTIN_MOD_NAME, &error_msg))
- throw ModError("Failed to load and run " + script_path
- + "\nError from Lua:\n" + error_msg);
+ m_script->loadMod(script_path, BUILTIN_MOD_NAME);
// Print mods
infostream << "Server: Loading mods: ";
for(std::vector<ModSpec>::iterator i = m_mods.begin();
- i != m_mods.end(); i++) {
+ i != m_mods.end(); ++i) {
const ModSpec &mod = *i;
infostream << mod.name << " ";
}
infostream << std::endl;
// Load and run "mod" scripts
- for (std::vector<ModSpec>::iterator i = m_mods.begin();
- i != m_mods.end(); i++) {
- const ModSpec &mod = *i;
+ for (std::vector<ModSpec>::iterator it = m_mods.begin();
+ it != m_mods.end(); ++it) {
+ const ModSpec &mod = *it;
if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
- std::ostringstream err;
- err << "Error loading mod \"" << mod.name
- << "\": mod_name does not follow naming conventions: "
- << "Only chararacters [a-z0-9_] are allowed." << std::endl;
- errorstream << err.str().c_str();
- throw ModError(err.str());
+ throw ModError("Error loading mod \"" + mod.name +
+ "\": Mod name does not follow naming conventions: "
+ "Only chararacters [a-z0-9_] are allowed.");
}
- std::string script_path = mod.path + DIR_DELIM "init.lua";
+ std::string script_path = mod.path + DIR_DELIM + "init.lua";
infostream << " [" << padStringRight(mod.name, 12) << "] [\""
<< script_path << "\"]" << std::endl;
- if (!m_script->loadMod(script_path, mod.name, &error_msg)) {
- errorstream << "Server: Failed to load and run "
- << script_path << std::endl;
- throw ModError("Failed to load and run " + script_path
- + "\nError from Lua:\n" + error_msg);
- }
+ m_script->loadMod(script_path, mod.name);
}
// Read Textures and calculate sha1 sums
@@ -335,6 +320,9 @@ Server::Server(
// Perform pending node name resolutions
m_nodedef->runNodeResolveCallbacks();
+ // unmap node names for connected nodeboxes
+ m_nodedef->mapNodeboxConnections();
+
// init the recipe hashes to speed up crafting
m_craftdef->initHashes(this);
@@ -359,10 +347,11 @@ Server::Server(
servermap->addEventReceiver(this);
// If file exists, load environment metadata
- if(fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt"))
- {
- infostream<<"Server: Loading environment metadata"<<std::endl;
+ if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
+ infostream << "Server: Loading environment metadata" << std::endl;
m_env->loadMeta();
+ } else {
+ m_env->loadDefaultMeta();
}
// Add some test ActiveBlockModifiers to environment
@@ -379,7 +368,7 @@ Server::~Server()
SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down");
{
- JMutexAutoLock envlock(m_env_mutex);
+ MutexAutoLock envlock(m_env_mutex);
// Execute script shutdown hooks
m_script->on_shutdown();
@@ -432,14 +421,14 @@ Server::~Server()
// Delete detached inventories
for (std::map<std::string, Inventory*>::iterator
i = m_detached_inventories.begin();
- i != m_detached_inventories.end(); i++) {
+ i != m_detached_inventories.end(); ++i) {
delete i->second;
}
}
void Server::start(Address bind_addr)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
m_bind_addr = bind_addr;
@@ -447,14 +436,14 @@ void Server::start(Address bind_addr)
<< bind_addr.serializeString() <<"..."<<std::endl;
// Stop thread if already running
- m_thread->Stop();
+ m_thread->stop();
// Initialize connection
m_con.SetTimeoutMs(30);
m_con.Serve(bind_addr);
// Start thread
- m_thread->Start();
+ m_thread->start();
// ASCII art for the win!
actionstream
@@ -472,14 +461,14 @@ void Server::start(Address bind_addr)
void Server::stop()
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
infostream<<"Server: Stopping and waiting threads"<<std::endl;
// Stop threads (set run=false first so both start stopping)
- m_thread->Stop();
+ m_thread->stop();
//m_emergethread.setRun(false);
- m_thread->Wait();
+ m_thread->wait();
//m_emergethread.stop();
infostream<<"Server: Threads stopped"<<std::endl;
@@ -487,41 +476,35 @@ void Server::stop()
void Server::step(float dtime)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
// Limit a bit
- if(dtime > 2.0)
+ if (dtime > 2.0)
dtime = 2.0;
{
- JMutexAutoLock lock(m_step_dtime_mutex);
+ MutexAutoLock lock(m_step_dtime_mutex);
m_step_dtime += dtime;
}
// Throw if fatal error occurred in thread
std::string async_err = m_async_fatal_error.get();
- if(async_err != "") {
- if (m_simple_singleplayer_mode) {
- throw ServerError(async_err);
- }
- else {
+ if (!async_err.empty()) {
+ if (!m_simple_singleplayer_mode) {
m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
g_settings->get("kick_msg_crash"),
g_settings->getBool("ask_reconnect_on_crash"));
- errorstream << "UNRECOVERABLE error occurred. Stopping server. "
- << "Please fix the following error:" << std::endl
- << async_err << std::endl;
- FATAL_ERROR(async_err.c_str());
}
+ throw ServerError(async_err);
}
}
void Server::AsyncRunStep(bool initial_step)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
g_profiler->add("Server::AsyncRunStep (num)", 1);
float dtime;
{
- JMutexAutoLock lock1(m_step_dtime_mutex);
+ MutexAutoLock lock1(m_step_dtime_mutex);
dtime = m_step_dtime;
}
@@ -539,7 +522,7 @@ void Server::AsyncRunStep(bool initial_step)
//infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
{
- JMutexAutoLock lock1(m_step_dtime_mutex);
+ MutexAutoLock lock1(m_step_dtime_mutex);
m_step_dtime -= dtime;
}
@@ -570,7 +553,7 @@ void Server::AsyncRunStep(bool initial_step)
}
{
- JMutexAutoLock lock(m_env_mutex);
+ MutexAutoLock lock(m_env_mutex);
// Figure out and report maximum lag to environment
float max_lag = m_env->getMaxLagEstimate();
max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
@@ -590,12 +573,28 @@ void Server::AsyncRunStep(bool initial_step)
static const float map_timer_and_unload_dtime = 2.92;
if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
{
- JMutexAutoLock lock(m_env_mutex);
+ MutexAutoLock lock(m_env_mutex);
// Run Map's timers and unload unused data
ScopeProfiler sp(g_profiler, "Server: map timer and unload");
m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
g_settings->getFloat("server_unload_unused_data_timeout"),
- (u32)-1);
+ U32_MAX);
+ }
+
+ /*
+ Listen to the admin chat, if available
+ */
+ if (m_admin_chat) {
+ if (!m_admin_chat->command_queue.empty()) {
+ MutexAutoLock lock(m_env_mutex);
+ while (!m_admin_chat->command_queue.empty()) {
+ ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
+ handleChatInterfaceEvent(evt);
+ delete evt;
+ }
+ }
+ m_admin_chat->outgoing_queue.push_back(
+ new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
}
/*
@@ -608,7 +607,7 @@ void Server::AsyncRunStep(bool initial_step)
{
m_liquid_transform_timer -= m_liquid_transform_every;
- JMutexAutoLock lock(m_env_mutex);
+ MutexAutoLock lock(m_env_mutex);
ScopeProfiler sp(g_profiler, "Server: liquid transform");
@@ -669,27 +668,28 @@ void Server::AsyncRunStep(bool initial_step)
*/
{
//infostream<<"Server: Checking added and deleted active objects"<<std::endl;
- JMutexAutoLock envlock(m_env_mutex);
+ MutexAutoLock envlock(m_env_mutex);
- m_clients.Lock();
+ m_clients.lock();
std::map<u16, RemoteClient*> clients = m_clients.getClientList();
ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
// Radius inside which objects are active
- s16 radius = g_settings->getS16("active_object_send_range_blocks");
- s16 player_radius = g_settings->getS16("player_transfer_distance");
-
- if (player_radius == 0 && g_settings->exists("unlimited_player_transfer_distance") &&
- !g_settings->getBool("unlimited_player_transfer_distance"))
+ static const s16 radius =
+ g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
+
+ // Radius inside which players are active
+ static const bool is_transfer_limited =
+ g_settings->exists("unlimited_player_transfer_distance") &&
+ !g_settings->getBool("unlimited_player_transfer_distance");
+ static 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;
- radius *= MAP_BLOCKSIZE;
- player_radius *= MAP_BLOCKSIZE;
-
- for(std::map<u16, RemoteClient*>::iterator
+ for (std::map<u16, RemoteClient*>::iterator
i = clients.begin();
- i != clients.end(); ++i)
- {
+ i != clients.end(); ++i) {
RemoteClient *client = i->second;
// If definitions and textures have not been sent, don't
@@ -698,27 +698,23 @@ void Server::AsyncRunStep(bool initial_step)
continue;
Player *player = m_env->getPlayer(client->peer_id);
- if(player==NULL)
- {
+ if(player == NULL) {
// This can happen if the client timeouts somehow
- /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
+ /*warningstream<<FUNCTION_NAME<<": Client "
<<client->peer_id
<<" has no associated player"<<std::endl;*/
continue;
}
- v3s16 pos = floatToInt(player->getPosition(), BS);
- std::set<u16> removed_objects;
- std::set<u16> added_objects;
- m_env->getRemovedActiveObjects(pos, radius, player_radius,
+ std::queue<u16> removed_objects;
+ std::queue<u16> added_objects;
+ m_env->getRemovedActiveObjects(player, radius, player_radius,
client->m_known_objects, removed_objects);
- m_env->getAddedActiveObjects(pos, radius, player_radius,
+ m_env->getAddedActiveObjects(player, radius, player_radius,
client->m_known_objects, added_objects);
// Ignore if nothing happened
- if(removed_objects.empty() && added_objects.empty())
- {
- //infostream<<"active objects: none changed"<<std::endl;
+ if (removed_objects.empty() && added_objects.empty()) {
continue;
}
@@ -729,12 +725,9 @@ void Server::AsyncRunStep(bool initial_step)
// Handle removed objects
writeU16((u8*)buf, removed_objects.size());
data_buffer.append(buf, 2);
- for(std::set<u16>::iterator
- i = removed_objects.begin();
- i != removed_objects.end(); ++i)
- {
+ while (!removed_objects.empty()) {
// Get object
- u16 id = *i;
+ u16 id = removed_objects.front();
ServerActiveObject* obj = m_env->getActiveObject(id);
// Add to data buffer for sending
@@ -746,23 +739,21 @@ void Server::AsyncRunStep(bool initial_step)
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);
- for(std::set<u16>::iterator
- i = added_objects.begin();
- i != added_objects.end(); ++i)
- {
+ while (!added_objects.empty()) {
// Get object
- u16 id = *i;
+ u16 id = added_objects.front();
ServerActiveObject* obj = m_env->getActiveObject(id);
// Get object type
u8 type = ACTIVEOBJECT_TYPE_INVALID;
if(obj == NULL)
- infostream<<"WARNING: "<<__FUNCTION_NAME
+ warningstream<<FUNCTION_NAME
<<": NULL object"<<std::endl;
else
type = obj->getSendType();
@@ -784,6 +775,8 @@ void Server::AsyncRunStep(bool initial_step)
if(obj)
obj->m_known_by_count++;
+
+ added_objects.pop();
}
u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
@@ -792,14 +785,14 @@ void Server::AsyncRunStep(bool initial_step)
<< added_objects.size() << " added, "
<< "packet size is " << pktSize << std::endl;
}
- m_clients.Unlock();
+ m_clients.unlock();
}
/*
Send object messages
*/
{
- JMutexAutoLock envlock(m_env_mutex);
+ MutexAutoLock envlock(m_env_mutex);
ScopeProfiler sp(g_profiler, "Server: sending object messages");
// Key = object id
@@ -825,7 +818,7 @@ void Server::AsyncRunStep(bool initial_step)
message_list->push_back(aom);
}
- m_clients.Lock();
+ m_clients.lock();
std::map<u16, RemoteClient*> clients = m_clients.getClientList();
// Route data to every client
for (std::map<u16, RemoteClient*>::iterator
@@ -876,7 +869,7 @@ void Server::AsyncRunStep(bool initial_step)
SendActiveObjectMessages(client->peer_id, unreliable_data, false);
}
}
- m_clients.Unlock();
+ m_clients.unlock();
// Clear buffered_messages
for(std::map<u16, std::vector<ActiveObjectMessage>* >::iterator
@@ -891,7 +884,7 @@ void Server::AsyncRunStep(bool initial_step)
*/
{
// We will be accessing the environment
- JMutexAutoLock lock(m_env_mutex);
+ MutexAutoLock lock(m_env_mutex);
// Don't send too many at a time
//u32 count = 0;
@@ -945,7 +938,7 @@ void Server::AsyncRunStep(bool initial_step)
break;
default:
prof.add("unknown", 1);
- infostream << "WARNING: Server: Unknown MapEditEvent "
+ warningstream << "Server: Unknown MapEditEvent "
<< ((u32)event->type) << std::endl;
break;
}
@@ -997,8 +990,7 @@ void Server::AsyncRunStep(bool initial_step)
{
float &counter = m_emergethread_trigger_timer;
counter += dtime;
- if(counter >= 2.0)
- {
+ if (counter >= 2.0) {
counter = 0.0;
m_emerge->startThreads();
@@ -1009,10 +1001,11 @@ void Server::AsyncRunStep(bool initial_step)
{
float &counter = m_savemap_timer;
counter += dtime;
- if(counter >= g_settings->getFloat("server_map_save_interval"))
- {
+ static const float save_interval =
+ g_settings->getFloat("server_map_save_interval");
+ if (counter >= save_interval) {
counter = 0.0;
- JMutexAutoLock lock(m_env_mutex);
+ MutexAutoLock lock(m_env_mutex);
ScopeProfiler sp(g_profiler, "Server: saving stuff");
@@ -1035,7 +1028,7 @@ void Server::AsyncRunStep(bool initial_step)
void Server::Receive()
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
SharedBuffer<u8> data;
u16 peer_id;
try {
@@ -1068,7 +1061,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
{
std::string playername = "";
PlayerSAO *playersao = NULL;
- m_clients.Lock();
+ m_clients.lock();
try {
RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
if (client != NULL) {
@@ -1076,10 +1069,10 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
}
} catch (std::exception &e) {
- m_clients.Unlock();
+ m_clients.unlock();
throw;
}
- m_clients.Unlock();
+ m_clients.unlock();
RemotePlayer *player =
static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
@@ -1131,16 +1124,19 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
// Send information about joining in chat
{
- std::wstring name = L"unknown";
+ std::string name = "unknown";
Player *player = m_env->getPlayer(peer_id);
if(player != NULL)
- name = narrow_to_wide(player->getName());
+ name = player->getName();
std::wstring message;
message += L"*** ";
- message += name;
+ message += narrow_to_wide(name);
message += L" joined the game.";
SendChatMessage(PEER_ID_INEXISTENT,message);
+ if (m_admin_chat)
+ m_admin_chat->outgoing_queue.push_back(
+ new ChatEventNick(CET_NICK_ADD, name));
}
}
Address addr = getPeerAddress(player->peer_id);
@@ -1155,7 +1151,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
actionstream<<player->getName() <<" joins game. List of players: ";
for (std::vector<std::string>::iterator i = names.begin();
- i != names.end(); i++) {
+ i != names.end(); ++i) {
actionstream << *i << " ";
}
@@ -1172,9 +1168,9 @@ inline void Server::handleCommand(NetworkPacket* pkt)
void Server::ProcessData(NetworkPacket *pkt)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
// Environment is locked first.
- JMutexAutoLock envlock(m_env_mutex);
+ MutexAutoLock envlock(m_env_mutex);
ScopeProfiler sp(g_profiler, "Server::ProcessData");
u32 peer_id = pkt->getPeerId();
@@ -1356,19 +1352,19 @@ void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
{
std::vector<u16> clients = m_clients.getClientIDs();
- m_clients.Lock();
+ m_clients.lock();
// Set the modified blocks unsent for all the clients
for (std::vector<u16>::iterator i = clients.begin();
i != clients.end(); ++i) {
if (RemoteClient *client = m_clients.lockedGetClientNoEx(*i))
client->SetBlocksNotSent(block);
}
- m_clients.Unlock();
+ m_clients.unlock();
}
void Server::peerAdded(con::Peer *peer)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
verbosestream<<"Server::peerAdded(): peer->id="
<<peer->id<<std::endl;
@@ -1381,7 +1377,7 @@ void Server::peerAdded(con::Peer *peer)
void Server::deletingPeer(con::Peer *peer, bool timeout)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
verbosestream<<"Server::deletingPeer(): peer->id="
<<peer->id<<", timeout="<<timeout<<std::endl;
@@ -1413,11 +1409,11 @@ bool Server::getClientInfo(
)
{
*state = m_clients.getClientState(peer_id);
- m_clients.Lock();
+ m_clients.lock();
RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
if (client == NULL) {
- m_clients.Unlock();
+ m_clients.unlock();
return false;
}
@@ -1430,7 +1426,7 @@ bool Server::getClientInfo(
*patch = client->getPatch();
*vers_string = client->getPatch();
- m_clients.Unlock();
+ m_clients.unlock();
return true;
}
@@ -1463,6 +1459,16 @@ void Server::handlePeerChanges()
}
}
+void Server::printToConsoleOnly(const std::string &text)
+{
+ if (m_admin_chat) {
+ m_admin_chat->outgoing_queue.push_back(
+ new ChatEventChat("", utf8_to_wide(text)));
+ } else {
+ std::cout << text << std::endl;
+ }
+}
+
void Server::Send(NetworkPacket* pkt)
{
m_clients.send(pkt->getPeerId(),
@@ -1473,7 +1479,7 @@ void Server::Send(NetworkPacket* pkt)
void Server::SendMovement(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
@@ -1510,7 +1516,7 @@ void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
void Server::SendHP(u16 peer_id, u8 hp)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
pkt << hp;
@@ -1519,7 +1525,7 @@ void Server::SendHP(u16 peer_id, u8 hp)
void Server::SendBreath(u16 peer_id, u16 breath)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
pkt << (u16) breath;
@@ -1543,7 +1549,7 @@ void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
pkt << reason;
@@ -1553,7 +1559,7 @@ void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
v3f camera_point_target)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
pkt << set_camera_point_target << camera_point_target;
@@ -1563,7 +1569,7 @@ void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
void Server::SendItemDef(u16 peer_id,
IItemDefManager *itemdef, u16 protocol_version)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
@@ -1588,7 +1594,7 @@ void Server::SendItemDef(u16 peer_id,
void Server::SendNodeDef(u16 peer_id,
INodeDefManager *nodedef, u16 protocol_version)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
@@ -1617,7 +1623,7 @@ void Server::SendNodeDef(u16 peer_id,
void Server::SendInventory(PlayerSAO* playerSAO)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
UpdateCrafting(playerSAO->getPlayer());
@@ -1638,7 +1644,7 @@ void Server::SendInventory(PlayerSAO* playerSAO)
void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
pkt << message;
@@ -1654,7 +1660,7 @@ void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
const std::string &formname)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
@@ -1669,7 +1675,7 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat
float expirationtime, float size, bool collisiondetection,
bool vertical, std::string texture)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
@@ -1691,7 +1697,7 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3
v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
@@ -1713,7 +1719,7 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3
void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY, 2, peer_id);
@@ -1823,7 +1829,7 @@ void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
pkt << time << time_speed;
@@ -1838,9 +1844,9 @@ void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
void Server::SendPlayerHP(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
- // In some rare case, if the player is disconnected
+ // In some rare case if the player is disconnected
// while Lua call l_punch, for example, this can be NULL
if (!playersao)
return;
@@ -1856,7 +1862,7 @@ void Server::SendPlayerHP(u16 peer_id)
void Server::SendPlayerBreath(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
@@ -1866,7 +1872,7 @@ void Server::SendPlayerBreath(u16 peer_id)
void Server::SendMovePlayer(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
Player *player = m_env->getPlayer(peer_id);
assert(player);
@@ -1918,7 +1924,7 @@ void Server::SendPlayerPrivileges(u16 peer_id)
pkt << (u16) privs.size();
for(std::set<std::string>::const_iterator i = privs.begin();
- i != privs.end(); i++) {
+ i != privs.end(); ++i) {
pkt << (*i);
}
@@ -2018,7 +2024,7 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
<< (u8) params.type << pos << params.object << params.loop;
for(std::vector<u16>::iterator i = dst_clients.begin();
- i != dst_clients.end(); i++) {
+ i != dst_clients.end(); ++i) {
psound.clients.insert(*i);
m_clients.send(*i, 0, &pkt, true);
}
@@ -2037,7 +2043,7 @@ void Server::stopSound(s32 handle)
pkt << handle;
for(std::set<u16>::iterator i = psound.clients.begin();
- i != psound.clients.end(); i++) {
+ i != psound.clients.end(); ++i) {
// Send as reliable
m_clients.send(*i, 0, &pkt, true);
}
@@ -2098,7 +2104,7 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
}
NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
- m_clients.Lock();
+ m_clients.lock();
RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
if (client != 0) {
pkt << p << n.param0 << n.param1 << n.param2
@@ -2108,11 +2114,11 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
if (client->net_proto_version <= 21) {
// Old clients always clear metadata; fix it
// by sending the full block again.
- client->SetBlockNotSent(p);
+ client->SetBlockNotSent(getNodeBlockPos(p));
}
}
}
- m_clients.Unlock();
+ m_clients.unlock();
// Send as reliable
if (pkt.getSize() > 0)
@@ -2123,18 +2129,18 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
void Server::setBlockNotSent(v3s16 p)
{
std::vector<u16> clients = m_clients.getClientIDs();
- m_clients.Lock();
+ m_clients.lock();
for(std::vector<u16>::iterator i = clients.begin();
i != clients.end(); ++i) {
RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
client->SetBlockNotSent(p);
}
- m_clients.Unlock();
+ m_clients.unlock();
}
void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
v3s16 p = block->getPos();
@@ -2156,9 +2162,9 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto
void Server::SendBlocks(float dtime)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
- JMutexAutoLock envlock(m_env_mutex);
+ 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");
@@ -2172,7 +2178,7 @@ void Server::SendBlocks(float dtime)
std::vector<u16> clients = m_clients.getClientIDs();
- m_clients.Lock();
+ m_clients.lock();
for(std::vector<u16>::iterator i = clients.begin();
i != clients.end(); ++i) {
RemoteClient *client = m_clients.lockedGetClientNoEx(*i, CS_Active);
@@ -2183,7 +2189,7 @@ void Server::SendBlocks(float dtime)
total_sending += client->SendingCount();
client->GetNextBlocks(m_env,m_emerge, dtime, queue);
}
- m_clients.Unlock();
+ m_clients.unlock();
}
// Sort.
@@ -2191,7 +2197,7 @@ void Server::SendBlocks(float dtime)
// Lowest is most important.
std::sort(queue.begin(), queue.end());
- m_clients.Lock();
+ m_clients.lock();
for(u32 i=0; i<queue.size(); i++)
{
//TODO: Calculate limit dynamically
@@ -2221,19 +2227,19 @@ void Server::SendBlocks(float dtime)
client->SentBlock(q.pos);
total_sending++;
}
- m_clients.Unlock();
+ m_clients.unlock();
}
void Server::fillMediaCache()
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
infostream<<"Server: Calculating media file checksums"<<std::endl;
// Collect all media file paths
std::vector<std::string> paths;
for(std::vector<ModSpec>::iterator i = m_mods.begin();
- i != m_mods.end(); i++) {
+ i != m_mods.end(); ++i) {
const ModSpec &mod = *i;
paths.push_back(mod.path + DIR_DELIM + "textures");
paths.push_back(mod.path + DIR_DELIM + "sounds");
@@ -2244,7 +2250,7 @@ void Server::fillMediaCache()
// Collect media file information from paths into cache
for(std::vector<std::string>::iterator i = paths.begin();
- i != paths.end(); i++) {
+ i != paths.end(); ++i) {
std::string mediapath = *i;
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
for (u32 j = 0; j < dirlist.size(); j++) {
@@ -2322,7 +2328,7 @@ void Server::fillMediaCache()
void Server::sendMediaAnnouncement(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
verbosestream << "Server: Announcing files to id(" << peer_id << ")"
<< std::endl;
@@ -2359,7 +2365,7 @@ struct SendableMedia
void Server::sendRequestedMedia(u16 peer_id,
const std::vector<std::string> &tosend)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
verbosestream<<"Server::sendRequestedMedia(): "
<<"Sending files to client"<<std::endl;
@@ -2466,7 +2472,7 @@ void Server::sendRequestedMedia(u16 peer_id,
void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
{
if(m_detached_inventories.count(name) == 0) {
- errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
+ errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
return;
}
Inventory *inv = m_detached_inventories[name];
@@ -2491,11 +2497,11 @@ void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
void Server::sendDetachedInventories(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
for(std::map<std::string, Inventory*>::iterator
i = m_detached_inventories.begin();
- i != m_detached_inventories.end(); i++) {
+ i != m_detached_inventories.end(); ++i) {
const std::string &name = i->first;
//Inventory *inv = i->second;
sendDetachedInventory(name, peer_id);
@@ -2508,10 +2514,12 @@ void Server::sendDetachedInventories(u16 peer_id)
void Server::DiePlayer(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
-
+ DSTACK(FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
- assert(playersao);
+ // 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;
infostream << "Server::DiePlayer(): Player "
<< playersao->getPlayer()->getName()
@@ -2528,7 +2536,7 @@ void Server::DiePlayer(u16 peer_id)
void Server::RespawnPlayer(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
@@ -2554,7 +2562,7 @@ void Server::RespawnPlayer(u16 peer_id)
void Server::DenySudoAccess(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
Send(&pkt);
@@ -2565,7 +2573,7 @@ void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode
const std::string &str_reason, bool reconnect)
{
if (proto_ver >= 25) {
- SendAccessDenied(peer_id, reason, str_reason);
+ SendAccessDenied(peer_id, reason, str_reason, reconnect);
} else {
std::wstring wreason = utf8_to_wide(
reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
@@ -2580,7 +2588,7 @@ void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode
void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
SendAccessDenied(peer_id, reason, custom_reason);
m_clients.event(peer_id, CSE_SetDenied);
@@ -2591,7 +2599,7 @@ void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string
// the minimum version for MT users, maybe in 1 year
void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
SendAccessDenied_Legacy(peer_id, reason);
m_clients.event(peer_id, CSE_SetDenied);
@@ -2600,7 +2608,7 @@ void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
void Server::acceptAuth(u16 peer_id, bool forSudoMode)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
if (!forSudoMode) {
RemoteClient* client = getClient(peer_id, CS_Invalid);
@@ -2631,7 +2639,7 @@ void Server::acceptAuth(u16 peer_id, bool forSudoMode)
void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
std::wstring message;
{
/*
@@ -2646,7 +2654,7 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
if(psound.clients.empty())
m_playing_sounds.erase(i++);
else
- i++;
+ ++i;
}
Player *player = m_env->getPlayer(peer_id);
@@ -2696,13 +2704,17 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
os << player->getName() << " ";
}
- actionstream << player->getName() << " "
+ std::string name = player->getName();
+ actionstream << name << " "
<< (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
<< " List of players: " << os.str() << std::endl;
+ if (m_admin_chat)
+ m_admin_chat->outgoing_queue.push_back(
+ new ChatEventNick(CET_NICK_REMOVE, name));
}
}
{
- JMutexAutoLock env_lock(m_env_mutex);
+ MutexAutoLock env_lock(m_env_mutex);
m_clients.DeleteClient(peer_id);
}
}
@@ -2714,7 +2726,7 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
void Server::UpdateCrafting(Player* player)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
// Get a preview for crafting
ItemStack preview;
@@ -2731,6 +2743,102 @@ void Server::UpdateCrafting(Player* player)
plist->changeItem(0, preview);
}
+void Server::handleChatInterfaceEvent(ChatEvent *evt)
+{
+ if (evt->type == CET_NICK_ADD) {
+ // The terminal informed us of its nick choice
+ m_admin_nick = ((ChatEventNick *)evt)->nick;
+ if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
+ errorstream << "You haven't set up an account." << std::endl
+ << "Please log in using the client as '"
+ << m_admin_nick << "' with a secure password." << std::endl
+ << "Until then, you can't execute admin tasks via the console," << std::endl
+ << "and everybody can claim the user account instead of you," << std::endl
+ << "giving them full control over this server." << std::endl;
+ }
+ } else {
+ assert(evt->type == CET_CHAT);
+ handleAdminChat((ChatEventChat *)evt);
+ }
+}
+
+std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
+ const std::wstring &wmessage, bool check_shout_priv,
+ u16 peer_id_to_avoid_sending)
+{
+ // If something goes wrong, this player is to blame
+ RollbackScopeActor rollback_scope(m_rollback,
+ std::string("player:") + name);
+
+ // Line to send
+ std::wstring line;
+ // Whether to send line to the player that sent the message, or to all players
+ bool broadcast_line = true;
+
+ // Run script hook
+ bool ate = m_script->on_chat_message(name,
+ wide_to_utf8(wmessage));
+ // If script ate the message, don't proceed
+ if (ate)
+ return L"";
+
+ // Commands are implemented in Lua, so only catch invalid
+ // commands that were not "eaten" and send an error back
+ if (wmessage[0] == L'/') {
+ std::wstring wcmd = wmessage.substr(1);
+ broadcast_line = false;
+ if (wcmd.length() == 0)
+ line += L"-!- Empty command";
+ else
+ line += L"-!- Invalid command: " + str_split(wcmd, L' ')[0];
+ } else {
+ if (check_shout_priv && !checkPriv(name, "shout")) {
+ line += L"-!- You don't have permission to shout.";
+ broadcast_line = false;
+ } else {
+ line += L"<";
+ line += wname;
+ line += L"> ";
+ line += wmessage;
+ }
+ }
+
+ /*
+ Tell calling method to send the message to sender
+ */
+ if (!broadcast_line) {
+ return line;
+ } else {
+ /*
+ Send the message to others
+ */
+ actionstream << "CHAT: " << wide_to_narrow(line) << std::endl;
+
+ std::vector<u16> clients = m_clients.getClientIDs();
+
+ for (u16 i = 0; i < clients.size(); i++) {
+ u16 cid = clients[i];
+ if (cid != peer_id_to_avoid_sending)
+ SendChatMessage(cid, line);
+ }
+ }
+ return L"";
+}
+
+void Server::handleAdminChat(const ChatEventChat *evt)
+{
+ std::string name = evt->nick;
+ std::wstring wname = utf8_to_wide(name);
+ std::wstring wmessage = evt->evt_msg;
+
+ std::wstring answer = handleChat(name, wname, wmessage);
+
+ // If asked to send answer to sender
+ if (!answer.empty()) {
+ m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
+ }
+}
+
RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
{
RemoteClient *client = getClientNoEx(peer_id,state_min);
@@ -2862,9 +2970,14 @@ void Server::notifyPlayer(const char *name, const std::wstring &msg)
if (!m_env)
return;
+ if (m_admin_nick == name && !m_admin_nick.empty()) {
+ m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
+ }
+
Player *player = m_env->getPlayer(name);
- if (!player)
+ if (!player) {
return;
+ }
if (player->peer_id == PEER_ID_INEXISTENT)
return;
@@ -2929,7 +3042,8 @@ bool Server::hudSetFlags(Player *player, u32 flags, u32 mask)
return false;
SendHUDSetFlags(player->peer_id, flags, mask);
- player->hud_flags = flags;
+ player->hud_flags &= ~mask;
+ player->hud_flags |= flags;
PlayerSAO* playersao = player->getPlayerSAO();
@@ -3082,19 +3196,7 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime,
peer_id = player->peer_id;
}
- u32 id = 0;
- for(;;) // look for unused particlespawner id
- {
- id++;
- if (std::find(m_particlespawner_ids.begin(),
- m_particlespawner_ids.end(), id)
- == m_particlespawner_ids.end())
- {
- m_particlespawner_ids.push_back(id);
- break;
- }
- }
-
+ u32 id = m_env->addParticleSpawner(spawntime);
SendAddParticleSpawner(peer_id, amount, spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc,
minexptime, maxexptime, minsize, maxsize,
@@ -3117,13 +3219,16 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id)
peer_id = player->peer_id;
}
- m_particlespawner_ids.erase(
- std::remove(m_particlespawner_ids.begin(),
- m_particlespawner_ids.end(), id),
- m_particlespawner_ids.end());
+ m_env->deleteParticleSpawner(id);
SendDeleteParticleSpawner(peer_id, id);
}
+void Server::deleteParticleSpawnerAll(u32 id)
+{
+ m_env->deleteParticleSpawner(id);
+ SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id);
+}
+
Inventory* Server::createDetachedInventory(const std::string &name)
{
if(m_detached_inventories.count(name) > 0){
@@ -3159,7 +3264,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
for(std::list<RollbackAction>::const_iterator
i = actions.begin();
- i != actions.end(); i++)
+ i != actions.end(); ++i)
{
const RollbackAction &action = *i;
num_tried++;
@@ -3277,30 +3382,24 @@ v3f Server::findSpawnPos()
return nodeposf * BS;
}
- // Default position is static_spawnpoint
- // We will return it if we don't found a good place
- v3s16 nodepos(nodeposf.X, nodeposf.Y, nodeposf.Z);
-
- s16 water_level = map.getWaterLevel();
-
bool is_good = false;
// Try to find a good place a few times
- for(s32 i = 0; i < 1000 && !is_good; i++) {
+ for(s32 i = 0; i < 4000 && !is_good; i++) {
s32 range = 1 + i;
// We're going to try to throw the player to this position
v2s16 nodepos2d = v2s16(
-range + (myrand() % (range * 2)),
-range + (myrand() % (range * 2)));
- // Get ground height at point
- s16 groundheight = map.findGroundLevel(nodepos2d);
- if (groundheight <= water_level) // Don't go underwater
- continue;
- if (groundheight > water_level + 6) // Don't go to high places
+ // 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;
- nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
+ v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
s32 air_count = 0;
for (s32 i = 0; i < 10; i++) {
@@ -3309,7 +3408,11 @@ v3f Server::findSpawnPos()
content_t c = map.getNodeNoEx(nodepos).getContent();
if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
air_count++;
- if (air_count >= 2){
+ if (air_count >= 2) {
+ nodeposf = intToFloat(nodepos, BS);
+ // Don't spawn the player outside map boundaries
+ if (objectpos_over_limit(nodeposf))
+ continue;
is_good = true;
break;
}
@@ -3318,7 +3421,7 @@ v3f Server::findSpawnPos()
}
}
- return intToFloat(nodepos, BS);
+ return nodeposf;
}
PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
@@ -3367,6 +3470,16 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version
// Add player to environment
m_env->addPlayer(player);
+ } else {
+ // If the player exists, ensure that they respawn inside legal bounds
+ // This fixes an assert crash when the player can't be added
+ // to the environment
+ if (objectpos_over_limit(player->getPosition())) {
+ actionstream << "Respawn position for player \""
+ << name << "\" outside limits, resetting" << std::endl;
+ v3f pos = findSpawnPos();
+ player->setPosition(pos);
+ }
}
// Create a new player active object
@@ -3392,15 +3505,17 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version
void dedicated_server_loop(Server &server, bool &kill)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
verbosestream<<"dedicated_server_loop()"<<std::endl;
IntervalLimiter m_profiler_interval;
- for(;;)
- {
- float steplen = g_settings->getFloat("dedicated_server_step");
+ static const float steplen = g_settings->getFloat("dedicated_server_step");
+ static const float profiler_print_interval =
+ g_settings->getFloat("profiler_print_interval");
+
+ for(;;) {
// This is kind of a hack but can be done like this
// because server.step() is very light
{
@@ -3422,10 +3537,7 @@ void dedicated_server_loop(Server &server, bool &kill)
/*
Profiler
*/
- float profiler_print_interval =
- g_settings->getFloat("profiler_print_interval");
- if(profiler_print_interval != 0)
- {
+ if (profiler_print_interval != 0) {
if(m_profiler_interval.step(steplen, profiler_print_interval))
{
infostream<<"Profiler:"<<std::endl;