/*
-Minetest-c55
-Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
+Minetest
+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
#include "server.h"
#include <iostream>
#include <queue>
+#include <algorithm>
#include "clientserver.h"
#include "map.h"
#include "jmutexautolock.h"
#include "constants.h"
#include "voxel.h"
#include "config.h"
-#include "servercommand.h"
#include "filesys.h"
#include "mapblock.h"
#include "serverobject.h"
#include "settings.h"
#include "profiler.h"
#include "log.h"
-#include "script.h"
-#include "scriptapi.h"
+#include "script/cpp_api/scriptapi.h"
#include "nodedef.h"
#include "itemdef.h"
#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 "sound.h" // dummySoundManager
#include "event_manager.h"
#include "hex.h"
+#include "serverlist.h"
#include "util/string.h"
#include "util/pointedthing.h"
#include "util/mathconstants.h"
-
-#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
-
-#define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
-
-class MapEditEventIgnorer
-{
-public:
- MapEditEventIgnorer(bool *flag):
- m_flag(flag)
- {
- if(*m_flag == false)
- *m_flag = true;
- else
- m_flag = NULL;
- }
-
- ~MapEditEventIgnorer()
- {
- if(m_flag)
- {
- assert(*m_flag);
- *m_flag = false;
- }
- }
-
-private:
- bool *m_flag;
-};
-
-class MapEditEventAreaIgnorer
-{
-public:
- MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
- m_ignorevariable(ignorevariable)
- {
- if(m_ignorevariable->getVolume() == 0)
- *m_ignorevariable = a;
- else
- m_ignorevariable = NULL;
- }
-
- ~MapEditEventAreaIgnorer()
- {
- if(m_ignorevariable)
- {
- assert(m_ignorevariable->getVolume() != 0);
- *m_ignorevariable = VoxelArea();
- }
- }
-
-private:
- VoxelArea *m_ignorevariable;
-};
+#include "rollback.h"
+#include "util/serialize.h"
+#include "defaultsettings.h"
void * ServerThread::Thread()
{
//TimeTaker timer("AsyncRunStep()");
m_server->AsyncRunStep();
}
-
+
//infostream<<"Running m_server->Receive()"<<std::endl;
m_server->Receive();
}
m_server->setAsyncFatalError(e.what());
}
}
-
- END_DEBUG_EXCEPTION_HANDLER(errorstream)
-
- return NULL;
-}
-
-void * EmergeThread::Thread()
-{
- ThreadStarted();
-
- log_register_thread("EmergeThread");
-
- DSTACK(__FUNCTION_NAME);
-
- BEGIN_DEBUG_EXCEPTION_HANDLER
-
- bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
-
- v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
-
- /*
- Get block info from queue, emerge them and send them
- to clients.
-
- After queue is empty, exit.
- */
- while(getRun())
- try{
- QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
- if(qptr == NULL)
- break;
-
- SharedPtr<QueuedBlockEmerge> q(qptr);
-
- v3s16 &p = q->pos;
- v2s16 p2d(p.X,p.Z);
-
- last_tried_pos = p;
-
- /*
- Do not generate over-limit
- */
- if(blockpos_over_limit(p))
- continue;
-
- //infostream<<"EmergeThread::Thread(): running"<<std::endl;
-
- //TimeTaker timer("block emerge");
-
- /*
- Try to emerge it from somewhere.
-
- If it is only wanted as optional, only loading from disk
- will be allowed.
- */
-
- /*
- Check if any peer wants it as non-optional. In that case it
- will be generated.
-
- Also decrement the emerge queue count in clients.
- */
-
- bool only_from_disk = true;
-
- {
- core::map<u16, u8>::Iterator i;
- for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
- {
- //u16 peer_id = i.getNode()->getKey();
-
- // Check flags
- u8 flags = i.getNode()->getValue();
- if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
- only_from_disk = false;
-
- }
- }
-
- if(enable_mapgen_debug_info)
- infostream<<"EmergeThread: p="
- <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
- <<"only_from_disk="<<only_from_disk<<std::endl;
-
- ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
-
- MapBlock *block = NULL;
- bool got_block = true;
- core::map<v3s16, MapBlock*> modified_blocks;
-
- /*
- Try to fetch block from memory or disk.
- If not found and asked to generate, initialize generator.
- */
-
- bool started_generate = false;
- mapgen::BlockMakeData data;
-
- {
- JMutexAutoLock envlock(m_server->m_env_mutex);
-
- // Load sector if it isn't loaded
- if(map.getSectorNoGenerateNoEx(p2d) == NULL)
- map.loadSectorMeta(p2d);
-
- // Attempt to load block
- block = map.getBlockNoCreateNoEx(p);
- if(!block || block->isDummy() || !block->isGenerated())
- {
- if(enable_mapgen_debug_info)
- infostream<<"EmergeThread: not in memory, "
- <<"attempting to load from disk"<<std::endl;
-
- block = map.loadBlock(p);
- }
-
- // If could not load and allowed to generate, start generation
- // inside this same envlock
- if(only_from_disk == false &&
- (block == NULL || block->isGenerated() == false)){
- if(enable_mapgen_debug_info)
- infostream<<"EmergeThread: generating"<<std::endl;
- started_generate = true;
-
- map.initBlockMake(&data, p);
- }
- }
-
- /*
- If generator was initialized, generate now when envlock is free.
- */
- if(started_generate)
- {
- {
- ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
- SPT_AVG);
- TimeTaker t("mapgen::make_block()");
-
- mapgen::make_block(&data);
-
- if(enable_mapgen_debug_info == false)
- t.stop(true); // Hide output
- }
-
- do{ // enable break
- // Lock environment again to access the map
- JMutexAutoLock envlock(m_server->m_env_mutex);
-
- ScopeProfiler sp(g_profiler, "EmergeThread: after "
- "mapgen::make_block (envlock)", SPT_AVG);
-
- // Blit data back on map, update lighting, add mobs and
- // whatever this does
- map.finishBlockMake(&data, modified_blocks);
-
- // Get central block
- block = map.getBlockNoCreateNoEx(p);
-
- // If block doesn't exist, don't try doing anything with it
- // This happens if the block is not in generation boundaries
- if(!block)
- break;
-
- /*
- Do some post-generate stuff
- */
-
- v3s16 minp = data.blockpos_min*MAP_BLOCKSIZE;
- v3s16 maxp = data.blockpos_max*MAP_BLOCKSIZE +
- v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
-
- /*
- Ignore map edit events, they will not need to be
- sent to anybody because the block hasn't been sent
- to anybody
- */
- //MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
- MapEditEventAreaIgnorer ign(
- &m_server->m_ignore_map_edit_events_area,
- VoxelArea(minp, maxp));
- {
- TimeTaker timer("on_generated");
- scriptapi_environment_on_generated(m_server->m_lua,
- minp, maxp, mapgen::get_blockseed(data.seed, minp));
- /*int t = timer.stop(true);
- dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/
- }
-
- if(enable_mapgen_debug_info)
- infostream<<"EmergeThread: ended up with: "
- <<analyze_block(block)<<std::endl;
-
- // Activate objects and stuff
- m_server->m_env->activateBlock(block, 0);
- }while(false);
- }
-
- if(block == NULL)
- got_block = false;
-
- /*
- Set sent status of modified blocks on clients
- */
-
- // NOTE: Server's clients are also behind the connection mutex
- JMutexAutoLock lock(m_server->m_con_mutex);
-
- /*
- Add the originally fetched block to the modified list
- */
- if(got_block)
- {
- modified_blocks.insert(p, block);
- }
-
- /*
- Set the modified blocks unsent for all the clients
- */
-
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_server->m_clients.getIterator();
- i.atEnd() == false; i++)
- {
- RemoteClient *client = i.getNode()->getValue();
-
- if(modified_blocks.size() > 0)
- {
- // Remove block from sent history
- client->SetBlocksNotSent(modified_blocks);
- }
- }
- }
- catch(VersionMismatchException &e)
- {
- std::ostringstream err;
- err<<"World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl;
- err<<"----"<<std::endl;
- err<<"\""<<e.what()<<"\""<<std::endl;
- err<<"See debug.txt."<<std::endl;
- err<<"World probably saved by a newer version of Minetest."<<std::endl;
- m_server->setAsyncFatalError(err.str());
- }
- catch(SerializationError &e)
- {
- std::ostringstream err;
- err<<"Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl;
- err<<"----"<<std::endl;
- err<<"\""<<e.what()<<"\""<<std::endl;
- err<<"See debug.txt."<<std::endl;
- err<<"You can ignore this using [ignore_world_load_errors = true]."<<std::endl;
- m_server->setAsyncFatalError(err.str());
- }
END_DEBUG_EXCEPTION_HANDLER(errorstream)
- log_deregister_thread();
-
return NULL;
}
}
void RemoteClient::GetNextBlocks(Server *server, float dtime,
- core::array<PrioritySortedBlockTransfer> &dest)
+ std::vector<PrioritySortedBlockTransfer> &dest)
{
DSTACK(__FUNCTION_NAME);
-
+
/*u32 timer_result;
TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
-
+
// Increment timers
m_nothing_to_send_pause_timer -= dtime;
m_nearest_unsent_reset_timer += dtime;
-
+
if(m_nothing_to_send_pause_timer >= 0)
- {
return;
- }
+
+ Player *player = server->m_env->getPlayer(peer_id);
+ // This can happen sometimes; clients and players are not in perfect sync.
+ if(player == NULL)
+ return;
// Won't send anything if already sending
if(m_blocks_sending.size() >= g_settings->getU16
}
//TimeTaker timer("RemoteClient::GetNextBlocks");
-
- Player *player = server->m_env->getPlayer(peer_id);
-
- assert(player != NULL);
v3f playerpos = player->getPosition();
v3f playerspeed = player->getSpeed();
v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
v3s16 center = getNodeBlockPos(center_nodepos);
-
+
// Camera position and direction
v3f camera_pos = player->getEyePosition();
v3f camera_dir = v3f(0,0,1);
/*
Get the starting value of the block finder radius.
*/
-
+
if(m_last_center != center)
{
m_nearest_unsent_d = 0;
/*infostream<<"m_nearest_unsent_reset_timer="
<<m_nearest_unsent_reset_timer<<std::endl;*/
-
+
// Reset periodically to workaround for some bugs or stuff
if(m_nearest_unsent_reset_timer > 20.0)
{
/*
Check the time from last addNode/removeNode.
-
+
Decrease send rate if player is building stuff.
*/
m_time_from_building += dtime;
max_simul_sends_usually
= LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
}
-
+
/*
Number of blocks sending + number of blocks selected for sending
*/
u32 num_blocks_selected = m_blocks_sending.size();
-
+
/*
next time d will be continued from the d from which the nearest
unsent block was found this time.
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
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;
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++)
{
/*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),
Get the border/face dot coordinates of a "d-radiused"
box
*/
- core::list<v3s16> list;
+ std::list<v3s16> list;
getFacePositions(list, d);
-
- core::list<v3s16>::Iterator li;
- for(li=list.begin(); li!=list.end(); li++)
+
+ std::list<v3s16>::iterator li;
+ for(li=list.begin(); li!=list.end(); ++li)
{
v3s16 p = *li + center;
-
+
/*
Send throttling
- Don't allow too many simultaneous transfers
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;
queue_is_full = true;
goto queue_full_break;
}
-
+
// Don't send blocks that are currently being transferred
- if(m_blocks_sending.find(p) != NULL)
+ if(m_blocks_sending.find(p) != m_blocks_sending.end())
continue;
-
+
/*
Do not go over-limit
*/
|| p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
|| p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
continue;
-
+
// If this is true, inexistent block will be made from scratch
bool generate = d <= d_max_gen;
-
+
{
/*// Limit the generating area vertically to 2/3
if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
v2s16 p2d_nodes_center(
MAP_BLOCKSIZE*p.X,
MAP_BLOCKSIZE*p.Z);
-
+
// Get ground height in nodes
s16 gh = server->m_env->getServerMap().findGroundLevel(
p2d_nodes_center);
Don't send already sent blocks
*/
{
- if(m_blocks_sent.find(p) != NULL)
+ if(m_blocks_sent.find(p) != m_blocks_sent.end())
{
continue;
}
Check if map has this block
*/
MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
-
+
bool surely_not_found_on_disk = false;
bool block_is_invalid = false;
if(block != NULL)
{
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(block == NULL || surely_not_found_on_disk || block_is_invalid)
{
- //TODO: Get value from somewhere
+ /* //TODO: Get value from somewhere
// Allow only one block in emerge queue
//if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
// Allow two blocks in queue per client
if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
{
//infostream<<"Adding block to emerge queue"<<std::endl;
-
+
// Add it to the emerge queue and trigger the thread
-
+
u8 flags = 0;
if(generate == false)
flags |= BLOCK_EMERGE_FLAG_FROMDISK;
-
+
server->m_emerge_queue.addBlock(peer_id, p, flags);
server->m_emergethread.trigger();
} else {
if(nearest_emergefull_d == -1)
nearest_emergefull_d = d;
+ goto queue_full_break;
+ }
+ */
+
+ 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;
}
// get next one.
queue_full_break:
//infostream<<"Stopped at "<<d<<std::endl;
-
+
// If nothing was found for sending and nothing was queued for
// emerging, continue next time browsing from here
if(nearest_emerged_d != -1){
/*timer_result = timer.stop(true);
if(timer_result != 0)
- infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
+ infostream<<"GetNextBlocks timeout: "<<timer_result<<" (!=0)"<<std::endl;*/
}
void RemoteClient::GotBlock(v3s16 p)
{
- if(m_blocks_sending.find(p) != NULL)
- m_blocks_sending.remove(p);
+ if(m_blocks_sending.find(p) != m_blocks_sending.end())
+ m_blocks_sending.erase(p);
else
{
/*infostream<<"RemoteClient::GotBlock(): Didn't find in"
" m_blocks_sending"<<std::endl;*/
m_excess_gotblocks++;
}
- m_blocks_sent.insert(p, true);
+ m_blocks_sent.insert(p);
}
void RemoteClient::SentBlock(v3s16 p)
{
- if(m_blocks_sending.find(p) == NULL)
- m_blocks_sending.insert(p, 0.0);
+ if(m_blocks_sending.find(p) == m_blocks_sending.end())
+ m_blocks_sending[p] = 0.0;
else
infostream<<"RemoteClient::SentBlock(): Sent block"
" already in m_blocks_sending"<<std::endl;
void RemoteClient::SetBlockNotSent(v3s16 p)
{
m_nearest_unsent_d = 0;
-
- if(m_blocks_sending.find(p) != NULL)
- m_blocks_sending.remove(p);
- if(m_blocks_sent.find(p) != NULL)
- m_blocks_sent.remove(p);
+
+ 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);
}
-void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
+void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
{
m_nearest_unsent_d = 0;
-
- for(core::map<v3s16, MapBlock*>::Iterator
- i = blocks.getIterator();
- i.atEnd()==false; i++)
+
+ for(std::map<v3s16, MapBlock*>::iterator
+ i = blocks.begin();
+ i != blocks.end(); ++i)
{
- v3s16 p = i.getNode()->getKey();
+ v3s16 p = i->first;
- if(m_blocks_sending.find(p) != NULL)
- m_blocks_sending.remove(p);
- if(m_blocks_sent.find(p) != NULL)
- m_blocks_sent.remove(p);
+ 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);
}
}
m_simple_singleplayer_mode(simple_singleplayer_mode),
m_async_fatal_error(""),
m_env(NULL),
- m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
+ m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT,
+ g_settings->getBool("enable_ipv6") && g_settings->getBool("ipv6_server"), this),
m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
- m_lua(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()),
m_craftdef(createCraftDefManager()),
m_event(new EventManager()),
m_thread(this),
- m_emergethread(this),
m_time_of_day_send_timer(0),
m_uptime(0),
m_shutdown_requested(false),
m_ignore_map_edit_events_peer_id(0)
{
m_liquid_transform_timer = 0.0;
+ m_liquid_transform_every = 1.0;
m_print_info_timer = 0.0;
+ m_masterserver_timer = 0.0;
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();
if(path_world == "")
throw ServerError("Supplied empty world path");
-
+
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;
infostream<<"- config: "<<m_path_config<<std::endl;
infostream<<"- game: "<<m_gamespec.path<<std::endl;
- // Add world mod search path
- m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
- // Add addon mod search path
- for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
- i != m_gamespec.mods_paths.end(); i++)
- m_modspaths.push_front((*i));
-
- // Print out mod search paths
- for(core::list<std::string>::Iterator i = m_modspaths.begin();
- i != m_modspaths.end(); i++){
- std::string modspath = *i;
- infostream<<"- mods: "<<modspath<<std::endl;
- }
+ // 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);
- // Path to builtin.lua
- std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
+ // Create emerge manager
+ m_emerge = new EmergeManager(this);
+
+ // 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");
+ 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())
+ {
+ for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
+ it != unsatisfied_mods.end(); ++it)
+ {
+ 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;
+ }
+ }
+
+ Settings worldmt_settings;
+ std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
+ worldmt_settings.readConfigFile(worldmt.c_str());
+ std::vector<std::string> names = worldmt_settings.getNames();
+ std::set<std::string> load_mod_names;
+ for(std::vector<std::string>::iterator it = names.begin();
+ it != names.end(); ++it)
+ {
+ std::string name = *it;
+ if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
+ load_mod_names.insert(name.substr(9));
+ }
+ // complain about mods declared to be loaded, but not found
+ for(std::vector<ModSpec>::iterator it = m_mods.begin();
+ it != m_mods.end(); ++it)
+ load_mod_names.erase((*it).name);
+ for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
+ it != unsatisfied_mods.end(); ++it)
+ load_mod_names.erase((*it).name);
+ if(!load_mod_names.empty())
+ {
+ errorstream << "The following mods could not be found:";
+ for(std::set<std::string>::iterator it = load_mod_names.begin();
+ it != load_mod_names.end(); ++it)
+ errorstream << " \"" << (*it) << "\"";
+ errorstream << std::endl;
+ }
+
+ // Path to builtin.lua
+ std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
+
// Lock environment
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
// Initialize scripting
-
+
infostream<<"Server: Initializing Lua"<<std::endl;
- m_lua = script_init();
- assert(m_lua);
- // Export API
- scriptapi_export(m_lua, this);
+
+ m_script = new ScriptApi(this);
+
+
// Load and run builtin.lua
infostream<<"Server: Loading builtin.lua [\""
<<builtinpath<<"\"]"<<std::endl;
- bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
+ bool success = m_script->loadMod(builtinpath, "__builtin");
if(!success){
errorstream<<"Server: Failed to load and run "
<<builtinpath<<std::endl;
throw ModError("Failed to load and run "+builtinpath);
}
- // Find mods in mod search paths
- m_mods = getMods(m_modspaths);
// Print 'em
infostream<<"Server: Loading mods: ";
- for(core::list<ModSpec>::Iterator i = m_mods.begin();
+ for(std::vector<ModSpec>::iterator i = m_mods.begin();
i != m_mods.end(); i++){
const ModSpec &mod = *i;
infostream<<mod.name<<" ";
}
infostream<<std::endl;
// Load and run "mod" scripts
- for(core::list<ModSpec>::Iterator i = m_mods.begin();
+ for(std::vector<ModSpec>::iterator i = m_mods.begin();
i != m_mods.end(); i++){
const ModSpec &mod = *i;
std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
<<scriptpath<<"\"]"<<std::endl;
- bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
+ bool success = m_script->loadMod(scriptpath, mod.name);
if(!success){
errorstream<<"Server: Failed to load and run "
<<scriptpath<<std::endl;
throw ModError("Failed to load and run "+scriptpath);
}
}
-
+
// Read Textures and calculate sha1 sums
fillMediaCache();
m_nodedef->updateAliases(m_itemdef);
// Initialize Environment
+ ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
+ m_env = new ServerEnvironment(servermap, m_script, this, this);
+
+ // Run some callbacks after the MG params have been set up but before activation
+ MapgenParams *mgparams = servermap->getMapgenParams();
+ m_script->environment_OnMapgenInit(mgparams);
- m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
- this, this);
+ // Initialize mapgens
+ m_emerge->initMapgens(mgparams);
// Give environment reference to scripting api
- scriptapi_add_environment(m_lua, m_env);
-
+ m_script->initializeEnvironment(m_env);
+
// Register us to receive map edit events
- m_env->getMap().addEventReceiver(this);
+ servermap->addEventReceiver(this);
// If file exists, load environment metadata
if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
Add some test ActiveBlockModifiers to environment
*/
add_legacy_abms(m_env, m_nodedef);
+
+ m_liquid_transform_every = g_settings->getFloat("liquid_update");
}
Server::~Server()
*/
{
JMutexAutoLock conlock(m_con_mutex);
-
+
std::wstring line = L"*** Server shutting down";
/*
Send the message to clients
*/
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ 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.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
+ RemoteClient *client = i->second;
+ assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
{}
}
}
-
+
+ {
+ JMutexAutoLock envlock(m_env_mutex);
+ JMutexAutoLock conlock(m_con_mutex);
+
+ /*
+ Execute script shutdown hooks
+ */
+ m_script->on_shutdown();
+ }
+
{
JMutexAutoLock envlock(m_env_mutex);
infostream<<"Server: Saving environment metadata"<<std::endl;
m_env->saveMeta(m_path_world);
}
-
+
/*
Stop threads
*/
stop();
-
+
+ //shutdown all emerge threads first!
+ delete m_emerge;
+
/*
Delete clients
*/
{
JMutexAutoLock clientslock(m_con_mutex);
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ for(std::map<u16, RemoteClient*>::iterator
+ i = m_clients.begin();
+ i != m_clients.end(); ++i)
{
- /*// Delete player
- // NOTE: These are removed by env destructor
- {
- u16 peer_id = i.getNode()->getKey();
- JMutexAutoLock envlock(m_env_mutex);
- m_env->removePlayer(peer_id);
- }*/
-
+
// Delete client
- delete i.getNode()->getValue();
+ delete i->second;
}
}
-
+
// Delete things in the reverse order of creation
delete m_env;
+ delete m_rollback;
delete m_event;
delete m_itemdef;
delete m_nodedef;
delete m_craftdef;
-
+
// Deinitialize scripting
infostream<<"Server: Deinitializing scripting"<<std::endl;
- script_deinit(m_lua);
+ delete m_script;
+
+ // Delete detached inventories
+ {
+ for(std::map<std::string, Inventory*>::iterator
+ i = m_detached_inventories.begin();
+ i != m_detached_inventories.end(); i++){
+ delete i->second;
+ }
+ }
}
void Server::start(unsigned short port)
// Stop thread if already running
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!
actionstream
<<" .__ __ __ "<<std::endl
void Server::stop()
{
DSTACK(__FUNCTION_NAME);
-
+
infostream<<"Server: Stopping and waiting threads"<<std::endl;
// Stop threads (set run=false first so both start stopping)
m_thread.setRun(false);
- m_emergethread.setRun(false);
+ //m_emergethread.setRun(false);
m_thread.stop();
- m_emergethread.stop();
-
+ //m_emergethread.stop();
+
infostream<<"Server: Threads stopped"<<std::endl;
}
void Server::AsyncRunStep()
{
DSTACK(__FUNCTION_NAME);
-
+
g_profiler->add("Server::AsyncRunStep (num)", 1);
-
+
float dtime;
{
JMutexAutoLock lock1(m_step_dtime_mutex);
dtime = m_step_dtime;
}
-
+
{
// Send blocks to clients
SendBlocks(dtime);
}
-
+
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;
-
+
{
JMutexAutoLock lock1(m_step_dtime_mutex);
m_step_dtime -= dtime;
{
m_uptime.set(m_uptime.get() + dtime);
}
-
+
{
// Process connection's timeouts
JMutexAutoLock lock2(m_con_mutex);
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
//JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ for(std::map<u16, RemoteClient*>::iterator
+ i = m_clients.begin();
+ i != m_clients.end(); ++i)
{
- RemoteClient *client = i.getNode()->getValue();
+ RemoteClient *client = i->second;
SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
// Send as reliable
ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
m_env->step(dtime);
}
-
+
const float map_timer_and_unload_dtime = 2.92;
if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
{
m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
g_settings->getFloat("server_unload_unused_data_timeout"));
}
-
+
/*
Do background stuff
*/
ScopeProfiler sp(g_profiler, "Server: handle players");
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ for(std::map<u16, RemoteClient*>::iterator
+ i = m_clients.begin();
+ i != m_clients.end(); ++i)
{
- RemoteClient *client = i.getNode()->getValue();
+ RemoteClient *client = i->second;
PlayerSAO *playersao = getPlayerSAO(client->peer_id);
if(playersao == NULL)
continue;
/*
Handle player HPs (die if hp=0)
*/
- if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
- DiePlayer(client->peer_id);
+ 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 and HPs if necessary
+ Send player inventories if necessary
*/
- if(playersao->m_teleported){
+ if(playersao->m_moved){
SendMovePlayer(client->peer_id);
- playersao->m_teleported = false;
+ playersao->m_moved = false;
}
if(playersao->m_inventory_not_sent){
UpdateCrafting(client->peer_id);
SendInventory(client->peer_id);
}
- if(playersao->m_hp_not_sent){
- SendPlayerHP(client->peer_id);
- }
}
}
-
+
/* Transform liquids */
m_liquid_transform_timer += dtime;
- if(m_liquid_transform_timer >= 1.00)
+ if(m_liquid_transform_timer >= m_liquid_transform_every)
{
- m_liquid_transform_timer -= 1.00;
-
+ m_liquid_transform_timer -= m_liquid_transform_every;
+
JMutexAutoLock lock(m_env_mutex);
ScopeProfiler sp(g_profiler, "Server: liquid transform");
- core::map<v3s16, MapBlock*> modified_blocks;
+ std::map<v3s16, MapBlock*> modified_blocks;
m_env->getMap().transformLiquids(modified_blocks);
-#if 0
+#if 0
/*
Update lighting
*/
core::map<v3s16, MapBlock*> lighting_modified_blocks;
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();
/*
Set the modified blocks unsent for all the clients
*/
-
+
JMutexAutoLock lock2(m_con_mutex);
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ for(std::map<u16, RemoteClient*>::iterator
+ i = m_clients.begin();
+ i != m_clients.end(); ++i)
{
- RemoteClient *client = i.getNode()->getValue();
-
+ RemoteClient *client = i->second;
+
if(modified_blocks.size() > 0)
{
// Remove block from sent history
counter = 0.0;
JMutexAutoLock lock2(m_con_mutex);
-
+ m_clients_number = 0;
if(m_clients.size() != 0)
infostream<<"Players:"<<std::endl;
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ for(std::map<u16, RemoteClient*>::iterator
+ i = m_clients.begin();
+ i != m_clients.end(); ++i)
{
//u16 peer_id = i.getNode()->getKey();
- RemoteClient *client = i.getNode()->getValue();
+ RemoteClient *client = i->second;
Player *player = m_env->getPlayer(client->peer_id);
if(player==NULL)
continue;
infostream<<"* "<<player->getName()<<"\t";
client->PrintInfo(infostream);
+ ++m_clients_number;
}
}
}
+
+#if USE_CURL
+ // send masterserver announce
+ {
+ float &counter = m_masterserver_timer;
+ if((!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);
+ counter = 0.01;
+ }
+ counter += dtime;
+ }
+#endif
+
//if(g_settings->getBool("enable_experimental"))
{
s16 radius = g_settings->getS16("active_object_send_range_blocks");
radius *= MAP_BLOCKSIZE;
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ for(std::map<u16, RemoteClient*>::iterator
+ i = m_clients.begin();
+ i != m_clients.end(); ++i)
{
- RemoteClient *client = i.getNode()->getValue();
+ RemoteClient *client = i->second;
// If definitions and textures have not been sent, don't
// send objects either
}
v3s16 pos = floatToInt(player->getPosition(), BS);
- core::map<u16, bool> removed_objects;
- core::map<u16, bool> added_objects;
+ std::set<u16> removed_objects;
+ std::set<u16> added_objects;
m_env->getRemovedActiveObjects(pos, radius,
client->m_known_objects, removed_objects);
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);
- for(core::map<u16, bool>::Iterator
- i = removed_objects.getIterator();
- i.atEnd()==false; i++)
+ for(std::set<u16>::iterator
+ i = removed_objects.begin();
+ i != removed_objects.end(); ++i)
{
// Get object
- u16 id = i.getNode()->getKey();
+ u16 id = *i;
ServerActiveObject* obj = m_env->getActiveObject(id);
// Add to data buffer for sending
- writeU16((u8*)buf, i.getNode()->getKey());
+ writeU16((u8*)buf, id);
data_buffer.append(buf, 2);
-
+
// Remove from known objects
- client->m_known_objects.remove(i.getNode()->getKey());
+ 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);
- for(core::map<u16, bool>::Iterator
- i = added_objects.getIterator();
- i.atEnd()==false; i++)
+ for(std::set<u16>::iterator
+ i = added_objects.begin();
+ i != added_objects.end(); ++i)
{
// Get object
- u16 id = i.getNode()->getKey();
+ u16 id = *i;
ServerActiveObject* obj = m_env->getActiveObject(id);
-
+
// Get object type
u8 type = ACTIVEOBJECT_TYPE_INVALID;
if(obj == NULL)
data_buffer.append(buf, 2);
writeU8((u8*)buf, type);
data_buffer.append(buf, 1);
-
+
if(obj)
data_buffer.append(serializeLongString(
- obj->getClientInitializationData()));
+ obj->getClientInitializationData(client->net_proto_version)));
else
data_buffer.append(serializeLongString(""));
// Add to known objects
- client->m_known_objects.insert(i.getNode()->getKey(), false);
+ client->m_known_objects.insert(id);
if(obj)
obj->m_known_by_count++;
all_known_objects[id] = true;
}
}
-
+
m_env->setKnownActiveObjects(whatever);
#endif
// Key = object id
// Value = data sent by object
- core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
+ std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
// Get active object messages from environment
for(;;)
ActiveObjectMessage aom = m_env->getActiveObjectMessage();
if(aom.id == 0)
break;
-
- core::list<ActiveObjectMessage>* message_list = NULL;
- core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
+
+ std::list<ActiveObjectMessage>* message_list = NULL;
+ std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
n = buffered_messages.find(aom.id);
- if(n == NULL)
+ if(n == buffered_messages.end())
{
- message_list = new core::list<ActiveObjectMessage>;
- buffered_messages.insert(aom.id, message_list);
+ message_list = new std::list<ActiveObjectMessage>;
+ buffered_messages[aom.id] = message_list;
}
else
{
- message_list = n->getValue();
+ message_list = n->second;
}
message_list->push_back(aom);
}
-
+
// Route data to every client
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd()==false; i++)
+ for(std::map<u16, RemoteClient*>::iterator
+ i = m_clients.begin();
+ i != m_clients.end(); ++i)
{
- RemoteClient *client = i.getNode()->getValue();
+ RemoteClient *client = i->second;
std::string reliable_data;
std::string unreliable_data;
// Go through all objects in message buffer
- for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
- j = buffered_messages.getIterator();
- j.atEnd()==false; j++)
+ 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
- u16 id = j.getNode()->getKey();
- if(client->m_known_objects.find(id) == NULL)
+ u16 id = j->first;
+ if(client->m_known_objects.find(id) == client->m_known_objects.end())
continue;
// Get message list of object
- core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
+ std::list<ActiveObjectMessage>* list = j->second;
// Go through every message
- for(core::list<ActiveObjectMessage>::Iterator
- k = list->begin(); k != list->end(); k++)
+ for(std::list<ActiveObjectMessage>::iterator
+ k = list->begin(); k != list->end(); ++k)
{
// Compose the full new data with header
ActiveObjectMessage aom = *k;
}
// Clear buffered_messages
- for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
- i = buffered_messages.getIterator();
- i.atEnd()==false; i++)
+ for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
+ i = buffered_messages.begin();
+ i != buffered_messages.end(); ++i)
{
- delete i.getNode()->getValue();
+ delete i->second;
}
}
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.
- core::list<u16> far_players;
+ std::list<u16> far_players;
if(event->type == MEET_ADDNODE)
{
{
infostream<<"Server: MEET_OTHER"<<std::endl;
prof.add("MEET_OTHER", 1);
- for(core::map<v3s16, bool>::Iterator
- i = event->modified_blocks.getIterator();
- i.atEnd()==false; i++)
+ for(std::set<v3s16>::iterator
+ i = event->modified_blocks.begin();
+ i != event->modified_blocks.end(); ++i)
{
- v3s16 p = i.getNode()->getKey();
- setBlockNotSent(p);
+ setBlockNotSent(*i);
}
}
else
infostream<<"WARNING: Server: Unknown MapEditEvent "
<<((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
- core::map<v3s16, MapBlock*> modified_blocks2;
- for(core::map<v3s16, bool>::Iterator
- i = event->modified_blocks.getIterator();
- i.atEnd()==false; i++)
+ std::map<v3s16, MapBlock*> modified_blocks2;
+ for(std::set<v3s16>::iterator
+ i = event->modified_blocks.begin();
+ i != event->modified_blocks.end(); ++i)
{
- v3s16 p = i.getNode()->getKey();
- modified_blocks2.insert(p,
- m_env->getMap().getBlockNoCreateNoEx(p));
+ modified_blocks2[*i] =
+ m_env->getMap().getBlockNoCreateNoEx(*i);
}
// Set blocks not sent
- for(core::list<u16>::Iterator
+ for(std::list<u16>::iterator
i = far_players.begin();
- i != far_players.end(); i++)
+ i != far_players.end(); ++i)
{
u16 peer_id = *i;
RemoteClient *client = getClient(peer_id);
verbosestream<<"Server: MapEditEvents:"<<std::endl;
prof.print(verbosestream);
}
-
+
}
/*
if(counter >= 2.0)
{
counter = 0.0;
-
- m_emergethread.trigger();
+
+ for (unsigned int i = 0; i != m_emerge->emergethread.size(); i++)
+ m_emerge->emergethread[i]->trigger();
+
+ // Update m_enable_rollback_recording here too
+ m_enable_rollback_recording =
+ g_settings->getBool("enable_rollback_recording");
}
}
//Ban stuff
if(m_banmanager.isModified())
m_banmanager.save();
-
+
// Save changed parts of map
m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
// Save players
m_env->serializePlayers(m_path_world);
-
+
// Save environment metadata
m_env->saveMeta(m_path_world);
}
catch(con::PeerNotFoundException &e)
{
//NOTE: This is not needed anymore
-
+
// The peer has been disconnected.
// Find the associated player and remove it.
// Environment is locked first.
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
-
+
ScopeProfiler sp(g_profiler, "Server::ProcessData");
-
+
try{
Address address = m_con.GetPeerAddress(peer_id);
std::string addr_s = address.serializeString();
<<peer_id<<" not found"<<std::endl;
return;
}
-
+
std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
u8 peer_ser_ver = getClient(peer_id)->serialization_version;
return;
ToServerCommand command = (ToServerCommand)readU16(&data[0]);
-
+
if(command == TOSERVER_INIT)
{
// [0] u16 TOSERVER_INIT
u8 client_max = data[2];
u8 our_max = SER_FMT_VER_HIGHEST;
// Use the highest version supported by both
- u8 deployed = core::min_(client_max, our_max);
+ u8 deployed = std::min(client_max, our_max);
// 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;
-
+
if(deployed == SER_FMT_VER_INVALID)
{
actionstream<<"Server: A mismatched client tried to connect from "
);
return;
}
-
+
/*
Read and check network protocol version
*/
- u16 net_proto_version = 0;
+ 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)
{
- net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
+ // 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: "<<peer_id<<" 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 == 0)
+ if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
+ net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
{
- actionstream<<"Server: An old tried to connect from "<<addr_s
+ actionstream<<"Server: A mismatched client tried to connect from "<<addr_s
<<std::endl;
SendAccessDenied(m_con, 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(VERSION_STRING) + 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))
);
return;
}
-
+
if(g_settings->getBool("strict_protocol_version_checking"))
{
- if(net_proto_version != PROTOCOL_VERSION)
+ if(net_proto_version != LATEST_PROTOCOL_VERSION)
{
- actionstream<<"Server: A mismatched client tried to connect"
- <<" from "<<addr_s<<std::endl;
+ actionstream<<"Server: A mismatched (strict) client tried to "
+ <<"connect from "<<addr_s<<std::endl;
SendAccessDenied(m_con, peer_id, std::wstring(
L"Your client's version is not supported.\n"
L"Server version is ")
+ narrow_to_wide(VERSION_STRING) + L",\n"
- + L"server's PROTOCOL_VERSION is "
- + narrow_to_wide(itos(PROTOCOL_VERSION))
+ + L"server's PROTOCOL_VERSION (strict) is "
+ + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
+ L", client's PROTOCOL_VERSION is "
- + narrow_to_wide(itos(net_proto_version))
+ + narrow_to_wide(itos(min_net_proto_version))
+ + L"..."
+ + narrow_to_wide(itos(max_net_proto_version))
);
return;
}
/*
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 "
return;
}
+ if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
+ {
+ actionstream<<"Server: Player with an invalid name "
+ <<"tried to connect from "<<addr_s<<std::endl;
+ SendAccessDenied(m_con, peer_id,
+ L"Name is not allowed");
+ return;
+ }
+
infostream<<"Server: New connection: \""<<playername<<"\" from "
<<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
SendAccessDenied(m_con, peer_id, L"Invalid password hash");
return;
}
-
+
std::string checkpwd; // Password hash to check against
- bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
-
+ bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
+
// If no authentication info exists for user, create it
if(!has_auth){
if(!isSingleplayer() &&
if (raw_default_password.length() == 0)
initial_password = given_password;
- scriptapi_create_auth(m_lua, playername, initial_password);
+ m_script->createAuth(playername, initial_password);
}
-
- has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
+
+ has_auth = m_script->getAuth(playername, &checkpwd, NULL);
if(!has_auth){
SendAccessDenied(m_con, peer_id, L"Not allowed to login");
L"Running in simple singleplayer mode.");
return;
}
-
+
// Enforce user limit.
// Don't enforce for users that have some admin right
if(m_clients.size() >= g_settings->getU16("max_users") &&
Answer with a TOCLIENT_INIT
*/
{
- SharedBuffer<u8> reply(2+1+6+8);
+ 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));
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);
}
return;
}
- getClient(peer_id)->serialization_version
- = getClient(peer_id)->pending_serialization_version;
+ RemoteClient *client = getClient(peer_id);
+ client->serialization_version =
+ getClient(peer_id)->pending_serialization_version;
/*
Send some initialization data
infostream<<"Server: Sending content to "
<<getPlayerName(peer_id)<<std::endl;
+ // Send player movement settings
+ SendMovement(m_con, peer_id);
+
// Send item definitions
- SendItemDef(m_con, peer_id, m_itemdef);
-
+ SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version);
+
// Send node definitions
- SendNodeDef(m_con, peer_id, m_nodedef);
-
+ SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
+
// Send media announcement
sendMediaAnnouncement(peer_id);
-
+
// Send privileges
SendPlayerPrivileges(peer_id);
-
+
// Send inventory formspec
SendPlayerInventoryFormspec(peer_id);
// Send inventory
UpdateCrafting(peer_id);
SendInventory(peer_id);
-
+
// Send HP
- SendPlayerHP(peer_id);
-
+ if(g_settings->getBool("enable_damage"))
+ SendPlayerHP(peer_id);
+
+ // Send Breath
+ SendPlayerBreath(peer_id);
+
+ // 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));
m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
m_con.Send(peer_id, 0, data, true);
}
-
+
// 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;
BroadcastChatMessage(message);
}
}
-
+
// Warnings about protocol version can be issued here
- if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
+ if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
{
- SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER!");
+ SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
+ L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
}
/*
*/
{
std::ostringstream os(std::ios_base::binary);
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ for(std::map<u16, RemoteClient*>::iterator
+ i = m_clients.begin();
+ i != m_clients.end(); ++i)
{
- RemoteClient *client = i.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
+ RemoteClient *client = i->second;
+ assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
// Get player
os<<player->getName()<<" ";
}
- actionstream<<player->getName()<<" joins game. List of players: "
+ actionstream<<player->getName()<<" ["<<addr_s<<"] "<<" joins game. List of players: "
<<os.str()<<std::endl;
}
" Skipping incoming command="<<command<<std::endl;
return;
}
-
+
Player *player = m_env->getPlayer(peer_id);
if(player == NULL){
infostream<<"Server::ProcessData(): Cancelling: "
{
if(datasize < 2+12+12+4+4)
return;
-
+
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]);
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);
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);
+
/*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
<<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
<<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
{
if(datasize < 2+1)
return;
-
+
/*
[0] u16 command
[2] u8 count
{
if(datasize < 2+1)
return;
-
+
/*
[0] u16 command
[2] u8 count
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.
delete a;
return;
}
-
- // If player is not an admin, check for ownership of src and dst
- /*if(!checkPriv(player->getName(), "server"))
- {
- std::string owner_from = getInventoryOwner(ma->from_inv);
- if(owner_from != "" && owner_from != player->getName())
- {
- infostream<<"WARNING: "<<player->getName()
- <<" tried to access an inventory that"
- <<" belongs to "<<owner_from<<std::endl;
- delete a;
- return;
- }
-
- std::string owner_to = getInventoryOwner(ma->to_inv);
- if(owner_to != "" && owner_to != player->getName())
- {
- infostream<<"WARNING: "<<player->getName()
- <<" tried to access an inventory that"
- <<" belongs to "<<owner_to<<std::endl;
- delete a;
- return;
- }
- }*/
}
/*
Handle restrictions and special cases of the drop action
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"))
{
delete a;
return;
}
- // If player is not an admin, check for ownership
- /*else if(!checkPriv(player->getName(), "server"))
- {
- std::string owner_from = getInventoryOwner(da->from_inv);
- if(owner_from != "" && owner_from != player->getName())
- {
- infostream<<"WARNING: "<<player->getName()
- <<" tried to access an inventory that"
- <<" belongs to "<<owner_from<<std::endl;
- delete a;
- return;
- }
- }*/
}
/*
Handle restrictions and special cases of the craft action
delete a;
return;
}
-
- // If player is not an admin, check for ownership of inventory
- /*if(!checkPriv(player->getName(), "server"))
- {
- std::string owner_craft = getInventoryOwner(ca->craft_inv);
- if(owner_craft != "" && owner_craft != player->getName())
- {
- infostream<<"WARNING: "<<player->getName()
- <<" tried to access an inventory that"
- <<" belongs to "<<owner_craft<<std::endl;
- delete a;
- return;
- }
- }*/
}
-
+
// Do the action
a->apply(this, playersao, this);
// Eat the action
u8 buf[6];
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
-
+
// Read stuff
is.read((char*)buf, 2);
u16 len = readU16(buf);
-
+
std::wstring message;
for(u16 i=0; i<len; i++)
{
message += (wchar_t)readU16(buf);
}
+ // If something goes wrong, this player is to blame
+ RollbackScopeActor rollback_scope(m_rollback,
+ std::string("player:")+player->getName());
+
// Get player name of this client
std::wstring name = narrow_to_wide(player->getName());
-
+
// Run script hook
- bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
+ bool ate = m_script->on_chat_message(player->getName(),
wide_to_narrow(message));
// If script ate the message, don't proceed
if(ate)
return;
-
+
// 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;
-
- // Parse commands
+
+ // Commands are implemented in Lua, so only catch invalid
+ // commands that were not "eaten" and send an error back
if(message[0] == L'/')
{
- size_t strip_size = 1;
- if (message[1] == L'#') // support old-style commans
- ++strip_size;
- message = message.substr(strip_size);
-
- WStrfnd f1(message);
- f1.next(L" "); // Skip over /#whatever
- std::wstring paramstring = f1.next(L"");
-
- ServerCommandContext *ctx = new ServerCommandContext(
- str_split(message, L' '),
- paramstring,
- this,
- m_env,
- player);
-
- std::wstring reply(processServerCommand(ctx));
- send_to_sender = ctx->flags & SEND_TO_SENDER;
- send_to_others = ctx->flags & SEND_TO_OTHERS;
-
- if (ctx->flags & SEND_NO_PREFIX)
- line += reply;
+ message = message.substr(1);
+ send_to_sender = true;
+ if(message.length() == 0)
+ line += L"-!- Empty command";
else
- line += L"Server: " + reply;
-
- delete ctx;
-
+ line += L"-!- Invalid command: " + str_split(message, L' ')[0];
}
else
{
send_to_sender = true;
}
}
-
+
if(line != L"")
{
if(send_to_others)
/*
Send the message to clients
*/
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ 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.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
+ RemoteClient *client = i->second;
+ assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
std::istringstream is(datastring, std::ios_base::binary);
u8 damage = readU8(is);
- actionstream<<player->getName()<<" damaged by "
- <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
- <<std::endl;
+ 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);
+ playersao->setHP(playersao->getHP() - damage);
- if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
- DiePlayer(peer_id);
+ if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
+ DiePlayer(peer_id);
- if(playersao->m_hp_not_sent)
- SendPlayerHP(peer_id);
+ if(playersao->m_hp_not_sent)
+ SendPlayerHP(peer_id);
+ }
}
- else if(command == TOSERVER_PASSWORD)
+ 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);
+ }
+ else if(command == TOSERVER_PASSWORD)
{
/*
[0] u16 TOSERVER_PASSWORD
std::string playername = player->getName();
std::string checkpwd;
- scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
+ m_script->getAuth(playername, &checkpwd, NULL);
if(oldpwd != checkpwd)
{
return;
}
- bool success = scriptapi_set_password(m_lua, playername, newpwd);
+ bool success = m_script->setPassword(playername, newpwd);
if(success){
actionstream<<player->getName()<<" changes password"<<std::endl;
SendChatMessage(peer_id, L"Password change successful.");
}
else if(command == TOSERVER_RESPAWN)
{
- if(player->hp != 0)
+ if(player->hp != 0 || !g_settings->getBool("enable_damage"))
return;
-
+
RespawnPlayer(peer_id);
-
+
actionstream<<player->getName()<<" respawns at "
<<PP(player->getPosition()/BS)<<std::endl;
else if(command == TOSERVER_REQUEST_MEDIA) {
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
-
- core::list<MediaRequest> tosend;
+
+ std::list<MediaRequest> tosend;
u16 numfiles = readU16(is);
infostream<<"Sending "<<numfiles<<" files to "
// (definitions and files)
getClient(peer_id)->definitions_sent = true;
}
+ else if(command == TOSERVER_RECEIVED_MEDIA) {
+ getClient(peer_id)->definitions_sent = true;
+ }
else if(command == TOSERVER_INTERACT)
{
std::string datastring((char*)&data[2], datasize-2);
return;
}
+ /*
+ If something goes wrong, this player is to blame
+ */
+ RollbackScopeActor rollback_scope(m_rollback,
+ std::string("player:")+player->getName());
+
/*
0: start digging or punch object
*/
infostream<<"Server: Not punching: Node not found."
<<" Adding block to emerge queue."
<<std::endl;
- m_emerge_queue.addBlock(peer_id,
- getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
+ m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
}
if(n.getContent() != CONTENT_IGNORE)
- scriptapi_node_on_punch(m_lua, p_under, n, playersao);
+ m_script->node_on_punch(p_under, n, playersao);
+ // Cheat prevention
+ playersao->noCheatDigStart(p_under);
}
else if(pointed.type == POINTEDTHING_OBJECT)
{
*/
else if(action == 2)
{
- // Only complete digging of nodes
+ // Only digging of nodes
if(pointed.type == POINTEDTHING_NODE)
{
MapNode n(CONTENT_IGNORE);
infostream<<"Server: Not finishing digging: Node not found."
<<" Adding block to emerge queue."
<<std::endl;
- m_emerge_queue.addBlock(peer_id,
- getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
+ m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
}
- if(n.getContent() != CONTENT_IGNORE)
- scriptapi_node_on_dig(m_lua, p_under, n, playersao);
- if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
+ /* 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;
+ }
+ // 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;
+ }
+ // If time is considerably too short, ignore dig
+ // Check time only for medium and slow timed digs
+ if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
+ infostream<<"Server: NoCheat: "<<player->getName()
+ <<" completed digging "
+ <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
+ <<params.time<<"s; not digging."<<std::endl;
+ is_valid_dig = false;
+ }
+ }
+
+ /* 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);
}
}
} // action == 2
-
+
/*
3: place block or right-click object
*/
// Do stuff
pointed_object->rightClick(playersao);
}
- else if(scriptapi_item_on_place(m_lua,
+ else if(m_script->item_OnPlace(
item, playersao, pointed))
{
// Placement was handled in lua
// Apply returned ItemStack
- if(g_settings->getBool("creative_mode") == false)
- playersao->setWieldedItem(item);
+ playersao->setWieldedItem(item);
}
-
- // If item has node placement prediction, always send the above
- // node to make sure the client knows what exactly happened
+
+ // If item has node placement prediction, always send the
+ // blocks to make sure the client knows what exactly happened
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);
+ }
}
} // action == 3
actionstream<<player->getName()<<" uses "<<item.name
<<", pointing at "<<pointed.dump()<<std::endl;
- if(scriptapi_item_on_use(m_lua,
+ if(m_script->item_OnUse(
item, playersao, pointed))
{
// Apply returned ItemStack
- if(g_settings->getBool("creative_mode") == false)
- playersao->setWieldedItem(item);
+ playersao->setWieldedItem(item);
}
} // action == 4
+
/*
Catch invalid actions
{
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);
fields[fieldname] = fieldvalue;
}
- scriptapi_node_on_receive_fields(m_lua, p, formname, fields,
- playersao);
+ // 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);
}
else
{
infostream<<"Server::ProcessData(): Ignoring "
"unknown command "<<command<<std::endl;
}
-
+
} //try
catch(SendFailedException &e)
{
return meta->getInventory();
}
break;
+ case InventoryLocation::DETACHED:
+ {
+ if(m_detached_inventories.count(loc.name) == 0)
+ return NULL;
+ return m_detached_inventories[loc.name];
+ }
+ break;
default:
assert(0);
}
MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
if(block)
block->raiseModified(MOD_STATE_WRITE_NEEDED);
-
+
setBlockNotSent(blockpos);
}
break;
+ case InventoryLocation::DETACHED:
+ {
+ sendDetachedInventoryToAll(loc.name);
+ }
+ break;
default:
assert(0);
}
}
-core::list<PlayerInfo> Server::getPlayerInfo()
-{
- DSTACK(__FUNCTION_NAME);
- JMutexAutoLock envlock(m_env_mutex);
- JMutexAutoLock conlock(m_con_mutex);
-
- core::list<PlayerInfo> list;
-
- core::list<Player*> players = m_env->getPlayers();
-
- core::list<Player*>::Iterator i;
- for(i = players.begin();
- i != players.end(); i++)
- {
- PlayerInfo info;
-
- Player *player = *i;
-
- try{
- // Copy info from connection to info struct
- info.id = player->peer_id;
- info.address = m_con.GetPeerAddress(player->peer_id);
- info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
- }
- catch(con::PeerNotFoundException &e)
- {
- // Set dummy peer info
- info.id = 0;
- info.address = Address(0,0,0,0,0);
- info.avg_rtt = 0.0;
- }
-
- snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
- info.position = player->getPosition();
-
- list.push_back(info);
- }
-
- return list;
-}
+//std::list<PlayerInfo> Server::getPlayerInfo()
+//{
+// DSTACK(__FUNCTION_NAME);
+// JMutexAutoLock envlock(m_env_mutex);
+// JMutexAutoLock conlock(m_con_mutex);
+//
+// std::list<PlayerInfo> list;
+//
+// std::list<Player*> players = m_env->getPlayers();
+//
+// std::list<Player*>::iterator i;
+// for(i = players.begin();
+// i != players.end(); ++i)
+// {
+// PlayerInfo info;
+//
+// Player *player = *i;
+//
+// try{
+// // Copy info from connection to info struct
+// info.id = player->peer_id;
+// info.address = m_con.GetPeerAddress(player->peer_id);
+// info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
+// }
+// catch(con::PeerNotFoundException &e)
+// {
+// // Set dummy peer info
+// info.id = 0;
+// info.address = Address(0,0,0,0,0);
+// info.avg_rtt = 0.0;
+// }
+//
+// snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
+// info.position = player->getPosition();
+//
+// list.push_back(info);
+// }
+//
+// return list;
+//}
void Server::peerAdded(con::Peer *peer)
DSTACK(__FUNCTION_NAME);
verbosestream<<"Server::peerAdded(): peer->id="
<<peer->id<<std::endl;
-
+
PeerChange c;
c.type = PEER_ADDED;
c.peer_id = peer->id;
DSTACK(__FUNCTION_NAME);
verbosestream<<"Server::deletingPeer(): peer->id="
<<peer->id<<", timeout="<<timeout<<std::endl;
-
+
PeerChange c;
c.type = PEER_REMOVED;
c.peer_id = peer->id;
Static send methods
*/
+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);
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)
{
}
void Server::SendItemDef(con::Connection &con, u16 peer_id,
- IItemDefManager *itemdef)
+ IItemDefManager *itemdef, u16 protocol_version)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
*/
writeU16(os, TOCLIENT_ITEMDEF);
std::ostringstream tmp_os(std::ios::binary);
- itemdef->serialize(tmp_os);
+ itemdef->serialize(tmp_os, protocol_version);
std::ostringstream tmp_os2(std::ios::binary);
compressZlib(tmp_os.str(), tmp_os2);
os<<serializeLongString(tmp_os2.str());
}
void Server::SendNodeDef(con::Connection &con, u16 peer_id,
- INodeDefManager *nodedef)
+ INodeDefManager *nodedef, u16 protocol_version)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
*/
writeU16(os, TOCLIENT_NODEDEF);
std::ostringstream tmp_os(std::ios::binary);
- nodedef->serialize(tmp_os);
+ nodedef->serialize(tmp_os, protocol_version);
std::ostringstream tmp_os2(std::ios::binary);
compressZlib(tmp_os.str(), tmp_os2);
os<<serializeLongString(tmp_os2.str());
con.Send(peer_id, 0, data, true);
}
-/*
- Non-static send methods
-*/
-
-void Server::SendInventory(u16 peer_id)
+/*
+ Non-static send methods
+*/
+
+void Server::SendInventory(u16 peer_id)
+{
+ DSTACK(__FUNCTION_NAME);
+
+ PlayerSAO *playersao = getPlayerSAO(peer_id);
+ assert(playersao);
+
+ playersao->m_inventory_not_sent = false;
+
+ /*
+ Serialize it
+ */
+
+ 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++)
+ {
+ 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)
+{
+ std::ostringstream os(std::ios_base::binary);
+
+ // Write command
+ writeU16(os, TOCLIENT_HUDADD);
+ writeU32(os, id);
+ writeU8(os, (u8)form->type);
+ writeV2F1000(os, form->pos);
+ os << serializeString(form->name);
+ writeV2F1000(os, form->scale);
+ os << serializeString(form->text);
+ writeU32(os, form->number);
+ writeU32(os, form->item);
+ writeU32(os, form->dir);
+ writeV2F1000(os, form->align);
+ writeV2F1000(os, form->offset);
+
+ // 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)
+{
+ std::ostringstream os(std::ios_base::binary);
+
+ // Write command
+ writeU16(os, TOCLIENT_HUDRM);
+ 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)
+{
+ std::ostringstream os(std::ios_base::binary);
+
+ // Write command
+ writeU16(os, TOCLIENT_HUDCHANGE);
+ writeU32(os, id);
+ writeU8(os, (u8)stat);
+ switch (stat) {
+ case HUD_STAT_POS:
+ case HUD_STAT_SCALE:
+ case HUD_STAT_ALIGN:
+ 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;
+ }
+
+ // 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::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
{
- DSTACK(__FUNCTION_NAME);
-
- PlayerSAO *playersao = getPlayerSAO(peer_id);
- assert(playersao);
-
- playersao->m_inventory_not_sent = false;
-
- /*
- Serialize it
- */
+ std::ostringstream os(std::ios_base::binary);
- std::ostringstream os;
- playersao->getInventory()->serialize(os);
+ // 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(s.size()+2);
- writeU16(&data[0], TOCLIENT_INVENTORY);
- memcpy(&data[2], s.c_str(), s.size());
-
+ SharedBuffer<u8> data((u8 *)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)
+void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
{
- 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++)
- {
- u16 w = message[i];
- writeU16(buf, w);
- os.write((char*)buf, 2);
- }
-
+ 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());
+ 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)
{
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ 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.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
+ RemoteClient *client = i->second;
+ assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
SendHP(m_con, peer_id, playersao->getHP());
}
+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);
writeV3F1000(os, player->getPosition());
writeF1000(os, player->getPitch());
writeF1000(os, player->getYaw());
-
+
{
v3f pos = player->getPosition();
f32 pitch = player->getPitch();
return;
std::set<std::string> privs;
- scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
-
+ m_script->getAuth(player->getName(), NULL, &privs);
+
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_PRIVILEGES);
writeU16(os, privs.size());
}
else
{
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator(); i.atEnd() == false; i++)
+ for(std::map<u16, RemoteClient*>::iterator
+ i = m_clients.begin(); i != m_clients.end(); ++i)
{
- RemoteClient *client = i.getNode()->getValue();
+ RemoteClient *client = i->second;
Player *player = m_env->getPlayer(client->peer_id);
if(!player)
continue;
}
void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
- core::list<u16> *far_players, float far_d_nodes)
+ std::list<u16> *far_players, float far_d_nodes)
{
float maxd = far_d_nodes*BS;
v3f p_f = intToFloat(p, BS);
writeS16(&reply[4], p.Y);
writeS16(&reply[6], p.Z);
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ 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.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
+ 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
}
void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
- core::list<u16> *far_players, float far_d_nodes)
+ std::list<u16> *far_players, float far_d_nodes)
{
float maxd = far_d_nodes*BS;
v3f p_f = intToFloat(p, BS);
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ 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.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
+ RemoteClient *client = i->second;
+ assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
void Server::setBlockNotSent(v3s16 p)
{
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd()==false; i++)
+ for(std::map<u16, RemoteClient*>::iterator
+ i = m_clients.begin();
+ i != m_clients.end(); ++i)
{
- RemoteClient *client = i.getNode()->getValue();
+ RemoteClient *client = i->second;
client->SetBlockNotSent(p);
}
}
DSTACK(__FUNCTION_NAME);
v3s16 p = block->getPos();
-
+
#if 0
// Analyze it a bit
bool completely_air = true;
/*
Create a packet with the block in the right format
*/
-
+
std::ostringstream os(std::ios_base::binary);
block->serialize(os, ver, false);
std::string s = os.str();
/*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<": \tpacket size: "<<replysize<<std::endl;*/
-
+
/*
Send packet
*/
ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
- core::array<PrioritySortedBlockTransfer> queue;
+ std::vector<PrioritySortedBlockTransfer> queue;
s32 total_sending = 0;
-
+
{
ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ for(std::map<u16, RemoteClient*>::iterator
+ i = m_clients.begin();
+ i != m_clients.end(); ++i)
{
- RemoteClient *client = i.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
+ RemoteClient *client = i->second;
+ assert(client->peer_id == i->first);
// If definitions and textures have not been sent, don't
// send MapBlocks either
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.
- queue.sort();
+ std::sort(queue.begin(), queue.end());
for(u32 i=0; i<queue.size(); i++)
{
if(total_sending >= g_settings->getS32
("max_simultaneous_block_sends_server_total"))
break;
-
+
PrioritySortedBlockTransfer q = queue[i];
MapBlock *block = NULL;
DSTACK(__FUNCTION_NAME);
infostream<<"Server: Calculating media file checksums"<<std::endl;
-
+
// Collect all media file paths
std::list<std::string> paths;
- for(core::list<ModSpec>::Iterator i = m_mods.begin();
+ for(std::vector<ModSpec>::iterator i = m_mods.begin();
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");
}
std::string path_all = "textures";
paths.push_back(path_all + DIR_DELIM + "all");
-
+
// Collect media file information from paths into cache
for(std::list<std::string>::iterator i = paths.begin();
i != paths.end(); i++)
".png", ".jpg", ".bmp", ".tga",
".pcx", ".ppm", ".psd", ".wal", ".rgb",
".ogg",
+ ".x", ".b3d", ".md2", ".obj",
NULL
};
if(removeStringEnd(filename, supported_ext) == ""){
verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
<<std::endl;
- core::list<SendableMediaAnnouncement> file_announcements;
+ std::list<SendableMediaAnnouncement> file_announcements;
for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
i != m_media.end(); i++){
string sha1_digest
}
*/
-
+
writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
writeU16(os, file_announcements.size());
- for(core::list<SendableMediaAnnouncement>::Iterator
+ for(std::list<SendableMediaAnnouncement>::iterator
j = file_announcements.begin();
- j != file_announcements.end(); j++){
+ 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();
// Send as reliable
m_con.Send(peer_id, 0, data, true);
-
}
struct SendableMedia
};
void Server::sendRequestedMedia(u16 peer_id,
- const core::list<MediaRequest> &tosend)
+ const std::list<MediaRequest> &tosend)
{
DSTACK(__FUNCTION_NAME);
// Put 5kB in one bunch (this is not accurate)
u32 bytes_per_bunch = 5000;
- core::array< core::list<SendableMedia> > file_bunches;
- file_bunches.push_back(core::list<SendableMedia>());
+ std::vector< std::list<SendableMedia> > file_bunches;
+ file_bunches.push_back(std::list<SendableMedia>());
u32 file_size_bunch_total = 0;
- for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
- i != tosend.end(); i++)
+ for(std::list<MediaRequest>::const_iterator i = tosend.begin();
+ i != tosend.end(); ++i)
{
if(m_media.find(i->name) == m_media.end()){
errorstream<<"Server::sendRequestedMedia(): Client asked for "
// Start next bunch if got enough data
if(file_size_bunch_total >= bytes_per_bunch){
- file_bunches.push_back(core::list<SendableMedia>());
+ file_bunches.push_back(std::list<SendableMedia>());
file_size_bunch_total = 0;
}
writeU16(os, i);
writeU32(os, file_bunches[i].size());
- for(core::list<SendableMedia>::Iterator
+ for(std::list<SendableMedia>::iterator
j = file_bunches[i].begin();
- j != file_bunches[i].end(); j++){
+ j != file_bunches[i].end(); ++j){
os<<serializeString(j->name);
os<<serializeLongString(j->data);
}
}
}
+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);
+
+ for(std::map<u16, RemoteClient*>::iterator
+ i = m_clients.begin();
+ i != m_clients.end(); ++i){
+ RemoteClient *client = i->second;
+ 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)
{
DSTACK(__FUNCTION_NAME);
-
+
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
playersao->setHP(0);
// Trigger scripted stuff
- scriptapi_on_dieplayer(m_lua, playersao);
+ m_script->on_dieplayer(playersao);
SendPlayerHP(peer_id);
SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
playersao->setHP(PLAYER_MAX_HP);
- bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
+ bool repositioned = m_script->on_respawnplayer(playersao);
if(!repositioned){
v3f pos = findSpawnPos(m_env->getServerMap());
playersao->setPos(pos);
void Server::UpdateCrafting(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
-
+
Player* player = m_env->getPlayer(peer_id);
assert(player);
// Get a preview for crafting
ItemStack preview;
- // No crafting in creative mode
- if(g_settings->getBool("creative_mode") == false)
- getCraftingResult(&player->inventory, preview, false, this);
+ getCraftingResult(&player->inventory, preview, false, this);
// Put the new preview in
InventoryList *plist = player->inventory.getList("craftpreview");
{
DSTACK(__FUNCTION_NAME);
//JMutexAutoLock lock(m_con_mutex);
- core::map<u16, RemoteClient*>::Node *n;
+ std::map<u16, RemoteClient*>::iterator n;
n = m_clients.find(peer_id);
// A client should exist for all peers
- assert(n != NULL);
- return n->getValue();
+ assert(n != m_clients.end());
+ return n->second;
}
std::wstring Server::getStatusString()
// Uptime
os<<L", uptime="<<m_uptime.get();
// Information about clients
+ std::map<u16, RemoteClient*>::iterator i;
+ bool first;
os<<L", clients={";
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ for(i = m_clients.begin(), first = true;
+ i != m_clients.end(); ++i)
{
// Get client and check that it is valid
- RemoteClient *client = i.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
+ RemoteClient *client = i->second;
+ assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
// Get player
if(player != NULL)
name = narrow_to_wide(player->getName());
// Add name to information string
- os<<name<<L",";
+ if(!first)
+ os<<L",";
+ else
+ first = false;
+ os<<name;
}
os<<L"}";
if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
{
std::set<std::string> privs;
- scriptapi_get_auth(m_lua, name, NULL, &privs);
+ m_script->getAuth(name, NULL, &privs);
return privs;
}
void Server::reportPrivsModified(const std::string &name)
{
if(name == ""){
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++){
- RemoteClient *client = i.getNode()->getValue();
+ 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());
}
g_settings->updateConfigFile(m_path_config.c_str());
}
-void Server::notifyPlayer(const char *name, const std::wstring msg)
+void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true)
{
Player *player = m_env->getPlayer(name);
if(!player)
return;
- SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
+ 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;
+
+ u32 id = hud_get_free_id(player);
+ if (id < player->hud.size())
+ player->hud[id] = form;
+ else
+ player->hud.push_back(form);
+
+ SendHUDAdd(player->peer_id, id, form);
+ return id;
+}
+
+bool Server::hudRemove(Player *player, u32 id) {
+ if (!player || id >= player->hud.size() || !player->hud[id])
+ return false;
+
+ delete player->hud[id];
+ player->hud[id] = NULL;
+
+ SendHUDRemove(player->peer_id, id);
+ return true;
+}
+
+bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
+ if (!player)
+ 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::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);
+}
+
void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
{
- u8 flags = 0;
- if(!allow_generate)
- flags |= BLOCK_EMERGE_FLAG_FROMDISK;
- m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
+ m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate);
+}
+
+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;
}
// IGameDef interface
{
return NULL;
}
+IShaderSource* Server::getShaderSource()
+{
+ return NULL;
+}
u16 Server::allocateUnknownNodeId(const std::string &name)
{
return m_nodedef->allocateDummy(name);
{
return m_event;
}
+IRollbackReportSink* Server::getRollbackReportSink()
+{
+ if(!m_enable_rollback_recording)
+ return NULL;
+ if(!m_rollback_sink_enabled)
+ return NULL;
+ return m_rollback;
+}
IWritableItemDefManager* Server::getWritableItemDefManager()
{
const ModSpec* Server::getModSpec(const std::string &modname)
{
- for(core::list<ModSpec>::Iterator i = m_mods.begin();
+ for(std::vector<ModSpec>::iterator i = m_mods.begin();
i != m_mods.end(); i++){
const ModSpec &mod = *i;
if(mod.name == modname)
}
return NULL;
}
+void Server::getModNames(std::list<std::string> &modlist)
+{
+ for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
+ {
+ modlist.push_back(i->name);
+ }
+}
std::string Server::getBuiltinLuaPath()
{
return porting::path_share + DIR_DELIM + "builtin";
//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
- v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
- -range + (myrand()%(range*2)));
- //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
- // Get ground height at point (fallbacks to heightmap function)
+ v2s16 nodepos2d = v2s16(
+ -range + (myrand() % (range * 2)),
+ -range + (myrand() % (range * 2)));
+
+ // Get ground height at point
s16 groundheight = map.findGroundLevel(nodepos2d);
- // Don't go underwater
- if(groundheight < WATER_LEVEL)
- {
- //infostream<<"-> Underwater"<<std::endl;
+ if (groundheight <= water_level) // Don't go underwater
continue;
- }
- // Don't go to high places
- if(groundheight > WATER_LEVEL + 4)
- {
- //infostream<<"-> Underwater"<<std::endl;
+ if (groundheight > water_level + 6) // Don't go to high places
continue;
- }
-
- nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
+
+ nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
bool is_good = false;
s32 air_count = 0;
- for(s32 i=0; i<10; i++){
+ for (s32 i = 0; i < 10; i++) {
v3s16 blockpos = getNodeBlockPos(nodepos);
map.emergeBlock(blockpos, true);
- MapNode n = map.getNodeNoEx(nodepos);
- if(n.getContent() == CONTENT_AIR){
+ content_t c = map.getNodeNoEx(nodepos).getContent();
+ if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
air_count++;
- if(air_count >= 2){
+ if (air_count >= 2){
is_good = true;
- nodepos.Y -= 1;
break;
}
}
}
}
#endif
-
+
return intToFloat(nodepos, BS);
}
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)
- scriptapi_on_newplayer(m_lua, playersao);
-
- scriptapi_on_joinplayer(m_lua, playersao);
+ m_script->on_newplayer(playersao);
- /* Creative mode */
- if(g_settings->getBool("creative_mode"))
- playersao->createCreativeInventory();
+ m_script->on_joinplayer(playersao);
return playersao;
}
{
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
-
+
if(c.type == PEER_ADDED)
{
/*
*/
// Error check
- core::map<u16, RemoteClient*>::Node *n;
+ std::map<u16, RemoteClient*>::iterator n;
n = m_clients.find(c.peer_id);
// The client shouldn't already exist
- assert(n == NULL);
+ assert(n == m_clients.end());
// Create client
RemoteClient *client = new RemoteClient();
client->peer_id = c.peer_id;
- m_clients.insert(client->peer_id, client);
+ m_clients[client->peer_id] = client;
} // PEER_ADDED
else if(c.type == PEER_REMOVED)
*/
// Error check
- core::map<u16, RemoteClient*>::Node *n;
+ std::map<u16, RemoteClient*>::iterator n;
n = m_clients.find(c.peer_id);
// The client should exist
- assert(n != NULL);
-
+ assert(n != m_clients.end());
+
/*
Mark objects to be not known by the client
*/
- RemoteClient *client = n->getValue();
+ RemoteClient *client = n->second;
// Handle objects
- for(core::map<u16, bool>::Iterator
- i = client->m_known_objects.getIterator();
- i.atEnd()==false; i++)
+ for(std::set<u16>::iterator
+ i = client->m_known_objects.begin();
+ i != client->m_known_objects.end(); ++i)
{
// Get object
- u16 id = i.getNode()->getKey();
+ u16 id = *i;
ServerActiveObject* obj = m_env->getActiveObject(id);
-
+
if(obj && obj->m_known_by_count > 0)
obj->m_known_by_count--;
}
message += L" (timed out)";
}
}
-
+
/* Run scripts and remove from environment */
{
if(player != NULL)
PlayerSAO *playersao = player->getPlayerSAO();
assert(playersao);
- scriptapi_on_leaveplayer(m_lua, playersao);
+ m_script->on_leaveplayer(playersao);
playersao->disconnected();
}
if(player != NULL)
{
std::ostringstream os(std::ios_base::binary);
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
+ for(std::map<u16, RemoteClient*>::iterator
+ i = m_clients.begin();
+ i != m_clients.end(); ++i)
{
- RemoteClient *client = i.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
+ RemoteClient *client = i->second;
+ assert(client->peer_id == i->first);
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
// Get player
<<os.str()<<std::endl;
}
}
-
+
// Delete client
delete m_clients[c.peer_id];
- m_clients.remove(c.peer_id);
+ m_clients.erase(c.peer_id);
// Send player info to all remaining clients
//SendPlayerInfos();
-
+
// Send leave chat message to all remaining clients
if(message.length() != 0)
BroadcastChatMessage(message);
-
+
} // PEER_REMOVED
else
{
void dedicated_server_loop(Server &server, bool &kill)
{
DSTACK(__FUNCTION_NAME);
-
+
verbosestream<<"dedicated_server_loop()"<<std::endl;
IntervalLimiter m_profiler_interval;
if(server.getShutdownRequested() || kill)
{
infostream<<"Dedicated server quitting"<<std::endl;
+#if USE_CURL
+ if(g_settings->getBool("server_announce") == true)
+ ServerList::sendAnnounce("delete");
+#endif
break;
}