mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
Merge remote-tracking branch 'upstream/master' into Visuals-Vol-2
This commit is contained in:
commit
71e648a776
647 changed files with 60434 additions and 37195 deletions
236
src/server.cpp
236
src/server.cpp
|
@ -75,6 +75,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "gameparams.h"
|
||||
#include "particles.h"
|
||||
#include "gettext.h"
|
||||
#include "util/tracy_wrapper.h"
|
||||
|
||||
class ClientNotFoundException : public BaseException
|
||||
{
|
||||
|
@ -101,6 +102,8 @@ private:
|
|||
|
||||
void *ServerThread::run()
|
||||
{
|
||||
ZoneScoped;
|
||||
|
||||
BEGIN_DEBUG_EXCEPTION_HANDLER
|
||||
|
||||
/*
|
||||
|
@ -110,17 +113,22 @@ void *ServerThread::run()
|
|||
* server-step frequency. Receive() is used for waiting between the steps.
|
||||
*/
|
||||
|
||||
auto framemarker = FrameMarker("ServerThread::run()-frame").started();
|
||||
try {
|
||||
m_server->AsyncRunStep(0.0f, true);
|
||||
} catch (con::ConnectionBindFailed &e) {
|
||||
m_server->setAsyncFatalError(e.what());
|
||||
} catch (LuaError &e) {
|
||||
m_server->setAsyncFatalError(e);
|
||||
} catch (ModError &e) {
|
||||
m_server->setAsyncFatalError(e.what());
|
||||
}
|
||||
framemarker.end();
|
||||
|
||||
float dtime = 0.0f;
|
||||
|
||||
while (!stopRequested()) {
|
||||
framemarker.start();
|
||||
ScopeProfiler spm(g_profiler, "Server::RunStep() (max)", SPT_MAX);
|
||||
|
||||
u64 t0 = porting::getTimeUs();
|
||||
|
@ -142,9 +150,12 @@ void *ServerThread::run()
|
|||
m_server->setAsyncFatalError(e.what());
|
||||
} catch (LuaError &e) {
|
||||
m_server->setAsyncFatalError(e);
|
||||
} catch (ModError &e) {
|
||||
m_server->setAsyncFatalError(e.what());
|
||||
}
|
||||
|
||||
dtime = 1e-6f * (porting::getTimeUs() - t0);
|
||||
framemarker.end();
|
||||
}
|
||||
|
||||
END_DEBUG_EXCEPTION_HANDLER
|
||||
|
@ -254,11 +265,7 @@ Server::Server(
|
|||
m_simple_singleplayer_mode(simple_singleplayer_mode),
|
||||
m_dedicated(dedicated),
|
||||
m_async_fatal_error(""),
|
||||
m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
|
||||
512,
|
||||
CONNECTION_TIMEOUT,
|
||||
m_bind_addr.isIPv6(),
|
||||
this)),
|
||||
m_con(con::createMTP(CONNECTION_TIMEOUT, m_bind_addr.isIPv6(), this)),
|
||||
m_itemdef(createItemDefManager()),
|
||||
m_nodedef(createNodeDefManager()),
|
||||
m_craftdef(createCraftDefManager()),
|
||||
|
@ -329,27 +336,6 @@ Server::~Server()
|
|||
SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
|
||||
L"*** Server shutting down"));
|
||||
|
||||
if (m_env) {
|
||||
MutexAutoLock envlock(m_env_mutex);
|
||||
|
||||
infostream << "Server: Saving players" << std::endl;
|
||||
m_env->saveLoadedPlayers();
|
||||
|
||||
infostream << "Server: Kicking players" << std::endl;
|
||||
std::string kick_msg;
|
||||
bool reconnect = false;
|
||||
if (isShutdownRequested()) {
|
||||
reconnect = m_shutdown_state.should_reconnect;
|
||||
kick_msg = m_shutdown_state.message;
|
||||
}
|
||||
if (kick_msg.empty()) {
|
||||
kick_msg = g_settings->get("kick_msg_shutdown");
|
||||
}
|
||||
m_env->saveLoadedPlayers(true);
|
||||
kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
|
||||
kick_msg, reconnect);
|
||||
}
|
||||
|
||||
actionstream << "Server: Shutting down" << std::endl;
|
||||
|
||||
// Stop server step from happening
|
||||
|
@ -369,16 +355,33 @@ Server::~Server()
|
|||
if (m_env) {
|
||||
MutexAutoLock envlock(m_env_mutex);
|
||||
|
||||
infostream << "Server: Executing shutdown hooks" << std::endl;
|
||||
try {
|
||||
// Empty out the environment, this can also invoke callbacks.
|
||||
m_env->deactivateBlocksAndObjects();
|
||||
m_script->on_shutdown();
|
||||
} catch (ModError &e) {
|
||||
addShutdownError(e);
|
||||
}
|
||||
|
||||
infostream << "Server: Executing shutdown hooks" << std::endl;
|
||||
infostream << "Server: Saving players" << std::endl;
|
||||
m_env->saveLoadedPlayers();
|
||||
|
||||
infostream << "Server: Kicking players" << std::endl;
|
||||
std::string kick_msg;
|
||||
bool reconnect = false;
|
||||
if (isShutdownRequested()) {
|
||||
reconnect = m_shutdown_state.should_reconnect;
|
||||
kick_msg = m_shutdown_state.message;
|
||||
}
|
||||
if (kick_msg.empty()) {
|
||||
kick_msg = g_settings->get("kick_msg_shutdown");
|
||||
}
|
||||
m_env->saveLoadedPlayers(true);
|
||||
kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
|
||||
kick_msg, reconnect);
|
||||
|
||||
try {
|
||||
m_script->on_shutdown();
|
||||
// Empty out the environment, this can also invoke callbacks.
|
||||
m_env->deactivateBlocksAndObjects();
|
||||
} catch (ModError &e) {
|
||||
addShutdownError(e);
|
||||
}
|
||||
|
@ -555,7 +558,6 @@ void Server::start()
|
|||
m_thread->stop();
|
||||
|
||||
// Initialize connection
|
||||
m_con->SetTimeoutMs(30);
|
||||
m_con->Serve(m_bind_addr);
|
||||
|
||||
// Start thread
|
||||
|
@ -612,6 +614,9 @@ void Server::step()
|
|||
|
||||
void Server::AsyncRunStep(float dtime, bool initial_step)
|
||||
{
|
||||
ZoneScoped;
|
||||
auto framemarker = FrameMarker("Server::AsyncRunStep()-frame").started();
|
||||
|
||||
{
|
||||
// Send blocks to clients
|
||||
SendBlocks(dtime);
|
||||
|
@ -628,8 +633,6 @@ void Server::AsyncRunStep(float dtime, bool initial_step)
|
|||
*/
|
||||
m_uptime_counter->increment(dtime);
|
||||
|
||||
handlePeerChanges();
|
||||
|
||||
/*
|
||||
Update time of day and overall game time
|
||||
*/
|
||||
|
@ -785,6 +788,12 @@ void Server::AsyncRunStep(float dtime, bool initial_step)
|
|||
//infostream<<"Server: Checking added and deleted active objects"<<std::endl;
|
||||
MutexAutoLock envlock(m_env_mutex);
|
||||
|
||||
// This guarantees that each object recomputes its cache only once per server step,
|
||||
// unless get_effective_observers is called.
|
||||
// If we were to update observer sets eagerly in set_observers instead,
|
||||
// the total costs of calls to set_observers could theoretically be higher.
|
||||
m_env->invalidateActiveObjectObserverCaches();
|
||||
|
||||
{
|
||||
ClientInterface::AutoLock clientlock(m_clients);
|
||||
const RemoteClientMap &clients = m_clients.getClientList();
|
||||
|
@ -1056,6 +1065,9 @@ void Server::AsyncRunStep(float dtime, bool initial_step)
|
|||
|
||||
void Server::Receive(float timeout)
|
||||
{
|
||||
ZoneScoped;
|
||||
auto framemarker = FrameMarker("Server::Receive()-frame").started();
|
||||
|
||||
const u64 t0 = porting::getTimeUs();
|
||||
const float timeout_us = timeout * 1e6f;
|
||||
auto remaining_time_us = [&]() -> float {
|
||||
|
@ -1075,6 +1087,8 @@ void Server::Receive(float timeout)
|
|||
// and a faster server-step is better than busy waiting.
|
||||
if (remaining_time_us() < 1000.0f)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
peer_id = pkt.getPeerId();
|
||||
|
@ -1145,10 +1159,6 @@ PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
|
|||
// Send HP
|
||||
SendPlayerHP(playersao, false);
|
||||
|
||||
// Send death screen
|
||||
if (playersao->isDead())
|
||||
SendDeathscreen(peer_id, false, v3f(0,0,0));
|
||||
|
||||
// Send Breath
|
||||
SendPlayerBreath(playersao);
|
||||
|
||||
|
@ -1157,7 +1167,7 @@ PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
|
|||
*/
|
||||
{
|
||||
NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
|
||||
notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
|
||||
notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << player->getName();
|
||||
m_clients.sendToAll(¬ice_pkt);
|
||||
}
|
||||
{
|
||||
|
@ -1252,21 +1262,20 @@ void Server::onMapEditEvent(const MapEditEvent &event)
|
|||
m_unsent_map_edit_queue.push(new MapEditEvent(event));
|
||||
}
|
||||
|
||||
void Server::peerAdded(con::Peer *peer)
|
||||
void Server::peerAdded(con::IPeer *peer)
|
||||
{
|
||||
verbosestream<<"Server::peerAdded(): peer->id="
|
||||
<<peer->id<<std::endl;
|
||||
verbosestream << "Server::peerAdded(): id=" << peer->id << std::endl;
|
||||
|
||||
m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
|
||||
m_clients.CreateClient(peer->id);
|
||||
}
|
||||
|
||||
void Server::deletingPeer(con::Peer *peer, bool timeout)
|
||||
void Server::deletingPeer(con::IPeer *peer, bool timeout)
|
||||
{
|
||||
verbosestream<<"Server::deletingPeer(): peer->id="
|
||||
<<peer->id<<", timeout="<<timeout<<std::endl;
|
||||
verbosestream << "Server::deletingPeer(): id=" << peer->id
|
||||
<< ", timeout=" << timeout << std::endl;
|
||||
|
||||
m_clients.event(peer->id, CSE_Disconnect);
|
||||
m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
|
||||
DeleteClient(peer->id, timeout ? CDR_TIMEOUT : CDR_LEAVE);
|
||||
}
|
||||
|
||||
bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
|
||||
|
@ -1310,34 +1319,6 @@ const ClientDynamicInfo *Server::getClientDynamicInfo(session_t peer_id)
|
|||
return &client->getDynamicInfo();
|
||||
}
|
||||
|
||||
void Server::handlePeerChanges()
|
||||
{
|
||||
while(!m_peer_change_queue.empty())
|
||||
{
|
||||
con::PeerChange c = m_peer_change_queue.front();
|
||||
m_peer_change_queue.pop();
|
||||
|
||||
verbosestream<<"Server: Handling peer change: "
|
||||
<<"id="<<c.peer_id<<", timeout="<<c.timeout
|
||||
<<std::endl;
|
||||
|
||||
switch(c.type)
|
||||
{
|
||||
case con::PEER_ADDED:
|
||||
m_clients.CreateClient(c.peer_id);
|
||||
break;
|
||||
|
||||
case con::PEER_REMOVED:
|
||||
DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
|
||||
break;
|
||||
|
||||
default:
|
||||
FATAL_ERROR("Invalid peer change event received!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::printToConsoleOnly(const std::string &text)
|
||||
{
|
||||
if (m_admin_chat) {
|
||||
|
@ -1411,25 +1392,12 @@ void Server::SendBreath(session_t peer_id, u16 breath)
|
|||
}
|
||||
|
||||
void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
|
||||
const std::string &custom_reason, bool reconnect)
|
||||
std::string_view custom_reason, bool reconnect)
|
||||
{
|
||||
assert(reason < SERVER_ACCESSDENIED_MAX);
|
||||
|
||||
NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
|
||||
pkt << (u8)reason;
|
||||
if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
|
||||
pkt << custom_reason;
|
||||
else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
|
||||
reason == SERVER_ACCESSDENIED_CRASH)
|
||||
pkt << custom_reason << (u8)reconnect;
|
||||
Send(&pkt);
|
||||
}
|
||||
|
||||
void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
|
||||
v3f camera_point_target)
|
||||
{
|
||||
NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
|
||||
pkt << set_camera_point_target << camera_point_target;
|
||||
pkt << (u8)reason << custom_reason << (u8)reconnect;
|
||||
Send(&pkt);
|
||||
}
|
||||
|
||||
|
@ -1863,7 +1831,7 @@ void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
|
|||
{
|
||||
NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
|
||||
pkt << params.density << params.color_bright << params.color_ambient
|
||||
<< params.height << params.thickness << params.speed;
|
||||
<< params.height << params.thickness << params.speed << params.color_shadow;
|
||||
Send(&pkt);
|
||||
}
|
||||
|
||||
|
@ -1893,7 +1861,7 @@ void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
|
|||
<< lighting.exposure.speed_bright_dark
|
||||
<< lighting.exposure.center_weight_power;
|
||||
|
||||
pkt << lighting.volumetric_light_strength;
|
||||
pkt << lighting.volumetric_light_strength << lighting.shadow_tint;
|
||||
|
||||
pkt << lighting.artificial_light_color;
|
||||
|
||||
|
@ -2060,19 +2028,10 @@ void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersa
|
|||
// Removed objects
|
||||
pkt << static_cast<u16>(removed_objects.size());
|
||||
|
||||
std::vector<u16> sounds_to_stop;
|
||||
|
||||
for (auto &it : removed_objects) {
|
||||
const auto [gone, id] = it;
|
||||
ServerActiveObject *obj = m_env->getActiveObject(id);
|
||||
|
||||
// Stop sounds if objects go out of range.
|
||||
// This fixes https://github.com/minetest/minetest/issues/8094.
|
||||
// We may not remove sounds if an entity was removed on the server.
|
||||
// See https://github.com/minetest/minetest/issues/14422.
|
||||
if (!gone) // just out of range for client, not gone on server?
|
||||
sounds_to_stop.push_back(id);
|
||||
|
||||
pkt << id;
|
||||
|
||||
// Remove from known objects
|
||||
|
@ -2081,8 +2040,10 @@ void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersa
|
|||
obj->m_known_by_count--;
|
||||
}
|
||||
|
||||
if (!sounds_to_stop.empty())
|
||||
stopAttachedSounds(client->peer_id, sounds_to_stop);
|
||||
// Note: Do yet NOT stop or remove object-attached sounds where the object goes out
|
||||
// of range (client side). Such sounds would need to be re-sent when coming into range.
|
||||
// Currently, the client will initiate m_playing_sounds clean ups indirectly by
|
||||
// "Server::handleCommand_RemovedSounds".
|
||||
|
||||
// Added objects
|
||||
pkt << static_cast<u16>(added_objects.size());
|
||||
|
@ -2266,37 +2227,6 @@ void Server::fadeSound(s32 handle, float step, float gain)
|
|||
m_playing_sounds.erase(it);
|
||||
}
|
||||
|
||||
void Server::stopAttachedSounds(session_t peer_id,
|
||||
const std::vector<u16> &object_ids)
|
||||
{
|
||||
assert(peer_id != PEER_ID_INEXISTENT);
|
||||
assert(!object_ids.empty());
|
||||
|
||||
auto cb = [&] (const s32 id, ServerPlayingSound &sound) -> bool {
|
||||
if (!CONTAINS(object_ids, sound.object))
|
||||
return false;
|
||||
|
||||
auto clients_it = sound.clients.find(peer_id);
|
||||
if (clients_it == sound.clients.end())
|
||||
return false;
|
||||
|
||||
NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
|
||||
pkt << id;
|
||||
Send(peer_id, &pkt);
|
||||
|
||||
sound.clients.erase(clients_it);
|
||||
// delete if client list empty
|
||||
return sound.clients.empty();
|
||||
};
|
||||
|
||||
for (auto it = m_playing_sounds.begin(); it != m_playing_sounds.end(); ) {
|
||||
if (cb(it->first, it->second))
|
||||
it = m_playing_sounds.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
|
||||
float far_d_nodes)
|
||||
{
|
||||
|
@ -2533,7 +2463,7 @@ bool Server::addMediaFile(const std::string &filename,
|
|||
const char *supported_ext[] = {
|
||||
".png", ".jpg", ".bmp", ".tga",
|
||||
".ogg",
|
||||
".x", ".b3d", ".obj",
|
||||
".x", ".b3d", ".obj", ".gltf",
|
||||
// Custom translation file format
|
||||
".tr",
|
||||
NULL
|
||||
|
@ -2863,32 +2793,8 @@ void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason
|
|||
|
||||
// Trigger scripted stuff
|
||||
m_script->on_dieplayer(playersao, reason);
|
||||
|
||||
SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
|
||||
}
|
||||
|
||||
void Server::RespawnPlayer(session_t peer_id)
|
||||
{
|
||||
PlayerSAO *playersao = getPlayerSAO(peer_id);
|
||||
assert(playersao);
|
||||
|
||||
infostream << "Server::RespawnPlayer(): Player "
|
||||
<< playersao->getPlayer()->getName()
|
||||
<< " respawns" << std::endl;
|
||||
|
||||
const auto *prop = playersao->accessObjectProperties();
|
||||
playersao->setHP(prop->hp_max,
|
||||
PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
|
||||
playersao->setBreath(prop->breath_max);
|
||||
|
||||
bool repositioned = m_script->on_respawnplayer(playersao);
|
||||
if (!repositioned) {
|
||||
// setPos will send the new position to client
|
||||
playersao->setPos(findSpawnPos());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Server::DenySudoAccess(session_t peer_id)
|
||||
{
|
||||
NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
|
||||
|
@ -2897,7 +2803,7 @@ void Server::DenySudoAccess(session_t peer_id)
|
|||
|
||||
|
||||
void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
|
||||
const std::string &custom_reason, bool reconnect)
|
||||
std::string_view custom_reason, bool reconnect)
|
||||
{
|
||||
SendAccessDenied(peer_id, reason, custom_reason, reconnect);
|
||||
m_clients.event(peer_id, CSE_SetDenied);
|
||||
|
@ -2970,9 +2876,6 @@ void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
|
|||
PlayerSAO *playersao = player->getPlayerSAO();
|
||||
assert(playersao);
|
||||
|
||||
playersao->clearChildAttachments();
|
||||
playersao->clearParentAttachment();
|
||||
|
||||
// inform connected clients
|
||||
const std::string &player_name = player->getName();
|
||||
NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
|
||||
|
@ -3218,14 +3121,11 @@ std::string Server::getStatusString()
|
|||
bool first = true;
|
||||
os << " | clients: ";
|
||||
if (m_env) {
|
||||
std::vector<session_t> clients = m_clients.getClientIDs();
|
||||
for (session_t client_id : clients) {
|
||||
RemotePlayer *player = m_env->getPlayer(client_id);
|
||||
std::vector<std::string> player_names = m_clients.getPlayerNames();
|
||||
|
||||
// Get name of player
|
||||
const char *name = player ? player->getName() : "<unknown>";
|
||||
std::sort(player_names.begin(), player_names.end());
|
||||
|
||||
// Add name to information string
|
||||
for (const std::string& name : player_names) {
|
||||
if (!first)
|
||||
os << ", ";
|
||||
else
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue