#include "server/serverinventorymgr.h"
#include "translation.h"
#include "database/database-sqlite3.h"
+#if USE_POSTGRESQL
+#include "database/database-postgresql.h"
+#endif
#include "database/database-files.h"
#include "database/database-dummy.h"
#include "gameparams.h"
return nullptr;
}
-v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
+v3f ServerPlayingSound::getPos(ServerEnvironment *env, bool *pos_exists) const
{
- if(pos_exists) *pos_exists = false;
- switch(type){
- case SSP_LOCAL:
+ if (pos_exists)
+ *pos_exists = false;
+
+ switch (type ){
+ case SoundLocation::Local:
return v3f(0,0,0);
- case SSP_POSITIONAL:
- if(pos_exists) *pos_exists = true;
+ case SoundLocation::Position:
+ if (pos_exists)
+ *pos_exists = true;
return pos;
- case SSP_OBJECT: {
- if(object == 0)
- return v3f(0,0,0);
- ServerActiveObject *sao = env->getActiveObject(object);
- if(!sao)
- return v3f(0,0,0);
- if(pos_exists) *pos_exists = true;
- return sao->getBasePosition(); }
+ case SoundLocation::Object:
+ {
+ if (object == 0)
+ return v3f(0,0,0);
+ ServerActiveObject *sao = env->getActiveObject(object);
+ if (!sao)
+ return v3f(0,0,0);
+ if (pos_exists)
+ *pos_exists = true;
+ return sao->getBasePosition();
+ }
}
+
return v3f(0,0,0);
}
m_mod_storage_database->beginSave();
m_modmgr = std::make_unique<ServerModManager>(m_path_world);
- std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
+
// complain about mods with unsatisfied dependencies
if (!m_modmgr->isConsistent()) {
- m_modmgr->printUnsatisfiedModsError();
+ std::string error = m_modmgr->getUnsatisfiedModsError();
+ throw ServerError(error);
}
//lock environment
m_inventory_mgr = std::make_unique<ServerInventoryManager>();
m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
+ m_script->checkSetByBuiltin();
m_gamespec.checkAndLog();
m_modmgr->loadMods(m_script);
m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
m_env = new ServerEnvironment(servermap, m_script, this,
m_path_world, m_metrics_backend.get());
+ m_env->init();
m_inventory_mgr->setEnv(m_env);
m_clients.setEnv(m_env);
// 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_MAX);
+ std::max(g_settings->getFloat("server_unload_unused_data_timeout"), 0.0f),
+ -1);
}
/*
std::map<v3s16, MapBlock*> modified_blocks;
m_env->getServerMap().transformLiquids(modified_blocks, m_env);
- /*
- Set the modified blocks unsent for all the clients
- */
if (!modified_blocks.empty()) {
- SetBlocksNotSent(modified_blocks);
+ MapEditEvent event;
+ event.type = MEET_OTHER;
+ for (const auto &pair : modified_blocks) {
+ event.modified_blocks.insert(pair.first);
+ }
+ m_env->getMap().dispatchEvent(event);
}
}
m_clients.step(dtime);
// We'll log the amount of each
Profiler prof;
- std::list<v3s16> node_meta_updates;
+ std::unordered_set<v3s16> node_meta_updates;
while (!m_unsent_map_edit_queue.empty()) {
MapEditEvent* event = m_unsent_map_edit_queue.front();
case MEET_BLOCK_NODE_METADATA_CHANGED: {
prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
if (!event->is_private_change) {
- // Don't send the change yet. Collect them to eliminate dupes.
- node_meta_updates.remove(event->p);
- node_meta_updates.push_back(event->p);
+ node_meta_updates.emplace(event->p);
}
if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
}
// Send all metadata updates
- if (node_meta_updates.size())
+ if (!node_meta_updates.empty())
sendMetadataChanged(node_meta_updates);
}
if (!playersao || !player) {
if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
actionstream << "Server: Failed to emerge player \"" << playername
- << "\" (player allocated to an another client)" << std::endl;
+ << "\" (player allocated to another client)" << std::endl;
DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
} else {
errorstream << "Server: " << playername << ": Failed to emerge player"
SendInventory(playersao, false);
// Send HP
- SendPlayerHP(playersao);
+ SendPlayerHP(playersao, false);
// Send death screen
if (playersao->isDead())
m_unsent_map_edit_queue.push(new MapEditEvent(event));
}
-void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
-{
- std::vector<session_t> clients = m_clients.getClientIDs();
- ClientInterface::AutoLock clientlock(m_clients);
- // Set the modified blocks unsent for all the clients
- for (const session_t client_id : clients) {
- if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
- client->SetBlocksNotSent(block);
- }
-}
-
void Server::peerAdded(con::Peer *peer)
{
verbosestream<<"Server::peerAdded(): peer->id="
void Server::SendMovement(session_t peer_id)
{
- std::ostringstream os(std::ios_base::binary);
-
NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
pkt << g_settings->getFloat("movement_acceleration_default");
void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
{
m_script->player_event(playersao, "health_changed");
- SendPlayerHP(playersao);
+ SendPlayerHP(playersao, reason.type != PlayerHPChangeReason::SET_HP_MAX);
// Send to other clients
playersao->sendPunchCommand();
HandlePlayerDeath(playersao, reason);
}
-void Server::SendPlayerHP(PlayerSAO *playersao)
+void Server::SendPlayerHP(PlayerSAO *playersao, bool effect)
{
- SendHP(playersao->getPeerID(), playersao->getHP());
+ SendHP(playersao->getPeerID(), playersao->getHP(), effect);
}
-void Server::SendHP(session_t peer_id, u16 hp)
+void Server::SendHP(session_t peer_id, u16 hp, bool effect)
{
- NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
- pkt << hp;
+ NetworkPacket pkt(TOCLIENT_HP, 3, peer_id);
+ pkt << hp << effect;
Send(&pkt);
}
if (peer_id == PEER_ID_INEXISTENT) {
std::vector<session_t> clients = m_clients.getClientIDs();
- const v3f pos = (p.minpos + p.maxpos) / 2.0f * BS;
+ const v3f pos = (
+ p.pos.start.min.val +
+ p.pos.start.max.val +
+ p.pos.end.min.val +
+ p.pos.end.max.val
+ ) / 4.0f * BS;
const float radius_sq = radius * radius;
/* Don't send short-lived spawners to distant players.
* This could be replaced with proper tracking at some point. */
NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
- pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel
- << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime
- << p.minsize << p.maxsize << p.collisiondetection;
+ pkt << p.amount << p.time;
+ { // serialize legacy fields
+ std::ostringstream os(std::ios_base::binary);
+ p.pos.start.legacySerialize(os);
+ p.vel.start.legacySerialize(os);
+ p.acc.start.legacySerialize(os);
+ p.exptime.start.legacySerialize(os);
+ p.size.start.legacySerialize(os);
+ pkt.putRawString(os.str());
+ }
+ pkt << p.collisiondetection;
- pkt.putLongString(p.texture);
+ pkt.putLongString(p.texture.string);
pkt << id << p.vertical << p.collision_removal << attached_id;
{
pkt << p.glow << p.object_collision;
pkt << p.node.param0 << p.node.param2 << p.node_tile;
+ { // serialize new fields
+ // initial bias for older properties
+ pkt << p.pos.start.bias
+ << p.vel.start.bias
+ << p.acc.start.bias
+ << p.exptime.start.bias
+ << p.size.start.bias;
+
+ std::ostringstream os(std::ios_base::binary);
+
+ // final tween frames of older properties
+ p.pos.end.serialize(os);
+ p.vel.end.serialize(os);
+ p.acc.end.serialize(os);
+ p.exptime.end.serialize(os);
+ p.size.end.serialize(os);
+
+ // properties for legacy texture field
+ p.texture.serialize(os, protocol_version, true);
+
+ // new properties
+ p.drag.serialize(os);
+ p.jitter.serialize(os);
+ p.bounce.serialize(os);
+ ParticleParamTypes::serializeParameterValue(os, p.attractor_kind);
+ if (p.attractor_kind != ParticleParamTypes::AttractorKind::none) {
+ p.attract.serialize(os);
+ p.attractor_origin.serialize(os);
+ writeU16(os, p.attractor_attachment); /* object ID */
+ writeU8(os, p.attractor_kill);
+ if (p.attractor_kind != ParticleParamTypes::AttractorKind::point) {
+ p.attractor_direction.serialize(os);
+ writeU16(os, p.attractor_direction_attachment);
+ }
+ }
+ p.radius.serialize(os);
+
+ ParticleParamTypes::serializeParameterValue(os, (u16)p.texpool.size());
+ for (const auto& tex : p.texpool) {
+ tex.serialize(os, protocol_version);
+ }
+
+ pkt.putRawString(os.str());
+ }
+
Send(&pkt);
}
NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
pkt << params.visible << params.count
- << params.starcolor << params.scale;
+ << params.starcolor << params.scale
+ << params.day_opacity;
Send(&pkt);
}
{
NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
4, peer_id);
-
+
pkt << lighting.shadow_intensity;
Send(&pkt);
return ret;
}
-s32 Server::playSound(const SimpleSoundSpec &spec,
- const ServerSoundParams ¶ms, bool ephemeral)
+s32 Server::playSound(ServerPlayingSound ¶ms, bool ephemeral)
{
// Find out initial position of sound
bool pos_exists = false;
- v3f pos = params.getPos(m_env, &pos_exists);
+ const v3f pos = params.getPos(m_env, &pos_exists);
// If position is not found while it should be, cancel sound
- if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
+ if(pos_exists != (params.type != SoundLocation::Local))
return -1;
// Filter destination clients
if(dst_clients.empty())
return -1;
- // Create the sound
- s32 id;
- ServerPlayingSound *psound = nullptr;
- if (ephemeral) {
- id = -1; // old clients will still use this, so pick a reserved ID
- } else {
- id = nextSoundId();
- // The sound will exist as a reference in m_playing_sounds
- m_playing_sounds[id] = ServerPlayingSound();
- psound = &m_playing_sounds[id];
- psound->params = params;
- psound->spec = spec;
- }
+ // old clients will still use this, so pick a reserved ID (-1)
+ const s32 id = ephemeral ? -1 : nextSoundId();
- float gain = params.gain * spec.gain;
+ float gain = params.gain * params.spec.gain;
NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
- pkt << id << spec.name << gain
+ pkt << id << params.spec.name << gain
<< (u8) params.type << pos << params.object
- << params.loop << params.fade << params.pitch
+ << params.spec.loop << params.spec.fade << params.spec.pitch
<< ephemeral;
bool as_reliable = !ephemeral;
- for (const u16 dst_client : dst_clients) {
- if (psound)
- psound->clients.insert(dst_client);
- m_clients.send(dst_client, 0, &pkt, as_reliable);
+ for (const session_t peer_id : dst_clients) {
+ if (!ephemeral)
+ params.clients.insert(peer_id);
+ m_clients.send(peer_id, 0, &pkt, as_reliable);
}
+
+ if (!ephemeral)
+ m_playing_sounds[id] = std::move(params);
return id;
}
void Server::stopSound(s32 handle)
{
- // Get sound reference
- std::unordered_map<s32, ServerPlayingSound>::iterator i =
- m_playing_sounds.find(handle);
- if (i == m_playing_sounds.end())
+ auto it = m_playing_sounds.find(handle);
+ if (it == m_playing_sounds.end())
return;
- ServerPlayingSound &psound = i->second;
+
+ ServerPlayingSound &psound = it->second;
NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
pkt << handle;
- for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
- si != psound.clients.end(); ++si) {
+ for (session_t peer_id : psound.clients) {
// Send as reliable
- m_clients.send(*si, 0, &pkt, true);
+ m_clients.send(peer_id, 0, &pkt, true);
}
+
// Remove sound reference
- m_playing_sounds.erase(i);
+ m_playing_sounds.erase(it);
}
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())
+ auto it = m_playing_sounds.find(handle);
+ if (it == m_playing_sounds.end())
return;
- ServerPlayingSound &psound = i->second;
- psound.params.gain = gain;
+ ServerPlayingSound &psound = it->second;
+ psound.gain = gain; // destination 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++);
- }
+ for (session_t peer_id : psound.clients) {
+ // Send as reliable
+ m_clients.send(peer_id, 0, &pkt, true);
}
// Remove sound reference
- if (!play_sound || psound.clients.empty())
- m_playing_sounds.erase(i);
-
- if (play_sound && !compat_psound.clients.empty()) {
- // Play new sound volume on older clients
- playSound(compat_psound.spec, compat_psound.params);
- }
+ if (gain <= 0 || psound.clients.empty())
+ m_playing_sounds.erase(it);
}
void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
float far_d_nodes)
{
- float maxd = far_d_nodes * BS;
v3f p_f = intToFloat(p, BS);
v3s16 block_pos = getNodeBlockPos(p);
NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
pkt << p;
- std::vector<session_t> clients = m_clients.getClientIDs();
- ClientInterface::AutoLock clientlock(m_clients);
-
- for (session_t client_id : clients) {
- RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
- if (!client)
- continue;
-
- RemotePlayer *player = m_env->getPlayer(client_id);
- PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
-
- // If player is far away, only set modified blocks not sent
- if (!client->isBlockSent(block_pos) || (sao &&
- sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
- if (far_players)
- far_players->emplace(client_id);
- else
- client->SetBlockNotSent(block_pos);
- continue;
- }
-
- // Send as reliable
- m_clients.send(client_id, 0, &pkt, true);
- }
+ sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
}
void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
float far_d_nodes, bool remove_metadata)
{
- float maxd = far_d_nodes * BS;
v3f p_f = intToFloat(p, BS);
v3s16 block_pos = getNodeBlockPos(p);
NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
pkt << p << n.param0 << n.param1 << n.param2
<< (u8) (remove_metadata ? 0 : 1);
+ sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
+}
+void Server::sendNodeChangePkt(NetworkPacket &pkt, v3s16 block_pos,
+ v3f p, float far_d_nodes, std::unordered_set<u16> *far_players)
+{
+ float maxd = far_d_nodes * BS;
std::vector<session_t> clients = m_clients.getClientIDs();
ClientInterface::AutoLock clientlock(m_clients);
// If player is far away, only set modified blocks not sent
if (!client->isBlockSent(block_pos) || (sao &&
- sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
+ sao->getBasePosition().getDistanceFrom(p) > maxd)) {
if (far_players)
far_players->emplace(client_id);
else
}
}
-void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far_d_nodes)
+void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes)
{
- float maxd = far_d_nodes * BS;
NodeMetadataList meta_updates_list(false);
- std::vector<session_t> clients = m_clients.getClientIDs();
+ std::ostringstream os(std::ios::binary);
+ std::vector<session_t> clients = m_clients.getClientIDs();
ClientInterface::AutoLock clientlock(m_clients);
for (session_t i : clients) {
if (!client)
continue;
- ServerActiveObject *player = m_env->getActiveObject(i);
- v3f player_pos = player ? player->getBasePosition() : v3f();
+ ServerActiveObject *player = getPlayerSAO(i);
+ v3s16 player_pos;
+ if (player)
+ player_pos = floatToInt(player->getBasePosition(), BS);
- for (const v3s16 &pos : meta_updates) {
+ for (const v3s16 pos : positions) {
NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
if (!meta)
continue;
v3s16 block_pos = getNodeBlockPos(pos);
- if (!client->isBlockSent(block_pos) || (player &&
- player_pos.getDistanceFrom(intToFloat(pos, BS)) > maxd)) {
+ if (!client->isBlockSent(block_pos) ||
+ player_pos.getDistanceFrom(pos) > far_d_nodes) {
client->SetBlockNotSent(block_pos);
continue;
}
continue;
// Send the meta changes
- std::ostringstream os(std::ios::binary);
+ os.str("");
meta_updates_list.serialize(os, client->serialization_version, false, true, true);
- std::ostringstream oss(std::ios::binary);
- compressZlib(os.str(), oss);
+ std::string raw = os.str();
+ os.str("");
+ compressZlib(raw, os);
- NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0);
- pkt.putLongString(oss.str());
- m_clients.send(i, 0, &pkt, true);
+ NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i);
+ pkt.putLongString(os.str());
+ Send(&pkt);
meta_updates_list.clear();
}
u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
if (new_hud_flags == player->hud_flags) // no change
return true;
-
+
SendHUDSetFlags(player->getPeerId(), flags, mask);
player->hud_flags = new_hud_flags;
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(0, range*2),
+ -range + myrand_range(0, 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
return playersao;
}
-bool Server::registerModStorage(ModMetadata *storage)
-{
- if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
- errorstream << "Unable to register same mod storage twice. Storage name: "
- << storage->getModName() << std::endl;
- return false;
- }
-
- m_mod_storages[storage->getModName()] = storage;
- return true;
-}
-
-void Server::unregisterModStorage(const std::string &name)
-{
- std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
- if (it != m_mod_storages.end())
- m_mod_storages.erase(name);
-}
-
void dedicated_server_loop(Server &server, bool &kill)
{
verbosestream<<"dedicated_server_loop()"<<std::endl;
return translations;
}
-ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
+ModStorageDatabase *Server::openModStorageDatabase(const std::string &world_path)
{
std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
Settings world_mt;
return openModStorageDatabase(backend, world_path, world_mt);
}
-ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
+ModStorageDatabase *Server::openModStorageDatabase(const std::string &backend,
const std::string &world_path, const Settings &world_mt)
{
if (backend == "sqlite3")
- return new ModMetadataDatabaseSQLite3(world_path);
+ return new ModStorageDatabaseSQLite3(world_path);
+
+#if USE_POSTGRESQL
+ if (backend == "postgresql") {
+ std::string connect_string;
+ world_mt.getNoEx("pgsql_mod_storage_connection", connect_string);
+ return new ModStorageDatabasePostgreSQL(connect_string);
+ }
+#endif // USE_POSTGRESQL
if (backend == "files")
- return new ModMetadataDatabaseFiles(world_path);
+ return new ModStorageDatabaseFiles(world_path);
if (backend == "dummy")
return new Database_Dummy();
return false;
}
- ModMetadataDatabase *srcdb = nullptr;
- ModMetadataDatabase *dstdb = nullptr;
+ ModStorageDatabase *srcdb = nullptr;
+ ModStorageDatabase *dstdb = nullptr;
bool succeeded = false;