#include "mg_biome.h"
#include "content_mapnode.h"
#include "content_nodemeta.h"
-#include "content_abm.h"
#include "content_sao.h"
#include "mods.h"
#include "event_manager.h"
m_simple_singleplayer_mode(simple_singleplayer_mode),
m_dedicated(dedicated),
m_async_fatal_error(""),
- m_env(NULL),
m_con(PROTOCOL_ID,
512,
CONNECTION_TIMEOUT,
ipv6,
this),
- m_banmanager(NULL),
- m_rollback(NULL),
- m_enable_rollback_recording(false),
- m_emerge(NULL),
- m_script(NULL),
m_itemdef(createItemDefManager()),
m_nodedef(createNodeDefManager()),
m_craftdef(createCraftDefManager()),
m_event(new EventManager()),
- m_thread(NULL),
- m_time_of_day_send_timer(0),
m_uptime(0),
m_clients(&m_con),
- m_shutdown_requested(false),
- m_shutdown_ask_reconnect(false),
- m_shutdown_timer(0.0f),
- m_admin_chat(iface),
- m_ignore_map_edit_events(false),
- m_ignore_map_edit_events_peer_id(0),
- m_next_sound_id(0),
- m_mod_storage_save_timer(10.0f)
-{
- m_liquid_transform_timer = 0.0;
- m_liquid_transform_every = 1.0;
- m_masterserver_timer = 0.0;
- m_emergethread_trigger_timer = 0.0;
- m_savemap_timer = 0.0;
-
- m_step_dtime = 0.0;
+ m_admin_chat(iface)
+{
m_lag = g_settings->getFloat("dedicated_server_step");
if(path_world == "")
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);
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) << "] [\""
m_env->loadDefaultMeta();
}
- // Add some test ActiveBlockModifiers to environment
- add_legacy_abms(m_env, m_nodedef);
-
m_liquid_transform_every = g_settings->getFloat("liquid_update");
m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
}
MutexAutoLock envlock(m_env_mutex);
m_clients.lock();
- UNORDERED_MAP<u16, RemoteClient*> clients = m_clients.getClientList();
+ RemoteClientMap clients = m_clients.getClientList();
ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
// Radius inside which objects are active
- static const s16 radius =
+ static thread_local 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 =
+ static thread_local 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;
+ 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;
- for (UNORDERED_MAP<u16, RemoteClient*>::iterator i = clients.begin();
+ for (RemoteClientMap::iterator i = clients.begin();
i != clients.end(); ++i) {
RemoteClient *client = i->second;
if (m_mod_storage_save_timer <= 0.0f) {
infostream << "Saving registered mod storages." << std::endl;
m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
- for (UNORDERED_MAP<std::string, ModMetadata *>::const_iterator
+ for (std::unordered_map<std::string, ModMetadata *>::const_iterator
it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
if (it->second->isModified()) {
it->second->save(getModStoragePath());
// Key = object id
// Value = data sent by object
- UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* > buffered_messages;
+ std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
// Get active object messages from environment
for(;;) {
break;
std::vector<ActiveObjectMessage>* message_list = NULL;
- UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator n;
+ std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
n = buffered_messages.find(aom.id);
if (n == buffered_messages.end()) {
message_list = new std::vector<ActiveObjectMessage>;
}
m_clients.lock();
- UNORDERED_MAP<u16, RemoteClient*> clients = m_clients.getClientList();
+ RemoteClientMap clients = m_clients.getClientList();
// Route data to every client
- for (UNORDERED_MAP<u16, RemoteClient*>::iterator i = clients.begin();
- i != clients.end(); ++i) {
+ for (std::unordered_map<u16, RemoteClient*>::iterator i = clients.begin();
+ i != clients.end(); ++i) {
RemoteClient *client = i->second;
std::string reliable_data;
std::string unreliable_data;
// Go through all objects in message buffer
- for (UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator
+ for (std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator
j = buffered_messages.begin();
j != buffered_messages.end(); ++j) {
// If object is not known by client, skip it
m_clients.unlock();
// Clear buffered_messages
- for (UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator
+ for (std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator
i = buffered_messages.begin();
i != buffered_messages.end(); ++i) {
delete i->second;
{
float &counter = m_savemap_timer;
counter += dtime;
- static const float save_interval =
+ static thread_local const float save_interval =
g_settings->getFloat("server_map_save_interval");
if (counter >= save_interval) {
counter = 0.0;
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 {
+ for (u16 id : m_clients.getClientIDs())
+ SendChatMessage(id, message);
}
}
const struct TileAnimationParams &animation, u8 glow)
{
DSTACK(FUNCTION_NAME);
+ static thread_local 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,
}
void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
- const std::string &type, const std::vector<std::string> ¶ms)
+ const std::string &type, const std::vector<std::string> ¶ms,
+ bool &clouds)
{
NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
pkt << bgcolor << type << (u16) params.size();
for(size_t i=0; i<params.size(); i++)
pkt << params[i];
+ pkt << clouds;
+
Send(&pkt);
}
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 << params.pitch;
+
+ // 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;
}
void Server::stopSound(s32 handle)
{
// Get sound reference
- UNORDERED_MAP<s32, ServerPlayingSound>::iterator i = m_playing_sounds.find(handle);
+ std::unordered_map<s32, ServerPlayingSound>::iterator i =
+ m_playing_sounds.find(handle);
if (i == m_playing_sounds.end())
return;
ServerPlayingSound &psound = i->second;
NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
pkt << handle;
- for (UNORDERED_SET<u16>::iterator i = psound.clients.begin();
- i != psound.clients.end(); ++i) {
+ for (std::unordered_set<u16>::const_iterator si = psound.clients.begin();
+ si != psound.clients.end(); ++si) {
// Send as reliable
- m_clients.send(*i, 0, &pkt, true);
+ m_clients.send(*si, 0, &pkt, true);
}
// Remove sound reference
m_playing_sounds.erase(i);
}
+void Server::fadeSound(s32 handle, float step, float gain)
+{
+ // Get sound reference
+ std::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 (std::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)
{
NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
pkt << (u16) m_media.size();
- for (UNORDERED_MAP<std::string, MediaInfo>::iterator i = m_media.begin();
+ for (std::unordered_map<std::string, MediaInfo>::iterator i = m_media.begin();
i != m_media.end(); ++i) {
pkt << i->first << i->second.sha1_digest;
}
/*
Clear references to playing sounds
*/
- for (UNORDERED_MAP<s32, ServerPlayingSound>::iterator
+ for (std::unordered_map<s32, ServerPlayingSound>::iterator
i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
ServerPlayingSound &psound = i->second;
psound.clients.erase(peer_id);
PlayerSAO *playersao = player->getPlayerSAO();
assert(playersao);
+ // inform connected clients
+ NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
+ // (u16) 1 + std::string represents a vector serialization representation
+ notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
+ m_clients.sendToAll(¬ice);
+ // run scripts
m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
playersao->disconnected();
}
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: {
/*
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();
}
bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
- const std::string &type, const std::vector<std::string> ¶ms)
+ const std::string &type, const std::vector<std::string> ¶ms,
+ 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 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)),
if (delay == 0.0f) {
// No delay, shutdown immediately
m_shutdown_requested = true;
- // only print to the infostream, a chat message saying
+ // 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) {
void Server::unregisterModStorage(const std::string &name)
{
- UNORDERED_MAP<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
+ std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
if (it != m_mod_storages.end()) {
// Save unconditionaly on unregistration
it->second->save(getModStoragePath());
IntervalLimiter m_profiler_interval;
- static const float steplen = g_settings->getFloat("dedicated_server_step");
- static const float profiler_print_interval =
+ static thread_local const float steplen =
+ g_settings->getFloat("dedicated_server_step");
+ static thread_local const float profiler_print_interval =
g_settings->getFloat("profiler_print_interval");
for(;;) {