Minetest/src/server.cpp

5537 lines
140 KiB
C++
Raw Normal View History

/*
2013-02-24 09:40:43 -08:00
Minetest
2013-02-24 10:38:45 -08:00
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
2010-11-26 15:02:21 -08:00
#include "server.h"
#include <iostream>
#include <queue>
#include <algorithm>
2010-11-26 15:02:21 -08:00
#include "clientserver.h"
#include "ban.h"
#include "environment.h"
2010-11-26 15:02:21 -08:00
#include "map.h"
2013-09-15 20:00:01 -07:00
#include "jthread/jmutexautolock.h"
2010-11-26 15:02:21 -08:00
#include "main.h"
#include "constants.h"
#include "voxel.h"
2011-04-24 14:31:22 -07:00
#include "config.h"
#include "version.h"
#include "filesys.h"
2011-06-25 16:34:36 -07:00
#include "mapblock.h"
#include "serverobject.h"
#include "genericobject.h"
2011-10-12 03:53:38 -07:00
#include "settings.h"
#include "profiler.h"
#include "log.h"
#include "scripting_game.h"
2011-11-14 11:41:30 -08:00
#include "nodedef.h"
#include "itemdef.h"
2011-11-16 16:28:46 -08:00
#include "craftdef.h"
#include "emerge.h"
#include "mapgen.h"
#include "biome.h"
#include "content_mapnode.h"
#include "content_nodemeta.h"
#include "content_abm.h"
#include "content_sao.h"
2011-12-02 17:23:14 -08:00
#include "mods.h"
#include "sha1.h"
#include "base64.h"
#include "tool.h"
#include "sound.h" // dummySoundManager
#include "event_manager.h"
#include "hex.h"
2013-02-21 14:00:44 -08:00
#include "serverlist.h"
#include "util/string.h"
#include "util/pointedthing.h"
2012-06-23 06:06:03 -07:00
#include "util/mathconstants.h"
#include "rollback.h"
#include "util/serialize.h"
#include "util/thread.h"
2013-03-21 12:42:23 -07:00
#include "defaultsettings.h"
class ClientNotFoundException : public BaseException
{
public:
ClientNotFoundException(const char *s):
BaseException(s)
{}
};
class ServerThread : public JThread
{
Server *m_server;
public:
ServerThread(Server *server):
JThread(),
m_server(server)
{
}
void * Thread();
};
2010-11-26 15:02:21 -08:00
void * ServerThread::Thread()
{
ThreadStarted();
log_register_thread("ServerThread");
2010-11-26 15:02:21 -08:00
DSTACK(__FUNCTION_NAME);
BEGIN_DEBUG_EXCEPTION_HANDLER
while(!StopRequested())
2010-11-26 15:02:21 -08:00
{
try{
//TimeTaker timer("AsyncRunStep() + Receive()");
{
//TimeTaker timer("AsyncRunStep()");
m_server->AsyncRunStep();
}
//infostream<<"Running m_server->Receive()"<<std::endl;
2010-11-26 15:02:21 -08:00
m_server->Receive();
}
catch(con::NoIncomingDataException &e)
{
}
2011-01-23 07:29:15 -08:00
catch(con::PeerNotFoundException &e)
{
infostream<<"Server: PeerNotFoundException"<<std::endl;
2011-01-23 07:29:15 -08:00
}
catch(ClientNotFoundException &e)
{
}
catch(con::ConnectionBindFailed &e)
{
m_server->setAsyncFatalError(e.what());
}
catch(LuaError &e)
{
m_server->setAsyncFatalError(e.what());
}
2010-11-26 15:02:21 -08:00
}
END_DEBUG_EXCEPTION_HANDLER(errorstream)
2010-11-26 15:02:21 -08:00
return NULL;
}
v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
{
if(pos_exists) *pos_exists = false;
switch(type){
case SSP_LOCAL:
return v3f(0,0,0);
case SSP_POSITIONAL:
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(); }
}
return v3f(0,0,0);
}
2010-11-27 08:10:11 -08:00
void RemoteClient::GetNextBlocks(Server *server, float dtime,
2012-12-20 09:19:49 -08:00
std::vector<PrioritySortedBlockTransfer> &dest)
2010-11-27 08:10:11 -08:00
{
DSTACK(__FUNCTION_NAME);
/*u32 timer_result;
TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
2010-12-19 06:51:45 -08:00
// Increment timers
m_nothing_to_send_pause_timer -= dtime;
2011-10-18 16:17:23 -07:00
m_nearest_unsent_reset_timer += dtime;
if(m_nothing_to_send_pause_timer >= 0)
return;
2012-09-01 08:32:15 -07:00
Player *player = server->m_env->getPlayer(peer_id);
// This can happen sometimes; clients and players are not in perfect sync.
if(player == NULL)
return;
2010-12-19 06:51:45 -08:00
2010-11-27 08:10:11 -08:00
// Won't send anything if already sending
2011-10-12 03:53:38 -07:00
if(m_blocks_sending.size() >= g_settings->getU16
("max_simultaneous_block_sends_per_client"))
2010-11-27 08:10:11 -08:00
{
//infostream<<"Not sending any blocks, Queue full."<<std::endl;
return;
2010-11-27 08:10:11 -08:00
}
//TimeTaker timer("RemoteClient::GetNextBlocks");
2010-11-27 08:10:11 -08:00
v3f playerpos = player->getPosition();
v3f playerspeed = player->getSpeed();
v3f playerspeeddir(0,0,0);
if(playerspeed.getLength() > 1.0*BS)
playerspeeddir = playerspeed / playerspeed.getLength();
// Predict to next block
v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
2010-11-27 08:10:11 -08:00
v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
2010-11-27 08:10:11 -08:00
v3s16 center = getNodeBlockPos(center_nodepos);
// Camera position and direction
v3f camera_pos = player->getEyePosition();
v3f camera_dir = v3f(0,0,1);
camera_dir.rotateYZBy(player->getPitch());
camera_dir.rotateXZBy(player->getYaw());
2010-11-27 08:10:11 -08:00
/*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
<<camera_dir.Z<<")"<<std::endl;*/
2010-11-27 08:10:11 -08:00
/*
Get the starting value of the block finder radius.
*/
if(m_last_center != center)
{
m_nearest_unsent_d = 0;
m_last_center = center;
}
2010-11-27 08:10:11 -08:00
/*infostream<<"m_nearest_unsent_reset_timer="
<<m_nearest_unsent_reset_timer<<std::endl;*/
2011-10-18 16:17:23 -07:00
// Reset periodically to workaround for some bugs or stuff
if(m_nearest_unsent_reset_timer > 20.0)
{
m_nearest_unsent_reset_timer = 0;
m_nearest_unsent_d = 0;
2011-10-18 16:17:23 -07:00
//infostream<<"Resetting m_nearest_unsent_d for "
// <<server->getPlayerName(peer_id)<<std::endl;
2010-11-27 08:10:11 -08:00
}
//s16 last_nearest_unsent_d = m_nearest_unsent_d;
s16 d_start = m_nearest_unsent_d;
//infostream<<"d_start="<<d_start<<std::endl;
2011-10-12 03:53:38 -07:00
u16 max_simul_sends_setting = g_settings->getU16
("max_simultaneous_block_sends_per_client");
u16 max_simul_sends_usually = max_simul_sends_setting;
2010-11-27 08:10:11 -08:00
/*
Check the time from last addNode/removeNode.
Decrease send rate if player is building stuff.
*/
m_time_from_building += dtime;
2011-10-12 03:53:38 -07:00
if(m_time_from_building < g_settings->getFloat(
"full_block_send_enable_min_time_from_building"))
2010-11-27 08:10:11 -08:00
{
max_simul_sends_usually
= LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
2010-11-27 08:10:11 -08:00
}
/*
Number of blocks sending + number of blocks selected for sending
*/
u32 num_blocks_selected = m_blocks_sending.size();
2010-12-19 06:51:45 -08:00
/*
next time d will be continued from the d from which the nearest
unsent block was found this time.
This is because not necessarily any of the blocks found this
time are actually sent.
*/
s32 new_nearest_unsent_d = -1;
2010-11-27 08:10:11 -08:00
2011-10-12 03:53:38 -07:00
s16 d_max = g_settings->getS16("max_block_send_distance");
s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
// Don't loop very much at a time
2011-10-18 16:17:23 -07:00
s16 max_d_increment_at_time = 2;
if(d_max > d_start + max_d_increment_at_time)
d_max = d_start + max_d_increment_at_time;
/*if(d_max_gen > d_start+2)
d_max_gen = d_start+2;*/
//infostream<<"Starting from "<<d_start<<std::endl;
2010-11-27 08:10:11 -08:00
2011-10-18 16:17:23 -07:00
s32 nearest_emerged_d = -1;
s32 nearest_emergefull_d = -1;
s32 nearest_sent_d = -1;
bool queue_is_full = false;
s16 d;
for(d = d_start; d <= d_max; d++)
2010-11-27 08:10:11 -08:00
{
2011-10-18 16:17:23 -07:00
/*errorstream<<"checking d="<<d<<" for "
<<server->getPlayerName(peer_id)<<std::endl;*/
//infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
/*
If m_nearest_unsent_d was changed by the EmergeThread
(it can change it to 0 through SetBlockNotSent),
update our d to it.
Else update m_nearest_unsent_d
*/
/*if(m_nearest_unsent_d != last_nearest_unsent_d)
2010-11-27 08:10:11 -08:00
{
d = m_nearest_unsent_d;
last_nearest_unsent_d = m_nearest_unsent_d;
}*/
2010-11-27 08:10:11 -08:00
/*
Get the border/face dot coordinates of a "d-radiused"
box
*/
2012-12-20 09:19:49 -08:00
std::list<v3s16> list;
2010-11-27 08:10:11 -08:00
getFacePositions(list, d);
2012-12-20 09:19:49 -08:00
std::list<v3s16>::iterator li;
for(li=list.begin(); li!=list.end(); ++li)
2010-11-27 08:10:11 -08:00
{
v3s16 p = *li + center;
2010-11-27 08:10:11 -08:00
/*
Send throttling
- Don't allow too many simultaneous transfers
- EXCEPT when the blocks are very close
2010-11-27 08:10:11 -08:00
Also, don't send blocks that are already flying.
*/
// Start with the usual maximum
u16 max_simul_dynamic = max_simul_sends_usually;
// If block is very close, allow full maximum
if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
max_simul_dynamic = max_simul_sends_setting;
// Don't select too many blocks for sending
if(num_blocks_selected >= max_simul_dynamic)
{
queue_is_full = true;
goto queue_full_break;
}
// Don't send blocks that are currently being transferred
2012-12-20 09:19:49 -08:00
if(m_blocks_sending.find(p) != m_blocks_sending.end())
continue;
2010-11-27 08:10:11 -08:00
/*
Do not go over-limit
*/
if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
|| p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
|| p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
|| p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
|| p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
|| p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
continue;
2011-01-24 06:36:58 -08:00
// If this is true, inexistent block will be made from scratch
2010-11-27 08:10:11 -08:00
bool generate = d <= d_max_gen;
{
2011-02-02 08:46:14 -08:00
/*// Limit the generating area vertically to 2/3
if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
2011-02-02 08:46:14 -08:00
generate = false;*/
2011-10-18 16:17:23 -07:00
// Limit the send area vertically to 1/2
if(abs(p.Y - center.Y) > d_max / 2)
2011-02-02 08:46:14 -08:00
continue;
}
2011-10-18 16:17:23 -07:00
#if 0
2011-01-24 06:36:58 -08:00
/*
If block is far away, don't generate it unless it is
near ground level.
2011-01-24 06:36:58 -08:00
*/
if(d >= 4)
{
#if 1
// Block center y in nodes
f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
// Don't generate if it's very high or very low
if(y < -64 || y > 64)
generate = false;
#endif
#if 0
v2s16 p2d_nodes_center(
MAP_BLOCKSIZE*p.X,
MAP_BLOCKSIZE*p.Z);
// Get ground height in nodes
2011-11-11 09:33:17 -08:00
s16 gh = server->m_env->getServerMap().findGroundLevel(
p2d_nodes_center);
2011-01-24 06:36:58 -08:00
// If differs a lot, don't generate
if(fabs(gh - y) > MAP_BLOCKSIZE*2)
generate = false;
// Actually, don't even send it
//continue;
#endif
2011-01-24 06:36:58 -08:00
}
#endif
2011-01-24 06:36:58 -08:00
//infostream<<"d="<<d<<std::endl;
2011-10-18 16:17:23 -07:00
#if 1
/*
Don't generate or send if not in sight
FIXME This only works if the client uses a small enough
FOV setting. The default of 72 degrees is fine.
*/
float camera_fov = (72.0*M_PI/180) * 4./3.;
if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
{
continue;
}
2011-10-18 16:17:23 -07:00
#endif
2010-11-27 08:10:11 -08:00
/*
Don't send already sent blocks
*/
{
2012-12-20 09:19:49 -08:00
if(m_blocks_sent.find(p) != m_blocks_sent.end())
{
2010-11-27 08:10:11 -08:00
continue;
}
2010-11-27 08:10:11 -08:00
}
2010-12-19 06:51:45 -08:00
2010-11-27 08:10:11 -08:00
/*
Check if map has this block
*/
2011-11-11 09:33:17 -08:00
MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
2010-11-27 08:10:11 -08:00
bool surely_not_found_on_disk = false;
bool block_is_invalid = false;
2010-11-27 08:10:11 -08:00
if(block != NULL)
{
// Reset usage timer, this block will be of use in the future.
block->resetUsageTimer();
// Block is dummy if data doesn't exist.
// It means it has been not found from disk and not generated
2010-11-27 08:10:11 -08:00
if(block->isDummy())
{
surely_not_found_on_disk = true;
}
// Block is valid if lighting is up-to-date and data exists
if(block->isValid() == false)
{
block_is_invalid = true;
}
/*if(block->isFullyGenerated() == false)
{
block_is_invalid = true;
}*/
#if 0
v2s16 p2d(p.X, p.Z);
2011-11-11 09:33:17 -08:00
ServerMap *map = (ServerMap*)(&server->m_env->getMap());
v2s16 chunkpos = map->sector_to_chunk(p2d);
if(map->chunkNonVolatile(chunkpos) == false)
block_is_invalid = true;
#endif
if(block->isGenerated() == false)
block_is_invalid = true;
#if 1
/*
If block is not close, don't send it unless it is near
ground level.
Block is near ground level if night-time mesh
differs from day-time mesh.
*/
2011-10-18 16:17:23 -07:00
if(d >= 4)
{
if(block->getDayNightDiff() == false)
continue;
}
#endif
2010-11-27 08:10:11 -08:00
}
/*
If block has been marked to not exist on disk (dummy)
and generating new ones is not wanted, skip block.
2010-11-27 08:10:11 -08:00
*/
if(generate == false && surely_not_found_on_disk == true)
{
// get next one.
continue;
}
/*
Add inexistent block to emerge queue.
*/
if(block == NULL || surely_not_found_on_disk || block_is_invalid)
2010-11-27 08:10:11 -08:00
{
/* //TODO: Get value from somewhere
2010-11-27 08:10:11 -08:00
// Allow only one block in emerge queue
//if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
// Allow two blocks in queue per client
2011-10-18 16:17:23 -07:00
//if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
u32 max_emerge = 5;
2011-11-25 08:17:59 -08:00
// Make it more responsive when needing to generate stuff
if(surely_not_found_on_disk)
max_emerge = 1;
2011-11-25 08:17:59 -08:00
if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
2010-11-27 08:10:11 -08:00
{
//infostream<<"Adding block to emerge queue"<<std::endl;
2010-11-27 08:10:11 -08:00
// Add it to the emerge queue and trigger the thread
2010-11-27 08:10:11 -08:00
u8 flags = 0;
if(generate == false)
flags |= BLOCK_EMERGE_FLAG_FROMDISK;
2010-11-27 08:10:11 -08:00
server->m_emerge_queue.addBlock(peer_id, p, flags);
server->m_emergethread.trigger();
2011-10-18 16:17:23 -07:00
if(nearest_emerged_d == -1)
nearest_emerged_d = d;
} else {
if(nearest_emergefull_d == -1)
nearest_emergefull_d = d;
goto queue_full_break;
2010-11-27 08:10:11 -08:00
}
*/
if (server->m_emerge->enqueueBlockEmerge(peer_id, p, generate)) {
if (nearest_emerged_d == -1)
nearest_emerged_d = d;
} else {
if (nearest_emergefull_d == -1)
nearest_emergefull_d = d;
goto queue_full_break;
}
2010-11-27 08:10:11 -08:00
// get next one.
continue;
}
2011-10-18 16:17:23 -07:00
if(nearest_sent_d == -1)
nearest_sent_d = d;
2010-11-27 08:10:11 -08:00
/*
Add block to send queue
2010-11-27 08:10:11 -08:00
*/
2011-10-18 16:17:23 -07:00
/*errorstream<<"sending from d="<<d<<" to "
<<server->getPlayerName(peer_id)<<std::endl;*/
2010-11-27 08:10:11 -08:00
PrioritySortedBlockTransfer q((float)d, p, peer_id);
dest.push_back(q);
2010-12-19 06:51:45 -08:00
num_blocks_selected += 1;
2010-11-27 08:10:11 -08:00
}
}
queue_full_break:
2010-11-27 08:10:11 -08:00
//infostream<<"Stopped at "<<d<<std::endl;
2011-10-18 16:17:23 -07:00
// If nothing was found for sending and nothing was queued for
// emerging, continue next time browsing from here
if(nearest_emerged_d != -1){
new_nearest_unsent_d = nearest_emerged_d;
} else if(nearest_emergefull_d != -1){
new_nearest_unsent_d = nearest_emergefull_d;
} else {
if(d > g_settings->getS16("max_block_send_distance")){
new_nearest_unsent_d = 0;
m_nothing_to_send_pause_timer = 2.0;
/*infostream<<"GetNextBlocks(): d wrapped around for "
<<server->getPlayerName(peer_id)
<<"; setting to 0 and pausing"<<std::endl;*/
} else {
if(nearest_sent_d != -1)
new_nearest_unsent_d = nearest_sent_d;
else
new_nearest_unsent_d = d;
}
2010-12-19 06:51:45 -08:00
}
if(new_nearest_unsent_d != -1)
m_nearest_unsent_d = new_nearest_unsent_d;
/*timer_result = timer.stop(true);
if(timer_result != 0)
2012-07-17 06:00:04 -07:00
infostream<<"GetNextBlocks timeout: "<<timer_result<<" (!=0)"<<std::endl;*/
2010-11-27 08:10:11 -08:00
}
2010-11-26 15:02:21 -08:00
void RemoteClient::GotBlock(v3s16 p)
{
2012-12-20 09:19:49 -08:00
if(m_blocks_sending.find(p) != m_blocks_sending.end())
m_blocks_sending.erase(p);
2010-11-26 15:02:21 -08:00
else
{
/*infostream<<"RemoteClient::GotBlock(): Didn't find in"
" m_blocks_sending"<<std::endl;*/
m_excess_gotblocks++;
}
2012-12-20 09:19:49 -08:00
m_blocks_sent.insert(p);
2010-11-26 15:02:21 -08:00
}
void RemoteClient::SentBlock(v3s16 p)
{
2012-12-20 09:19:49 -08:00
if(m_blocks_sending.find(p) == m_blocks_sending.end())
m_blocks_sending[p] = 0.0;
2010-11-26 15:02:21 -08:00
else
infostream<<"RemoteClient::SentBlock(): Sent block"
2010-11-26 15:02:21 -08:00
" already in m_blocks_sending"<<std::endl;
}
void RemoteClient::SetBlockNotSent(v3s16 p)
{
m_nearest_unsent_d = 0;
2012-12-20 09:19:49 -08:00
if(m_blocks_sending.find(p) != m_blocks_sending.end())
m_blocks_sending.erase(p);
if(m_blocks_sent.find(p) != m_blocks_sent.end())
m_blocks_sent.erase(p);
2010-11-26 15:02:21 -08:00
}
2012-12-20 09:19:49 -08:00
void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
2010-11-26 15:02:21 -08:00
{
m_nearest_unsent_d = 0;
2012-12-20 09:19:49 -08:00
for(std::map<v3s16, MapBlock*>::iterator
i = blocks.begin();
i != blocks.end(); ++i)
2010-11-26 15:02:21 -08:00
{
2012-12-20 09:19:49 -08:00
v3s16 p = i->first;
2010-11-26 15:02:21 -08:00
2012-12-20 09:19:49 -08:00
if(m_blocks_sending.find(p) != m_blocks_sending.end())
m_blocks_sending.erase(p);
if(m_blocks_sent.find(p) != m_blocks_sent.end())
m_blocks_sent.erase(p);
2010-11-26 15:02:21 -08:00
}
}
/*
Server
*/
Server::Server(
2012-03-11 05:54:23 -07:00
const std::string &path_world,
const SubgameSpec &gamespec,
bool simple_singleplayer_mode
2010-11-26 15:02:21 -08:00
):
2012-03-10 05:56:24 -08:00
m_path_world(path_world),
2012-03-11 05:54:23 -07:00
m_gamespec(gamespec),
m_simple_singleplayer_mode(simple_singleplayer_mode),
m_async_fatal_error(""),
2011-11-11 09:33:17 -08:00
m_env(NULL),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT,
g_settings->getBool("enable_ipv6") && g_settings->getBool("ipv6_server"), this),
m_banmanager(NULL),
m_rollback(NULL),
m_rollback_sink_enabled(true),
m_enable_rollback_recording(false),
m_emerge(NULL),
m_script(NULL),
m_itemdef(createItemDefManager()),
m_nodedef(createNodeDefManager()),
2011-11-16 16:28:46 -08:00
m_craftdef(createCraftDefManager()),
m_event(new EventManager()),
m_thread(NULL),
m_time_of_day_send_timer(0),
m_uptime(0),
2011-02-22 16:49:57 -08:00
m_shutdown_requested(false),
m_ignore_map_edit_events(false),
m_ignore_map_edit_events_peer_id(0)
2010-11-26 15:02:21 -08:00
{
m_liquid_transform_timer = 0.0;
2013-02-24 06:39:07 -08:00
m_liquid_transform_every = 1.0;
2010-12-19 06:51:45 -08:00
m_print_info_timer = 0.0;
2013-02-21 14:00:44 -08:00
m_masterserver_timer = 0.0;
2010-12-19 06:51:45 -08:00
m_objectdata_timer = 0.0;
m_emergethread_trigger_timer = 0.0;
m_savemap_timer = 0.0;
2010-11-26 15:02:21 -08:00
m_step_dtime = 0.0;
2011-11-11 09:33:17 -08:00
if(path_world == "")
throw ServerError("Supplied empty world path");
2012-03-11 05:54:23 -07:00
if(!gamespec.isValid())
throw ServerError("Supplied invalid gamespec");
infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
if(m_simple_singleplayer_mode)
infostream<<" in simple singleplayer mode"<<std::endl;
else
infostream<<std::endl;
2012-03-11 05:54:23 -07:00
infostream<<"- world: "<<m_path_world<<std::endl;
infostream<<"- game: "<<m_gamespec.path<<std::endl;
2012-03-10 18:15:45 -08:00
2013-03-21 12:42:23 -07:00
// Initialize default settings and override defaults with those provided
// by the game
set_default_settings(g_settings);
Settings gamedefaults;
getGameMinetestConfig(gamespec.path, gamedefaults);
override_default_settings(g_settings, &gamedefaults);
// Create server thread
m_thread = new ServerThread(this);
// Create emerge manager
2013-04-06 08:19:59 -07:00
m_emerge = new EmergeManager(this);
// Create ban manager
std::string ban_path = m_path_world+DIR_DELIM+"ipban.txt";
m_banmanager = new BanManager(ban_path);
// Create rollback manager
std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
m_rollback = createRollbackManager(rollback_path, this);
// Create world if it doesn't exist
if(!initializeWorld(m_path_world, m_gamespec.id))
throw ServerError("Failed to initialize world");
2012-03-11 06:04:50 -07:00
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
ModConfiguration modconf(m_path_world);
m_mods = modconf.getMods();
std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
// complain about mods with unsatisfied dependencies
2013-11-30 16:52:06 -08:00
if(!modconf.isConsistent())
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
{
for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
it != unsatisfied_mods.end(); ++it)
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
{
ModSpec mod = *it;
errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
dep_it != mod.unsatisfied_depends.end(); ++dep_it)
errorstream << " \"" << *dep_it << "\"";
errorstream << std::endl;
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
}
}
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;
2013-11-30 16:52:06 -08:00
for(std::vector<std::string>::iterator it = names.begin();
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
it != names.end(); ++it)
2013-11-30 16:52:06 -08:00
{
std::string name = *it;
if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
load_mod_names.insert(name.substr(9));
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
}
// complain about mods declared to be loaded, but not found
for(std::vector<ModSpec>::iterator it = m_mods.begin();
it != m_mods.end(); ++it)
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
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);
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
if(!load_mod_names.empty())
2013-11-30 16:52:06 -08:00
{
errorstream << "The following mods could not be found:";
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
for(std::set<std::string>::iterator it = load_mod_names.begin();
it != load_mod_names.end(); ++it)
errorstream << " \"" << (*it) << "\"";
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
errorstream << std::endl;
}
// Path to builtin.lua
std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
2012-03-10 05:56:24 -08:00
// Lock environment
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
2011-11-11 09:33:17 -08:00
// Initialize scripting
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
2012-03-10 18:15:45 -08:00
infostream<<"Server: Initializing Lua"<<std::endl;
m_script = new GameScripting(this);
2011-11-25 11:32:12 -08:00
// Load and run builtin.lua
2012-03-10 18:15:45 -08:00
infostream<<"Server: Loading builtin.lua [\""
<<builtinpath<<"\"]"<<std::endl;
bool success = m_script->loadMod(builtinpath, "__builtin");
2011-11-25 11:32:12 -08:00
if(!success){
errorstream<<"Server: Failed to load and run "
<<builtinpath<<std::endl;
2011-12-02 17:23:14 -08:00
throw ModError("Failed to load and run "+builtinpath);
2011-11-25 11:32:12 -08:00
}
2012-03-10 18:15:45 -08:00
// Print 'em
infostream<<"Server: Loading mods: ";
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
for(std::vector<ModSpec>::iterator i = m_mods.begin();
2012-03-10 18:15:45 -08:00
i != m_mods.end(); i++){
const ModSpec &mod = *i;
infostream<<mod.name<<" ";
}
infostream<<std::endl;
// Load and run "mod" scripts
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
for(std::vector<ModSpec>::iterator i = m_mods.begin();
2011-12-11 06:49:40 -08:00
i != m_mods.end(); i++){
const ModSpec &mod = *i;
2011-11-15 01:02:47 -08:00
std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
2012-03-10 18:15:45 -08:00
infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
<<scriptpath<<"\"]"<<std::endl;
bool success = m_script->loadMod(scriptpath, mod.name);
2011-11-15 01:02:47 -08:00
if(!success){
errorstream<<"Server: Failed to load and run "
<<scriptpath<<std::endl;
2011-12-02 17:23:14 -08:00
throw ModError("Failed to load and run "+scriptpath);
}
2011-11-11 10:50:09 -08:00
}
// Read Textures and calculate sha1 sums
2012-03-25 01:50:29 -07:00
fillMediaCache();
// Apply item aliases in the node definition manager
m_nodedef->updateAliases(m_itemdef);
// Initialize Environment
ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
m_env = new ServerEnvironment(servermap, m_script, this, m_emerge);
// Run some callbacks after the MG params have been set up but before activation
MapgenParams *mgparams = servermap->getMapgenParams();
m_script->environment_OnMapgenInit(mgparams);
// Initialize mapgens
m_emerge->initMapgens(mgparams);
servermap->setMapgenParams(m_emerge->params);
2011-11-11 16:25:30 -08:00
// Give environment reference to scripting api
m_script->initializeEnvironment(m_env);
// Register us to receive map edit events
servermap->addEventReceiver(this);
2011-02-22 16:49:57 -08:00
// If file exists, load environment metadata
2012-03-10 05:56:24 -08:00
if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
{
infostream<<"Server: Loading environment metadata"<<std::endl;
2012-03-10 05:56:24 -08:00
m_env->loadMeta(m_path_world);
}
// Load players
infostream<<"Server: Loading players"<<std::endl;
2012-03-10 05:56:24 -08:00
m_env->deSerializePlayers(m_path_world);
/*
Add some test ActiveBlockModifiers to environment
*/
add_legacy_abms(m_env, m_nodedef);
2013-02-24 06:39:07 -08:00
m_liquid_transform_every = g_settings->getFloat("liquid_update");
2010-11-26 15:02:21 -08:00
}
Server::~Server()
{
2012-03-10 18:15:45 -08:00
infostream<<"Server destructing"<<std::endl;
/*
Send shutdown message
*/
{
JMutexAutoLock conlock(m_con_mutex);
std::wstring line = L"*** Server shutting down";
/*
Send the message to clients
*/
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
{
// Get client and check that it is valid
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
try{
SendChatMessage(client->peer_id, line);
}
catch(con::PeerNotFoundException &e)
{}
}
}
{
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
/*
Execute script shutdown hooks
*/
m_script->on_shutdown();
}
{
JMutexAutoLock envlock(m_env_mutex);
/*
Save players
*/
infostream<<"Server: Saving players"<<std::endl;
2012-03-10 05:56:24 -08:00
m_env->serializePlayers(m_path_world);
/*
Save environment metadata
*/
infostream<<"Server: Saving environment metadata"<<std::endl;
2012-03-10 05:56:24 -08:00
m_env->saveMeta(m_path_world);
}
/*
Stop threads
*/
2010-11-26 15:02:21 -08:00
stop();
delete m_thread;
2013-04-07 13:27:27 -07:00
//shutdown all emerge threads first!
delete m_emerge;
/*
Delete clients
*/
2010-11-26 15:02:21 -08:00
{
JMutexAutoLock clientslock(m_con_mutex);
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
2010-11-26 15:02:21 -08:00
{
// Delete client
2012-12-20 09:19:49 -08:00
delete i->second;
}
2010-11-26 15:02:21 -08:00
}
// Delete things in the reverse order of creation
2011-11-11 09:33:17 -08:00
delete m_env;
delete m_rollback;
delete m_banmanager;
delete m_event;
delete m_itemdef;
delete m_nodedef;
2011-11-29 07:15:18 -08:00
delete m_craftdef;
2011-11-11 09:33:17 -08:00
// Deinitialize scripting
infostream<<"Server: Deinitializing scripting"<<std::endl;
delete m_script;
2012-07-24 10:57:17 -07:00
// Delete detached inventories
{
for(std::map<std::string, Inventory*>::iterator
i = m_detached_inventories.begin();
i != m_detached_inventories.end(); i++){
delete i->second;
}
}
2010-11-26 15:02:21 -08:00
}
void Server::start(unsigned short port)
{
DSTACK(__FUNCTION_NAME);
infostream<<"Starting server on port "<<port<<"..."<<std::endl;
2010-11-26 15:02:21 -08:00
// Stop thread if already running
m_thread->Stop();
2010-11-26 15:02:21 -08:00
// Initialize connection
m_con.SetTimeoutMs(30);
2010-11-26 15:02:21 -08:00
m_con.Serve(port);
// Start thread
m_thread->Start();
2012-03-10 18:15:45 -08:00
// ASCII art for the win!
actionstream
<<" .__ __ __ "<<std::endl
<<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
<<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
<<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
<<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
<<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
2012-03-11 05:54:23 -07:00
actionstream<<"Server for gameid=\""<<m_gamespec.id
<<"\" listening on port "<<port<<"."<<std::endl;
2010-11-26 15:02:21 -08:00
}
void Server::stop()
{
DSTACK(__FUNCTION_NAME);
infostream<<"Server: Stopping and waiting threads"<<std::endl;
2010-11-26 15:02:21 -08:00
// Stop threads (set run=false first so both start stopping)
m_thread->Stop();
//m_emergethread.setRun(false);
m_thread->Wait();
//m_emergethread.stop();
infostream<<"Server: Threads stopped"<<std::endl;
2010-11-26 15:02:21 -08:00
}
void Server::step(float dtime)
{
DSTACK(__FUNCTION_NAME);
// Limit a bit
if(dtime > 2.0)
dtime = 2.0;
{
JMutexAutoLock 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 != ""){
throw ServerError(async_err);
}
2010-11-26 15:02:21 -08:00
}
void Server::AsyncRunStep()
{
DSTACK(__FUNCTION_NAME);
g_profiler->add("Server::AsyncRunStep (num)", 1);
2010-11-26 15:02:21 -08:00
float dtime;
{
JMutexAutoLock lock1(m_step_dtime_mutex);
dtime = m_step_dtime;
}
{
// Send blocks to clients
SendBlocks(dtime);
}
2010-12-13 11:32:35 -08:00
if(dtime < 0.001)
return;
g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
//infostream<<"Server steps "<<dtime<<std::endl;
//infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
2010-12-13 11:32:35 -08:00
{
JMutexAutoLock lock1(m_step_dtime_mutex);
2010-12-19 06:51:45 -08:00
m_step_dtime -= dtime;
2010-12-13 11:32:35 -08:00
}
/*
Update uptime
*/
{
m_uptime.set(m_uptime.get() + dtime);
}
{
// Process connection's timeouts
JMutexAutoLock lock2(m_con_mutex);
2011-10-12 03:53:38 -07:00
ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
m_con.RunTimeouts(dtime);
}
{
// This has to be called so that the client list gets synced
// with the peer list of the connection
handlePeerChanges();
}
/*
Update time of day and overall game time
*/
{
JMutexAutoLock envlock(m_env_mutex);
m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
/*
Send to clients at constant intervals
*/
m_time_of_day_send_timer -= dtime;
if(m_time_of_day_send_timer < 0.0)
{
2011-10-12 03:53:38 -07:00
m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
//JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
u16 time = m_env->getTimeOfDay();
float time_speed = g_settings->getFloat("time_speed");
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
{
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
SendTimeOfDay(client->peer_id, time, time_speed);
}
}
}
2010-12-13 11:32:35 -08:00
2010-11-26 15:02:21 -08:00
{
JMutexAutoLock lock(m_env_mutex);
2013-08-03 13:16:37 -07:00
// 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)
if(dtime > max_lag){
if(dtime > 0.1 && dtime > max_lag * 2.0)
infostream<<"Server: Maximum lag peaked to "<<dtime
<<" s"<<std::endl;
max_lag = dtime;
}
m_env->reportMaxLagEstimate(max_lag);
// Step environment
ScopeProfiler sp(g_profiler, "SEnv step");
ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
2011-11-11 09:33:17 -08:00
m_env->step(dtime);
2010-11-26 15:02:21 -08:00
}
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);
// Run Map's timers and unload unused data
2011-10-12 03:53:38 -07:00
ScopeProfiler sp(g_profiler, "Server: map timer and unload");
2011-11-11 09:33:17 -08:00
m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
2011-10-12 03:53:38 -07:00
g_settings->getFloat("server_unload_unused_data_timeout"));
}
2010-11-26 15:02:21 -08:00
/*
Do background stuff
*/
2011-11-26 04:30:57 -08:00
/*
Handle players
2011-11-26 04:30:57 -08:00
*/
{
JMutexAutoLock lock(m_env_mutex);
JMutexAutoLock lock2(m_con_mutex);
ScopeProfiler sp(g_profiler, "Server: handle players");
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
{
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
PlayerSAO *playersao = getPlayerSAO(client->peer_id);
if(playersao == NULL)
continue;
/*
Handle player HPs (die if hp=0)
*/
if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
{
if(playersao->getHP() == 0)
DiePlayer(client->peer_id);
else
SendPlayerHP(client->peer_id);
}
/*
Send player breath if changed
*/
if(playersao->m_breath_not_sent){
SendPlayerBreath(client->peer_id);
}
/*
Send player inventories if necessary
*/
Update attachments at the ending of the addToScene function for parents. And with this... *drum roll* Client-side attachments are at last functional and stick visibly. Fix the last segmentation fault (apparently). So far attachments seem to be fully functional, although removing the parent causes children to go to origin 0,0,0 and possibly still cause such a fault (though this should already be addressed) Fix a bug in falling code where entities get stuck Also check if the parent has been removed server-side, and detach the child if so. Fixes children going to origin 0,0,0 when their parent is removed. Unset all attachment properties when permanently detaching (on both the client and server). Also store less data we don't need Create a separate function for detaching, and also update lua api documentation When a child is detached, update its position from the server to clients. This WILL cause it to get positioned slightly differently client side, as the server attachment system only copies parent origin and knows not about mesh / bone transformation. This prevents different clients seeing the object detached in different spots which is most correct Update the position of attached players to clients. An attached player will see himself move, but this is currently VERY ugly and laggy as it is done by the server (it probably must stay this way too) Use a different approach for locally attached players. This allows for smooth positio transitions to work, as well at the player turning around freely. Still buggy however
2012-11-07 08:42:38 -08:00
if(playersao->m_moved){
SendMovePlayer(client->peer_id);
Update attachments at the ending of the addToScene function for parents. And with this... *drum roll* Client-side attachments are at last functional and stick visibly. Fix the last segmentation fault (apparently). So far attachments seem to be fully functional, although removing the parent causes children to go to origin 0,0,0 and possibly still cause such a fault (though this should already be addressed) Fix a bug in falling code where entities get stuck Also check if the parent has been removed server-side, and detach the child if so. Fixes children going to origin 0,0,0 when their parent is removed. Unset all attachment properties when permanently detaching (on both the client and server). Also store less data we don't need Create a separate function for detaching, and also update lua api documentation When a child is detached, update its position from the server to clients. This WILL cause it to get positioned slightly differently client side, as the server attachment system only copies parent origin and knows not about mesh / bone transformation. This prevents different clients seeing the object detached in different spots which is most correct Update the position of attached players to clients. An attached player will see himself move, but this is currently VERY ugly and laggy as it is done by the server (it probably must stay this way too) Use a different approach for locally attached players. This allows for smooth positio transitions to work, as well at the player turning around freely. Still buggy however
2012-11-07 08:42:38 -08:00
playersao->m_moved = false;
}
if(playersao->m_inventory_not_sent){
UpdateCrafting(client->peer_id);
SendInventory(client->peer_id);
}
}
}
/* Transform liquids */
m_liquid_transform_timer += dtime;
2013-02-24 06:39:07 -08:00
if(m_liquid_transform_timer >= m_liquid_transform_every)
{
2013-02-24 06:39:07 -08:00
m_liquid_transform_timer -= m_liquid_transform_every;
JMutexAutoLock lock(m_env_mutex);
2011-10-12 03:53:38 -07:00
ScopeProfiler sp(g_profiler, "Server: liquid transform");
2012-12-20 09:19:49 -08:00
std::map<v3s16, MapBlock*> modified_blocks;
2011-11-11 09:33:17 -08:00
m_env->getMap().transformLiquids(modified_blocks);
#if 0
/*
Update lighting
*/
core::map<v3s16, MapBlock*> lighting_modified_blocks;
2011-11-11 09:33:17 -08:00
ServerMap &map = ((ServerMap&)m_env->getMap());
map.updateLighting(modified_blocks, lighting_modified_blocks);
// Add blocks modified by lighting to modified_blocks
for(core::map<v3s16, MapBlock*>::Iterator
i = lighting_modified_blocks.getIterator();
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
modified_blocks.insert(block->getPos(), block);
}
#endif
/*
Set the modified blocks unsent for all the clients
*/
JMutexAutoLock lock2(m_con_mutex);
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
{
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
if(modified_blocks.size() > 0)
{
// Remove block from sent history
client->SetBlocksNotSent(modified_blocks);
}
}
}
2010-11-26 15:02:21 -08:00
// Periodically print some info
{
2010-12-19 06:51:45 -08:00
float &counter = m_print_info_timer;
2010-11-26 15:02:21 -08:00
counter += dtime;
if(counter >= 30.0)
{
counter = 0.0;
JMutexAutoLock lock2(m_con_mutex);
2013-10-17 14:32:49 -07:00
m_clients_names.clear();
if(m_clients.size() != 0)
infostream<<"Players:"<<std::endl;
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
2010-11-26 15:02:21 -08:00
{
//u16 peer_id = i.getNode()->getKey();
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
2011-11-11 09:33:17 -08:00
Player *player = m_env->getPlayer(client->peer_id);
2011-04-11 15:59:09 -07:00
if(player==NULL)
continue;
infostream<<"* "<<player->getName()<<"\t";
client->PrintInfo(infostream);
2013-10-17 14:32:49 -07:00
m_clients_names.push_back(player->getName());
2010-11-26 15:02:21 -08:00
}
}
}
2013-02-21 14:00:44 -08:00
#if USE_CURL
// send masterserver announce
{
float &counter = m_masterserver_timer;
2013-07-27 06:44:59 -07:00
if(!isSingleplayer() && (!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true)
2013-02-21 14:00:44 -08:00
{
2013-10-17 14:32:49 -07:00
ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_names, m_uptime.get(), m_env->getGameTime(), m_gamespec.id, m_mods);
2013-02-21 14:00:44 -08:00
counter = 0.01;
}
counter += dtime;
}
#endif
2011-10-12 03:53:38 -07:00
//if(g_settings->getBool("enable_experimental"))
2011-02-22 16:49:57 -08:00
{
/*
Check added and deleted active objects
*/
{
//infostream<<"Server: Checking added and deleted active objects"<<std::endl;
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
2011-02-22 16:49:57 -08:00
// Radius inside which objects are active
2011-10-14 16:28:57 -07:00
s16 radius = g_settings->getS16("active_object_send_range_blocks");
radius *= MAP_BLOCKSIZE;
2010-11-26 15:02:21 -08:00
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
2010-11-26 15:02:21 -08:00
{
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
// If definitions and textures have not been sent, don't
// send objects either
if(!client->definitions_sent)
continue;
2011-11-11 09:33:17 -08:00
Player *player = m_env->getPlayer(client->peer_id);
2011-02-22 16:49:57 -08:00
if(player==NULL)
{
// This can happen if the client timeouts somehow
/*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
<<client->peer_id
<<" has no associated player"<<std::endl;*/
2011-02-22 16:49:57 -08:00
continue;
}
v3s16 pos = floatToInt(player->getPosition(), BS);
2012-12-20 09:19:49 -08:00
std::set<u16> removed_objects;
std::set<u16> added_objects;
2011-11-11 09:33:17 -08:00
m_env->getRemovedActiveObjects(pos, radius,
client->m_known_objects, removed_objects);
2011-11-11 09:33:17 -08:00
m_env->getAddedActiveObjects(pos, radius,
client->m_known_objects, added_objects);
// Ignore if nothing happened
if(removed_objects.size() == 0 && added_objects.size() == 0)
{
//infostream<<"active objects: none changed"<<std::endl;
continue;
}
std::string data_buffer;
char buf[4];
// Handle removed objects
writeU16((u8*)buf, removed_objects.size());
data_buffer.append(buf, 2);
2012-12-20 09:19:49 -08:00
for(std::set<u16>::iterator
i = removed_objects.begin();
i != removed_objects.end(); ++i)
{
// Get object
2012-12-20 09:19:49 -08:00
u16 id = *i;
2011-11-11 09:33:17 -08:00
ServerActiveObject* obj = m_env->getActiveObject(id);
// Add to data buffer for sending
2012-12-20 09:19:49 -08:00
writeU16((u8*)buf, id);
data_buffer.append(buf, 2);
// Remove from known objects
2012-12-20 09:19:49 -08:00
client->m_known_objects.erase(id);
if(obj && obj->m_known_by_count > 0)
obj->m_known_by_count--;
}
// Handle added objects
writeU16((u8*)buf, added_objects.size());
data_buffer.append(buf, 2);
2012-12-20 09:19:49 -08:00
for(std::set<u16>::iterator
i = added_objects.begin();
i != added_objects.end(); ++i)
{
// Get object
2012-12-20 09:19:49 -08:00
u16 id = *i;
2011-11-11 09:33:17 -08:00
ServerActiveObject* obj = m_env->getActiveObject(id);
// Get object type
u8 type = ACTIVEOBJECT_TYPE_INVALID;
if(obj == NULL)
infostream<<"WARNING: "<<__FUNCTION_NAME
<<": NULL object"<<std::endl;
else
type = obj->getSendType();
// Add to data buffer for sending
writeU16((u8*)buf, id);
data_buffer.append(buf, 2);
writeU8((u8*)buf, type);
data_buffer.append(buf, 1);
if(obj)
data_buffer.append(serializeLongString(
obj->getClientInitializationData(client->net_proto_version)));
else
data_buffer.append(serializeLongString(""));
// Add to known objects
2012-12-20 09:19:49 -08:00
client->m_known_objects.insert(id);
if(obj)
obj->m_known_by_count++;
}
// Send packet
SharedBuffer<u8> reply(2 + data_buffer.size());
writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
memcpy((char*)&reply[2], data_buffer.c_str(),
data_buffer.size());
// Send as reliable
m_con.Send(client->peer_id, 0, reply, true);
2012-03-10 18:15:45 -08:00
verbosestream<<"Server: Sent object remove/add: "
<<removed_objects.size()<<" removed, "
<<added_objects.size()<<" added, "
<<"packet size is "<<reply.getSize()<<std::endl;
}
2011-04-09 18:15:10 -07:00
#if 0
/*
Collect a list of all the objects known by the clients
and report it back to the environment.
*/
core::map<u16, bool> all_known_objects;
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
i.atEnd() == false; i++)
{
RemoteClient *client = i.getNode()->getValue();
// Go through all known objects of client
for(core::map<u16, bool>::Iterator
i = client->m_known_objects.getIterator();
i.atEnd()==false; i++)
{
u16 id = i.getNode()->getKey();
all_known_objects[id] = true;
}
}
2011-11-11 09:33:17 -08:00
m_env->setKnownActiveObjects(whatever);
2011-04-09 18:15:10 -07:00
#endif
}
/*
Send object messages
*/
{
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
ScopeProfiler sp(g_profiler, "Server: sending object messages");
// Key = object id
// Value = data sent by object
2012-12-20 09:19:49 -08:00
std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
// Get active object messages from environment
for(;;)
{
2011-11-11 09:33:17 -08:00
ActiveObjectMessage aom = m_env->getActiveObjectMessage();
if(aom.id == 0)
break;
2012-12-20 09:19:49 -08:00
std::list<ActiveObjectMessage>* message_list = NULL;
std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
n = buffered_messages.find(aom.id);
2012-12-20 09:19:49 -08:00
if(n == buffered_messages.end())
{
2012-12-20 09:19:49 -08:00
message_list = new std::list<ActiveObjectMessage>;
buffered_messages[aom.id] = message_list;
}
else
{
2012-12-20 09:19:49 -08:00
message_list = n->second;
}
message_list->push_back(aom);
}
// Route data to every client
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
{
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
std::string reliable_data;
std::string unreliable_data;
// Go through all objects in message buffer
2012-12-20 09:19:49 -08:00
for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
j = buffered_messages.begin();
j != buffered_messages.end(); ++j)
{
// If object is not known by client, skip it
2012-12-20 09:19:49 -08:00
u16 id = j->first;
if(client->m_known_objects.find(id) == client->m_known_objects.end())
continue;
// Get message list of object
2012-12-20 09:19:49 -08:00
std::list<ActiveObjectMessage>* list = j->second;
// Go through every message
2012-12-20 09:19:49 -08:00
for(std::list<ActiveObjectMessage>::iterator
k = list->begin(); k != list->end(); ++k)
{
// Compose the full new data with header
ActiveObjectMessage aom = *k;
std::string new_data;
// Add object id
char buf[2];
writeU16((u8*)&buf[0], aom.id);
new_data.append(buf, 2);
// Add data
new_data += serializeString(aom.datastring);
// Add data to buffer
if(aom.reliable)
reliable_data += new_data;
else
unreliable_data += new_data;
}
}
/*
reliable_data and unreliable_data are now ready.
Send them.
*/
if(reliable_data.size() > 0)
{
SharedBuffer<u8> reply(2 + reliable_data.size());
writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
memcpy((char*)&reply[2], reliable_data.c_str(),
reliable_data.size());
// Send as reliable
m_con.Send(client->peer_id, 0, reply, true);
}
if(unreliable_data.size() > 0)
{
SharedBuffer<u8> reply(2 + unreliable_data.size());
writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
memcpy((char*)&reply[2], unreliable_data.c_str(),
unreliable_data.size());
// Send as unreliable
m_con.Send(client->peer_id, 0, reply, false);
}
/*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
{
infostream<<"Server: Size of object message data: "
<<"reliable: "<<reliable_data.size()
<<", unreliable: "<<unreliable_data.size()
<<std::endl;
}*/
}
// Clear buffered_messages
2012-12-20 09:19:49 -08:00
for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
i = buffered_messages.begin();
i != buffered_messages.end(); ++i)
{
2012-12-20 09:19:49 -08:00
delete i->second;
2010-11-26 15:02:21 -08:00
}
}
2010-11-26 15:02:21 -08:00
2011-02-22 16:49:57 -08:00
} // enable_experimental
/*
Send queued-for-sending map edit events.
*/
{
// We will be accessing the environment and the connection
JMutexAutoLock lock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
// Don't send too many at a time
//u32 count = 0;
// Single change sending is disabled if queue size is not small
bool disable_single_change_sending = false;
if(m_unsent_map_edit_queue.size() >= 4)
disable_single_change_sending = true;
2012-03-10 18:15:45 -08:00
int event_count = m_unsent_map_edit_queue.size();
// We'll log the amount of each
Profiler prof;
2011-02-22 16:49:57 -08:00
while(m_unsent_map_edit_queue.size() != 0)
{
MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
// Players far away from the change are stored here.
// Instead of sending the changes, MapBlocks are set not sent
// for them.
2012-12-20 09:19:49 -08:00
std::list<u16> far_players;
2011-02-22 16:49:57 -08:00
2013-11-23 06:35:49 -08:00
if(event->type == MEET_ADDNODE || event->type == MEET_SWAPNODE)
2011-02-22 16:49:57 -08:00
{
//infostream<<"Server: MEET_ADDNODE"<<std::endl;
prof.add("MEET_ADDNODE", 1);
if(disable_single_change_sending)
sendAddNode(event->p, event->n, event->already_known_by_peer,
2013-11-23 06:35:49 -08:00
&far_players, 5, event->type == MEET_ADDNODE);
else
sendAddNode(event->p, event->n, event->already_known_by_peer,
2013-11-23 06:35:49 -08:00
&far_players, 30, event->type == MEET_ADDNODE);
2011-02-22 16:49:57 -08:00
}
else if(event->type == MEET_REMOVENODE)
{
//infostream<<"Server: MEET_REMOVENODE"<<std::endl;
prof.add("MEET_REMOVENODE", 1);
if(disable_single_change_sending)
sendRemoveNode(event->p, event->already_known_by_peer,
&far_players, 5);
else
sendRemoveNode(event->p, event->already_known_by_peer,
&far_players, 30);
2011-02-22 16:49:57 -08:00
}
else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
{
infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
setBlockNotSent(event->p);
}
2011-02-22 16:49:57 -08:00
else if(event->type == MEET_OTHER)
{
infostream<<"Server: MEET_OTHER"<<std::endl;
prof.add("MEET_OTHER", 1);
2012-12-20 09:19:49 -08:00
for(std::set<v3s16>::iterator
i = event->modified_blocks.begin();
i != event->modified_blocks.end(); ++i)
2011-08-16 02:23:19 -07:00
{
2012-12-20 09:19:49 -08:00
setBlockNotSent(*i);
2011-08-16 02:23:19 -07:00
}
2011-02-22 16:49:57 -08:00
}
else
{
prof.add("unknown", 1);
infostream<<"WARNING: Server: Unknown MapEditEvent "
2011-02-22 16:49:57 -08:00
<<((u32)event->type)<<std::endl;
}
/*
Set blocks not sent to far players
*/
if(far_players.size() > 0)
{
// Convert list format to that wanted by SetBlocksNotSent
2012-12-20 09:19:49 -08:00
std::map<v3s16, MapBlock*> modified_blocks2;
for(std::set<v3s16>::iterator
i = event->modified_blocks.begin();
i != event->modified_blocks.end(); ++i)
{
2012-12-20 09:19:49 -08:00
modified_blocks2[*i] =
m_env->getMap().getBlockNoCreateNoEx(*i);
}
// Set blocks not sent
2012-12-20 09:19:49 -08:00
for(std::list<u16>::iterator
i = far_players.begin();
2012-12-20 09:19:49 -08:00
i != far_players.end(); ++i)
{
u16 peer_id = *i;
RemoteClient *client = getClient(peer_id);
if(client==NULL)
continue;
client->SetBlocksNotSent(modified_blocks2);
}
}
2011-02-22 16:49:57 -08:00
delete event;
/*// Don't send too many at a time
count++;
2011-06-25 06:32:09 -07:00
if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
break;*/
2011-02-22 16:49:57 -08:00
}
2012-03-10 18:15:45 -08:00
if(event_count >= 5){
infostream<<"Server: MapEditEvents:"<<std::endl;
prof.print(infostream);
2012-03-10 18:15:45 -08:00
} else if(event_count != 0){
verbosestream<<"Server: MapEditEvents:"<<std::endl;
prof.print(verbosestream);
}
2011-02-22 16:49:57 -08:00
}
2011-02-05 04:55:16 -08:00
/*
Trigger emergethread (it somehow gets to a non-triggered but
bysy state sometimes)
*/
{
2010-12-19 06:51:45 -08:00
float &counter = m_emergethread_trigger_timer;
counter += dtime;
if(counter >= 2.0)
{
counter = 0.0;
m_emerge->startAllThreads();
// Update m_enable_rollback_recording here too
m_enable_rollback_recording =
g_settings->getBool("enable_rollback_recording");
}
}
2010-11-26 15:02:21 -08:00
// Save map, players and auth stuff
2010-11-26 15:02:21 -08:00
{
2010-12-19 06:51:45 -08:00
float &counter = m_savemap_timer;
2010-11-26 15:02:21 -08:00
counter += dtime;
2011-10-12 03:53:38 -07:00
if(counter >= g_settings->getFloat("server_map_save_interval"))
2010-11-26 15:02:21 -08:00
{
counter = 0.0;
JMutexAutoLock lock(m_env_mutex);
2010-11-26 15:02:21 -08:00
2011-10-12 03:53:38 -07:00
ScopeProfiler sp(g_profiler, "Server: saving stuff");
//Ban stuff
if(m_banmanager->isModified())
m_banmanager->save();
// Save changed parts of map
m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
// Save players
2012-03-10 05:56:24 -08:00
m_env->serializePlayers(m_path_world);
// Save environment metadata
2012-03-10 05:56:24 -08:00
m_env->saveMeta(m_path_world);
2010-11-26 15:02:21 -08:00
}
}
}
void Server::Receive()
{
DSTACK(__FUNCTION_NAME);
SharedBuffer<u8> data;
2010-11-26 15:02:21 -08:00
u16 peer_id;
u32 datasize;
try{
{
JMutexAutoLock conlock(m_con_mutex);
datasize = m_con.Receive(peer_id, data);
2010-11-26 15:02:21 -08:00
}
// This has to be called so that the client list gets synced
// with the peer list of the connection
handlePeerChanges();
2010-11-26 15:02:21 -08:00
ProcessData(*data, datasize, peer_id);
}
catch(con::InvalidIncomingDataException &e)
{
infostream<<"Server::Receive(): "
2010-11-26 15:02:21 -08:00
"InvalidIncomingDataException: what()="
<<e.what()<<std::endl;
}
catch(con::PeerNotFoundException &e)
{
//NOTE: This is not needed anymore
2010-11-26 15:02:21 -08:00
// The peer has been disconnected.
// Find the associated player and remove it.
/*JMutexAutoLock envlock(m_env_mutex);
infostream<<"ServerThread: peer_id="<<peer_id
2010-11-26 15:02:21 -08:00
<<" has apparently closed connection. "
<<"Removing player."<<std::endl;
2011-11-11 09:33:17 -08:00
m_env->removePlayer(peer_id);*/
2010-11-26 15:02:21 -08:00
}
}
void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
// Environment is locked first.
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
ScopeProfiler sp(g_profiler, "Server::ProcessData");
2013-07-23 09:31:05 -07:00
std::string addr_s;
2010-11-26 15:02:21 -08:00
try{
Address address = m_con.GetPeerAddress(peer_id);
2013-07-23 09:31:05 -07:00
addr_s = address.serializeString();
// drop player if is ip is banned
if(m_banmanager->isIpBanned(addr_s)){
std::string ban_name = m_banmanager->getBanName(addr_s);
infostream<<"Server: A banned client tried to connect from "
<<addr_s<<"; banned name was "
<<ban_name<<std::endl;
// This actually doesn't seem to transfer to the client
DenyAccess(peer_id, L"Your ip is banned. Banned name was "
+narrow_to_wide(ban_name));
m_con.DeletePeer(peer_id);
return;
}
2010-11-26 15:02:21 -08:00
}
catch(con::PeerNotFoundException &e)
{
infostream<<"Server::ProcessData(): Cancelling: peer "
2010-11-26 15:02:21 -08:00
<<peer_id<<" not found"<<std::endl;
return;
}
u8 peer_ser_ver = getClient(peer_id)->serialization_version;
2010-11-26 15:02:21 -08:00
try
{
if(datasize < 2)
return;
ToServerCommand command = (ToServerCommand)readU16(&data[0]);
2010-11-26 15:02:21 -08:00
if(command == TOSERVER_INIT)
{
// [0] u16 TOSERVER_INIT
2013-08-01 13:51:36 -07:00
// [2] u8 SER_FMT_VER_HIGHEST_READ
2010-11-26 15:02:21 -08:00
// [3] u8[20] player_name
// [23] u8[28] password <--- can be sent without this, from old versions
2010-11-26 15:02:21 -08:00
if(datasize < 2+1+PLAYERNAME_SIZE)
2010-11-26 15:02:21 -08:00
return;
// If net_proto_version is set, this client has already been handled
if(getClient(peer_id)->net_proto_version != 0){
verbosestream<<"Server: Ignoring multiple TOSERVER_INITs from "
<<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
return;
}
verbosestream<<"Server: Got TOSERVER_INIT from "<<addr_s<<" (peer_id="
<<peer_id<<")"<<std::endl;
2010-11-26 15:02:21 -08:00
// Do not allow multiple players in simple singleplayer mode.
// This isn't a perfect way to do it, but will suffice for now.
if(m_simple_singleplayer_mode && m_clients.size() > 1){
infostream<<"Server: Not allowing another client ("<<addr_s
<<") to connect in simple singleplayer mode"<<std::endl;
DenyAccess(peer_id, L"Running in simple singleplayer mode.");
return;
}
2010-11-26 15:02:21 -08:00
// First byte after command is maximum supported
// serialization version
u8 client_max = data[2];
2013-08-01 13:51:36 -07:00
u8 our_max = SER_FMT_VER_HIGHEST_READ;
2010-11-26 15:02:21 -08:00
// Use the highest version supported by both
2012-12-20 09:19:49 -08:00
u8 deployed = std::min(client_max, our_max);
2010-11-26 15:02:21 -08:00
// If it's lower than the lowest supported, give up.
if(deployed < SER_FMT_VER_LOWEST)
deployed = SER_FMT_VER_INVALID;
//peer->serialization_version = deployed;
getClient(peer_id)->pending_serialization_version = deployed;
2010-11-26 15:02:21 -08:00
if(deployed == SER_FMT_VER_INVALID)
{
2012-03-10 18:15:45 -08:00
actionstream<<"Server: A mismatched client tried to connect from "
<<addr_s<<std::endl;
infostream<<"Server: Cannot negotiate serialization version with "
<<addr_s<<std::endl;
DenyAccess(peer_id, std::wstring(
L"Your client's version is not supported.\n"
L"Server version is ")
+ narrow_to_wide(minetest_version_simple) + L"."
);
return;
}
/*
Read and check network protocol version
*/
u16 min_net_proto_version = 0;
if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
// Use same version as minimum and maximum if maximum version field
// doesn't exist (backwards compatibility)
u16 max_net_proto_version = min_net_proto_version;
if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
// Start with client's maximum version
u16 net_proto_version = max_net_proto_version;
// Figure out a working version if it is possible at all
if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
{
// If maximum is larger than our maximum, go with our maximum
if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
// Else go with client's maximum
else
net_proto_version = max_net_proto_version;
}
verbosestream<<"Server: "<<addr_s<<": Protocol version: min: "
<<min_net_proto_version<<", max: "<<max_net_proto_version
<<", chosen: "<<net_proto_version<<std::endl;
getClient(peer_id)->net_proto_version = net_proto_version;
if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
{
actionstream<<"Server: A mismatched client tried to connect from "
<<addr_s<<std::endl;
DenyAccess(peer_id, std::wstring(
L"Your client's version is not supported.\n"
L"Server version is ")
+ narrow_to_wide(minetest_version_simple) + L",\n"
+ L"server's PROTOCOL_VERSION is "
+ narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
+ L"..."
+ narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
+ L", client's PROTOCOL_VERSION is "
+ narrow_to_wide(itos(min_net_proto_version))
+ L"..."
+ narrow_to_wide(itos(max_net_proto_version))
);
2011-07-30 10:10:16 -07:00
return;
}
if(g_settings->getBool("strict_protocol_version_checking"))
{
if(net_proto_version != LATEST_PROTOCOL_VERSION)
{
actionstream<<"Server: A mismatched (strict) client tried to "
<<"connect from "<<addr_s<<std::endl;
DenyAccess(peer_id, std::wstring(
L"Your client's version is not supported.\n"
L"Server version is ")
+ narrow_to_wide(minetest_version_simple) + L",\n"
+ L"server's PROTOCOL_VERSION (strict) is "
+ narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
+ L", client's PROTOCOL_VERSION is "
+ narrow_to_wide(itos(min_net_proto_version))
+ L"..."
+ narrow_to_wide(itos(max_net_proto_version))
);
return;
}
}
2010-11-26 15:02:21 -08:00
/*
Set up player
*/
// Get player name
char playername[PLAYERNAME_SIZE];
for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
{
playername[i] = data[3+i];
}
playername[PLAYERNAME_SIZE-1] = 0;
if(playername[0]=='\0')
{
actionstream<<"Server: Player with an empty name "
2012-03-10 18:15:45 -08:00
<<"tried to connect from "<<addr_s<<std::endl;
DenyAccess(peer_id, L"Empty name");
return;
}
if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
{
actionstream<<"Server: Player with an invalid name "
2012-03-10 18:15:45 -08:00
<<"tried to connect from "<<addr_s<<std::endl;
DenyAccess(peer_id, L"Name contains unallowed characters");
return;
}
if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
{
actionstream<<"Server: Player with the name \"singleplayer\" "
<<"tried to connect from "<<addr_s<<std::endl;
DenyAccess(peer_id, L"Name is not allowed");
return;
}
2013-12-11 22:51:35 -08:00
{
std::string reason;
if(m_script->on_prejoinplayer(playername, addr_s, reason))
{
actionstream<<"Server: Player with the name \""<<playername<<"\" "
<<"tried to connect from "<<addr_s<<" "
<<"but it was disallowed for the following reason: "
<<reason<<std::endl;
DenyAccess(peer_id, narrow_to_wide(reason.c_str()));
return;
}
}
2012-03-10 18:15:45 -08:00
infostream<<"Server: New connection: \""<<playername<<"\" from "
<<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
2012-03-10 18:15:45 -08:00
// Get password
2012-06-06 16:11:28 -07:00
char given_password[PASSWORD_SIZE];
if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
{
// old version - assume blank password
2012-06-06 16:11:28 -07:00
given_password[0] = 0;
}
else
{
for(u32 i=0; i<PASSWORD_SIZE-1; i++)
{
2012-06-06 16:11:28 -07:00
given_password[i] = data[23+i];
}
2012-06-06 16:11:28 -07:00
given_password[PASSWORD_SIZE-1] = 0;
}
2012-06-03 10:32:44 -07:00
2012-06-06 16:11:28 -07:00
if(!base64_is_valid(given_password)){
actionstream<<"Server: "<<playername
2012-06-06 16:11:28 -07:00
<<" supplied invalid password hash"<<std::endl;
DenyAccess(peer_id, L"Invalid password hash");
return;
}
// Enforce user limit.
// Don't enforce for users that have some admin right
if(m_clients.size() >= g_settings->getU16("max_users") &&
!checkPriv(playername, "server") &&
!checkPriv(playername, "ban") &&
!checkPriv(playername, "privs") &&
!checkPriv(playername, "password") &&
playername != g_settings->get("name"))
{
actionstream<<"Server: "<<playername<<" tried to join, but there"
<<" are already max_users="
<<g_settings->getU16("max_users")<<" players."<<std::endl;
DenyAccess(peer_id, L"Too many users.");
2012-06-03 10:32:44 -07:00
return;
}
2012-06-06 16:11:28 -07:00
std::string checkpwd; // Password hash to check against
bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
2012-06-06 16:11:28 -07:00
// If no authentication info exists for user, create it
if(!has_auth){
2012-06-06 16:11:28 -07:00
if(!isSingleplayer() &&
g_settings->getBool("disallow_empty_password") &&
std::string(given_password) == ""){
actionstream<<"Server: "<<playername
<<" supplied empty password"<<std::endl;
DenyAccess(peer_id, L"Empty passwords are "
2012-06-06 16:11:28 -07:00
L"disallowed. Set a password and try again.");
return;
}
std::wstring raw_default_password =
narrow_to_wide(g_settings->get("default_password"));
2012-06-06 16:11:28 -07:00
std::string initial_password =
translatePassword(playername, raw_default_password);
// If default_password is empty, allow any initial password
if (raw_default_password.length() == 0)
2012-06-06 16:11:28 -07:00
initial_password = given_password;
m_script->createAuth(playername, initial_password);
}
has_auth = m_script->getAuth(playername, &checkpwd, NULL);
if(!has_auth){
actionstream<<"Server: "<<playername<<" cannot be authenticated"
<<" (auth handler does not work?)"<<std::endl;
DenyAccess(peer_id, L"Not allowed to login");
return;
}
2012-06-06 16:11:28 -07:00
if(given_password != checkpwd){
actionstream<<"Server: "<<playername<<" supplied wrong password"
<<std::endl;
DenyAccess(peer_id, L"Wrong password");
2011-09-26 01:57:51 -07:00
return;
}
// Get player
PlayerSAO *playersao = emergePlayer(playername, peer_id);
// If failed, cancel
if(playersao == NULL)
{
RemotePlayer *player =
static_cast<RemotePlayer*>(m_env->getPlayer(playername));
if(player && player->peer_id != 0){
errorstream<<"Server: "<<playername<<": Failed to emerge player"
<<" (player allocated to an another client)"<<std::endl;
DenyAccess(peer_id, L"Another client is connected with this "
L"name. If your client closed unexpectedly, try again in "
L"a minute.");
} else {
errorstream<<"Server: "<<playername<<": Failed to emerge player"
<<std::endl;
DenyAccess(peer_id, L"Could not allocate player.");
}
return;
}
/*
Answer with a TOCLIENT_INIT
*/
{
SharedBuffer<u8> reply(2+1+6+8+4);
writeU16(&reply[0], TOCLIENT_INIT);
writeU8(&reply[2], deployed);
writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2011-11-11 09:33:17 -08:00
writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
// Send as reliable
m_con.Send(peer_id, 0, reply, true);
}
/*
Send complete position information
*/
SendMovePlayer(peer_id);
2010-11-26 15:02:21 -08:00
return;
}
2010-11-26 15:02:21 -08:00
if(command == TOSERVER_INIT2)
{
2012-03-10 18:15:45 -08:00
verbosestream<<"Server: Got TOSERVER_INIT2 from "
<<peer_id<<std::endl;
2010-11-26 15:02:21 -08:00
Player *player = m_env->getPlayer(peer_id);
if(!player){
verbosestream<<"Server: TOSERVER_INIT2: "
<<"Player not found; ignoring."<<std::endl;
return;
}
2010-11-26 15:02:21 -08:00
RemoteClient *client = getClient(peer_id);
client->serialization_version =
getClient(peer_id)->pending_serialization_version;
2010-11-26 15:02:21 -08:00
/*
Send some initialization data
*/
2011-11-15 01:02:47 -08:00
2012-03-10 18:15:45 -08:00
infostream<<"Server: Sending content to "
<<getPlayerName(peer_id)<<std::endl;
2013-02-08 12:54:01 -08:00
// Send player movement settings
SendMovement(m_con, peer_id);
// Send item definitions
SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version);
2011-11-15 10:32:56 -08:00
// Send node definitions
SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
// Send media announcement
2012-03-25 01:50:29 -07:00
sendMediaAnnouncement(peer_id);
// Send privileges
SendPlayerPrivileges(peer_id);
// Send inventory formspec
SendPlayerInventoryFormspec(peer_id);
// Send inventory
UpdateCrafting(peer_id);
SendInventory(peer_id);
2012-07-24 10:57:17 -07:00
// Send HP
if(g_settings->getBool("enable_damage"))
SendPlayerHP(peer_id);
// Send Breath
SendPlayerBreath(peer_id);
2012-07-24 10:57:17 -07:00
// Send detached inventories
sendDetachedInventories(peer_id);
// Show death screen if necessary
if(player->hp == 0)
SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
// Send time of day
{
u16 time = m_env->getTimeOfDay();
float time_speed = g_settings->getFloat("time_speed");
SendTimeOfDay(peer_id, time, time_speed);
}
// Note things in chat if not in simple singleplayer mode
if(!m_simple_singleplayer_mode)
{
// Send information about server to player in chat
SendChatMessage(peer_id, getStatusString());
// Send information about joining in chat
{
std::wstring name = L"unknown";
Player *player = m_env->getPlayer(peer_id);
if(player != NULL)
name = narrow_to_wide(player->getName());
std::wstring message;
message += L"*** ";
message += name;
message += L" joined the game.";
BroadcastChatMessage(message);
}
}
// Warnings about protocol version can be issued here
if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
2011-07-30 10:10:16 -07:00
{
SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
2011-10-15 04:46:59 -07:00
}
/*
Print out action
*/
2011-10-15 04:46:59 -07:00
{
std::ostringstream os(std::ios_base::binary);
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
{
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
// Get player
2011-11-11 09:33:17 -08:00
Player *player = m_env->getPlayer(client->peer_id);
if(!player)
continue;
// Get name of player
os<<player->getName()<<" ";
}
2013-07-23 09:31:05 -07:00
actionstream<<player->getName()<<" ["<<addr_s<<"] "<<"joins game. List of players: "
<<os.str()<<std::endl;
2011-10-15 04:46:59 -07:00
}
2011-07-30 10:10:16 -07:00
2010-11-26 15:02:21 -08:00
return;
}
if(peer_ser_ver == SER_FMT_VER_INVALID)
{
infostream<<"Server::ProcessData(): Cancelling: Peer"
2010-11-26 15:02:21 -08:00
" serialization format invalid or not initialized."
" Skipping incoming command="<<command<<std::endl;
return;
}
2011-11-11 09:33:17 -08:00
Player *player = m_env->getPlayer(peer_id);
2010-11-26 15:02:21 -08:00
if(player == NULL){
infostream<<"Server::ProcessData(): Cancelling: "
2010-11-26 15:02:21 -08:00
"No player for peer_id="<<peer_id
<<std::endl;
return;
}
PlayerSAO *playersao = player->getPlayerSAO();
if(playersao == NULL){
infostream<<"Server::ProcessData(): Cancelling: "
"No player object for peer_id="<<peer_id
<<std::endl;
return;
}
2010-11-26 15:02:21 -08:00
if(command == TOSERVER_PLAYERPOS)
{
if(datasize < 2+12+12+4+4)
2010-11-26 15:02:21 -08:00
return;
2010-11-26 15:02:21 -08:00
u32 start = 0;
v3s32 ps = readV3S32(&data[start+2]);
v3s32 ss = readV3S32(&data[start+2+12]);
f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
u32 keyPressed = 0;
if(datasize >= 2+12+12+4+4+4)
keyPressed = (u32)readU32(&data[2+12+12+4+4]);
2010-11-26 15:02:21 -08:00
v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
pitch = wrapDegrees(pitch);
yaw = wrapDegrees(yaw);
2010-11-26 15:02:21 -08:00
player->setPosition(position);
player->setSpeed(speed);
player->setPitch(pitch);
player->setYaw(yaw);
player->keyPressed=keyPressed;
player->control.up = (bool)(keyPressed&1);
player->control.down = (bool)(keyPressed&2);
player->control.left = (bool)(keyPressed&4);
player->control.right = (bool)(keyPressed&8);
player->control.jump = (bool)(keyPressed&16);
player->control.aux1 = (bool)(keyPressed&32);
player->control.sneak = (bool)(keyPressed&64);
player->control.LMB = (bool)(keyPressed&128);
player->control.RMB = (bool)(keyPressed&256);
bool cheated = playersao->checkMovementCheat();
if(cheated){
// Call callbacks
m_script->on_cheat(playersao, "moved_too_fast");
}
2013-08-03 13:16:37 -07:00
/*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
<<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
<<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2010-11-26 15:02:21 -08:00
}
else if(command == TOSERVER_GOTBLOCKS)
{
if(datasize < 2+1)
return;
2010-11-26 15:02:21 -08:00
/*
[0] u16 command
[2] u8 count
[3] v3s16 pos_0
[3+6] v3s16 pos_1
...
*/
u16 count = data[2];
for(u16 i=0; i<count; i++)
{
if((s16)datasize < 2+1+(i+1)*6)
throw con::InvalidIncomingDataException
("GOTBLOCKS length is too short");
v3s16 p = readV3S16(&data[2+1+i*6]);
/*infostream<<"Server: GOTBLOCKS ("
2010-11-26 15:02:21 -08:00
<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
RemoteClient *client = getClient(peer_id);
client->GotBlock(p);
}
}
else if(command == TOSERVER_DELETEDBLOCKS)
{
if(datasize < 2+1)
return;
2010-11-26 15:02:21 -08:00
/*
[0] u16 command
[2] u8 count
[3] v3s16 pos_0
[3+6] v3s16 pos_1
...
*/
u16 count = data[2];
for(u16 i=0; i<count; i++)
{
if((s16)datasize < 2+1+(i+1)*6)
throw con::InvalidIncomingDataException
("DELETEDBLOCKS length is too short");
v3s16 p = readV3S16(&data[2+1+i*6]);
/*infostream<<"Server: DELETEDBLOCKS ("
2010-11-26 15:02:21 -08:00
<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
RemoteClient *client = getClient(peer_id);
client->SetBlockNotSent(p);
}
}
else if(command == TOSERVER_CLICK_OBJECT)
{
infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2011-10-14 16:28:57 -07:00
return;
2010-11-26 15:02:21 -08:00
}
2011-04-09 18:15:10 -07:00
else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
{
2011-11-29 07:15:18 -08:00
infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
return;
}
else if(command == TOSERVER_GROUND_ACTION)
{
infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
return;
2011-04-09 18:15:10 -07:00
2011-11-29 07:15:18 -08:00
}
else if(command == TOSERVER_RELEASE)
{
infostream<<"Server: RELEASE not supported anymore"<<std::endl;
return;
}
else if(command == TOSERVER_SIGNTEXT)
{
infostream<<"Server: SIGNTEXT not supported anymore"
<<std::endl;
return;
}
else if(command == TOSERVER_SIGNNODETEXT)
{
2012-03-18 17:08:04 -07:00
infostream<<"Server: SIGNNODETEXT not supported anymore"
<<std::endl;
return;
2011-11-29 07:15:18 -08:00
}
else if(command == TOSERVER_INVENTORY_ACTION)
{
// Strip command and create a stream
std::string datastring((char*)&data[2], datasize-2);
2012-03-10 18:15:45 -08:00
verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2011-11-29 07:15:18 -08:00
std::istringstream is(datastring, std::ios_base::binary);
// Create an action
InventoryAction *a = InventoryAction::deSerialize(is);
if(a == NULL)
2011-11-29 07:15:18 -08:00
{
infostream<<"TOSERVER_INVENTORY_ACTION: "
<<"InventoryAction::deSerialize() returned NULL"
<<std::endl;
return;
}
// If something goes wrong, this player is to blame
RollbackScopeActor rollback_scope(m_rollback,
std::string("player:")+player->getName());
/*
Note: Always set inventory not sent, to repair cases
where the client made a bad prediction.
*/
/*
Handle restrictions and special cases of the move action
*/
if(a->getType() == IACTION_MOVE)
{
IMoveAction *ma = (IMoveAction*)a;
ma->from_inv.applyCurrentPlayer(player->getName());
ma->to_inv.applyCurrentPlayer(player->getName());
setInventoryModified(ma->from_inv);
setInventoryModified(ma->to_inv);
bool from_inv_is_current_player =
(ma->from_inv.type == InventoryLocation::PLAYER) &&
(ma->from_inv.name == player->getName());
bool to_inv_is_current_player =
(ma->to_inv.type == InventoryLocation::PLAYER) &&
(ma->to_inv.name == player->getName());
/*
Disable moving items out of craftpreview
*/
if(ma->from_list == "craftpreview")
{
infostream<<"Ignoring IMoveAction from "
<<(ma->from_inv.dump())<<":"<<ma->from_list
<<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
<<" because src is "<<ma->from_list<<std::endl;
delete a;
return;
}
2011-11-29 07:15:18 -08:00
/*
Disable moving items into craftresult and craftpreview
*/
if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
{
infostream<<"Ignoring IMoveAction from "
<<(ma->from_inv.dump())<<":"<<ma->from_list
<<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
<<" because dst is "<<ma->to_list<<std::endl;
delete a;
return;
}
// Disallow moving items in elsewhere than player's inventory
// if not allowed to interact
if(!checkPriv(player->getName(), "interact") &&
(!from_inv_is_current_player ||
!to_inv_is_current_player))
{
infostream<<"Cannot move outside of player's inventory: "
<<"No interact privilege"<<std::endl;
delete a;
return;
}
}
/*
Handle restrictions and special cases of the drop action
*/
else if(a->getType() == IACTION_DROP)
{
IDropAction *da = (IDropAction*)a;
da->from_inv.applyCurrentPlayer(player->getName());
setInventoryModified(da->from_inv);
/*
Disable dropping items out of craftpreview
*/
if(da->from_list == "craftpreview")
{
infostream<<"Ignoring IDropAction from "
<<(da->from_inv.dump())<<":"<<da->from_list
<<" because src is "<<da->from_list<<std::endl;
delete a;
return;
}
// Disallow dropping items if not allowed to interact
if(!checkPriv(player->getName(), "interact"))
2011-11-29 07:15:18 -08:00
{
delete a;
return;
}
}
/*
Handle restrictions and special cases of the craft action
*/
else if(a->getType() == IACTION_CRAFT)
{
ICraftAction *ca = (ICraftAction*)a;
ca->craft_inv.applyCurrentPlayer(player->getName());
setInventoryModified(ca->craft_inv);
//bool craft_inv_is_current_player =
// (ca->craft_inv.type == InventoryLocation::PLAYER) &&
// (ca->craft_inv.name == player->getName());
// Disallow crafting if not allowed to interact
if(!checkPriv(player->getName(), "interact"))
{
infostream<<"Cannot craft: "
<<"No interact privilege"<<std::endl;
delete a;
return;
}
}
// Do the action
a->apply(this, playersao, this);
// Eat the action
delete a;
2011-04-09 18:15:10 -07:00
}
2011-11-29 07:15:18 -08:00
else if(command == TOSERVER_CHAT_MESSAGE)
2010-11-26 15:02:21 -08:00
{
/*
2011-11-29 07:15:18 -08:00
u16 command
u16 length
wstring message
*/
2011-11-29 07:15:18 -08:00
u8 buf[6];
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
2011-11-29 07:15:18 -08:00
// Read stuff
is.read((char*)buf, 2);
u16 len = readU16(buf);
2011-11-29 07:15:18 -08:00
std::wstring message;
for(u16 i=0; i<len; i++)
{
is.read((char*)buf, 2);
message += (wchar_t)readU16(buf);
}
// If something goes wrong, this player is to blame
RollbackScopeActor rollback_scope(m_rollback,
std::string("player:")+player->getName());
2011-11-29 07:15:18 -08:00
// Get player name of this client
std::wstring name = narrow_to_wide(player->getName());
2011-11-29 07:15:18 -08:00
// Run script hook
bool ate = m_script->on_chat_message(player->getName(),
2011-11-29 07:15:18 -08:00
wide_to_narrow(message));
// If script ate the message, don't proceed
if(ate)
return;
2011-11-29 07:15:18 -08:00
// Line to send to players
std::wstring line;
// Whether to send to the player that sent the line
bool send_to_sender = false;
// Whether to send to other players
bool send_to_others = false;
// Commands are implemented in Lua, so only catch invalid
// commands that were not "eaten" and send an error back
2011-11-29 07:15:18 -08:00
if(message[0] == L'/')
2010-11-26 15:02:21 -08:00
{
message = message.substr(1);
send_to_sender = true;
if(message.length() == 0)
line += L"-!- Empty command";
2011-11-29 07:15:18 -08:00
else
line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2011-11-29 07:15:18 -08:00
}
else
{
if(checkPriv(player->getName(), "shout")){
2011-11-29 07:15:18 -08:00
line += L"<";
line += name;
line += L"> ";
line += message;
send_to_others = true;
} else {
line += L"-!- You don't have permission to shout.";
2011-11-29 07:15:18 -08:00
send_to_sender = true;
}
2011-11-29 07:15:18 -08:00
}
2011-11-29 07:15:18 -08:00
if(line != L"")
{
if(send_to_others)
actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
/*
2011-11-29 07:15:18 -08:00
Send the message to clients
*/
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
2011-11-29 07:15:18 -08:00
{
// Get client and check that it is valid
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
2011-11-29 07:15:18 -08:00
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
2011-11-29 07:15:18 -08:00
// Filter recipient
bool sender_selected = (peer_id == client->peer_id);
if(sender_selected == true && send_to_sender == false)
continue;
if(sender_selected == false && send_to_others == false)
continue;
SendChatMessage(client->peer_id, line);
}
}
}
else if(command == TOSERVER_DAMAGE)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
u8 damage = readU8(is);
if(g_settings->getBool("enable_damage"))
{
actionstream<<player->getName()<<" damaged by "
<<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
<<std::endl;
playersao->setHP(playersao->getHP() - damage);
if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
DiePlayer(peer_id);
if(playersao->m_hp_not_sent)
SendPlayerHP(peer_id);
}
2011-11-29 07:15:18 -08:00
}
else if(command == TOSERVER_BREATH)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
u16 breath = readU16(is);
playersao->setBreath(breath);
}
2011-11-29 07:15:18 -08:00
else if(command == TOSERVER_PASSWORD)
{
/*
2011-11-29 07:15:18 -08:00
[0] u16 TOSERVER_PASSWORD
[2] u8[28] old password
[30] u8[28] new password
*/
2011-11-29 07:15:18 -08:00
if(datasize != 2+PASSWORD_SIZE*2)
return;
/*char password[PASSWORD_SIZE];
for(u32 i=0; i<PASSWORD_SIZE-1; i++)
password[i] = data[2+i];
password[PASSWORD_SIZE-1] = 0;*/
std::string oldpwd;
for(u32 i=0; i<PASSWORD_SIZE-1; i++)
{
2011-11-29 07:15:18 -08:00
char c = data[2+i];
if(c == 0)
break;
oldpwd += c;
}
std::string newpwd;
for(u32 i=0; i<PASSWORD_SIZE-1; i++)
{
char c = data[2+PASSWORD_SIZE+i];
if(c == 0)
break;
newpwd += c;
}
2012-06-03 10:32:44 -07:00
if(!base64_is_valid(newpwd)){
infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
// Wrong old password supplied!!
SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
return;
}
2011-11-29 07:15:18 -08:00
infostream<<"Server: Client requests a password change from "
<<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
std::string playername = player->getName();
std::string checkpwd;
m_script->getAuth(playername, &checkpwd, NULL);
2011-11-29 07:15:18 -08:00
if(oldpwd != checkpwd)
{
infostream<<"Server: invalid old password"<<std::endl;
// Wrong old password supplied!!
SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
return;
}
bool success = m_script->setPassword(playername, newpwd);
if(success){
actionstream<<player->getName()<<" changes password"<<std::endl;
SendChatMessage(peer_id, L"Password change successful.");
} else {
actionstream<<player->getName()<<" tries to change password but "
<<"it fails"<<std::endl;
SendChatMessage(peer_id, L"Password change failed or inavailable.");
}
2011-11-29 07:15:18 -08:00
}
else if(command == TOSERVER_PLAYERITEM)
{
if (datasize < 2+2)
return;
u16 item = readU16(&data[2]);
playersao->setWieldIndex(item);
2011-11-29 07:15:18 -08:00
}
else if(command == TOSERVER_RESPAWN)
{
if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2011-11-29 07:15:18 -08:00
return;
RespawnPlayer(peer_id);
2011-11-29 07:15:18 -08:00
actionstream<<player->getName()<<" respawns at "
<<PP(player->getPosition()/BS)<<std::endl;
// ActiveObject is added to environment in AsyncRunStep after
// the previous addition has been succesfully removed
2011-11-29 07:15:18 -08:00
}
2012-03-25 01:50:29 -07:00
else if(command == TOSERVER_REQUEST_MEDIA) {
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
Rewrite client media download and support hash-based remote download Move most of the media-related code in client.cpp into a new class ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other things, this class does the following things: - Download [remote_server][sha1] instead of [remote_server][name]. This is to support servers that provide the same file name with different contents. - Initially fetch [remote_server]index.mth. This file should follow the Minetest Hashset format (currently version 1) and contain a list of SHA1 hashes that exist on the server. - The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is requested, so servers can optionally narrow down the list to the needs of the client. - If index.mth is missing (HTTP response code 404), we enter compat mode, fetching [remote_server][name] as before this commit. - remote_server can now contain multiple servers, separated by commas. The downloader code attempts to split requests between the different servers, as permitted by each server's index.mth. If one server claims to have a file but actually doesn't (or something fails), we ask a different server that also claims to have it. - As before, when none of the remote servers provide a particular file, we download it via the conventional method, i.e. using the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA. - Bugfix: Every downloaded file's SHA1 is now verified against the SHA1 announced by the minetest server (before loading it and inserting it into the file cache). - Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all media. This should fix #863.
2013-08-28 20:22:18 -07:00
std::list<std::string> tosend;
2012-03-25 01:50:29 -07:00
u16 numfiles = readU16(is);
2012-03-25 01:50:29 -07:00
infostream<<"Sending "<<numfiles<<" files to "
2012-03-10 18:15:45 -08:00
<<getPlayerName(peer_id)<<std::endl;
2012-03-25 01:50:29 -07:00
verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2012-03-25 01:50:29 -07:00
for(int i = 0; i < numfiles; i++) {
std::string name = deSerializeString(is);
Rewrite client media download and support hash-based remote download Move most of the media-related code in client.cpp into a new class ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other things, this class does the following things: - Download [remote_server][sha1] instead of [remote_server][name]. This is to support servers that provide the same file name with different contents. - Initially fetch [remote_server]index.mth. This file should follow the Minetest Hashset format (currently version 1) and contain a list of SHA1 hashes that exist on the server. - The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is requested, so servers can optionally narrow down the list to the needs of the client. - If index.mth is missing (HTTP response code 404), we enter compat mode, fetching [remote_server][name] as before this commit. - remote_server can now contain multiple servers, separated by commas. The downloader code attempts to split requests between the different servers, as permitted by each server's index.mth. If one server claims to have a file but actually doesn't (or something fails), we ask a different server that also claims to have it. - As before, when none of the remote servers provide a particular file, we download it via the conventional method, i.e. using the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA. - Bugfix: Every downloaded file's SHA1 is now verified against the SHA1 announced by the minetest server (before loading it and inserting it into the file cache). - Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all media. This should fix #863.
2013-08-28 20:22:18 -07:00
tosend.push_back(name);
2012-03-25 01:50:29 -07:00
verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2012-03-10 18:15:45 -08:00
<<name<<std::endl;
}
2012-03-25 01:50:29 -07:00
sendRequestedMedia(peer_id, tosend);
// Now the client should know about everything
2012-03-25 01:50:29 -07:00
// (definitions and files)
getClient(peer_id)->definitions_sent = true;
}
else if(command == TOSERVER_RECEIVED_MEDIA) {
getClient(peer_id)->definitions_sent = true;
}
2011-11-29 07:15:18 -08:00
else if(command == TOSERVER_INTERACT)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
/*
2011-11-29 07:15:18 -08:00
[0] u16 command
[2] u8 action
[3] u16 item
[5] u32 length of the next item
[9] serialized PointedThing
actions:
0: start digging (from undersurface) or use
1: stop digging (all parameters ignored)
2: digging completed
3: place block or item (to abovesurface)
4: use item
*/
2011-11-29 07:15:18 -08:00
u8 action = readU8(is);
u16 item_i = readU16(is);
std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
PointedThing pointed;
pointed.deSerialize(tmp_is);
2012-03-10 18:15:45 -08:00
verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
<<item_i<<", pointed="<<pointed.dump()<<std::endl;
2011-11-29 07:15:18 -08:00
if(player->hp == 0)
{
verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
<<" tried to interact, but is dead!"<<std::endl;
return;
}
v3f player_pos = playersao->getLastGoodPosition();
2011-11-29 07:15:18 -08:00
// Update wielded item
playersao->setWieldIndex(item_i);
2011-11-29 07:15:18 -08:00
// Get pointed to node (undefined if not POINTEDTYPE_NODE)
v3s16 p_under = pointed.node_undersurface;
v3s16 p_above = pointed.node_abovesurface;
// Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
ServerActiveObject *pointed_object = NULL;
if(pointed.type == POINTEDTHING_OBJECT)
{
2011-11-29 07:15:18 -08:00
pointed_object = m_env->getActiveObject(pointed.object_id);
if(pointed_object == NULL)
{
2012-03-10 18:15:45 -08:00
verbosestream<<"TOSERVER_INTERACT: "
2011-11-29 07:15:18 -08:00
"pointed object is NULL"<<std::endl;
return;
}
2011-11-29 07:15:18 -08:00
}
v3f pointed_pos_under = player_pos;
v3f pointed_pos_above = player_pos;
if(pointed.type == POINTEDTHING_NODE)
{
pointed_pos_under = intToFloat(p_under, BS);
pointed_pos_above = intToFloat(p_above, BS);
}
else if(pointed.type == POINTEDTHING_OBJECT)
{
pointed_pos_under = pointed_object->getBasePosition();
pointed_pos_above = pointed_pos_under;
}
2011-11-29 07:15:18 -08:00
/*
Check that target is reasonably close
(only when digging or placing things)
*/
if(action == 0 || action == 2 || action == 3)
{
float d = player_pos.getDistanceFrom(pointed_pos_under);
float max_d = BS * 14; // Just some large enough value
2011-11-29 07:15:18 -08:00
if(d > max_d){
actionstream<<"Player "<<player->getName()
<<" tried to access "<<pointed.dump()
<<" from too far: "
<<"d="<<d<<", max_d="<<max_d
<<". ignoring."<<std::endl;
// Re-send block to revert change on client-side
RemoteClient *client = getClient(peer_id);
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
client->SetBlockNotSent(blockpos);
// Call callbacks
m_script->on_cheat(playersao, "interacted_too_far");
2011-11-29 07:15:18 -08:00
// Do nothing else
2010-11-26 15:02:21 -08:00
return;
}
2011-11-29 07:15:18 -08:00
}
/*
Make sure the player is allowed to do it
*/
if(!checkPriv(player->getName(), "interact"))
2011-11-29 07:15:18 -08:00
{
actionstream<<player->getName()<<" attempted to interact with "
<<pointed.dump()<<" without 'interact' privilege"
<<std::endl;
// Re-send block to revert change on client-side
RemoteClient *client = getClient(peer_id);
2012-06-10 02:46:48 -07:00
// Digging completed -> under
if(action == 2){
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
client->SetBlockNotSent(blockpos);
}
// Placement -> above
if(action == 3){
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
client->SetBlockNotSent(blockpos);
}
return;
2011-11-29 07:15:18 -08:00
}
/*
If something goes wrong, this player is to blame
*/
RollbackScopeActor rollback_scope(m_rollback,
std::string("player:")+player->getName());
2011-11-29 07:15:18 -08:00
/*
0: start digging or punch object
*/
if(action == 0)
{
if(pointed.type == POINTEDTHING_NODE)
2010-11-26 15:02:21 -08:00
{
2011-04-09 18:15:10 -07:00
/*
2011-11-29 07:15:18 -08:00
NOTE: This can be used in the future to check if
somebody is cheating, by checking the timing.
2011-04-09 18:15:10 -07:00
*/
2011-11-29 07:15:18 -08:00
MapNode n(CONTENT_IGNORE);
try
2010-11-26 15:02:21 -08:00
{
2011-11-29 07:15:18 -08:00
n = m_env->getMap().getNode(p_under);
}
2011-11-29 07:15:18 -08:00
catch(InvalidPositionException &e)
{
2011-11-29 07:15:18 -08:00
infostream<<"Server: Not punching: Node not found."
<<" Adding block to emerge queue."
<<std::endl;
m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2010-11-26 15:02:21 -08:00
}
if(n.getContent() != CONTENT_IGNORE)
m_script->node_on_punch(p_under, n, playersao);
// Cheat prevention
playersao->noCheatDigStart(p_under);
2011-11-29 07:15:18 -08:00
}
else if(pointed.type == POINTEDTHING_OBJECT)
{
// Skip if object has been removed
if(pointed_object->m_removed)
return;
2010-11-26 15:02:21 -08:00
2011-11-29 07:15:18 -08:00
actionstream<<player->getName()<<" punches object "
2012-03-10 18:15:45 -08:00
<<pointed.object_id<<": "
<<pointed_object->getDescription()<<std::endl;
2011-11-29 07:15:18 -08:00
ItemStack punchitem = playersao->getWieldedItem();
ToolCapabilities toolcap =
punchitem.getToolCapabilities(m_itemdef);
v3f dir = (pointed_object->getBasePosition() -
(player->getPosition() + player->getEyeOffset())
).normalize();
float time_from_last_punch =
playersao->resetTimeFromLastPunch();
pointed_object->punch(dir, &toolcap, playersao,
time_from_last_punch);
2010-11-26 15:02:21 -08:00
}
2011-11-29 07:15:18 -08:00
} // action == 0
2010-11-26 15:02:21 -08:00
/*
2011-11-29 07:15:18 -08:00
1: stop digging
2010-11-26 15:02:21 -08:00
*/
2011-11-29 07:15:18 -08:00
else if(action == 1)
2010-11-26 15:02:21 -08:00
{
2011-11-29 07:15:18 -08:00
} // action == 1
/*
2011-11-29 07:15:18 -08:00
2: Digging completed
*/
2011-11-29 07:15:18 -08:00
else if(action == 2)
{
// Only digging of nodes
if(pointed.type == POINTEDTHING_NODE)
2011-11-29 07:15:18 -08:00
{
MapNode n(CONTENT_IGNORE);
try
2011-11-26 19:01:38 -08:00
{
n = m_env->getMap().getNode(p_under);
2011-09-22 02:11:48 -07:00
}
catch(InvalidPositionException &e)
2011-11-29 07:15:18 -08:00
{
infostream<<"Server: Not finishing digging: Node not found."
<<" Adding block to emerge queue."
<<std::endl;
m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2011-11-29 07:15:18 -08:00
}
/* Cheat prevention */
bool is_valid_dig = true;
if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
{
v3s16 nocheat_p = playersao->getNoCheatDigPos();
float nocheat_t = playersao->getNoCheatDigTime();
playersao->noCheatDigEnd();
// If player didn't start digging this, ignore dig
if(nocheat_p != p_under){
infostream<<"Server: NoCheat: "<<player->getName()
<<" started digging "
<<PP(nocheat_p)<<" and completed digging "
<<PP(p_under)<<"; not digging."<<std::endl;
is_valid_dig = false;
// Call callbacks
m_script->on_cheat(playersao, "finished_unknown_dig");
}
// Get player's wielded item
ItemStack playeritem;
InventoryList *mlist = playersao->getInventory()->getList("main");
if(mlist != NULL)
playeritem = mlist->getItem(playersao->getWieldIndex());
ToolCapabilities playeritem_toolcap =
playeritem.getToolCapabilities(m_itemdef);
// Get diggability and expected digging time
DigParams params = getDigParams(m_nodedef->get(n).groups,
&playeritem_toolcap);
// If can't dig, try hand
if(!params.diggable){
const ItemDefinition &hand = m_itemdef->get("");
const ToolCapabilities *tp = hand.tool_capabilities;
if(tp)
params = getDigParams(m_nodedef->get(n).groups, tp);
}
// If can't dig, ignore dig
if(!params.diggable){
infostream<<"Server: NoCheat: "<<player->getName()
<<" completed digging "<<PP(p_under)
<<", which is not diggable with tool. not digging."
<<std::endl;
is_valid_dig = false;
// Call callbacks
m_script->on_cheat(playersao, "dug_unbreakable");
}
2013-08-03 13:16:37 -07:00
// Check digging time
// If already invalidated, we don't have to
if(!is_valid_dig){
// Well not our problem then
}
// Clean and long dig
else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){
// All is good, but grab time from pool; don't care if
// it's actually available
playersao->getDigPool().grab(params.time);
}
// Short or laggy dig
// Try getting the time from pool
else if(playersao->getDigPool().grab(params.time)){
// All is good
}
// Dig not possible
else{
infostream<<"Server: NoCheat: "<<player->getName()
2013-08-03 13:16:37 -07:00
<<" completed digging "<<PP(p_under)
<<"too fast; not digging."<<std::endl;
is_valid_dig = false;
// Call callbacks
m_script->on_cheat(playersao, "dug_too_fast");
}
}
/* Actually dig node */
if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
m_script->node_on_dig(p_under, n, playersao);
// Send unusual result (that is, node not being removed)
if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
{
// Re-send block to revert change on client-side
RemoteClient *client = getClient(peer_id);
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
client->SetBlockNotSent(blockpos);
}
2011-11-29 07:15:18 -08:00
}
} // action == 2
/*
2011-11-29 07:15:18 -08:00
3: place block or right-click object
*/
2011-11-29 07:15:18 -08:00
else if(action == 3)
{
ItemStack item = playersao->getWieldedItem();
2011-11-29 07:15:18 -08:00
// Reset build time counter
if(pointed.type == POINTEDTHING_NODE &&
item.getDefinition(m_itemdef).type == ITEM_NODE)
getClient(peer_id)->m_time_from_building = 0.0;
if(pointed.type == POINTEDTHING_OBJECT)
{
// Right click object
2011-11-29 07:15:18 -08:00
// Skip if object has been removed
if(pointed_object->m_removed)
return;
2011-11-29 07:15:18 -08:00
actionstream<<player->getName()<<" right-clicks object "
2012-03-10 18:15:45 -08:00
<<pointed.object_id<<": "
<<pointed_object->getDescription()<<std::endl;
2011-11-29 07:15:18 -08:00
// Do stuff
pointed_object->rightClick(playersao);
}
else if(m_script->item_OnPlace(
item, playersao, pointed))
{
// Placement was handled in lua
2011-05-23 12:40:25 -07:00
// Apply returned ItemStack
playersao->setWieldedItem(item);
}
// If item has node placement prediction, always send the
// blocks to make sure the client knows what exactly happened
2012-06-10 02:46:48 -07:00
if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
RemoteClient *client = getClient(peer_id);
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
client->SetBlockNotSent(blockpos);
v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
if(blockpos2 != blockpos){
client->SetBlockNotSent(blockpos2);
}
2012-06-10 02:46:48 -07:00
}
2011-11-29 07:15:18 -08:00
} // action == 3
/*
2011-11-29 07:15:18 -08:00
4: use
*/
2011-11-29 07:15:18 -08:00
else if(action == 4)
{
ItemStack item = playersao->getWieldedItem();
actionstream<<player->getName()<<" uses "<<item.name
2011-11-29 07:15:18 -08:00
<<", pointing at "<<pointed.dump()<<std::endl;
if(m_script->item_OnUse(
item, playersao, pointed))
2011-11-29 07:15:18 -08:00
{
// Apply returned ItemStack
playersao->setWieldedItem(item);
2011-11-29 07:15:18 -08:00
}
2011-11-29 07:15:18 -08:00
} // action == 4
2011-11-29 07:15:18 -08:00
/*
Catch invalid actions
*/
else
{
2011-11-29 07:15:18 -08:00
infostream<<"WARNING: Server: Invalid action "
<<action<<std::endl;
}
2011-10-15 04:46:59 -07:00
}
2012-03-24 10:01:26 -07:00
else if(command == TOSERVER_REMOVED_SOUNDS)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
int num = readU16(is);
for(int k=0; k<num; k++){
s32 id = readS32(is);
std::map<s32, ServerPlayingSound>::iterator i =
m_playing_sounds.find(id);
if(i == m_playing_sounds.end())
continue;
ServerPlayingSound &psound = i->second;
psound.clients.erase(peer_id);
if(psound.clients.size() == 0)
m_playing_sounds.erase(i++);
}
}
else if(command == TOSERVER_NODEMETA_FIELDS)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
v3s16 p = readV3S16(is);
std::string formname = deSerializeString(is);
int num = readU16(is);
std::map<std::string, std::string> fields;
for(int k=0; k<num; k++){
std::string fieldname = deSerializeString(is);
std::string fieldvalue = deSerializeLongString(is);
fields[fieldname] = fieldvalue;
}
// If something goes wrong, this player is to blame
RollbackScopeActor rollback_scope(m_rollback,
std::string("player:")+player->getName());
// Check the target node for rollback data; leave others unnoticed
RollbackNode rn_old(&m_env->getMap(), p, this);
m_script->node_on_receive_fields(p, formname, fields,playersao);
// Report rollback data
RollbackNode rn_new(&m_env->getMap(), p, this);
if(rollback() && rn_new != rn_old){
RollbackAction action;
action.setSetNode(p, rn_old, rn_new);
rollback()->reportAction(action);
}
}
else if(command == TOSERVER_INVENTORY_FIELDS)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
std::string formname = deSerializeString(is);
int num = readU16(is);
std::map<std::string, std::string> fields;
for(int k=0; k<num; k++){
std::string fieldname = deSerializeString(is);
std::string fieldvalue = deSerializeLongString(is);
fields[fieldname] = fieldvalue;
}
m_script->on_playerReceiveFields(playersao, formname, fields);
}
2010-11-26 15:02:21 -08:00
else
{
infostream<<"Server::ProcessData(): Ignoring "
2010-11-26 15:02:21 -08:00
"unknown command "<<command<<std::endl;
}
2010-11-26 15:02:21 -08:00
} //try
catch(SendFailedException &e)
{
errorstream<<"Server::ProcessData(): SendFailedException: "
2010-11-26 15:02:21 -08:00
<<"what="<<e.what()
<<std::endl;
}
}
void Server::setTimeOfDay(u32 time)
{
m_env->setTimeOfDay(time);
m_time_of_day_send_timer = 0;
}
2011-02-22 16:49:57 -08:00
void Server::onMapEditEvent(MapEditEvent *event)
2010-11-26 15:02:21 -08:00
{
//infostream<<"Server::onMapEditEvent()"<<std::endl;
2011-02-22 16:49:57 -08:00
if(m_ignore_map_edit_events)
return;
if(m_ignore_map_edit_events_area.contains(event->getArea()))
return;
2011-02-22 16:49:57 -08:00
MapEditEvent *e = event->clone();
m_unsent_map_edit_queue.push_back(e);
2010-11-26 15:02:21 -08:00
}
Inventory* Server::getInventory(const InventoryLocation &loc)
{
switch(loc.type){
case InventoryLocation::UNDEFINED:
{}
break;
case InventoryLocation::CURRENT_PLAYER:
{}
break;
case InventoryLocation::PLAYER:
{
Player *player = m_env->getPlayer(loc.name.c_str());
if(!player)
return NULL;
PlayerSAO *playersao = player->getPlayerSAO();
if(!playersao)
return NULL;
return playersao->getInventory();
}
break;
case InventoryLocation::NODEMETA:
{
NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
if(!meta)
return NULL;
return meta->getInventory();
}
break;
2012-07-24 10:57:17 -07:00
case InventoryLocation::DETACHED:
{
if(m_detached_inventories.count(loc.name) == 0)
return NULL;
return m_detached_inventories[loc.name];
}
break;
default:
assert(0);
}
return NULL;
}
void Server::setInventoryModified(const InventoryLocation &loc)
{
switch(loc.type){
case InventoryLocation::UNDEFINED:
{}
break;
case InventoryLocation::PLAYER:
{
Player *player = m_env->getPlayer(loc.name.c_str());
if(!player)
return;
PlayerSAO *playersao = player->getPlayerSAO();
if(!playersao)
return;
playersao->m_inventory_not_sent = true;
playersao->m_wielded_item_not_sent = true;
}
break;
case InventoryLocation::NODEMETA:
{
v3s16 blockpos = getNodeBlockPos(loc.p);
MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
if(block)
block->raiseModified(MOD_STATE_WRITE_NEEDED);
setBlockNotSent(blockpos);
}
break;
2012-07-24 10:57:17 -07:00
case InventoryLocation::DETACHED:
{
sendDetachedInventoryToAll(loc.name);
}
break;
default:
assert(0);
}
}
2011-04-04 05:13:19 -07:00
2010-11-26 15:02:21 -08:00
void Server::peerAdded(con::Peer *peer)
{
DSTACK(__FUNCTION_NAME);
2012-03-10 18:15:45 -08:00
verbosestream<<"Server::peerAdded(): peer->id="
2010-11-26 15:02:21 -08:00
<<peer->id<<std::endl;
PeerChange c;
c.type = PEER_ADDED;
c.peer_id = peer->id;
c.timeout = false;
m_peer_change_queue.push_back(c);
2010-11-26 15:02:21 -08:00
}
void Server::deletingPeer(con::Peer *peer, bool timeout)
{
DSTACK(__FUNCTION_NAME);
2012-03-10 18:15:45 -08:00
verbosestream<<"Server::deletingPeer(): peer->id="
2010-11-26 15:02:21 -08:00
<<peer->id<<", timeout="<<timeout<<std::endl;
PeerChange c;
c.type = PEER_REMOVED;
c.peer_id = peer->id;
c.timeout = timeout;
m_peer_change_queue.push_back(c);
2010-11-26 15:02:21 -08:00
}
/*
Static send methods
*/
2013-02-08 12:54:01 -08:00
void Server::SendMovement(con::Connection &con, u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_MOVEMENT);
writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
writeF1000(os, g_settings->getFloat("movement_speed_walk"));
writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
writeF1000(os, g_settings->getFloat("movement_speed_fast"));
writeF1000(os, g_settings->getFloat("movement_speed_climb"));
writeF1000(os, g_settings->getFloat("movement_speed_jump"));
writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
writeF1000(os, g_settings->getFloat("movement_gravity"));
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
con.Send(peer_id, 0, data, true);
}
void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_HP);
writeU8(os, hp);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
con.Send(peer_id, 0, data, true);
}
void Server::SendBreath(con::Connection &con, u16 peer_id, u16 breath)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_BREATH);
writeU16(os, breath);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
con.Send(peer_id, 0, data, true);
}
void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
const std::wstring &reason)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_ACCESS_DENIED);
os<<serializeWideString(reason);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
con.Send(peer_id, 0, data, true);
}
2011-10-15 04:46:59 -07:00
void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
bool set_camera_point_target, v3f camera_point_target)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_DEATHSCREEN);
writeU8(os, set_camera_point_target);
writeV3F1000(os, camera_point_target);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
con.Send(peer_id, 0, data, true);
}
void Server::SendItemDef(con::Connection &con, u16 peer_id,
IItemDefManager *itemdef, u16 protocol_version)
2011-11-14 15:00:16 -08:00
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
/*
u16 command
u32 length of the next item
zlib-compressed serialized ItemDefManager
2011-11-14 15:00:16 -08:00
*/
writeU16(os, TOCLIENT_ITEMDEF);
2011-11-14 15:00:16 -08:00
std::ostringstream tmp_os(std::ios::binary);
itemdef->serialize(tmp_os, protocol_version);
std::ostringstream tmp_os2(std::ios::binary);
compressZlib(tmp_os.str(), tmp_os2);
os<<serializeLongString(tmp_os2.str());
2011-11-14 15:00:16 -08:00
// Make data buffer
std::string s = os.str();
2012-03-10 18:15:45 -08:00
verbosestream<<"Server: Sending item definitions to id("<<peer_id
<<"): size="<<s.size()<<std::endl;
2011-11-15 10:32:56 -08:00
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
con.Send(peer_id, 0, data, true);
}
void Server::SendNodeDef(con::Connection &con, u16 peer_id,
INodeDefManager *nodedef, u16 protocol_version)
2011-11-15 10:32:56 -08:00
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
/*
u16 command
u32 length of the next item
zlib-compressed serialized NodeDefManager
2011-11-15 10:32:56 -08:00
*/
writeU16(os, TOCLIENT_NODEDEF);
std::ostringstream tmp_os(std::ios::binary);
nodedef->serialize(tmp_os, protocol_version);
std::ostringstream tmp_os2(std::ios::binary);
compressZlib(tmp_os.str(), tmp_os2);
os<<serializeLongString(tmp_os2.str());
2011-11-15 10:32:56 -08:00
// Make data buffer
std::string s = os.str();
2012-03-10 18:15:45 -08:00
verbosestream<<"Server: Sending node definitions to id("<<peer_id
<<"): size="<<s.size()<<std::endl;
2011-11-14 15:00:16 -08:00
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
con.Send(peer_id, 0, data, true);
}
/*
Non-static send methods
*/
2010-11-26 15:02:21 -08:00
void Server::SendInventory(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
2010-11-26 15:02:21 -08:00
playersao->m_inventory_not_sent = false;
2010-12-22 06:30:23 -08:00
/*
Serialize it
2010-12-22 06:30:23 -08:00
*/
std::ostringstream os;
playersao->getInventory()->serialize(os);
std::string s = os.str();
SharedBuffer<u8> data(s.size()+2);
writeU16(&data[0], TOCLIENT_INVENTORY);
memcpy(&data[2], s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
u8 buf[12];
// Write command
writeU16(buf, TOCLIENT_CHAT_MESSAGE);
os.write((char*)buf, 2);
// Write length
writeU16(buf, message.size());
os.write((char*)buf, 2);
// Write string
for(u32 i=0; i<message.size(); i++)
2010-12-22 06:30:23 -08:00
{
u16 w = message[i];
writeU16(buf, w);
os.write((char*)buf, 2);
}
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec,
const std::string formname)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
u8 buf[12];
// Write command
writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
os.write((char*)buf, 2);
os<<serializeLongString(formspec);
os<<serializeString(formname);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
// Spawns a particle on peer with peer_id
void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size, bool collisiondetection,
std::string texture)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_SPAWN_PARTICLE);
writeV3F1000(os, pos);
writeV3F1000(os, velocity);
writeV3F1000(os, acceleration);
writeF1000(os, expirationtime);
writeF1000(os, size);
writeU8(os, collisiondetection);
os<<serializeLongString(texture);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
// Spawns a particle on all peers
void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size, bool collisiondetection,
std::string texture)
{
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); i++)
{
// Get client and check that it is valid
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
SendSpawnParticle(client->peer_id, pos, velocity, acceleration,
expirationtime, size, collisiondetection, texture);
}
}
// Adds a ParticleSpawner on peer with peer_id
void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
writeU16(os, amount);
writeF1000(os, spawntime);
writeV3F1000(os, minpos);
writeV3F1000(os, maxpos);
writeV3F1000(os, minvel);
writeV3F1000(os, maxvel);
writeV3F1000(os, minacc);
writeV3F1000(os, maxacc);
writeF1000(os, minexptime);
writeF1000(os, maxexptime);
writeF1000(os, minsize);
writeF1000(os, maxsize);
writeU8(os, collisiondetection);
os<<serializeLongString(texture);
writeU32(os, id);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
// Adds a ParticleSpawner on all peers
void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
{
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); i++)
{
// Get client and check that it is valid
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
SendAddParticleSpawner(client->peer_id, amount, spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc,
minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id);
}
}
void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
writeU16(os, id);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::SendDeleteParticleSpawnerAll(u32 id)
{
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); i++)
{
// Get client and check that it is valid
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
SendDeleteParticleSpawner(client->peer_id, id);
}
}
void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
2013-04-11 11:23:38 -07:00
{
std::ostringstream os(std::ios_base::binary);
// Write command
writeU16(os, TOCLIENT_HUDADD);
2013-04-11 11:23:38 -07:00
writeU32(os, id);
writeU8(os, (u8)form->type);
2013-04-11 11:23:38 -07:00
writeV2F1000(os, form->pos);
os << serializeString(form->name);
2013-04-11 11:23:38 -07:00
writeV2F1000(os, form->scale);
os << serializeString(form->text);
2013-04-11 11:23:38 -07:00
writeU32(os, form->number);
writeU32(os, form->item);
writeU32(os, form->dir);
writeV2F1000(os, form->align);
2013-04-22 16:47:59 -07:00
writeV2F1000(os, form->offset);
2013-04-11 11:23:38 -07:00
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::SendHUDRemove(u16 peer_id, u32 id)
2013-04-11 11:23:38 -07:00
{
std::ostringstream os(std::ios_base::binary);
// Write command
writeU16(os, TOCLIENT_HUDRM);
2013-04-11 11:23:38 -07:00
writeU32(os, id);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
2013-04-11 11:23:38 -07:00
{
std::ostringstream os(std::ios_base::binary);
// Write command
writeU16(os, TOCLIENT_HUDCHANGE);
2013-04-11 11:23:38 -07:00
writeU32(os, id);
writeU8(os, (u8)stat);
switch (stat) {
case HUD_STAT_POS:
case HUD_STAT_SCALE:
case HUD_STAT_ALIGN:
2013-04-22 16:47:59 -07:00
case HUD_STAT_OFFSET:
writeV2F1000(os, *(v2f *)value);
break;
case HUD_STAT_NAME:
case HUD_STAT_TEXT:
os << serializeString(*(std::string *)value);
break;
case HUD_STAT_NUMBER:
case HUD_STAT_ITEM:
case HUD_STAT_DIR:
default:
writeU32(os, *(u32 *)value);
break;
}
2013-04-11 11:23:38 -07:00
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
2013-04-11 11:23:38 -07:00
// Send as reliable
m_con.Send(peer_id, 0, data, true);
2013-04-11 11:23:38 -07:00
}
void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
{
std::ostringstream os(std::ios_base::binary);
// Write command
writeU16(os, TOCLIENT_HUD_SET_FLAGS);
writeU32(os, flags);
writeU32(os, mask);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
{
std::ostringstream os(std::ios_base::binary);
// Write command
writeU16(os, TOCLIENT_HUD_SET_PARAM);
writeU16(os, param);
os<<serializeString(value);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::BroadcastChatMessage(const std::wstring &message)
{
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
{
// Get client and check that it is valid
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
SendChatMessage(client->peer_id, message);
}
}
2010-12-24 15:54:39 -08:00
void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
{
DSTACK(__FUNCTION_NAME);
// Make packet
SharedBuffer<u8> data(2+2+4);
writeU16(&data[0], TOCLIENT_TIME_OF_DAY);
writeU16(&data[2], time);
writeF1000(&data[4], time_speed);
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::SendPlayerHP(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
playersao->m_hp_not_sent = false;
SendHP(m_con, peer_id, playersao->getHP());
// Send to other clients
std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
ActiveObjectMessage aom(playersao->getId(), true, str);
playersao->m_messages_out.push_back(aom);
}
void Server::SendPlayerBreath(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
playersao->m_breath_not_sent = false;
SendBreath(m_con, peer_id, playersao->getBreath());
}
void Server::SendMovePlayer(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
Player *player = m_env->getPlayer(peer_id);
assert(player);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_MOVE_PLAYER);
writeV3F1000(os, player->getPosition());
writeF1000(os, player->getPitch());
writeF1000(os, player->getYaw());
{
v3f pos = player->getPosition();
f32 pitch = player->getPitch();
f32 yaw = player->getYaw();
2012-03-10 18:15:45 -08:00
verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
<<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
<<" pitch="<<pitch
<<" yaw="<<yaw
<<std::endl;
}
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::SendPlayerPrivileges(u16 peer_id)
{
Player *player = m_env->getPlayer(peer_id);
assert(player);
if(player->peer_id == PEER_ID_INEXISTENT)
return;
std::set<std::string> privs;
m_script->getAuth(player->getName(), NULL, &privs);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_PRIVILEGES);
writeU16(os, privs.size());
for(std::set<std::string>::const_iterator i = privs.begin();
i != privs.end(); i++){
os<<serializeString(*i);
}
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::SendPlayerInventoryFormspec(u16 peer_id)
{
Player *player = m_env->getPlayer(peer_id);
assert(player);
if(player->peer_id == PEER_ID_INEXISTENT)
return;
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
os<<serializeLongString(player->inventory_formspec);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
2012-03-24 10:01:26 -07:00
s32 Server::playSound(const SimpleSoundSpec &spec,
const ServerSoundParams &params)
{
// Find out initial position of sound
bool pos_exists = false;
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))
return -1;
// Filter destination clients
std::set<RemoteClient*> dst_clients;
if(params.to_player != "")
{
Player *player = m_env->getPlayer(params.to_player.c_str());
if(!player){
infostream<<"Server::playSound: Player \""<<params.to_player
<<"\" not found"<<std::endl;
return -1;
}
if(player->peer_id == PEER_ID_INEXISTENT){
infostream<<"Server::playSound: Player \""<<params.to_player
<<"\" not connected"<<std::endl;
return -1;
}
RemoteClient *client = getClient(player->peer_id);
dst_clients.insert(client);
}
else
{
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin(); i != m_clients.end(); ++i)
2012-03-24 10:01:26 -07:00
{
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
2012-03-24 10:01:26 -07:00
Player *player = m_env->getPlayer(client->peer_id);
if(!player)
continue;
if(pos_exists){
if(player->getPosition().getDistanceFrom(pos) >
params.max_hear_distance)
continue;
}
dst_clients.insert(client);
}
}
if(dst_clients.size() == 0)
return -1;
// Create the sound
s32 id = m_next_sound_id++;
// The sound will exist as a reference in m_playing_sounds
m_playing_sounds[id] = ServerPlayingSound();
ServerPlayingSound &psound = m_playing_sounds[id];
psound.params = params;
for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
i != dst_clients.end(); i++)
psound.clients.insert((*i)->peer_id);
// Create packet
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_PLAY_SOUND);
writeS32(os, id);
os<<serializeString(spec.name);
writeF1000(os, spec.gain * params.gain);
writeU8(os, params.type);
writeV3F1000(os, pos);
writeU16(os, params.object);
writeU8(os, params.loop);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send
for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
i != dst_clients.end(); i++){
// Send as reliable
m_con.Send((*i)->peer_id, 0, data, true);
}
return id;
}
void Server::stopSound(s32 handle)
{
// Get sound reference
std::map<s32, ServerPlayingSound>::iterator i =
m_playing_sounds.find(handle);
if(i == m_playing_sounds.end())
return;
ServerPlayingSound &psound = i->second;
// Create packet
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_STOP_SOUND);
writeS32(os, handle);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send
for(std::set<u16>::iterator i = psound.clients.begin();
i != psound.clients.end(); i++){
// Send as reliable
m_con.Send(*i, 0, data, true);
}
// Remove sound reference
m_playing_sounds.erase(i);
}
void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2012-12-20 09:19:49 -08:00
std::list<u16> *far_players, float far_d_nodes)
{
float maxd = far_d_nodes*BS;
v3f p_f = intToFloat(p, BS);
// Create packet
u32 replysize = 8;
SharedBuffer<u8> reply(replysize);
writeU16(&reply[0], TOCLIENT_REMOVENODE);
writeS16(&reply[2], p.X);
writeS16(&reply[4], p.Y);
writeS16(&reply[6], p.Z);
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
{
// Get client and check that it is valid
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
// Don't send if it's the same one
if(client->peer_id == ignore_id)
continue;
if(far_players)
{
// Get player
2011-11-11 09:33:17 -08:00
Player *player = m_env->getPlayer(client->peer_id);
if(player)
2010-12-24 15:54:39 -08:00
{
// If player is far away, only set modified blocks not sent
v3f player_pos = player->getPosition();
if(player_pos.getDistanceFrom(p_f) > maxd)
2010-12-25 06:04:51 -08:00
{
far_players->push_back(client->peer_id);
continue;
2010-12-25 06:04:51 -08:00
}
2010-12-24 15:54:39 -08:00
}
}
2010-12-24 15:54:39 -08:00
// Send as reliable
m_con.Send(client->peer_id, 0, reply, true);
}
}
void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2013-11-23 06:35:49 -08:00
std::list<u16> *far_players, float far_d_nodes,
bool remove_metadata)
{
float maxd = far_d_nodes*BS;
v3f p_f = intToFloat(p, BS);
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
{
// Get client and check that it is valid
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
// Don't send if it's the same one
if(client->peer_id == ignore_id)
continue;
if(far_players)
{
// Get player
2011-11-11 09:33:17 -08:00
Player *player = m_env->getPlayer(client->peer_id);
if(player)
2010-12-22 06:30:23 -08:00
{
// If player is far away, only set modified blocks not sent
v3f player_pos = player->getPosition();
if(player_pos.getDistanceFrom(p_f) > maxd)
2010-12-25 06:04:51 -08:00
{
far_players->push_back(client->peer_id);
continue;
}
}
}
// Create packet
2013-11-23 06:35:49 -08:00
u32 replysize = 9 + MapNode::serializedLength(client->serialization_version);
SharedBuffer<u8> reply(replysize);
writeU16(&reply[0], TOCLIENT_ADDNODE);
writeS16(&reply[2], p.X);
writeS16(&reply[4], p.Y);
writeS16(&reply[6], p.Z);
n.serialize(&reply[8], client->serialization_version);
2013-11-23 06:35:49 -08:00
u32 index = 8 + MapNode::serializedLength(client->serialization_version);
writeU8(&reply[index], remove_metadata ? 0 : 1);
if (!remove_metadata) {
if (client->net_proto_version <= 21) {
// Old clients always clear metadata; fix it
// by sending the full block again.
client->SetBlockNotSent(p);
}
}
// Send as reliable
m_con.Send(client->peer_id, 0, reply, true);
}
}
void Server::setBlockNotSent(v3s16 p)
{
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
{
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
client->SetBlockNotSent(p);
}
}
2013-08-01 13:51:36 -07:00
void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
{
DSTACK(__FUNCTION_NAME);
v3s16 p = block->getPos();
#if 0
// Analyze it a bit
bool completely_air = true;
for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
{
if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
{
completely_air = false;
x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
}
}
// Print result
infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
if(completely_air)
infostream<<"[completely air] ";
infostream<<std::endl;
#endif
/*
Create a packet with the block in the right format
*/
std::ostringstream os(std::ios_base::binary);
block->serialize(os, ver, false);
2013-08-01 13:51:36 -07:00
block->serializeNetworkSpecific(os, net_proto_version);
std::string s = os.str();
SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
u32 replysize = 8 + blockdata.getSize();
SharedBuffer<u8> reply(replysize);
writeU16(&reply[0], TOCLIENT_BLOCKDATA);
writeS16(&reply[2], p.X);
writeS16(&reply[4], p.Y);
writeS16(&reply[6], p.Z);
memcpy(&reply[8], *blockdata, blockdata.getSize());
/*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<": \tpacket size: "<<replysize<<std::endl;*/
/*
Send packet
*/
m_con.Send(peer_id, 1, reply, true);
}
void Server::SendBlocks(float dtime)
{
DSTACK(__FUNCTION_NAME);
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2012-12-20 09:19:49 -08:00
std::vector<PrioritySortedBlockTransfer> queue;
s32 total_sending = 0;
{
2011-10-12 03:53:38 -07:00
ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
{
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
// If definitions and textures have not been sent, don't
// send MapBlocks either
if(!client->definitions_sent)
continue;
total_sending += client->SendingCount();
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
client->GetNextBlocks(this, dtime, queue);
}
}
// Sort.
// Lowest priority number comes first.
// Lowest is most important.
2012-12-20 09:19:49 -08:00
std::sort(queue.begin(), queue.end());
for(u32 i=0; i<queue.size(); i++)
{
//TODO: Calculate limit dynamically
2011-10-12 03:53:38 -07:00
if(total_sending >= g_settings->getS32
("max_simultaneous_block_sends_server_total"))
break;
PrioritySortedBlockTransfer q = queue[i];
MapBlock *block = NULL;
try
{
2011-11-11 09:33:17 -08:00
block = m_env->getMap().getBlockNoCreate(q.pos);
}
catch(InvalidPositionException &e)
{
continue;
}
RemoteClient *client = getClientNoEx(q.peer_id);
if(!client)
continue;
if(client->denied)
continue;
2013-08-01 13:51:36 -07:00
SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
client->SentBlock(q.pos);
total_sending++;
}
}
2012-03-25 01:50:29 -07:00
void Server::fillMediaCache()
2012-03-10 18:15:45 -08:00
{
DSTACK(__FUNCTION_NAME);
infostream<<"Server: Calculating media file checksums"<<std::endl;
// Collect all media file paths
std::list<std::string> paths;
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
for(std::vector<ModSpec>::iterator i = m_mods.begin();
2012-03-10 18:15:45 -08:00
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");
paths.push_back(mod.path + DIR_DELIM + "media");
paths.push_back(mod.path + DIR_DELIM + "models");
}
paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
// Collect media file information from paths into cache
for(std::list<std::string>::iterator i = paths.begin();
i != paths.end(); i++)
{
std::string mediapath = *i;
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2012-03-10 18:15:45 -08:00
for(u32 j=0; j<dirlist.size(); j++){
if(dirlist[j].dir) // Ignode dirs
continue;
std::string filename = dirlist[j].name;
// If name contains illegal characters, ignore the file
if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
infostream<<"Server: ignoring illegal file name: \""
<<filename<<"\""<<std::endl;
2012-03-10 18:15:45 -08:00
continue;
}
// If name is not in a supported format, ignore it
const char *supported_ext[] = {
".png", ".jpg", ".bmp", ".tga",
".pcx", ".ppm", ".psd", ".wal", ".rgb",
".ogg",
".x", ".b3d", ".md2", ".obj",
NULL
};
if(removeStringEnd(filename, supported_ext) == ""){
infostream<<"Server: ignoring unsupported file extension: \""
<<filename<<"\""<<std::endl;
continue;
}
// Ok, attempt to load the file and add to cache
std::string filepath = mediapath + DIR_DELIM + filename;
2012-03-10 18:15:45 -08:00
// Read data
std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2012-03-10 18:15:45 -08:00
if(fis.good() == false){
2012-03-25 01:50:29 -07:00
errorstream<<"Server::fillMediaCache(): Could not open \""
<<filename<<"\" for reading"<<std::endl;
2012-03-10 18:15:45 -08:00
continue;
}
std::ostringstream tmp_os(std::ios_base::binary);
bool bad = false;
for(;;){
char buf[1024];
fis.read(buf, 1024);
std::streamsize len = fis.gcount();
tmp_os.write(buf, len);
if(fis.eof())
break;
if(!fis.good()){
bad = true;
break;
2012-01-03 14:37:46 -08:00
}
2012-03-10 18:15:45 -08:00
}
if(bad){
2012-03-25 01:50:29 -07:00
errorstream<<"Server::fillMediaCache(): Failed to read \""
<<filename<<"\""<<std::endl;
2012-03-10 18:15:45 -08:00
continue;
}
if(tmp_os.str().length() == 0){
2012-03-25 01:50:29 -07:00
errorstream<<"Server::fillMediaCache(): Empty file \""
<<filepath<<"\""<<std::endl;
2012-03-10 18:15:45 -08:00
continue;
}
2012-03-10 18:15:45 -08:00
SHA1 sha1;
sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2012-03-10 18:15:45 -08:00
unsigned char *digest = sha1.getDigest();
std::string sha1_base64 = base64_encode(digest, 20);
std::string sha1_hex = hex_encode((char*)digest, 20);
2012-03-10 18:15:45 -08:00
free(digest);
2012-03-10 18:15:45 -08:00
// Put in list
this->m_media[filename] = MediaInfo(filepath, sha1_base64);
verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
2012-03-10 18:15:45 -08:00
}
}
}
2012-03-25 01:50:29 -07:00
struct SendableMediaAnnouncement
{
std::string name;
std::string sha1_digest;
2012-03-25 01:50:29 -07:00
SendableMediaAnnouncement(const std::string name_="",
const std::string sha1_digest_=""):
name(name_),
sha1_digest(sha1_digest_)
{}
};
2012-03-25 01:50:29 -07:00
void Server::sendMediaAnnouncement(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
2012-03-25 01:50:29 -07:00
verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
2012-03-10 18:15:45 -08:00
<<std::endl;
2012-12-20 09:19:49 -08:00
std::list<SendableMediaAnnouncement> file_announcements;
2012-03-25 01:50:29 -07:00
for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
i != m_media.end(); i++){
// Put in list
2012-03-25 01:50:29 -07:00
file_announcements.push_back(
SendableMediaAnnouncement(i->first, i->second.sha1_digest));
}
2012-03-25 01:50:29 -07:00
// Make packet
std::ostringstream os(std::ios_base::binary);
/*
u16 command
2012-03-25 01:50:29 -07:00
u32 number of files
for each texture {
u16 length of name
string name
2012-03-25 01:50:29 -07:00
u16 length of sha1_digest
string sha1_digest
}
*/
2012-03-25 01:50:29 -07:00
writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
writeU16(os, file_announcements.size());
2012-12-20 09:19:49 -08:00
for(std::list<SendableMediaAnnouncement>::iterator
2012-03-25 01:50:29 -07:00
j = file_announcements.begin();
2012-12-20 09:19:49 -08:00
j != file_announcements.end(); ++j){
os<<serializeString(j->name);
os<<serializeString(j->sha1_digest);
}
os<<serializeString(g_settings->get("remote_media"));
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
2012-03-25 01:50:29 -07:00
struct SendableMedia
2011-11-15 01:02:47 -08:00
{
std::string name;
std::string path;
std::string data;
2012-03-25 01:50:29 -07:00
SendableMedia(const std::string &name_="", const std::string path_="",
2011-11-15 01:02:47 -08:00
const std::string &data_=""):
name(name_),
path(path_),
data(data_)
{}
};
2012-03-25 01:50:29 -07:00
void Server::sendRequestedMedia(u16 peer_id,
Rewrite client media download and support hash-based remote download Move most of the media-related code in client.cpp into a new class ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other things, this class does the following things: - Download [remote_server][sha1] instead of [remote_server][name]. This is to support servers that provide the same file name with different contents. - Initially fetch [remote_server]index.mth. This file should follow the Minetest Hashset format (currently version 1) and contain a list of SHA1 hashes that exist on the server. - The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is requested, so servers can optionally narrow down the list to the needs of the client. - If index.mth is missing (HTTP response code 404), we enter compat mode, fetching [remote_server][name] as before this commit. - remote_server can now contain multiple servers, separated by commas. The downloader code attempts to split requests between the different servers, as permitted by each server's index.mth. If one server claims to have a file but actually doesn't (or something fails), we ask a different server that also claims to have it. - As before, when none of the remote servers provide a particular file, we download it via the conventional method, i.e. using the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA. - Bugfix: Every downloaded file's SHA1 is now verified against the SHA1 announced by the minetest server (before loading it and inserting it into the file cache). - Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all media. This should fix #863.
2013-08-28 20:22:18 -07:00
const std::list<std::string> &tosend)
2012-03-25 01:50:29 -07:00
{
2011-11-15 01:02:47 -08:00
DSTACK(__FUNCTION_NAME);
2012-03-25 01:50:29 -07:00
verbosestream<<"Server::sendRequestedMedia(): "
<<"Sending files to client"<<std::endl;
2012-03-25 01:50:29 -07:00
/* Read files */
2011-11-15 13:58:56 -08:00
// Put 5kB in one bunch (this is not accurate)
u32 bytes_per_bunch = 5000;
2012-12-20 09:19:49 -08:00
std::vector< std::list<SendableMedia> > file_bunches;
file_bunches.push_back(std::list<SendableMedia>());
2012-03-25 01:50:29 -07:00
u32 file_size_bunch_total = 0;
Rewrite client media download and support hash-based remote download Move most of the media-related code in client.cpp into a new class ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other things, this class does the following things: - Download [remote_server][sha1] instead of [remote_server][name]. This is to support servers that provide the same file name with different contents. - Initially fetch [remote_server]index.mth. This file should follow the Minetest Hashset format (currently version 1) and contain a list of SHA1 hashes that exist on the server. - The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is requested, so servers can optionally narrow down the list to the needs of the client. - If index.mth is missing (HTTP response code 404), we enter compat mode, fetching [remote_server][name] as before this commit. - remote_server can now contain multiple servers, separated by commas. The downloader code attempts to split requests between the different servers, as permitted by each server's index.mth. If one server claims to have a file but actually doesn't (or something fails), we ask a different server that also claims to have it. - As before, when none of the remote servers provide a particular file, we download it via the conventional method, i.e. using the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA. - Bugfix: Every downloaded file's SHA1 is now verified against the SHA1 announced by the minetest server (before loading it and inserting it into the file cache). - Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all media. This should fix #863.
2013-08-28 20:22:18 -07:00
for(std::list<std::string>::const_iterator i = tosend.begin();
2012-12-20 09:19:49 -08:00
i != tosend.end(); ++i)
2012-03-25 01:50:29 -07:00
{
Rewrite client media download and support hash-based remote download Move most of the media-related code in client.cpp into a new class ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other things, this class does the following things: - Download [remote_server][sha1] instead of [remote_server][name]. This is to support servers that provide the same file name with different contents. - Initially fetch [remote_server]index.mth. This file should follow the Minetest Hashset format (currently version 1) and contain a list of SHA1 hashes that exist on the server. - The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is requested, so servers can optionally narrow down the list to the needs of the client. - If index.mth is missing (HTTP response code 404), we enter compat mode, fetching [remote_server][name] as before this commit. - remote_server can now contain multiple servers, separated by commas. The downloader code attempts to split requests between the different servers, as permitted by each server's index.mth. If one server claims to have a file but actually doesn't (or something fails), we ask a different server that also claims to have it. - As before, when none of the remote servers provide a particular file, we download it via the conventional method, i.e. using the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA. - Bugfix: Every downloaded file's SHA1 is now verified against the SHA1 announced by the minetest server (before loading it and inserting it into the file cache). - Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all media. This should fix #863.
2013-08-28 20:22:18 -07:00
const std::string &name = *i;
if(m_media.find(name) == m_media.end()){
2012-03-25 01:50:29 -07:00
errorstream<<"Server::sendRequestedMedia(): Client asked for "
Rewrite client media download and support hash-based remote download Move most of the media-related code in client.cpp into a new class ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other things, this class does the following things: - Download [remote_server][sha1] instead of [remote_server][name]. This is to support servers that provide the same file name with different contents. - Initially fetch [remote_server]index.mth. This file should follow the Minetest Hashset format (currently version 1) and contain a list of SHA1 hashes that exist on the server. - The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is requested, so servers can optionally narrow down the list to the needs of the client. - If index.mth is missing (HTTP response code 404), we enter compat mode, fetching [remote_server][name] as before this commit. - remote_server can now contain multiple servers, separated by commas. The downloader code attempts to split requests between the different servers, as permitted by each server's index.mth. If one server claims to have a file but actually doesn't (or something fails), we ask a different server that also claims to have it. - As before, when none of the remote servers provide a particular file, we download it via the conventional method, i.e. using the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA. - Bugfix: Every downloaded file's SHA1 is now verified against the SHA1 announced by the minetest server (before loading it and inserting it into the file cache). - Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all media. This should fix #863.
2013-08-28 20:22:18 -07:00
<<"unknown file \""<<(name)<<"\""<<std::endl;
2012-01-03 14:37:46 -08:00
continue;
}
//TODO get path + name
Rewrite client media download and support hash-based remote download Move most of the media-related code in client.cpp into a new class ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other things, this class does the following things: - Download [remote_server][sha1] instead of [remote_server][name]. This is to support servers that provide the same file name with different contents. - Initially fetch [remote_server]index.mth. This file should follow the Minetest Hashset format (currently version 1) and contain a list of SHA1 hashes that exist on the server. - The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is requested, so servers can optionally narrow down the list to the needs of the client. - If index.mth is missing (HTTP response code 404), we enter compat mode, fetching [remote_server][name] as before this commit. - remote_server can now contain multiple servers, separated by commas. The downloader code attempts to split requests between the different servers, as permitted by each server's index.mth. If one server claims to have a file but actually doesn't (or something fails), we ask a different server that also claims to have it. - As before, when none of the remote servers provide a particular file, we download it via the conventional method, i.e. using the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA. - Bugfix: Every downloaded file's SHA1 is now verified against the SHA1 announced by the minetest server (before loading it and inserting it into the file cache). - Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all media. This should fix #863.
2013-08-28 20:22:18 -07:00
std::string tpath = m_media[name].path;
// Read data
std::ifstream fis(tpath.c_str(), std::ios_base::binary);
if(fis.good() == false){
2012-03-25 01:50:29 -07:00
errorstream<<"Server::sendRequestedMedia(): Could not open \""
<<tpath<<"\" for reading"<<std::endl;
continue;
}
std::ostringstream tmp_os(std::ios_base::binary);
bool bad = false;
for(;;){
char buf[1024];
fis.read(buf, 1024);
std::streamsize len = fis.gcount();
tmp_os.write(buf, len);
2012-03-25 01:50:29 -07:00
file_size_bunch_total += len;
if(fis.eof())
break;
if(!fis.good()){
bad = true;
break;
2011-11-15 13:58:56 -08:00
}
2011-11-15 01:02:47 -08:00
}
if(bad){
2012-03-25 01:50:29 -07:00
errorstream<<"Server::sendRequestedMedia(): Failed to read \""
Rewrite client media download and support hash-based remote download Move most of the media-related code in client.cpp into a new class ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other things, this class does the following things: - Download [remote_server][sha1] instead of [remote_server][name]. This is to support servers that provide the same file name with different contents. - Initially fetch [remote_server]index.mth. This file should follow the Minetest Hashset format (currently version 1) and contain a list of SHA1 hashes that exist on the server. - The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is requested, so servers can optionally narrow down the list to the needs of the client. - If index.mth is missing (HTTP response code 404), we enter compat mode, fetching [remote_server][name] as before this commit. - remote_server can now contain multiple servers, separated by commas. The downloader code attempts to split requests between the different servers, as permitted by each server's index.mth. If one server claims to have a file but actually doesn't (or something fails), we ask a different server that also claims to have it. - As before, when none of the remote servers provide a particular file, we download it via the conventional method, i.e. using the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA. - Bugfix: Every downloaded file's SHA1 is now verified against the SHA1 announced by the minetest server (before loading it and inserting it into the file cache). - Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all media. This should fix #863.
2013-08-28 20:22:18 -07:00
<<name<<"\""<<std::endl;
continue;
}
2012-03-25 01:50:29 -07:00
/*infostream<<"Server::sendRequestedMedia(): Loaded \""
<<tname<<"\""<<std::endl;*/
// Put in list
2012-03-25 01:50:29 -07:00
file_bunches[file_bunches.size()-1].push_back(
Rewrite client media download and support hash-based remote download Move most of the media-related code in client.cpp into a new class ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other things, this class does the following things: - Download [remote_server][sha1] instead of [remote_server][name]. This is to support servers that provide the same file name with different contents. - Initially fetch [remote_server]index.mth. This file should follow the Minetest Hashset format (currently version 1) and contain a list of SHA1 hashes that exist on the server. - The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is requested, so servers can optionally narrow down the list to the needs of the client. - If index.mth is missing (HTTP response code 404), we enter compat mode, fetching [remote_server][name] as before this commit. - remote_server can now contain multiple servers, separated by commas. The downloader code attempts to split requests between the different servers, as permitted by each server's index.mth. If one server claims to have a file but actually doesn't (or something fails), we ask a different server that also claims to have it. - As before, when none of the remote servers provide a particular file, we download it via the conventional method, i.e. using the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA. - Bugfix: Every downloaded file's SHA1 is now verified against the SHA1 announced by the minetest server (before loading it and inserting it into the file cache). - Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all media. This should fix #863.
2013-08-28 20:22:18 -07:00
SendableMedia(name, tpath, tmp_os.str()));
// Start next bunch if got enough data
2012-03-25 01:50:29 -07:00
if(file_size_bunch_total >= bytes_per_bunch){
2012-12-20 09:19:49 -08:00
file_bunches.push_back(std::list<SendableMedia>());
2012-03-25 01:50:29 -07:00
file_size_bunch_total = 0;
}
2011-11-15 01:02:47 -08:00
}
2011-11-15 13:58:56 -08:00
/* Create and send packets */
2012-03-25 01:50:29 -07:00
u32 num_bunches = file_bunches.size();
for(u32 i=0; i<num_bunches; i++)
{
std::ostringstream os(std::ios_base::binary);
2012-03-25 01:50:29 -07:00
/*
u16 command
u16 total number of texture bunches
u16 index of this bunch
u32 number of files in this bunch
for each file {
u16 length of name
string name
u32 length of data
data
2011-11-15 13:58:56 -08:00
}
2012-03-25 01:50:29 -07:00
*/
2011-11-15 01:02:47 -08:00
2012-03-25 01:50:29 -07:00
writeU16(os, TOCLIENT_MEDIA);
writeU16(os, num_bunches);
writeU16(os, i);
writeU32(os, file_bunches[i].size());
2012-12-20 09:19:49 -08:00
for(std::list<SendableMedia>::iterator
2012-03-25 01:50:29 -07:00
j = file_bunches[i].begin();
2012-12-20 09:19:49 -08:00
j != file_bunches[i].end(); ++j){
2012-03-25 01:50:29 -07:00
os<<serializeString(j->name);
os<<serializeLongString(j->data);
}
2012-03-25 01:50:29 -07:00
// Make data buffer
std::string s = os.str();
verbosestream<<"Server::sendRequestedMedia(): bunch "
<<i<<"/"<<num_bunches
<<" files="<<file_bunches[i].size()
<<" size=" <<s.size()<<std::endl;
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
2011-11-15 01:02:47 -08:00
}
2012-07-24 10:57:17 -07:00
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;
return;
}
Inventory *inv = m_detached_inventories[name];
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_DETACHED_INVENTORY);
os<<serializeString(name);
inv->serialize(os);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::sendDetachedInventoryToAll(const std::string &name)
{
DSTACK(__FUNCTION_NAME);
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i){
RemoteClient *client = i->second;
2012-07-24 10:57:17 -07:00
sendDetachedInventory(name, client->peer_id);
}
}
void Server::sendDetachedInventories(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
for(std::map<std::string, Inventory*>::iterator
i = m_detached_inventories.begin();
i != m_detached_inventories.end(); i++){
const std::string &name = i->first;
//Inventory *inv = i->second;
sendDetachedInventory(name, peer_id);
}
}
/*
Something random
*/
void Server::DiePlayer(u16 peer_id)
2011-10-15 04:46:59 -07:00
{
DSTACK(__FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
infostream<<"Server::DiePlayer(): Player "
<<playersao->getPlayer()->getName()
<<" dies"<<std::endl;
playersao->setHP(0);
// Trigger scripted stuff
m_script->on_dieplayer(playersao);
SendPlayerHP(peer_id);
SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2011-10-15 04:46:59 -07:00
}
void Server::RespawnPlayer(u16 peer_id)
2011-10-15 04:46:59 -07:00
{
DSTACK(__FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
infostream<<"Server::RespawnPlayer(): Player "
<<playersao->getPlayer()->getName()
<<" respawns"<<std::endl;
playersao->setHP(PLAYER_MAX_HP);
bool repositioned = m_script->on_respawnplayer(playersao);
if(!repositioned){
v3f pos = findSpawnPos(m_env->getServerMap());
playersao->setPos(pos);
}
2011-10-15 04:46:59 -07:00
}
void Server::DenyAccess(u16 peer_id, const std::wstring &reason)
{
DSTACK(__FUNCTION_NAME);
SendAccessDenied(m_con, peer_id, reason);
RemoteClient *client = getClientNoEx(peer_id);
if(client)
client->denied = true;
// If there are way too many clients, get rid of denied new ones immediately
if((int)m_clients.size() > 2 * g_settings->getU16("max_users")){
verbosestream<<"Server: DenyAccess: Too many clients; getting rid of "
<<"peer_id="<<peer_id<<" immediately"<<std::endl;
// Delete peer to stop sending it data
m_con.DeletePeer(peer_id);
// Delete client also to stop block sends and other stuff
DeleteClient(peer_id, CDR_DENY);
}
}
void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
{
DSTACK(__FUNCTION_NAME);
// Error check
std::map<u16, RemoteClient*>::iterator n;
n = m_clients.find(peer_id);
// The client may not exist; clients are immediately removed if their
// access is denied, and this event occurs later then.
if(n == m_clients.end())
return;
/*
Mark objects to be not known by the client
*/
RemoteClient *client = n->second;
// Handle objects
for(std::set<u16>::iterator
i = client->m_known_objects.begin();
i != client->m_known_objects.end(); ++i)
{
// Get object
u16 id = *i;
ServerActiveObject* obj = m_env->getActiveObject(id);
if(obj && obj->m_known_by_count > 0)
obj->m_known_by_count--;
}
/*
Clear references to playing sounds
*/
for(std::map<s32, ServerPlayingSound>::iterator
i = m_playing_sounds.begin();
i != m_playing_sounds.end();)
{
ServerPlayingSound &psound = i->second;
psound.clients.erase(peer_id);
if(psound.clients.size() == 0)
m_playing_sounds.erase(i++);
else
i++;
}
Player *player = m_env->getPlayer(peer_id);
// Collect information about leaving in chat
std::wstring message;
{
if(player != NULL && reason != CDR_DENY)
{
std::wstring name = narrow_to_wide(player->getName());
message += L"*** ";
message += name;
message += L" left the game.";
if(reason == CDR_TIMEOUT)
message += L" (timed out)";
}
}
/* Run scripts and remove from environment */
{
if(player != NULL)
{
PlayerSAO *playersao = player->getPlayerSAO();
assert(playersao);
m_script->on_leaveplayer(playersao);
playersao->disconnected();
}
}
/*
Print out action
*/
{
if(player != NULL && reason != CDR_DENY)
{
std::ostringstream os(std::ios_base::binary);
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i)
{
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
// Get player
Player *player = m_env->getPlayer(client->peer_id);
if(!player)
continue;
// Get name of player
os<<player->getName()<<" ";
}
actionstream<<player->getName()<<" "
<<(reason==CDR_TIMEOUT?"times out.":"leaves game.")
<<" List of players: "<<os.str()<<std::endl;
}
}
// Delete client
delete m_clients[peer_id];
m_clients.erase(peer_id);
// Send leave chat message to all remaining clients
if(message.length() != 0)
BroadcastChatMessage(message);
}
void Server::UpdateCrafting(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
2011-11-11 09:33:17 -08:00
Player* player = m_env->getPlayer(peer_id);
assert(player);
// Get a preview for crafting
ItemStack preview;
InventoryLocation loc;
loc.setPlayer(player->getName());
getCraftingResult(&player->inventory, preview, false, this);
m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc);
// Put the new preview in
InventoryList *plist = player->inventory.getList("craftpreview");
assert(plist);
assert(plist->getSize() >= 1);
plist->changeItem(0, preview);
2010-11-26 15:02:21 -08:00
}
RemoteClient* Server::getClient(u16 peer_id)
{
RemoteClient *client = getClientNoEx(peer_id);
if(!client)
throw ClientNotFoundException("Client not found");
return client;
}
RemoteClient* Server::getClientNoEx(u16 peer_id)
{
2012-12-20 09:19:49 -08:00
std::map<u16, RemoteClient*>::iterator n;
2010-11-26 15:02:21 -08:00
n = m_clients.find(peer_id);
// The client may not exist; clients are immediately removed if their
// access is denied, and this event occurs later then.
if(n == m_clients.end())
return NULL;
2012-12-20 09:19:49 -08:00
return n->second;
2010-11-26 15:02:21 -08:00
}
std::string Server::getPlayerName(u16 peer_id)
{
Player *player = m_env->getPlayer(peer_id);
if(player == NULL)
return "[id="+itos(peer_id)+"]";
return player->getName();
}
PlayerSAO* Server::getPlayerSAO(u16 peer_id)
{
Player *player = m_env->getPlayer(peer_id);
if(player == NULL)
return NULL;
return player->getPlayerSAO();
}
std::wstring Server::getStatusString()
{
std::wostringstream os(std::ios_base::binary);
os<<L"# Server: ";
2011-04-24 14:24:40 -07:00
// Version
os<<L"version="<<narrow_to_wide(minetest_version_simple);
// Uptime
2011-04-24 14:24:40 -07:00
os<<L", uptime="<<m_uptime.get();
2013-08-03 13:16:37 -07:00
// Max lag estimate
os<<L", max_lag="<<m_env->getMaxLagEstimate();
// Information about clients
2012-12-20 09:19:49 -08:00
std::map<u16, RemoteClient*>::iterator i;
bool first;
os<<L", clients={";
2012-12-20 09:19:49 -08:00
for(i = m_clients.begin(), first = true;
i != m_clients.end(); ++i)
{
// Get client and check that it is valid
2012-12-20 09:19:49 -08:00
RemoteClient *client = i->second;
assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
// Get player
2011-11-11 09:33:17 -08:00
Player *player = m_env->getPlayer(client->peer_id);
// Get name of player
std::wstring name = L"unknown";
if(player != NULL)
name = narrow_to_wide(player->getName());
// Add name to information string
if(!first)
os<<L",";
else
first = false;
os<<name;
}
os<<L"}";
2011-11-11 09:33:17 -08:00
if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2011-10-12 03:53:38 -07:00
if(g_settings->get("motd") != "")
os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
return os.str();
}
std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
{
std::set<std::string> privs;
m_script->getAuth(name, NULL, &privs);
return privs;
}
bool Server::checkPriv(const std::string &name, const std::string &priv)
{
std::set<std::string> privs = getPlayerEffectivePrivs(name);
return (privs.count(priv) != 0);
}
void Server::reportPrivsModified(const std::string &name)
{
if(name == ""){
2012-12-20 09:19:49 -08:00
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i){
RemoteClient *client = i->second;
Player *player = m_env->getPlayer(client->peer_id);
reportPrivsModified(player->getName());
}
} else {
Player *player = m_env->getPlayer(name.c_str());
if(!player)
return;
SendPlayerPrivileges(player->peer_id);
PlayerSAO *sao = player->getPlayerSAO();
if(!sao)
return;
sao->updatePrivileges(
getPlayerEffectivePrivs(name),
isSingleplayer());
}
}
void Server::reportInventoryFormspecModified(const std::string &name)
{
Player *player = m_env->getPlayer(name.c_str());
if(!player)
return;
SendPlayerInventoryFormspec(player->peer_id);
}
void Server::setIpBanned(const std::string &ip, const std::string &name)
2011-10-12 03:53:38 -07:00
{
m_banmanager->add(ip, name);
}
void Server::unsetIpBanned(const std::string &ip_or_name)
{
m_banmanager->remove(ip_or_name);
}
std::string Server::getBanDescription(const std::string &ip_or_name)
{
return m_banmanager->getBanDescription(ip_or_name);
2011-10-12 03:53:38 -07:00
}
void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true)
{
2011-11-11 09:33:17 -08:00
Player *player = m_env->getPlayer(name);
if(!player)
return;
if (prepend)
SendChatMessage(player->peer_id, std::wstring(L"Server -!- ")+msg);
else
SendChatMessage(player->peer_id, msg);
}
bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
{
Player *player = m_env->getPlayer(playername);
if(!player)
{
infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
return false;
}
SendShowFormspecMessage(player->peer_id, formspec, formname);
return true;
}
u32 Server::hudAdd(Player *player, HudElement *form) {
if (!player)
return -1;
2013-04-11 11:23:38 -07:00
u32 id = player->getFreeHudID();
if (id < player->hud.size())
player->hud[id] = form;
else
player->hud.push_back(form);
2013-04-11 11:23:38 -07:00
SendHUDAdd(player->peer_id, id, form);
return id;
2013-04-11 11:23:38 -07:00
}
bool Server::hudRemove(Player *player, u32 id) {
if (!player || id >= player->hud.size() || !player->hud[id])
2013-04-11 11:23:38 -07:00
return false;
delete player->hud[id];
player->hud[id] = NULL;
SendHUDRemove(player->peer_id, id);
2013-04-11 11:23:38 -07:00
return true;
}
bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
if (!player)
2013-04-11 11:23:38 -07:00
return false;
SendHUDChange(player->peer_id, id, stat, data);
return true;
}
bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
if (!player)
return false;
SendHUDSetFlags(player->peer_id, flags, mask);
return true;
}
bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
if (!player)
return false;
if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
return false;
std::ostringstream os(std::ios::binary);
writeS32(os, hotbar_itemcount);
SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
return true;
}
void Server::hudSetHotbarImage(Player *player, std::string name) {
if (!player)
return;
SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
}
void Server::hudSetHotbarSelectedImage(Player *player, std::string name) {
if (!player)
return;
SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
}
2011-10-17 14:01:50 -07:00
void Server::notifyPlayers(const std::wstring msg)
{
BroadcastChatMessage(msg);
}
void Server::spawnParticle(const char *playername, v3f pos,
v3f velocity, v3f acceleration,
float expirationtime, float size, bool
collisiondetection, std::string texture)
{
Player *player = m_env->getPlayer(playername);
if(!player)
return;
SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
expirationtime, size, collisiondetection, texture);
}
void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size,
bool collisiondetection, std::string texture)
{
SendSpawnParticleAll(pos, velocity, acceleration,
expirationtime, size, collisiondetection, texture);
}
u32 Server::addParticleSpawner(const char *playername,
u16 amount, float spawntime,
v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel,
v3f minacc, v3f maxacc,
float minexptime, float maxexptime,
float minsize, float maxsize,
bool collisiondetection, std::string texture)
{
Player *player = m_env->getPlayer(playername);
if(!player)
return -1;
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;
}
}
SendAddParticleSpawner(player->peer_id, amount, spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc,
minexptime, maxexptime, minsize, maxsize,
collisiondetection, texture, id);
return id;
}
u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel,
v3f minacc, v3f maxacc,
float minexptime, float maxexptime,
float minsize, float maxsize,
bool collisiondetection, std::string texture)
{
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;
}
}
SendAddParticleSpawnerAll(amount, spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc,
minexptime, maxexptime, minsize, maxsize,
collisiondetection, texture, id);
return id;
}
void Server::deleteParticleSpawner(const char *playername, u32 id)
{
Player *player = m_env->getPlayer(playername);
if(!player)
return;
m_particlespawner_ids.erase(
std::remove(m_particlespawner_ids.begin(),
m_particlespawner_ids.end(), id),
m_particlespawner_ids.end());
SendDeleteParticleSpawner(player->peer_id, id);
}
void Server::deleteParticleSpawnerAll(u32 id)
{
m_particlespawner_ids.erase(
std::remove(m_particlespawner_ids.begin(),
m_particlespawner_ids.end(), id),
m_particlespawner_ids.end());
SendDeleteParticleSpawnerAll(id);
}
2012-07-24 10:57:17 -07:00
Inventory* Server::createDetachedInventory(const std::string &name)
{
if(m_detached_inventories.count(name) > 0){
infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
delete m_detached_inventories[name];
} else {
infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
}
Inventory *inv = new Inventory(m_itemdef);
assert(inv);
m_detached_inventories[name] = inv;
sendDetachedInventoryToAll(name);
return inv;
}
class BoolScopeSet
{
public:
BoolScopeSet(bool *dst, bool val):
m_dst(dst)
{
m_orig_state = *m_dst;
*m_dst = val;
}
~BoolScopeSet()
{
*m_dst = m_orig_state;
}
private:
bool *m_dst;
bool m_orig_state;
};
// actions: time-reversed list
// Return value: success/failure
bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
std::list<std::string> *log)
{
infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
ServerMap *map = (ServerMap*)(&m_env->getMap());
// Disable rollback report sink while reverting
BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
// Fail if no actions to handle
if(actions.empty()){
log->push_back("Nothing to do.");
return false;
}
int num_tried = 0;
int num_failed = 0;
for(std::list<RollbackAction>::const_iterator
i = actions.begin();
i != actions.end(); i++)
{
const RollbackAction &action = *i;
num_tried++;
bool success = action.applyRevert(map, this, this);
if(!success){
num_failed++;
std::ostringstream os;
os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
if(log)
log->push_back(os.str());
}else{
std::ostringstream os;
os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
if(log)
log->push_back(os.str());
}
}
infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
<<" failed"<<std::endl;
// Call it done if less than half failed
return num_failed <= num_tried/2;
}
2011-11-14 11:41:30 -08:00
// IGameDef interface
// Under envlock
IItemDefManager* Server::getItemDefManager()
2011-11-14 11:41:30 -08:00
{
return m_itemdef;
2011-11-14 11:41:30 -08:00
}
INodeDefManager* Server::getNodeDefManager()
{
return m_nodedef;
2011-11-14 11:41:30 -08:00
}
2011-11-16 16:28:46 -08:00
ICraftDefManager* Server::getCraftDefManager()
{
return m_craftdef;
}
2011-11-14 11:41:30 -08:00
ITextureSource* Server::getTextureSource()
{
return NULL;
}
2012-03-18 18:59:12 -07:00
IShaderSource* Server::getShaderSource()
{
return NULL;
}
2011-11-16 03:03:28 -08:00
u16 Server::allocateUnknownNodeId(const std::string &name)
{
return m_nodedef->allocateDummy(name);
2011-11-16 03:03:28 -08:00
}
ISoundManager* Server::getSoundManager()
{
return &dummySoundManager;
}
MtEventManager* Server::getEventManager()
{
return m_event;
}
IRollbackReportSink* Server::getRollbackReportSink()
{
if(!m_enable_rollback_recording)
return NULL;
if(!m_rollback_sink_enabled)
return NULL;
return m_rollback;
}
2011-11-14 11:41:30 -08:00
IWritableItemDefManager* Server::getWritableItemDefManager()
2011-11-14 16:03:28 -08:00
{
return m_itemdef;
2011-11-14 16:03:28 -08:00
}
IWritableNodeDefManager* Server::getWritableNodeDefManager()
{
return m_nodedef;
2011-11-14 16:03:28 -08:00
}
2011-11-16 16:28:46 -08:00
IWritableCraftDefManager* Server::getWritableCraftDefManager()
{
return m_craftdef;
}
2011-11-14 16:03:28 -08:00
2011-12-11 06:49:40 -08:00
const ModSpec* Server::getModSpec(const std::string &modname)
{
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
for(std::vector<ModSpec>::iterator i = m_mods.begin();
2011-12-11 06:49:40 -08:00
i != m_mods.end(); i++){
const ModSpec &mod = *i;
if(mod.name == modname)
return &mod;
}
return NULL;
}
2012-12-20 09:19:49 -08:00
void Server::getModNames(std::list<std::string> &modlist)
2012-07-21 18:29:37 -07:00
{
Basic support for configuring which mods to load for each world settings.h: added function to return all keys used in settings, and a function to remove a setting mods.{h,cpp}: added class ModConfiguration that represents a subset of the installed mods. server.{h,cpp}: server does not load add-on mods that are disabled in the world.mt file. mods are disabled by a setting of the form "load_mod_<modname> = false". if no load_mod_<modname> = ... setting is found, the mod is loaded anyways for backwards compatibilty. server also complains to errorstream about mods with unstatisfied dependencies and about mods that are not installed. guiConfigureWorld.{h,cpp}: shows a treeview of installed add-on mods and modpacks with little icons in front of their name indicating their status: a checkmark for enabled mods, a cross for disabled mods, a question mark for "new" mods Mods can be enabled/disabled by a checkbox. Mods also show a list of dependencies and reverse dependencies. double-click on a mod in dependency or reverse dependency listbox selects the corresponding mod. Enabling a mod also enables all its dependencies. Disabling a mod also disables all its reverse dependencies. For modpacks, show buttons to enable/disable all mods (recursively, including their dependencies) in it. Button "Save" saves the current settings to the world.mt file and returns to the main menu. Button "Cancel" returns to main menu without saving. basic keyboard controls (if the proper widget has keyboard focus): up/down: scroll through tree of mods left/right: collaps/expand a modpack space: enable/disable the selected mod
2012-12-08 09:10:54 -08:00
for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
2012-07-21 18:29:37 -07:00
{
2012-12-20 09:19:49 -08:00
modlist.push_back(i->name);
2012-07-21 18:29:37 -07:00
}
}
std::string Server::getBuiltinLuaPath()
{
return porting::path_share + DIR_DELIM + "builtin";
}
2011-12-11 06:49:40 -08:00
v3f findSpawnPos(ServerMap &map)
{
//return v3f(50,50,50)*BS;
v3s16 nodepos;
#if 0
nodepos = v2s16(0,0);
groundheight = 20;
#endif
#if 1
s16 water_level = map.m_mgparams->water_level;
// Try to find a good place a few times
for(s32 i=0; i<1000; i++)
{
s32 range = 1 + i;
// We're going to try to throw the player to this position
2013-04-21 12:39:34 -07:00
v2s16 nodepos2d = v2s16(
-range + (myrand() % (range * 2)),
-range + (myrand() % (range * 2)));
// Get ground height at point
s16 groundheight = map.findGroundLevel(nodepos2d, g_settings->getBool("cache_block_before_spawn"));
2013-04-21 12:39:34 -07:00
if (groundheight <= water_level) // Don't go underwater
continue;
if (groundheight > water_level + g_settings->getS16("max_spawn_height")) // Don't go to high places
continue;
2013-04-21 12:39:34 -07:00
nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
bool is_good = false;
s32 air_count = 0;
2013-04-21 12:39:34 -07:00
for (s32 i = 0; i < 10; i++) {
v3s16 blockpos = getNodeBlockPos(nodepos);
map.emergeBlock(blockpos, true);
2013-04-21 12:39:34 -07:00
content_t c = map.getNodeNoEx(nodepos).getContent();
if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
air_count++;
2013-04-21 12:39:34 -07:00
if (air_count >= 2){
is_good = true;
break;
}
}
nodepos.Y++;
}
if(is_good){
// Found a good place
//infostream<<"Searched through "<<i<<" places."<<std::endl;
break;
}
}
#endif
return intToFloat(nodepos, BS);
}
PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
{
RemotePlayer *player = NULL;
bool newplayer = false;
/*
Try to get an existing player
*/
player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
// If player is already connected, cancel
if(player != NULL && player->peer_id != 0)
{
infostream<<"emergePlayer(): Player already connected"<<std::endl;
return NULL;
}
/*
If player with the wanted peer_id already exists, cancel.
*/
2011-11-11 09:33:17 -08:00
if(m_env->getPlayer(peer_id) != NULL)
{
infostream<<"emergePlayer(): Player with wrong name but same"
" peer_id already exists"<<std::endl;
return NULL;
}
/*
Create a new player if it doesn't exist yet
*/
if(player == NULL)
{
newplayer = true;
player = new RemotePlayer(this);
player->updateName(name);
/* Set player position */
infostream<<"Server: Finding spawn place for player \""
<<name<<"\""<<std::endl;
2011-11-11 09:33:17 -08:00
v3f pos = findSpawnPos(m_env->getServerMap());
player->setPosition(pos);
/* Add player to environment */
2011-11-11 09:33:17 -08:00
m_env->addPlayer(player);
}
/*
Create a new player active object
*/
PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
getPlayerEffectivePrivs(player->getName()),
isSingleplayer());
/* Clean up old HUD elements from previous sessions */
player->hud.clear();
/* Add object to environment */
m_env->addActiveObject(playersao);
/* Run scripts */
if(newplayer)
m_script->on_newplayer(playersao);
m_script->on_joinplayer(playersao);
return playersao;
}
void Server::handlePeerChange(PeerChange &c)
{
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
if(c.type == PEER_ADDED)
{
/*
Add
*/
// Error check
2012-12-20 09:19:49 -08:00
std::map<u16, RemoteClient*>::iterator n;
n = m_clients.find(c.peer_id);
// The client shouldn't already exist
2012-12-20 09:19:49 -08:00
assert(n == m_clients.end());
// Create client
RemoteClient *client = new RemoteClient();
client->peer_id = c.peer_id;
2012-12-20 09:19:49 -08:00
m_clients[client->peer_id] = client;
} // PEER_ADDED
else if(c.type == PEER_REMOVED)
{
/*
Delete
*/
DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
} // PEER_REMOVED
else
{
assert(0);
}
}
void Server::handlePeerChanges()
{
while(m_peer_change_queue.size() > 0)
{
PeerChange c = m_peer_change_queue.pop_front();
2012-03-10 18:15:45 -08:00
verbosestream<<"Server: Handling peer change: "
<<"id="<<c.peer_id<<", timeout="<<c.timeout
<<std::endl;
handlePeerChange(c);
}
}
2010-12-12 17:19:12 -08:00
void dedicated_server_loop(Server &server, bool &kill)
2011-01-23 07:29:15 -08:00
{
DSTACK(__FUNCTION_NAME);
2012-03-10 18:15:45 -08:00
verbosestream<<"dedicated_server_loop()"<<std::endl;
IntervalLimiter m_profiler_interval;
2011-01-23 07:29:15 -08:00
for(;;)
{
2012-03-07 11:44:53 -08:00
float steplen = g_settings->getFloat("dedicated_server_step");
2011-01-23 07:29:15 -08:00
// This is kind of a hack but can be done like this
// because server.step() is very light
{
2011-10-12 03:53:38 -07:00
ScopeProfiler sp(g_profiler, "dedicated server sleep");
2012-03-07 11:44:53 -08:00
sleep_ms((int)(steplen*1000.0));
}
2012-03-07 11:44:53 -08:00
server.step(steplen);
2011-01-23 07:29:15 -08:00
if(server.getShutdownRequested() || kill)
{
2012-03-10 18:15:45 -08:00
infostream<<"Dedicated server quitting"<<std::endl;
2013-02-21 14:00:44 -08:00
#if USE_CURL
if(g_settings->getBool("server_announce") == true)
ServerList::sendAnnounce("delete");
#endif
break;
}
/*
Profiler
*/
float profiler_print_interval =
2011-10-12 03:53:38 -07:00
g_settings->getFloat("profiler_print_interval");
if(profiler_print_interval != 0)
{
2012-03-07 11:44:53 -08:00
if(m_profiler_interval.step(steplen, profiler_print_interval))
{
infostream<<"Profiler:"<<std::endl;
g_profiler->print(infostream);
2011-10-12 03:53:38 -07:00
g_profiler->clear();
}
}
2011-01-23 07:29:15 -08:00
}
}
2010-11-26 15:02:21 -08:00