aboutsummaryrefslogtreecommitdiff
path: root/src/server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server.cpp')
-rw-r--r--src/server.cpp349
1 files changed, 222 insertions, 127 deletions
diff --git a/src/server.cpp b/src/server.cpp
index a9e5c3d08..4b54c2398 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -38,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "profiler.h"
#include "log.h"
-#include "serverscripting.h"
+#include "scripting_server.h"
#include "nodedef.h"
#include "itemdef.h"
#include "craftdef.h"
@@ -60,6 +60,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/base64.h"
#include "util/sha1.h"
#include "util/hex.h"
+#include "database.h"
class ClientNotFoundException : public BaseException
{
@@ -89,6 +90,15 @@ void *ServerThread::run()
DSTACK(FUNCTION_NAME);
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()) {
@@ -99,7 +109,6 @@ void *ServerThread::run()
m_server->Receive();
- } catch (con::NoIncomingDataException &e) {
} catch (con::PeerNotFoundException &e) {
infostream<<"Server: PeerNotFoundException"<<std::endl;
} catch (ClientNotFoundException &e) {
@@ -229,32 +238,6 @@ Server::Server(
modconf.printUnsatisfiedModsError();
}
- Settings worldmt_settings;
- std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
- worldmt_settings.readConfigFile(worldmt.c_str());
- std::vector<std::string> names = worldmt_settings.getNames();
- std::set<std::string> load_mod_names;
- for(std::vector<std::string>::iterator it = names.begin();
- it != names.end(); ++it) {
- std::string name = *it;
- if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
- load_mod_names.insert(name.substr(9));
- }
- // complain about mods declared to be loaded, but not found
- for(std::vector<ModSpec>::iterator it = m_mods.begin();
- it != m_mods.end(); ++it)
- load_mod_names.erase((*it).name);
- for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
- it != unsatisfied_mods.end(); ++it)
- load_mod_names.erase((*it).name);
- if(!load_mod_names.empty()) {
- errorstream << "The following mods could not be found:";
- for(std::set<std::string>::iterator it = load_mod_names.begin();
- it != load_mod_names.end(); ++it)
- errorstream << " \"" << (*it) << "\"";
- errorstream << std::endl;
- }
-
//lock environment
MutexAutoLock envlock(m_env_mutex);
@@ -282,7 +265,7 @@ Server::Server(
if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
throw ModError("Error loading mod \"" + mod.name +
"\": Mod name does not follow naming conventions: "
- "Only chararacters [a-z0-9_] are allowed.");
+ "Only characters [a-z0-9_] are allowed.");
}
std::string script_path = mod.path + DIR_DELIM + "init.lua";
infostream << " [" << padStringRight(mod.name, 12) << "] [\""
@@ -352,17 +335,14 @@ Server::Server(
Server::~Server()
{
- infostream<<"Server destructing"<<std::endl;
+ infostream << "Server destructing" << std::endl;
// Send shutdown message
SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down");
{
MutexAutoLock envlock(m_env_mutex);
-
- // Execute script shutdown hooks
- m_script->on_shutdown();
-
+
infostream << "Server: Saving players" << std::endl;
m_env->saveLoadedPlayers();
@@ -378,6 +358,20 @@ Server::~Server()
}
m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
kick_msg, reconnect);
+ }
+
+ // Do this before stopping the server in case mapgen callbacks need to access
+ // server-controlled resources (like ModStorages). Also do them before
+ // shutdown callbacks since they may modify state that is finalized in a
+ // callback.
+ m_emerge->stopThreads();
+
+ {
+ MutexAutoLock envlock(m_env_mutex);
+
+ // Execute script shutdown hooks
+ infostream << "Executing shutdown hooks" << std::endl;
+ m_script->on_shutdown();
infostream << "Server: Saving environment metadata" << std::endl;
m_env->saveMeta();
@@ -387,10 +381,6 @@ Server::~Server()
stop();
delete m_thread;
- // stop all emerge threads before deleting players that may have
- // requested blocks to be emerged
- m_emerge->stopThreads();
-
// Delete things in the reverse order of creation
delete m_emerge;
delete m_env;
@@ -402,7 +392,7 @@ Server::~Server()
delete m_craftdef;
// Deinitialize scripting
- infostream<<"Server: Deinitializing scripting"<<std::endl;
+ infostream << "Server: Deinitializing scripting" << std::endl;
delete m_script;
// Delete detached inventories
@@ -433,7 +423,7 @@ void Server::start(Address bind_addr)
m_thread->start();
// ASCII art for the win!
- actionstream
+ rawstream
<<" .__ __ __ "<<std::endl
<<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
<<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
@@ -599,7 +589,7 @@ void Server::AsyncRunStep(bool initial_step)
ScopeProfiler sp(g_profiler, "Server: liquid transform");
std::map<v3s16, MapBlock*> modified_blocks;
- m_env->getMap().transformLiquids(modified_blocks);
+ m_env->getMap().transformLiquids(modified_blocks, m_env);
#if 0
/*
Update lighting
@@ -1070,30 +1060,45 @@ void Server::Receive()
DSTACK(FUNCTION_NAME);
SharedBuffer<u8> data;
u16 peer_id;
- try {
- NetworkPacket pkt;
- m_con.Receive(&pkt);
- peer_id = pkt.getPeerId();
- ProcessData(&pkt);
- }
- catch(con::InvalidIncomingDataException &e) {
- infostream<<"Server::Receive(): "
- "InvalidIncomingDataException: what()="
- <<e.what()<<std::endl;
- }
- catch(SerializationError &e) {
- infostream<<"Server::Receive(): "
- "SerializationError: what()="
- <<e.what()<<std::endl;
- }
- catch(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(con::PeerNotFoundException &e) {
- // Do nothing
- }
+ bool first = true;
+ NetworkPacket pkt;
+ 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;
+ }
+ }
+
}
PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
@@ -1668,15 +1673,20 @@ void Server::SendInventory(PlayerSAO* playerSAO)
void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
{
DSTACK(FUNCTION_NAME);
+ if (peer_id != PEER_ID_INEXISTENT) {
+ NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
- NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
- pkt << message;
+ if (m_clients.getProtocolVersion(peer_id) < 27)
+ pkt << unescape_enriched(message);
+ else
+ pkt << message;
- if (peer_id != PEER_ID_INEXISTENT) {
Send(&pkt);
- }
- else {
- m_clients.sendToAll(&pkt);
+ } else {
+ std::vector<u16> clients = m_clients.getClientIDs();
+ for (std::vector<u16>::iterator it = clients.begin();
+ it != clients.end(); ++it)
+ SendChatMessage(*it, message);
}
}
@@ -1706,13 +1716,25 @@ void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version,
const struct TileAnimationParams &animation, u8 glow)
{
DSTACK(FUNCTION_NAME);
+ static const float radius =
+ g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
+
if (peer_id == PEER_ID_INEXISTENT) {
- // This sucks and should be replaced by a better solution in a refactor:
std::vector<u16> clients = m_clients.getClientIDs();
+
for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
RemotePlayer *player = m_env->getPlayer(*i);
if (!player)
continue;
+
+ PlayerSAO *sao = player->getPlayerSAO();
+ if (!sao)
+ continue;
+
+ // Do not send to distant clients
+ if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
+ continue;
+
SendSpawnParticle(*i, player->protocol_version,
pos, velocity, acceleration,
expirationtime, size, collisiondetection,
@@ -1870,7 +1892,8 @@ void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
}
void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
- const std::string &type, const std::vector<std::string> &params)
+ const std::string &type, const std::vector<std::string> &params,
+ bool &clouds)
{
NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
pkt << bgcolor << type << (u16) params.size();
@@ -1878,6 +1901,22 @@ void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
for(size_t i=0; i<params.size(); i++)
pkt << params[i];
+ pkt << clouds;
+
+ Send(&pkt);
+}
+
+void Server::SendCloudParams(u16 peer_id, float density,
+ const video::SColor &color_bright,
+ const video::SColor &color_ambient,
+ float height,
+ float thickness,
+ const v2f &speed)
+{
+ NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
+ pkt << density << color_bright << color_ambient
+ << height << thickness << speed;
+
Send(&pkt);
}
@@ -2085,15 +2124,23 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
m_playing_sounds[id] = ServerPlayingSound();
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 << (float) (spec.gain * params.gain)
- << (u8) params.type << pos << params.object << params.loop;
+ pkt << id << spec.name << gain
+ << (u8) params.type << pos << params.object
+ << params.loop << params.fade;
+
+ // Backwards compability
+ bool play_sound = gain > 0;
- for(std::vector<u16>::iterator i = dst_clients.begin();
+ for (std::vector<u16>::iterator i = dst_clients.begin();
i != dst_clients.end(); ++i) {
- psound.clients.insert(*i);
- m_clients.send(*i, 0, &pkt, true);
+ if (play_sound || m_clients.getProtocolVersion(*i) >= 32) {
+ psound.clients.insert(*i);
+ m_clients.send(*i, 0, &pkt, true);
+ }
}
return id;
}
@@ -2117,6 +2164,52 @@ void Server::stopSound(s32 handle)
m_playing_sounds.erase(i);
}
+void Server::fadeSound(s32 handle, float step, float gain)
+{
+ // Get sound reference
+ UNORDERED_MAP<s32, ServerPlayingSound>::iterator i =
+ m_playing_sounds.find(handle);
+ if (i == m_playing_sounds.end())
+ return;
+
+ ServerPlayingSound &psound = i->second;
+ psound.params.gain = gain;
+
+ NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
+ pkt << handle << step << gain;
+
+ // Backwards compability
+ bool play_sound = gain > 0;
+ ServerPlayingSound compat_psound = psound;
+ compat_psound.clients.clear();
+
+ NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
+ compat_pkt << handle;
+
+ for (UNORDERED_SET<u16>::iterator it = psound.clients.begin();
+ it != psound.clients.end();) {
+ if (m_clients.getProtocolVersion(*it) >= 32) {
+ // Send as reliable
+ m_clients.send(*it, 0, &pkt, true);
+ ++it;
+ } else {
+ compat_psound.clients.insert(*it);
+ // Stop old sound
+ m_clients.send(*it, 0, &compat_pkt, true);
+ psound.clients.erase(it++);
+ }
+ }
+
+ // Remove sound reference
+ if (!play_sound || psound.clients.size() == 0)
+ m_playing_sounds.erase(i);
+
+ if (play_sound && compat_psound.clients.size() > 0) {
+ // Play new sound volume on older clients
+ playSound(compat_psound.spec, compat_psound.params);
+ }
+}
+
void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
std::vector<u16> *far_players, float far_d_nodes)
{
@@ -2618,9 +2711,8 @@ void Server::RespawnPlayer(u16 peer_id)
bool repositioned = m_script->on_respawnplayer(playersao);
if (!repositioned) {
- v3f pos = findSpawnPos();
// setPos will send the new position to client
- playersao->setPos(pos);
+ playersao->setPos(findSpawnPos());
}
SendPlayerHP(peer_id);
@@ -2813,12 +2905,15 @@ void Server::handleChatInterfaceEvent(ChatEvent *evt)
}
std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
- const std::wstring &wmessage, bool check_shout_priv, RemotePlayer *player)
+ std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
{
// If something goes wrong, this player is to blame
RollbackScopeActor rollback_scope(m_rollback,
std::string("player:") + name);
+ if (g_settings->getBool("strip_color_codes"))
+ wmessage = unescape_enriched(wmessage);
+
if (player) {
switch (player->canSendChatMessage()) {
case RPLAYER_CHATRESULT_FLOODING: {
@@ -2873,7 +2968,7 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna
/*
Send the message to others
*/
- actionstream << "CHAT: " << wide_to_narrow(line) << std::endl;
+ actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
std::vector<u16> clients = m_clients.getClientIDs();
@@ -3186,13 +3281,30 @@ bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
}
bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
- const std::string &type, const std::vector<std::string> &params)
+ const std::string &type, const std::vector<std::string> &params,
+ bool &clouds)
{
if (!player)
return false;
- player->setSky(bgcolor, type, params);
- SendSetSky(player->peer_id, bgcolor, type, params);
+ player->setSky(bgcolor, type, params, clouds);
+ SendSetSky(player->peer_id, bgcolor, type, params, clouds);
+ return true;
+}
+
+bool Server::setClouds(RemotePlayer *player, float density,
+ const video::SColor &color_bright,
+ const video::SColor &color_ambient,
+ float height,
+ float thickness,
+ const v2f &speed)
+{
+ if (!player)
+ return false;
+
+ SendCloudParams(player->peer_id, density,
+ color_bright, color_ambient, height,
+ thickness, speed);
return true;
}
@@ -3436,14 +3548,16 @@ v3f Server::findSpawnPos()
}
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++) {
- s32 range = 1 + 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)));
+ -range + (myrand() % (range * 2)),
+ -range + (myrand() % (range * 2)));
// Get spawn level at point
s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
@@ -3479,9 +3593,16 @@ v3f Server::findSpawnPos()
void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
{
+ m_shutdown_timer = delay;
+ m_shutdown_msg = msg;
+ m_shutdown_ask_reconnect = reconnect;
+
if (delay == 0.0f) {
// No delay, shutdown immediately
m_shutdown_requested = true;
+ // only print to the infostream, a chat message saying
+ // "Server Shutting Down" is sent when the server destructs.
+ infostream << "*** Immediate Server shutdown requested." << std::endl;
} else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
// Negative delay, cancel shutdown if requested
m_shutdown_timer = 0.0f;
@@ -3495,10 +3616,7 @@ void Server::requestShutdown(const std::string &msg, bool reconnect, float delay
infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
SendChatMessage(PEER_ID_INEXISTENT, ws.str());
} else if (delay > 0.0f) {
- // Positive delay, delay the shutdown
- m_shutdown_timer = delay;
- m_shutdown_msg = msg;
- m_shutdown_ask_reconnect = reconnect;
+ // Positive delay, tell the clients when the server will shut down
std::wstringstream ws;
ws << L"*** Server shutting down in "
@@ -3512,8 +3630,6 @@ void Server::requestShutdown(const std::string &msg, bool reconnect, float delay
PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
{
- bool newplayer = false;
-
/*
Try to get an existing player
*/
@@ -3534,44 +3650,18 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version
return NULL;
}
- // Create a new player active object
- PlayerSAO *playersao = new PlayerSAO(m_env, peer_id, isSingleplayer());
- player = m_env->loadPlayer(name, playersao);
-
- // Create player if it doesn't exist
if (!player) {
- newplayer = true;
- player = new RemotePlayer(name, this->idef());
- // Set player position
- infostream<<"Server: Finding spawn place for player \""
- <<name<<"\""<<std::endl;
- playersao->setBasePosition(findSpawnPos());
-
- // Make sure the player is saved
- player->setModified(true);
-
- // 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(playersao->getBasePosition())) {
- actionstream << "Respawn position for player \""
- << name << "\" outside limits, resetting" << std::endl;
- playersao->setBasePosition(findSpawnPos());
- }
+ player = new RemotePlayer(name, idef());
}
- playersao->initialize(player, getPlayerEffectivePrivs(player->getName()));
-
- player->protocol_version = proto_version;
+ bool newplayer = false;
- /* Clean up old HUD elements from previous sessions */
- player->clearHud();
+ // Load player
+ PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
- /* Add object to environment */
- m_env->addActiveObject(playersao);
+ // Complete init with server parts
+ playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
+ player->protocol_version = proto_version;
/* Run scripts */
if (newplayer) {
@@ -3615,6 +3705,11 @@ void dedicated_server_loop(Server &server, bool &kill)
static 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