#include "constants.h"
#include "voxel.h"
#include "config.h"
+#include "version.h"
#include "filesys.h"
#include "mapblock.h"
#include "serverobject.h"
{}
};
-class ServerThread : public SimpleThread
+class ServerThread : public JThread
{
Server *m_server;
public:
ServerThread(Server *server):
- SimpleThread(),
+ JThread(),
m_server(server)
{
}
void * ServerThread::Thread()
{
- ThreadStarted();
-
log_register_thread("ServerThread");
DSTACK(__FUNCTION_NAME);
-
BEGIN_DEBUG_EXCEPTION_HANDLER
- while(getRun())
+ m_server->AsyncRunStep(true);
+
+ ThreadStarted();
+
+ while(!StopRequested())
{
try{
//TimeTaker timer("AsyncRunStep() + Receive()");
m_objectdata_timer = 0.0;
m_emergethread_trigger_timer = 0.0;
m_savemap_timer = 0.0;
- m_clients_number = 0;
- m_env_mutex.Init();
- m_con_mutex.Init();
- m_step_dtime_mutex.Init();
m_step_dtime = 0.0;
+ m_lag = g_settings->getFloat("dedicated_server_step");
if(path_world == "")
throw ServerError("Supplied empty world path");
// Create emerge manager
m_emerge = new EmergeManager(this);
+ // Create world if it doesn't exist
+ if(!initializeWorld(m_path_world, m_gamespec.id))
+ throw ServerError("Failed to initialize world");
+
// Create ban manager
std::string ban_path = m_path_world+DIR_DELIM+"ipban.txt";
m_banmanager = new BanManager(ban_path);
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");
-
ModConfiguration modconf(m_path_world);
m_mods = modconf.getMods();
std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
// complain about mods with unsatisfied dependencies
- if(!modconf.isConsistent())
+ if(!modconf.isConsistent())
{
for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
it != unsatisfied_mods.end(); ++it)
worldmt_settings.readConfigFile(worldmt.c_str());
std::vector<std::string> names = worldmt_settings.getNames();
std::set<std::string> load_mod_names;
- for(std::vector<std::string>::iterator it = names.begin();
+ for(std::vector<std::string>::iterator it = names.begin();
it != names.end(); ++it)
- {
- std::string name = *it;
+ {
+ std::string name = *it;
if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
load_mod_names.insert(name.substr(9));
}
it != unsatisfied_mods.end(); ++it)
load_mod_names.erase((*it).name);
if(!load_mod_names.empty())
- {
+ {
errorstream << "The following mods could not be found:";
for(std::set<std::string>::iterator it = load_mod_names.begin();
it != load_mod_names.end(); ++it)
// Initialize mapgens
m_emerge->initMapgens(mgparams);
+ servermap->setMapgenParams(m_emerge->params);
// Give environment reference to scripting api
m_script->initializeEnvironment(m_env);
infostream<<"Starting server on port "<<port<<"..."<<std::endl;
// Stop thread if already running
- m_thread->stop();
+ m_thread->Stop();
// Initialize connection
m_con.SetTimeoutMs(30);
m_con.Serve(port);
// Start thread
- m_thread->setRun(true);
m_thread->Start();
// ASCII art for the win!
infostream<<"Server: Stopping and waiting threads"<<std::endl;
// Stop threads (set run=false first so both start stopping)
- m_thread->setRun(false);
+ m_thread->Stop();
//m_emergethread.setRun(false);
- m_thread->stop();
+ m_thread->Wait();
//m_emergethread.stop();
infostream<<"Server: Threads stopped"<<std::endl;
}
}
-void Server::AsyncRunStep()
+void Server::AsyncRunStep(bool initial_step)
{
DSTACK(__FUNCTION_NAME);
SendBlocks(dtime);
}
- if(dtime < 0.001)
+ if((dtime < 0.001) && (initial_step == false))
return;
g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
counter = 0.0;
JMutexAutoLock lock2(m_con_mutex);
- m_clients_number = 0;
+ m_clients_names.clear();
if(m_clients.size() != 0)
infostream<<"Players:"<<std::endl;
for(std::map<u16, RemoteClient*>::iterator
continue;
infostream<<"* "<<player->getName()<<"\t";
client->PrintInfo(infostream);
- ++m_clients_number;
+ m_clients_names.push_back(player->getName());
}
}
}
+ m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
#if USE_CURL
// send masterserver announce
{
float &counter = m_masterserver_timer;
if(!isSingleplayer() && (!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true)
{
- ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number, m_uptime.get(), m_gamespec.id, m_mods);
+ ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_names, m_uptime.get(), m_env->getGameTime(), m_lag, m_gamespec.id, m_mods);
counter = 0.01;
}
counter += dtime;
memcpy((char*)&reply[2], unreliable_data.c_str(),
unreliable_data.size());
// Send as unreliable
- m_con.Send(client->peer_id, 0, reply, false);
+ m_con.Send(client->peer_id, 1, reply, false);
}
/*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
// for them.
std::list<u16> far_players;
- if(event->type == MEET_ADDNODE)
+ if(event->type == MEET_ADDNODE || event->type == MEET_SWAPNODE)
{
//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,
- &far_players, 5);
+ &far_players, 5, event->type == MEET_ADDNODE);
else
sendAddNode(event->p, event->n, event->already_known_by_peer,
- &far_players, 30);
+ &far_players, 30, event->type == MEET_ADDNODE);
}
else if(event->type == MEET_REMOVENODE)
{
{
counter = 0.0;
- m_emerge->triggerAllThreads();
+ m_emerge->startAllThreads();
// Update m_enable_rollback_recording here too
m_enable_rollback_recording =
DenyAccess(peer_id, std::wstring(
L"Your client's version is not supported.\n"
L"Server version is ")
- + narrow_to_wide(VERSION_STRING) + L"."
+ + narrow_to_wide(minetest_version_simple) + L"."
);
return;
}
DenyAccess(peer_id, std::wstring(
L"Your client's version is not supported.\n"
L"Server version is ")
- + narrow_to_wide(VERSION_STRING) + L",\n"
+ + narrow_to_wide(minetest_version_simple) + L",\n"
+ L"server's PROTOCOL_VERSION is "
+ narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
+ L"..."
DenyAccess(peer_id, std::wstring(
L"Your client's version is not supported.\n"
L"Server version is ")
- + narrow_to_wide(VERSION_STRING) + L",\n"
+ + 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 "
return;
}
+ {
+ 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;
+ }
+ }
+
infostream<<"Server: New connection: \""<<playername<<"\" from "
<<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
- std::list<MediaRequest> tosend;
+ std::list<std::string> tosend;
u16 numfiles = readU16(is);
infostream<<"Sending "<<numfiles<<" files to "
for(int i = 0; i < numfiles; i++) {
std::string name = deSerializeString(is);
- tosend.push_back(MediaRequest(name));
+ tosend.push_back(name);
verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
<<name<<std::endl;
}
// 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)
+ bool vertical, std::string texture)
{
DSTACK(__FUNCTION_NAME);
writeF1000(os, size);
writeU8(os, collisiondetection);
os<<serializeLongString(texture);
+ writeU8(os, vertical);
// Make data buffer
std::string s = os.str();
// Spawns a particle on all peers
void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size, bool collisiondetection,
- std::string texture)
+ bool vertical, std::string texture)
{
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
continue;
SendSpawnParticle(client->peer_id, pos, velocity, acceleration,
- expirationtime, size, collisiondetection, texture);
+ expirationtime, size, collisiondetection, vertical, 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)
+ float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id)
{
DSTACK(__FUNCTION_NAME);
writeU8(os, collisiondetection);
os<<serializeLongString(texture);
writeU32(os, id);
+ writeU8(os, vertical);
// Make data buffer
std::string s = os.str();
// 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)
+ float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id)
{
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
SendAddParticleSpawner(client->peer_id, amount, spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc,
- minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id);
+ minexptime, maxexptime, minsize, maxsize, collisiondetection, vertical, texture, id);
}
}
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);
+ m_con.Send(peer_id, 1, data, true);
}
void Server::SendHUDRemove(u16 peer_id, u32 id)
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);
+
+ m_con.Send(peer_id, 1, data, true);
}
void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
}
void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
- std::list<u16> *far_players, float far_d_nodes)
+ std::list<u16> *far_players, float far_d_nodes,
+ bool remove_metadata)
{
float maxd = far_d_nodes*BS;
v3f p_f = intToFloat(p, BS);
}
// Create packet
- u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
+ 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);
+ 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);
/*
Send packet
*/
- m_con.Send(peer_id, 1, reply, true);
+ m_con.Send(peer_id, 2, reply, true);
}
void Server::SendBlocks(float dtime)
};
void Server::sendRequestedMedia(u16 peer_id,
- const std::list<MediaRequest> &tosend)
+ const std::list<std::string> &tosend)
{
DSTACK(__FUNCTION_NAME);
u32 file_size_bunch_total = 0;
- for(std::list<MediaRequest>::const_iterator i = tosend.begin();
+ for(std::list<std::string>::const_iterator i = tosend.begin();
i != tosend.end(); ++i)
{
- if(m_media.find(i->name) == m_media.end()){
+ const std::string &name = *i;
+
+ if(m_media.find(name) == m_media.end()){
errorstream<<"Server::sendRequestedMedia(): Client asked for "
- <<"unknown file \""<<(i->name)<<"\""<<std::endl;
+ <<"unknown file \""<<(name)<<"\""<<std::endl;
continue;
}
//TODO get path + name
- std::string tpath = m_media[(*i).name].path;
+ std::string tpath = m_media[name].path;
// Read data
std::ifstream fis(tpath.c_str(), std::ios_base::binary);
}
if(bad){
errorstream<<"Server::sendRequestedMedia(): Failed to read \""
- <<(*i).name<<"\""<<std::endl;
+ <<name<<"\""<<std::endl;
continue;
}
/*infostream<<"Server::sendRequestedMedia(): Loaded \""
<<tname<<"\""<<std::endl;*/
// Put in list
file_bunches[file_bunches.size()-1].push_back(
- SendableMedia((*i).name, tpath, tmp_os.str()));
+ SendableMedia(name, tpath, tmp_os.str()));
// Start next bunch if got enough data
if(file_size_bunch_total >= bytes_per_bunch){
<<" 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);
+ m_con.Send(peer_id, 2, data, true);
}
}
// 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");
std::wostringstream os(std::ios_base::binary);
os<<L"# Server: ";
// Version
- os<<L"version="<<narrow_to_wide(VERSION_STRING);
+ os<<L"version="<<narrow_to_wide(minetest_version_simple);
// Uptime
os<<L", uptime="<<m_uptime.get();
// Max lag estimate
void Server::spawnParticle(const char *playername, v3f pos,
v3f velocity, v3f acceleration,
float expirationtime, float size, bool
- collisiondetection, std::string texture)
+ collisiondetection, bool vertical, std::string texture)
{
Player *player = m_env->getPlayer(playername);
if(!player)
return;
SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
- expirationtime, size, collisiondetection, texture);
+ expirationtime, size, collisiondetection, vertical, texture);
}
void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size,
- bool collisiondetection, std::string texture)
+ bool collisiondetection, bool vertical, std::string texture)
{
SendSpawnParticleAll(pos, velocity, acceleration,
- expirationtime, size, collisiondetection, texture);
+ expirationtime, size, collisiondetection, vertical, texture);
}
u32 Server::addParticleSpawner(const char *playername,
v3f minacc, v3f maxacc,
float minexptime, float maxexptime,
float minsize, float maxsize,
- bool collisiondetection, std::string texture)
+ bool collisiondetection, bool vertical, std::string texture)
{
Player *player = m_env->getPlayer(playername);
if(!player)
SendAddParticleSpawner(player->peer_id, amount, spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc,
minexptime, maxexptime, minsize, maxsize,
- collisiondetection, texture, id);
+ collisiondetection, vertical, texture, id);
return id;
}
v3f minacc, v3f maxacc,
float minexptime, float maxexptime,
float minsize, float maxsize,
- bool collisiondetection, std::string texture)
+ bool collisiondetection, bool vertical, std::string texture)
{
u32 id = 0;
for(;;) // look for unused particlespawner id
SendAddParticleSpawnerAll(amount, spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc,
minexptime, maxexptime, minsize, maxsize,
- collisiondetection, texture, id);
+ collisiondetection, vertical, texture, id);
return id;
}