X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fserver.cpp;h=3e22a023aded7a77cd05e7b395dfbda301fad514;hb=11716825fcf219349a7a3952e75688d167318714;hp=d3627a93f4317ff6475e110e71e24906ff8ae0e9;hpb=5ac900ddd9d574440b499b15516747a374fe3a5a;p=dragonfireclient.git diff --git a/src/server.cpp b/src/server.cpp index d3627a93f..2facb1469 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "utility.h" #include +#include #include "clientserver.h" #include "map.h" #include "jmutexautolock.h" @@ -27,20 +28,62 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "constants.h" #include "voxel.h" #include "materials.h" -#include "mineral.h" #include "config.h" #include "servercommand.h" #include "filesys.h" #include "content_mapnode.h" -#include "content_craft.h" #include "content_nodemeta.h" +#include "mapblock.h" +#include "serverobject.h" +#include "settings.h" +#include "profiler.h" +#include "log.h" +#include "script.h" +#include "scriptapi.h" +#include "nodedef.h" +#include "itemdef.h" +#include "craftdef.h" +#include "mapgen.h" +#include "content_abm.h" +#include "mods.h" +#include "sha1.h" +#include "base64.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; +}; + void * ServerThread::Thread() { ThreadStarted(); + log_register_thread("ServerThread"); + DSTACK(__FUNCTION_NAME); BEGIN_DEBUG_EXCEPTION_HANDLER @@ -55,7 +98,7 @@ void * ServerThread::Thread() m_server->AsyncRunStep(); } - //dout_server<<"Running m_server->Receive()"<Receive()"<Receive(); } catch(con::NoIncomingDataException &e) @@ -63,11 +106,11 @@ void * ServerThread::Thread() } catch(con::PeerNotFoundException &e) { - dout_server<<"Server: PeerNotFoundException"<getBool("enable_mapgen_debug_info"); + /* Get block info from queue, emerge them and send them to clients. @@ -102,15 +147,10 @@ void * EmergeThread::Thread() /* Do not generate over-limit */ - if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) + if(blockpos_over_limit(p)) continue; - //derr_server<<"EmergeThread::Thread(): running"<::Iterator i; @@ -139,134 +179,123 @@ void * EmergeThread::Thread() // Check flags u8 flags = i.getNode()->getValue(); if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false) - optional = false; + only_from_disk = false; } } - - /*dstream<<"EmergeThread: p=" - <<"("<getLightingExpired()){ - lighting_invalidated_blocks[block->getPos()] = block; - } + + // 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"<m_env_mutex); - - if(got_block) + /* + If generator was initialized, generate now when envlock is free. + */ + if(started_generate) { - /* - Collect a list of blocks that have been modified in - addition to the fetched one. - */ - - if(lighting_invalidated_blocks.size() > 0) { - /*dstream<<"lighting "<m_env_mutex); - // Update lighting without locking the environment mutex, - // add modified blocks to changed blocks - map.updateLighting(lighting_invalidated_blocks, modified_blocks); - } + 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); - // Add all from changed_blocks to modified_blocks - for(core::map::Iterator i = changed_blocks.getIterator(); - i.atEnd() == false; i++) - { - MapBlock *block = i.getNode()->getValue(); - modified_blocks.insert(block->getPos(), block); - } - } - // If we got no block, there should be no invalidated blocks - else - { - assert(lighting_invalidated_blocks.size() == 0); - } + // 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; - }//envlock + /* + Do some post-generate stuff + */ + + v3s16 minp = block->getPos()*MAP_BLOCKSIZE; + v3s16 maxp = minp + v3s16(1,1,1)*(MAP_BLOCKSIZE-1); + scriptapi_environment_on_generated(m_server->m_lua, + minp, maxp); + + if(enable_mapgen_debug_info) + infostream<<"EmergeThread: ended up with: " + <m_ignore_map_edit_events); + + // 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 */ @@ -301,7 +330,9 @@ void * EmergeThread::Thread() } - END_DEBUG_EXCEPTION_HANDLER + END_DEBUG_EXCEPTION_HANDLER(errorstream) + + log_deregister_thread(); return NULL; } @@ -316,25 +347,24 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, // Increment timers m_nothing_to_send_pause_timer -= dtime; + m_nearest_unsent_reset_timer += dtime; if(m_nothing_to_send_pause_timer >= 0) { - // Keep this reset - m_nearest_unsent_reset_timer = 0; return; } // Won't send anything if already sending - if(m_blocks_sending.size() >= g_settings.getU16 + if(m_blocks_sending.size() >= g_settings->getU16 ("max_simultaneous_block_sends_per_client")) { - //dstream<<"Not sending any blocks, Queue full."<m_env.getPlayer(peer_id); + Player *player = server->m_env->getPlayer(peer_id); assert(player != NULL); @@ -351,13 +381,12 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, v3s16 center = getNodeBlockPos(center_nodepos); // Camera position and direction - v3f camera_pos = - playerpos + v3f(0, BS+BS/2, 0); + v3f camera_pos = player->getEyePosition(); v3f camera_dir = v3f(0,0,1); camera_dir.rotateYZBy(player->getPitch()); camera_dir.rotateXZBy(player->getYaw()); - /*dstream<<"camera_dir=("<getPlayerName(peer_id)<getPlayerName(peer_id)<getFloat( "full_block_send_enable_min_time_from_building")) { max_simul_sends_usually @@ -422,27 +447,29 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, */ s32 new_nearest_unsent_d = -1; - s16 d_max = g_settings.getS16("max_block_send_distance"); - s16 d_max_gen = g_settings.getS16("max_block_generate_distance"); + 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 - if(d_max > d_start+1) - d_max = d_start+1; + 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;*/ - //dstream<<"Starting from "<getPlayerName(peer_id)<m_emerge_queue.addBlock(peer_id, p, flags); server->m_emergethread.trigger(); + + if(nearest_emerged_d == -1) + nearest_emerged_d = d; + } else { + if(nearest_emergefull_d == -1) + nearest_emergefull_d = d; } // get next one. continue; } + if(nearest_sent_d == -1) + nearest_sent_d = d; + /* Add block to send queue */ + /*errorstream<<"sending from d="<getPlayerName(peer_id)< g_settings->getS16("max_block_send_distance")){ + new_nearest_unsent_d = 0; + m_nothing_to_send_pause_timer = 2.0; + /*infostream<<"GetNextBlocks(): d wrapped around for " + <getPlayerName(peer_id) + <<"; setting to 0 and pausing"<= - g_settings.getS16("max_block_send_distance")) - { - // Pause time in seconds - m_nothing_to_send_pause_timer = 1.0; - /*dstream<<"nothing to send to " - <getPlayerName(peer_id) - <<" (d="< &stepped_blocks - ) -{ - DSTACK(__FUNCTION_NAME); - - // Can't send anything without knowing version - if(serialization_version == SER_FMT_VER_INVALID) - { - dstream<<"RemoteClient::SendObjectData(): Not sending, no version." - < players = server->m_env.getPlayers(true); - - // Write player count - u16 playercount = players.size(); - writeU16(buf, playercount); - os.write((char*)buf, 2); - - core::list::Iterator i; - for(i = players.begin(); - i != players.end(); i++) - { - Player *player = *i; - - v3f pf = player->getPosition(); - v3f sf = player->getSpeed(); - - v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100); - v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100); - s32 pitch_i (player->getPitch() * 100); - s32 yaw_i (player->getYaw() * 100); - - writeU16(buf, player->peer_id); - os.write((char*)buf, 2); - writeV3S32(buf, position_i); - os.write((char*)buf, 12); - writeV3S32(buf, speed_i); - os.write((char*)buf, 12); - writeS32(buf, pitch_i); - os.write((char*)buf, 4); - writeS32(buf, yaw_i); - os.write((char*)buf, 4); - } - - /* - Get and write object data - */ - - /* - Get nearby blocks. - - For making players to be able to build to their nearby - environment (building is not possible on blocks that are not - in memory): - - Set blocks changed - - Add blocks to emerge queue if they are not found - - SUGGESTION: These could be ignored from the backside of the player - */ - - Player *player = server->m_env.getPlayer(peer_id); - - assert(player); - - v3f playerpos = player->getPosition(); - v3f playerspeed = player->getSpeed(); - - v3s16 center_nodepos = floatToInt(playerpos, BS); - v3s16 center = getNodeBlockPos(center_nodepos); - - s16 d_max = g_settings.getS16("active_object_range"); - - // Number of blocks whose objects were written to bos - u16 blockcount = 0; - - std::ostringstream bos(std::ios_base::binary); - - for(s16 d = 0; d <= d_max; d++) - { - core::list list; - getFacePositions(list, d); - - core::list::Iterator li; - for(li=list.begin(); li!=list.end(); li++) - { - v3s16 p = *li + center; - - /* - Ignore blocks that haven't been sent to the client - */ - { - if(m_blocks_sent.find(p) == NULL) - continue; - } - - // Try stepping block and add it to a send queue - try - { - - // Get block - MapBlock *block = server->m_env.getMap().getBlockNoCreate(p); - - /* - Step block if not in stepped_blocks and add to stepped_blocks. - */ - if(stepped_blocks.find(p) == NULL) - { - block->stepObjects(dtime, true, server->m_env.getDayNightRatio()); - stepped_blocks.insert(p, true); - block->setChangedFlag(); - } - - // Skip block if there are no objects - if(block->getObjectCount() == 0) - continue; - - /* - Write objects - */ - - // Write blockpos - writeV3S16(buf, p); - bos.write((char*)buf, 6); - - // Write objects - //block->serializeObjects(bos, serialization_version); // DEPRECATED - // count=0 - writeU16(bos, 0); - - blockcount++; - - /* - Stop collecting objects if data is already too big - */ - // Sum of player and object data sizes - s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp(); - // break out if data too big - if(sum > MAX_OBJECTDATA_SIZE) - { - goto skip_subsequent; - } - - } //try - catch(InvalidPositionException &e) - { - // Not in memory - // Add it to the emerge queue and trigger the thread. - // Fetch the block only if it is on disk. - - // Grab and increment counter - /*SharedPtr lock - (m_num_blocks_in_emerge_queue.getLock()); - m_num_blocks_in_emerge_queue.m_value++;*/ - - // Add to queue as an anonymous fetch from disk - u8 flags = BLOCK_EMERGE_FLAG_FROMDISK; - server->m_emerge_queue.addBlock(0, p, flags); - server->m_emergethread.trigger(); - } - } - } - -skip_subsequent: - - // Write block count - writeU16(buf, blockcount); - os.write((char*)buf, 2); - - // Write block objects - os< data((u8*)s.c_str(), s.size()); - // Send as unreliable - server->m_con.Send(peer_id, 0, data, false); + infostream<<"GetNextBlocks duration: "< &l) */ Server::Server( - std::string mapsavedir + std::string mapsavedir, + std::string configpath ): - m_env(new ServerMap(mapsavedir), this), + m_env(NULL), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), - m_authmanager(mapsavedir+"/auth.txt"), + m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"), + m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"), + m_lua(NULL), + m_itemdef(createItemDefManager()), + m_nodedef(createNodeDefManager()), + m_craftdef(createCraftDefManager()), m_thread(this), m_emergethread(this), m_time_counter(0), m_time_of_day_send_timer(0), m_uptime(0), m_mapsavedir(mapsavedir), + m_configpath(configpath), m_shutdown_requested(false), m_ignore_map_edit_events(false), m_ignore_map_edit_events_peer_id(0) @@ -1046,25 +875,96 @@ Server::Server( m_con_mutex.Init(); m_step_dtime_mutex.Init(); m_step_dtime = 0.0; + + JMutexAutoLock envlock(m_env_mutex); + JMutexAutoLock conlock(m_con_mutex); + + // Path to builtin.lua + std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua"; + + // Add default global mod search path + m_modspaths.push_front(porting::path_data + DIR_DELIM + "mods"); + // Add world mod search path + m_modspaths.push_front(mapsavedir + DIR_DELIM + "worldmods"); + // Add user mod search path + m_modspaths.push_front(porting::path_userdata + DIR_DELIM + "usermods"); + + // Print out mod search paths + infostream<<"Mod search paths:"<::Iterator i = m_modspaths.begin(); + i != m_modspaths.end(); i++){ + std::string modspath = *i; + infostream<<" "<::Iterator i = m_mods.begin(); + i != m_mods.end(); i++){ + const ModSpec &mod = *i; + infostream<<"Server: Loading mod \""<updateAliases(m_itemdef); + + // Initialize Environment + + m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, + this, this); + + // Give environment reference to scripting api + scriptapi_add_environment(m_lua, m_env); // Register us to receive map edit events - m_env.getMap().addEventReceiver(this); + m_env->getMap().addEventReceiver(this); // If file exists, load environment metadata - if(fs::PathExists(m_mapsavedir+"/env_meta.txt")) + if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt")) { - dstream<<"Server: Loading environment metadata"<loadMeta(m_mapsavedir); } // Load players - dstream<<"Server: Loading players"<deSerializePlayers(m_mapsavedir); -Server::~Server() + /* + Add some test ActiveBlockModifiers to environment + */ + add_legacy_abms(m_env, m_nodedef); +} + +Server::~Server() { - dstream<<"Server::~Server()"<serializePlayers(m_mapsavedir); - /* - Save environment metadata - */ - dstream<<"Server: Saving environment metadata"<saveMeta(m_mapsavedir); + } + /* Stop threads */ @@ -1127,13 +1031,24 @@ Server::~Server() { u16 peer_id = i.getNode()->getKey(); JMutexAutoLock envlock(m_env_mutex); - m_env.removePlayer(peer_id); + m_env->removePlayer(peer_id); }*/ // Delete client delete i.getNode()->getValue(); } } + + // Delete Environment + delete m_env; + + delete m_itemdef; + delete m_nodedef; + delete m_craftdef; + + // Deinitialize scripting + infostream<<"Server: Deinitializing scripting"<add("Server::AsyncRunStep (num)", 1); + float dtime; { JMutexAutoLock lock1(m_step_dtime_mutex); @@ -1189,17 +1108,17 @@ void Server::AsyncRunStep() } { - ScopeProfiler sp(&g_profiler, "Server: selecting and sending " - "blocks to clients"); // Send blocks to clients SendBlocks(dtime); } if(dtime < 0.001) return; + + g_profiler->add("Server::AsyncRunStep with dtime (num)", 1); - //dstream<<"Server steps "<getFloat("time_speed") * 24000./(24.*3600); u32 units = (u32)(m_time_counter*speed); m_time_counter -= (f32)units / speed; - m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000); + m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000); - //dstream<<"Server: m_time_of_day = "<getFloat("time_send_interval"); //JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); @@ -1245,10 +1177,10 @@ void Server::AsyncRunStep() i.atEnd() == false; i++) { RemoteClient *client = i.getNode()->getValue(); - //Player *player = m_env.getPlayer(client->peer_id); + //Player *player = m_env->getPlayer(client->peer_id); SharedBuffer data = makePacket_TOCLIENT_TIME_OF_DAY( - m_env.getTimeOfDay()); + m_env->getTimeOfDay()); // Send as reliable m_con.Send(client->peer_id, 0, data, true); } @@ -1256,34 +1188,113 @@ void Server::AsyncRunStep() } { - // 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 - ScopeProfiler sp(&g_profiler, "Server: peer change handling"); - handlePeerChanges(); + JMutexAutoLock lock(m_env_mutex); + // Step environment + ScopeProfiler sp(g_profiler, "SEnv step"); + 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)) { - // Step environment - // This also runs Map's timers JMutexAutoLock lock(m_env_mutex); - ScopeProfiler sp(&g_profiler, "Server: environment step"); - m_env.step(dtime); + // Run Map's timers and unload unused data + ScopeProfiler sp(g_profiler, "Server: map timer and unload"); + m_env->getMap().timerUpdate(map_timer_and_unload_dtime, + g_settings->getFloat("server_unload_unused_data_timeout")); } /* Do background stuff */ - + /* - Transform liquids + Handle players */ + { + JMutexAutoLock lock(m_env_mutex); + JMutexAutoLock lock2(m_con_mutex); + + ScopeProfiler sp(g_profiler, "Server: handle players"); + + //float player_max_speed = BS * 4.0; // Normal speed + float player_max_speed = BS * 20; // Fast speed + float player_max_speed_up = BS * 20; + + player_max_speed *= 2.5; // Tolerance + player_max_speed_up *= 2.5; + + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + ServerRemotePlayer *player = + static_cast + (m_env->getPlayer(client->peer_id)); + if(player==NULL) + continue; + + /* + Check player movements + + NOTE: Actually the server should handle player physics like the + client does and compare player's position to what is calculated + on our side. This is required when eg. players fly due to an + explosion. + */ + player->m_last_good_position_age += dtime; + if(player->m_last_good_position_age >= 2.0){ + float age = player->m_last_good_position_age; + v3f diff = (player->getPosition() - player->m_last_good_position); + float d_vert = diff.Y; + diff.Y = 0; + float d_horiz = diff.getLength(); + /*infostream<getName()<<"'s horizontal speed is " + <<(d_horiz/age)<m_last_good_position = player->getPosition(); + } else { + actionstream<<"Player "<getName() + <<" moved too fast; resetting position" + <setPosition(player->m_last_good_position); + SendMovePlayer(player); + } + player->m_last_good_position_age = 0; + } + + /* + Handle player HPs (die if hp=0) + */ + if(player->hp == 0 && player->m_hp_not_sent) + DiePlayer(player); + + /* + Send player inventories and HPs if necessary + */ + if(player->m_inventory_not_sent){ + UpdateCrafting(player->peer_id); + SendInventory(player->peer_id); + } + if(player->m_hp_not_sent){ + SendPlayerHP(player); + } + + /* + Add to environment + */ + if(!player->m_is_in_environment){ + player->m_removed = false; + player->setId(0); + m_env->addActiveObject(player); + } + } + } + + /* Transform liquids */ m_liquid_transform_timer += dtime; if(m_liquid_transform_timer >= 1.00) { @@ -1291,16 +1302,16 @@ void Server::AsyncRunStep() JMutexAutoLock lock(m_env_mutex); - ScopeProfiler sp(&g_profiler, "Server: liquid transform"); + ScopeProfiler sp(g_profiler, "Server: liquid transform"); core::map modified_blocks; - m_env.getMap().transformLiquids(modified_blocks); + m_env->getMap().transformLiquids(modified_blocks); #if 0 /* Update lighting */ core::map lighting_modified_blocks; - ServerMap &map = ((ServerMap&)m_env.getMap()); + ServerMap &map = ((ServerMap&)m_env->getMap()); map.updateLighting(modified_blocks, lighting_modified_blocks); // Add blocks modified by lighting to modified_blocks @@ -1341,48 +1352,57 @@ void Server::AsyncRunStep() counter = 0.0; JMutexAutoLock lock2(m_con_mutex); - + + if(m_clients.size() != 0) + infostream<<"Players:"<::Iterator i = m_clients.getIterator(); i.atEnd() == false; i++) { //u16 peer_id = i.getNode()->getKey(); RemoteClient *client = i.getNode()->getValue(); - Player *player = m_env.getPlayer(client->peer_id); + Player *player = m_env->getPlayer(client->peer_id); if(player==NULL) continue; - std::cout<getName()<<"\t"; - client->PrintInfo(std::cout); + infostream<<"* "<getName()<<"\t"; + client->PrintInfo(infostream); } } } - //if(g_settings.getBool("enable_experimental")) + //if(g_settings->getBool("enable_experimental")) { /* Check added and deleted active objects */ { - //dstream<<"Server: Checking added and deleted active objects"<getS16("active_object_send_range_blocks"); + radius *= MAP_BLOCKSIZE; for(core::map::Iterator i = m_clients.getIterator(); i.atEnd() == false; i++) { RemoteClient *client = i.getNode()->getValue(); - Player *player = m_env.getPlayer(client->peer_id); + + // If definitions and textures have not been sent, don't + // send objects either + if(!client->definitions_sent) + continue; + + Player *player = m_env->getPlayer(client->peer_id); if(player==NULL) { // This can happen if the client timeouts somehow - /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client " + /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client " <peer_id <<" has no associated player"< removed_objects; core::map added_objects; - m_env.getRemovedActiveObjects(pos, radius, + m_env->getRemovedActiveObjects(pos, radius, client->m_known_objects, removed_objects); - m_env.getAddedActiveObjects(pos, radius, + m_env->getAddedActiveObjects(pos, radius, client->m_known_objects, added_objects); // Ignore if nothing happened if(removed_objects.size() == 0 && added_objects.size() == 0) { - //dstream<<"INFO: active objects: none changed"<getKey(); - ServerActiveObject* obj = m_env.getActiveObject(id); + ServerActiveObject* obj = m_env->getActiveObject(id); // Add to data buffer for sending writeU16((u8*)buf, i.getNode()->getKey()); @@ -1438,12 +1458,12 @@ void Server::AsyncRunStep() { // Get object u16 id = i.getNode()->getKey(); - ServerActiveObject* obj = m_env.getActiveObject(id); + ServerActiveObject* obj = m_env->getActiveObject(id); // Get object type u8 type = ACTIVEOBJECT_TYPE_INVALID; if(obj == NULL) - dstream<<"WARNING: "<<__FUNCTION_NAME + infostream<<"WARNING: "<<__FUNCTION_NAME <<": NULL object"<getType(); @@ -1475,7 +1495,7 @@ void Server::AsyncRunStep() // Send as reliable m_con.Send(client->peer_id, 0, reply, true); - dstream<<"INFO: Server: Sent object remove/add: " + infostream<<"Server: Sent object remove/add: " <setKnownActiveObjects(whatever); #endif } @@ -1516,7 +1536,7 @@ void Server::AsyncRunStep() JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - ScopeProfiler sp(&g_profiler, "Server: sending object messages"); + ScopeProfiler sp(g_profiler, "Server: sending object messages"); // Key = object id // Value = data sent by object @@ -1525,7 +1545,7 @@ void Server::AsyncRunStep() // Get active object messages from environment for(;;) { - ActiveObjectMessage aom = m_env.getActiveObjectMessage(); + ActiveObjectMessage aom = m_env->getActiveObjectMessage(); if(aom.id == 0) break; @@ -1608,7 +1628,7 @@ void Server::AsyncRunStep() /*if(reliable_data.size() > 0 || unreliable_data.size() > 0) { - dstream<<"INFO: Server: Size of object message data: " + infostream<<"Server: Size of object message data: " <<"reliable: "<= 4) + disable_single_change_sending = true; + + bool got_any_events = false; + + // We'll log the amount of each + Profiler prof; + while(m_unsent_map_edit_queue.size() != 0) { + got_any_events = true; + 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 far_players; if(event->type == MEET_ADDNODE) { - dstream<<"Server: MEET_ADDNODE"<p, event->n, event->already_known_by_peer); + //infostream<<"Server: MEET_ADDNODE"<p, event->n, event->already_known_by_peer, + &far_players, 5); + else + sendAddNode(event->p, event->n, event->already_known_by_peer, + &far_players, 30); } else if(event->type == MEET_REMOVENODE) { - dstream<<"Server: MEET_REMOVENODE"<p, event->already_known_by_peer); + //infostream<<"Server: MEET_REMOVENODE"<p, event->already_known_by_peer, + &far_players, 5); + else + sendRemoveNode(event->p, event->already_known_by_peer, + &far_players, 30); } else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED) { - dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<p); } else if(event->type == MEET_OTHER) { - dstream<<"WARNING: Server: MEET_OTHER not implemented" - <::Iterator + i = event->modified_blocks.getIterator(); + i.atEnd()==false; i++) + { + v3s16 p = i.getNode()->getKey(); + setBlockNotSent(p); + } } else { - dstream<<"WARNING: Server: Unknown MapEditEvent " + prof.add("unknown", 1); + infostream<<"WARNING: Server: Unknown MapEditEvent " <<((u32)event->type)< 0) + { + // Convert list format to that wanted by SetBlocksNotSent + core::map modified_blocks2; + for(core::map::Iterator + i = event->modified_blocks.getIterator(); + i.atEnd()==false; i++) + { + v3s16 p = i.getNode()->getKey(); + modified_blocks2.insert(p, + m_env->getMap().getBlockNoCreateNoEx(p)); + } + // Set blocks not sent + for(core::list::Iterator + i = far_players.begin(); + i != far_players.end(); i++) + { + u16 peer_id = *i; + RemoteClient *client = getClient(peer_id); + if(client==NULL) + continue; + client->SetBlocksNotSent(modified_blocks2); + } + } delete event; - } - } - /* - Send object positions - TODO: Get rid of MapBlockObjects - */ - { - float &counter = m_objectdata_timer; - counter += dtime; - if(counter >= g_settings.getFloat("objectdata_interval")) - { - JMutexAutoLock lock1(m_env_mutex); - JMutexAutoLock lock2(m_con_mutex); - - ScopeProfiler sp(&g_profiler, "Server: sending mbo positions"); - - SendObjectData(counter); - - counter = 0.0; + /*// Don't send too many at a time + count++; + if(count >= 1 && m_unsent_map_edit_queue.size() < 100) + break;*/ } - } - - /* - Step node metadata - TODO: Move to ServerEnvironment and utilize active block stuff - */ - /*{ - //TimeTaker timer("Step node metadata"); - - JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); - - ScopeProfiler sp(&g_profiler, "Server: stepping node metadata"); - - core::map changed_blocks; - m_env.getMap().nodeMetadataStep(dtime, changed_blocks); - - // Use setBlockNotSent - for(core::map::Iterator - i = changed_blocks.getIterator(); - i.atEnd() == false; i++) + if(got_any_events) { - MapBlock *block = i.getNode()->getValue(); - - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd()==false; i++) - { - RemoteClient *client = i.getNode()->getValue(); - client->SetBlockNotSent(block->getPos()); - } + infostream<<"Server: MapEditEvents:"<= g_settings.getFloat("server_map_save_interval")) + if(counter >= g_settings->getFloat("server_map_save_interval")) { counter = 0.0; + JMutexAutoLock lock(m_env_mutex); - ScopeProfiler sp(&g_profiler, "Server: saving stuff"); + ScopeProfiler sp(g_profiler, "Server: saving stuff"); // Auth stuff if(m_authmanager.isModified()) m_authmanager.save(); - - // Map - JMutexAutoLock lock(m_env_mutex); - if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true) - { - // Save only changed parts - m_env.getMap().save(true); - // Delete unused sectors - u32 deleted_count = m_env.getMap().deleteUnusedSectors( - g_settings.getFloat("server_unload_unused_sectors_timeout")); - if(deleted_count > 0) - { - dout_server<<"Server: Unloaded "<getMap().save(MOD_STATE_WRITE_NEEDED); - // Save players - m_env.serializePlayers(m_mapsavedir); - - // Save environment metadata - m_env.saveMeta(m_mapsavedir); - } + // Save players + m_env->serializePlayers(m_mapsavedir); + + // Save environment metadata + m_env->saveMeta(m_mapsavedir); } } } @@ -1775,14 +1814,13 @@ void Server::AsyncRunStep() void Server::Receive() { DSTACK(__FUNCTION_NAME); - u32 data_maxsize = 10000; - Buffer data(data_maxsize); + SharedBuffer data; u16 peer_id; u32 datasize; try{ { JMutexAutoLock conlock(m_con_mutex); - datasize = m_con.Receive(peer_id, *data, data_maxsize); + datasize = m_con.Receive(peer_id, data); } // This has to be called so that the client list gets synced @@ -1793,7 +1831,7 @@ void Server::Receive() } catch(con::InvalidIncomingDataException &e) { - derr_server<<"Server::Receive(): " + infostream<<"Server::Receive(): " "InvalidIncomingDataException: what()=" <removePlayer(peer_id);*/ } } @@ -1821,18 +1859,29 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - con::Peer *peer; + ScopeProfiler sp(g_profiler, "Server::ProcessData"); + try{ - peer = m_con.GetPeer(peer_id); + Address address = m_con.GetPeerAddress(peer_id); + + // drop player if is ip is banned + if(m_banmanager.isIpBanned(address.serializeString())){ + SendAccessDenied(m_con, peer_id, + L"Your ip is banned. Banned name was " + +narrow_to_wide(m_banmanager.getBanName( + address.serializeString()))); + m_con.DeletePeer(peer_id); + return; + } } catch(con::PeerNotFoundException &e) { - derr_server<id)->serialization_version; + + u8 peer_ser_ver = getClient(peer_id)->serialization_version; try { @@ -1852,8 +1901,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(datasize < 2+1+PLAYERNAME_SIZE) return; - derr_server<id<serialization_version = deployed; - getClient(peer->id)->pending_serialization_version = deployed; - + getClient(peer_id)->pending_serialization_version = deployed; + if(deployed == SER_FMT_VER_INVALID) { - derr_server<= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2) + { + net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]); + } + + getClient(peer_id)->net_proto_version = net_proto_version; + + if(net_proto_version == 0) + { + 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"." + ); return; } + + if(g_settings->getBool("strict_protocol_version_checking")) + { + if(net_proto_version != PROTOCOL_VERSION) + { + 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", client's PROTOCOL_VERSION is " + + narrow_to_wide(itos(net_proto_version)) + ); + return; + } + } /* Set up player @@ -1890,7 +1983,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(playername[0]=='\0') { - derr_server<get("default_password")); + std::string translated_default_password = + translatePassword(playername, default_password); + + // If default_password is empty, allow any initial password + if (default_password.length() == 0) + translated_default_password = password; + + infostream<<"Server: adding player "<get("default_privs"))); + m_authmanager.save(); } - - if(password != checkpwd && checkpwd != "") + + std::string checkpwd = m_authmanager.getPassword(playername); + + /*infostream<<"Server: Client gave password '"<= g_settings->getU16("max_users") && + (m_authmanager.getPrivs(playername) + & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 && + playername != g_settings->get("name")) { - derr_server<serialize(test_os); - dstream<<"Player serialization test: \""<deSerialize(test_is); - }*/ + ServerRemotePlayer *player = emergePlayer(playername, peer_id); // If failed, cancel if(player == NULL) { - derr_server<peer_id != 0) - { - derr_server<peer_id = peer_id; - */ - - // Check if player doesn't exist - if(player == NULL) - throw con::InvalidIncomingDataException - ("Server::ProcessData(): INIT: Player doesn't exist"); - - /*// update name if it was supplied - if(datasize >= 20+3) - { - data[20+3-1] = 0; - player->updateName((const char*)&data[3]); - }*/ - /* Answer with a TOCLIENT_INIT */ @@ -2007,7 +2078,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) writeU16(&reply[0], TOCLIENT_INIT); writeU8(&reply[2], deployed); writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS)); - writeU64(&reply[2+1+6], m_env.getServerMap().getSeed()); + writeU64(&reply[2+1+6], m_env->getServerMap().getSeed()); // Send as reliable m_con.Send(peer_id, 0, reply, true); @@ -2023,35 +2094,50 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(command == TOSERVER_INIT2) { - derr_server<id<id)->serialization_version - = getClient(peer->id)->pending_serialization_version; + getClient(peer_id)->serialization_version + = getClient(peer_id)->pending_serialization_version; /* Send some initialization data */ + + // Send item definitions + SendItemDef(m_con, peer_id, m_itemdef); + + // Send node definitions + SendNodeDef(m_con, peer_id, m_nodedef); + + // Send texture announcement + SendTextureAnnouncement(peer_id); // Send player info to all players - SendPlayerInfos(); + //SendPlayerInfos(); // Send inventory to player - UpdateCrafting(peer->id); - SendInventory(peer->id); + UpdateCrafting(peer_id); + SendInventory(peer_id); + + // Send player items to all players + SendPlayerItems(); + + Player *player = m_env->getPlayer(peer_id); // Send HP - { - Player *player = m_env.getPlayer(peer_id); - SendPlayerHP(player); - } + SendPlayerHP(player); + // Show death screen if necessary + if(player->hp == 0) + SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0)); + // Send time of day { SharedBuffer data = makePacket_TOCLIENT_TIME_OF_DAY( - m_env.getTimeOfDay()); - m_con.Send(peer->id, 0, data, true); + m_env->getTimeOfDay()); + m_con.Send(peer_id, 0, data, true); } // Send information about server to player in chat @@ -2060,7 +2146,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Send information about joining in chat { std::wstring name = L"unknown"; - Player *player = m_env.getPlayer(peer_id); + Player *player = m_env->getPlayer(peer_id); if(player != NULL) name = narrow_to_wide(player->getName()); @@ -2070,22 +2156,54 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) message += L" joined game"; BroadcastChatMessage(message); } + + // Warnings about protocol version can be issued here + if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION) + { + SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER"); + } - return; - } - - if(peer_ser_ver == SER_FMT_VER_INVALID) - { - derr_server<getName()<<" joins game. List of players: " + <GotBlock(p); @@ -2159,7 +2278,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) throw con::InvalidIncomingDataException ("DELETEDBLOCKS length is too short"); v3s16 p = readV3S16(&data[2+1+i*6]); - /*dstream<<"Server: DELETEDBLOCKS (" + /*infostream<<"Server: DELETEDBLOCKS (" <SetBlockNotSent(p); @@ -2167,855 +2286,253 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } else if(command == TOSERVER_CLICK_OBJECT) { - if(datasize < 13) - return; + infostream<<"Server: CLICK_OBJECT not supported anymore"<getObject(id); - - if(obj == NULL) - { - derr_server<<"CLICK_OBJECT object not found"<getMap().getNodeMetadata(p); + if(!meta) return; - } - //TODO: Check that object is reasonably close + meta->setText(text); - // Left click - if(button == 0) - { - InventoryList *ilist = player->inventory.getList("main"); - if(g_settings.getBool("creative_mode") == false && ilist != NULL) - { - - // Skip if inventory has no free space - if(ilist->getUsedSlots() == ilist->getSize()) - { - dout_server<<"Player inventory has no free space"<getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM) - { - item = ((ItemObject*)obj)->createInventoryItem(); - } - // Else create an item of the object - else - { - item = new MapBlockObjectItem - (obj->getInventoryString()); - } + actionstream<getName()<<" writes \""<addItem(item); - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); - } - - // Remove from block - block->removeObject(id); + v3s16 blockpos = getNodeBlockPos(p); + MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos); + if(block) + { + block->raiseModified(MOD_STATE_WRITE_NEEDED, + "sign node text"); } + + setBlockNotSent(blockpos); } - else if(command == TOSERVER_CLICK_ACTIVEOBJECT) + else if(command == TOSERVER_INVENTORY_ACTION) { - if(datasize < 7) - return; - - if((getPlayerPrivs(player) & PRIV_BUILD) == 0) - return; - - /* - length: 7 - [0] u16 command - [2] u8 button (0=left, 1=right) - [3] u16 id - [5] u16 item - */ - u8 button = readU8(&data[2]); - u16 id = readS16(&data[3]); - u16 item_i = readU16(&data[11]); - - ServerActiveObject *obj = m_env.getActiveObject(id); - - if(obj == NULL) + // Strip command and create a stream + std::string datastring((char*)&data[2], datasize-2); + infostream<<"TOSERVER_INVENTORY_ACTION: data="<inventory.getList("main"); - if(g_settings.getBool("creative_mode") == false && ilist != NULL) - { - - // Skip if inventory has no free space - if(ilist->getUsedSlots() == ilist->getSize()) - { - dout_server<<"Player inventory has no free space"<m_removed) - return; - - /* - Create the inventory item - */ - InventoryItem *item = obj->createPickedUpItem(); - - if(item) - { - // Add to inventory and send inventory - ilist->addItem(item); - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); - - // Remove object from environment - obj->m_removed = true; - } - else - { - /* - Item cannot be picked up. Punch it instead. - */ - - ToolItem *titem = NULL; - std::string toolname = ""; - - InventoryList *mlist = player->inventory.getList("main"); - if(mlist != NULL) - { - InventoryItem *item = mlist->getItem(item_i); - if(item && (std::string)item->getName() == "ToolItem") - { - titem = (ToolItem*)item; - toolname = titem->getToolName(); - } - } - - u16 wear = obj->punch(toolname); - - if(titem) - { - bool weared_out = titem->addWear(wear); - if(weared_out) - mlist->deleteItem(item_i); - SendInventory(player->peer_id); - } - } - } - } - } - else if(command == TOSERVER_GROUND_ACTION) - { - if(datasize < 17) - return; - /* - length: 17 - [0] u16 command - [2] u8 action - [3] v3s16 nodepos_undersurface - [9] v3s16 nodepos_abovesurface - [15] u16 item - actions: - 0: start digging - 1: place block - 2: stop digging (all parameters ignored) - 3: digging completed - */ - u8 action = readU8(&data[2]); - v3s16 p_under; - p_under.X = readS16(&data[3]); - p_under.Y = readS16(&data[5]); - p_under.Z = readS16(&data[7]); - v3s16 p_over; - p_over.X = readS16(&data[9]); - p_over.Y = readS16(&data[11]); - p_over.Z = readS16(&data[13]); - u16 item_i = readU16(&data[15]); - - //TODO: Check that target is reasonably close - /* - 0: start digging + Note: Always set inventory not sent, to repair cases + where the client made a bad prediction. */ - if(action == 0) - { - /* - NOTE: This can be used in the future to check if - somebody is cheating, by checking the timing. - */ - } // action == 0 /* - 2: stop digging + Handle restrictions and special cases of the move action */ - else if(action == 2) + if(a->getType() == IACTION_MOVE) { -#if 0 - RemoteClient *client = getClient(peer->id); - JMutexAutoLock digmutex(client->m_dig_mutex); - client->m_dig_tool_item = -1; -#endif - } + IMoveAction *ma = (IMoveAction*)a; - /* - 3: Digging completed - */ - else if(action == 3) - { - // Mandatory parameter; actually used for nothing - core::map modified_blocks; + ma->from_inv.applyCurrentPlayer(player->getName()); + ma->to_inv.applyCurrentPlayer(player->getName()); - u8 material = CONTENT_IGNORE; - u8 mineral = MINERAL_NONE; + setInventoryModified(ma->from_inv); + setInventoryModified(ma->to_inv); - bool cannot_remove_node = false; + bool from_inv_is_current_player = + (ma->from_inv.type == InventoryLocation::PLAYER) && + (ma->from_inv.name == player->getName()); - try - { - MapNode n = m_env.getMap().getNode(p_under); - // Get mineral - mineral = n.getMineral(); - // Get material at position - material = n.d; - // If not yet cancelled - if(cannot_remove_node == false) - { - // If it's not diggable, do nothing - if(content_diggable(material) == false) - { - derr_server<<"Server: Not finishing digging: " - <<"Node not diggable" - <nodeRemovalDisabled() == true) - { - derr_server<<"Server: Not finishing digging: " - <<"Node metadata disables removal" - <to_inv.type == InventoryLocation::PLAYER) && + (ma->to_inv.name == player->getName()); - // Make sure the player is allowed to do it - if((getPlayerPrivs(player) & PRIV_BUILD) == 0) + /* + Disable moving items out of craftpreview + */ + if(ma->from_list == "craftpreview") { - dstream<<"Player "<getName()<<" cannot remove node" - <<" because privileges are "<from_inv.dump())<<":"<from_list + <<" to "<<(ma->to_inv.dump())<<":"<to_list + <<" because src is "<from_list<to_list == "craftpreview" || ma->to_list == "craftresult") { - derr_server<<"Server: Not finishing digging."<SetBlockNotSent(blockpos); - + infostream<<"Ignoring IMoveAction from " + <<(ma->from_inv.dump())<<":"<from_list + <<" to "<<(ma->to_inv.dump())<<":"<to_list + <<" because dst is "<to_list< far_players; - sendRemoveNode(p_under, peer_id, &far_players, 100); - - /* - Update and send inventory - */ - if(g_settings.getBool("creative_mode") == false) + // Disallow moving items in elsewhere than player's inventory + // if not allowed to interact + if((getPlayerPrivs(player) & PRIV_INTERACT) == 0 + && (!from_inv_is_current_player + || !to_inv_is_current_player)) { - /* - Wear out tool - */ - InventoryList *mlist = player->inventory.getList("main"); - if(mlist != NULL) - { - InventoryItem *item = mlist->getItem(item_i); - if(item && (std::string)item->getName() == "ToolItem") - { - ToolItem *titem = (ToolItem*)item; - std::string toolname = titem->getToolName(); - - // Get digging properties for material and tool - DiggingProperties prop = - getDiggingProperties(material, toolname); - - if(prop.diggable == false) - { - derr_server<<"Server: WARNING: Player digged" - <<" with impossible material + tool" - <<" combination"<addWear(prop.wear); - - if(weared_out) - { - mlist->deleteItem(item_i); - } - } - } - - /* - Add dug item to inventory - */ - - InventoryItem *item = NULL; + infostream<<"Cannot move outside of player's inventory: " + <<"No interact privilege"<from_inv); + if(owner_from != "" && owner_from != player->getName()) { - std::string &dug_s = content_features(material).dug_item; - if(dug_s != "") - { - std::istringstream is(dug_s, std::ios::binary); - item = InventoryItem::deSerialize(is); - } + infostream<<"WARNING: "<getName() + <<" tried to access an inventory that" + <<" belongs to "<inventory.addItem("main", item); - // Send inventory - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); + std::string owner_to = getInventoryOwner(ma->to_inv); + if(owner_to != "" && owner_to != player->getName()) + { + infostream<<"WARNING: "<getName() + <<" tried to access an inventory that" + <<" belongs to "<::Iterator - i = far_players.begin(); - i != far_players.end(); i++) - { - u16 peer_id = *i; - RemoteClient *client = getClient(peer_id); - if(client==NULL) - continue; - client->SetBlocksNotSent(modified_blocks); - } } - /* - 1: place block + Handle restrictions and special cases of the drop action */ - else if(action == 1) + else if(a->getType() == IACTION_DROP) { + IDropAction *da = (IDropAction*)a; - InventoryList *ilist = player->inventory.getList("main"); - if(ilist == NULL) - return; - - // Get item - InventoryItem *item = ilist->getItem(item_i); - - // If there is no item, it is not possible to add it anywhere - if(item == NULL) - return; - - /* - Handle material items - */ - if(std::string("MaterialItem") == item->getName()) - { - try{ - // Don't add a node if this is not a free space - MapNode n2 = m_env.getMap().getNode(p_over); - bool no_enough_privs = - ((getPlayerPrivs(player) & PRIV_BUILD)==0); - if(no_enough_privs) - dstream<<"Player "<getName()<<" cannot add node" - <<" because privileges are "<SetBlockNotSent(blockpos); - return; - } - } - catch(InvalidPositionException &e) - { - derr_server<<"Server: Ignoring ADDNODE: Node not found" - <<" Adding block to emerge queue." - <id)->m_time_from_building = 0.0; - - // Create node data - MaterialItem *mitem = (MaterialItem*)item; - MapNode n; - n.d = mitem->getMaterial(); - if(content_features(n.d).wall_mounted) - n.dir = packDir(p_under - p_over); - - /* - Send to all players - */ - core::list far_players; - sendAddNode(p_over, n, 0, &far_players, 100); - - /* - Handle inventory - */ - InventoryList *ilist = player->inventory.getList("main"); - if(g_settings.getBool("creative_mode") == false && ilist) - { - // Remove from inventory and send inventory - if(mitem->getCount() == 1) - ilist->deleteItem(item_i); - else - mitem->remove(1); - // Send inventory - UpdateCrafting(peer_id); - SendInventory(peer_id); - } - - /* - Add node. + da->from_inv.applyCurrentPlayer(player->getName()); - This takes some time so it is done after the quick stuff - */ - core::map modified_blocks; - m_ignore_map_edit_events = true; - m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks); - m_ignore_map_edit_events = false; - - /* - Set blocks not sent to far players - */ - for(core::list::Iterator - i = far_players.begin(); - i != far_players.end(); i++) - { - u16 peer_id = *i; - RemoteClient *client = getClient(peer_id); - if(client==NULL) - continue; - client->SetBlocksNotSent(modified_blocks); - } + setInventoryModified(da->from_inv); - /* - Calculate special events - */ - - /*if(n.d == CONTENT_MESE) - { - u32 count = 0; - for(s16 z=-1; z<=1; z++) - for(s16 y=-1; y<=1; y++) - for(s16 x=-1; x<=1; x++) - { - - } - }*/ + // Disallow dropping items if not allowed to interact + if((getPlayerPrivs(player) & PRIV_INTERACT) == 0) + { + delete a; + return; } - /* - Place other item (not a block) - */ - else + // If player is not an admin, check for ownership + else if((getPlayerPrivs(player) & PRIV_SERVER) == 0) { - v3s16 blockpos = getNodeBlockPos(p_over); - - /* - Check that the block is loaded so that the item - can properly be added to the static list too - */ - MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos); - if(block==NULL) + std::string owner_from = getInventoryOwner(da->from_inv); + if(owner_from != "" && owner_from != player->getName()) { - derr_server<<"Error while placing object: " - "block not found"<getName() + <<" tried to access an inventory that" + <<" belongs to "<createSAO(&m_env, 0, pos); - - if(obj == NULL) - { - derr_server<<"WARNING: item resulted in NULL object, " - <<"not placing onto map" - <getDropCount(); - - // Delete item if all gone - if(item->getCount() <= dropcount) - { - if(item->getCount() < dropcount) - dstream<<"WARNING: Server: dropped more items" - <<" than the slot contains"<inventory.getList("main"); - if(ilist) - // Remove from inventory and send inventory - ilist->deleteItem(item_i); - } - // Else decrement it - else - item->remove(dropcount); - - // Send inventory - UpdateCrafting(peer_id); - SendInventory(peer_id); - } - } } - - } // action == 1 - - /* - Catch invalid actions - */ - else - { - derr_server<<"WARNING: Server: Invalid action " - <getObject(id); - if(obj == NULL) + else if(a->getType() == IACTION_CRAFT) { - derr_server<<"Error while setting sign text: " - "object not found"<getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN) - { - derr_server<<"Error while setting sign text: " - "object is not a sign"<setText(text); + ca->craft_inv.applyCurrentPlayer(player->getName()); - obj->getBlock()->setChangedFlag(); - } - else if(command == TOSERVER_SIGNNODETEXT) - { - if((getPlayerPrivs(player) & PRIV_BUILD) == 0) - return; - /* - u16 command - v3s16 p - u16 textlen - textdata - */ - std::string datastring((char*)&data[2], datasize-2); - std::istringstream is(datastring, std::ios_base::binary); - u8 buf[6]; - // Read stuff - is.read((char*)buf, 6); - v3s16 p = readV3S16(buf); - is.read((char*)buf, 2); - u16 textlen = readU16(buf); - std::string text; - for(u16 i=0; icraft_inv); - NodeMetadata *meta = m_env.getMap().getNodeMetadata(p); - if(!meta) - return; - if(meta->typeId() != CONTENT_SIGN_WALL) - return; - SignNodeMetadata *signmeta = (SignNodeMetadata*)meta; - signmeta->setText(text); - - v3s16 blockpos = getNodeBlockPos(p); - MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos); - if(block) - { - block->setChangedFlag(); - } + //bool craft_inv_is_current_player = + // (ca->craft_inv.type == InventoryLocation::PLAYER) && + // (ca->craft_inv.name == player->getName()); - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd()==false; i++) - { - RemoteClient *client = i.getNode()->getValue(); - client->SetBlockNotSent(blockpos); - } - } - else if(command == TOSERVER_INVENTORY_ACTION) - { - /*// Ignore inventory changes if in creative mode - if(g_settings.getBool("creative_mode") == true) - { - dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode" - <to_inv == "current_player" && - ma->from_inv == "current_player") - { - InventoryList *rlist = player->inventory.getList("craftresult"); - assert(rlist); - InventoryList *clist = player->inventory.getList("craft"); - assert(clist); - InventoryList *mlist = player->inventory.getList("main"); - assert(mlist); - /* - Craftresult is no longer preview if something - is moved into it - */ - if(ma->to_list == "craftresult" - && ma->from_list != "craftresult") - { - // If it currently is a preview, remove - // its contents - if(player->craftresult_is_preview) - { - rlist->deleteItem(0); - } - player->craftresult_is_preview = false; - } - /* - Crafting takes place if this condition is true. - */ - if(player->craftresult_is_preview && - ma->from_list == "craftresult") - { - player->craftresult_is_preview = false; - clist->decrementMaterials(1); - } - /* - If the craftresult is placed on itself, move it to - main inventory instead of doing the action - */ - if(ma->to_list == "craftresult" - && ma->from_list == "craftresult") - { - disable_action = true; - - InventoryItem *item1 = rlist->changeItem(0, NULL); - mlist->addItem(item1); - } - } - } - - if(disable_action == false) + // Disallow crafting if not allowed to interact + if((getPlayerPrivs(player) & PRIV_INTERACT) == 0) { - // Feed action to player inventory - a->apply(&c, this); - // Eat the action + infostream<<"Cannot craft: " + <<"No interact privilege"<peer_id); - SendInventory(player->peer_id); + std::string owner_craft = getInventoryOwner(ca->craft_inv); + if(owner_craft != "" && owner_craft != player->getName()) + { + infostream<<"WARNING: "<getName() + <<" tried to access an inventory that" + <<" belongs to "<apply(this, srp, this); + // Eat the action + delete a; } else if(command == TOSERVER_CHAT_MESSAGE) { @@ -3042,6 +2559,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // 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(), + 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 @@ -3054,23 +2578,34 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) u64 privs = getPlayerPrivs(player); // Parse commands - std::wstring commandprefix = L"/#"; - if(message.substr(0, commandprefix.size()) == commandprefix) + if(message[0] == L'/') { - line += L"Server: "; + size_t strip_size = 1; + if (message[1] == L'#') // support old-style commans + ++strip_size; + message = message.substr(strip_size); - message = message.substr(commandprefix.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, + m_env, player, privs); - line += processServerCommand(ctx); - send_to_sender = ctx->flags & 1; - send_to_others = ctx->flags & 2; + 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; + else + line += L"Server: " + reply; + delete ctx; } @@ -3093,7 +2628,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(line != L"") { - dstream<<"CHAT: "<(player); + + if(g_settings->getBool("enable_damage")) { - std::string datastring((char*)&data[2], datasize-2); - std::istringstream is(datastring, std::ios_base::binary); - u8 damage = readU8(is); - if(player->hp > damage) - { - player->hp -= damage; - } - else - { - player->hp = 0; + actionstream<getName()<<" damaged by " + <<(int)damage<<" hp at "<getPosition()/BS) + <setPosition(pos); - player->hp = 20; - SendMovePlayer(player); + srp->setHP(srp->getHP() - damage); + + if(srp->getHP() == 0 && srp->m_hp_not_sent) + DiePlayer(srp); + + if(srp->m_hp_not_sent) SendPlayerHP(player); - - //TODO: Throw items around - } } - - SendPlayerHP(player); + else + { + // Force send (to correct the client's predicted HP) + SendPlayerHP(player); + } } else if(command == TOSERVER_PASSWORD) { @@ -3180,42 +2714,343 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) newpwd += c; } + infostream<<"Server: Client requests a password change from " + <<"'"<getName(); if(m_authmanager.exists(playername) == false) { - dstream<<"Server: playername not found in authmanager"<getName()<<" changes password"<setWieldIndex(item); + SendWieldedItem(srp); + } + else if(command == TOSERVER_RESPAWN) + { + if(player->hp != 0) + return; + + RespawnPlayer(player); + + actionstream<getName()<<" respawns at " + <getPosition()/BS)< tosend; + + u16 numtextures = readU16(is); + + for(int i = 0; i < numtextures; i++) { + + std::string name = deSerializeString(is); + + tosend.push_back(TextureRequest(name)); + infostream<<"TOSERVER_REQUEST_TEXTURES: requested texture " << name <definitions_sent = true; + } + else if(command == TOSERVER_INTERACT) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + /* + [0] u16 command + [2] u8 action + [3] u16 item + [5] u32 length of the next item + [9] serialized PointedThing + actions: + 0: start digging (from undersurface) or use + 1: stop digging (all parameters ignored) + 2: digging completed + 3: place block or item (to abovesurface) + 4: use item + */ + u8 action = readU8(is); + u16 item_i = readU16(is); + std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary); + PointedThing pointed; + pointed.deSerialize(tmp_is); + + infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<getName() + <<" tried to interact, but is dead!"<m_last_good_position; + + // Update wielded item + if(srp->getWieldIndex() != item_i) + { + srp->setWieldIndex(item_i); + SendWieldedItem(srp); + } + + // Get pointed to node (undefined if not POINTEDTYPE_NODE) + v3s16 p_under = pointed.node_undersurface; + v3s16 p_above = pointed.node_abovesurface; + + // Get pointed to object (NULL if not POINTEDTYPE_OBJECT) + ServerActiveObject *pointed_object = NULL; + if(pointed.type == POINTEDTHING_OBJECT) + { + pointed_object = m_env->getActiveObject(pointed.object_id); + if(pointed_object == NULL) + { + infostream<<"TOSERVER_INTERACT: " + "pointed object is NULL"<getBasePosition(); + pointed_pos_above = pointed_pos_under; + } + + /* + Check that target is reasonably close + (only when digging or placing things) + */ + if(action == 0 || action == 2 || action == 3) + { + float d = player_pos.getDistanceFrom(pointed_pos_under); + float max_d = BS * 10; // Just some large enough value + if(d > max_d){ + actionstream<<"Player "<getName() + <<" tried to access "<SetBlockNotSent(blockpos); + // Do nothing else + return; + } + } + + /* + Make sure the player is allowed to do it + */ + if((getPlayerPrivs(player) & PRIV_INTERACT) == 0) + { + infostream<<"Ignoring interaction from player "<getName() + <<" because privileges are "<getMap().getNode(p_under); + } + catch(InvalidPositionException &e) + { + infostream<<"Server: Not punching: Node not found." + <<" Adding block to emerge queue." + <m_removed) + return; + + actionstream<getName()<<" punches object " + <punch(srp, srp->m_time_from_last_punch); + srp->m_time_from_last_punch = 0; + } + + } // action == 0 + + /* + 1: stop digging + */ + else if(action == 1) + { + } // action == 1 + + /* + 2: Digging completed + */ + else if(action == 2) + { + // Only complete digging of nodes + if(pointed.type == POINTEDTHING_NODE) + { + MapNode n(CONTENT_IGNORE); + try + { + n = m_env->getMap().getNode(p_under); + } + catch(InvalidPositionException &e) + { + infostream<<"Server: Not finishing digging: Node not found." + <<" Adding block to emerge queue." + <getWieldedItem(); + + // Reset build time counter + if(pointed.type == POINTEDTHING_NODE && + item.getDefinition(m_itemdef).type == ITEM_NODE) + getClient(peer_id)->m_time_from_building = 0.0; + + if(pointed.type == POINTEDTHING_OBJECT) + { + // Right click object + + // Skip if object has been removed + if(pointed_object->m_removed) + return; + + actionstream<getName()<<" right-clicks object " + <rightClick(srp); + } + else if(scriptapi_item_on_place(m_lua, + item, srp, pointed)) + { + // Placement was handled in lua + + // Apply returned ItemStack + if(g_settings->getBool("creative_mode") == false) + srp->setWieldedItem(item); + } + + } // action == 3 + + /* + 4: use + */ + else if(action == 4) + { + ItemStack item = srp->getWieldedItem(); + + actionstream<getName()<<" uses "<getBool("creative_mode") == false) + srp->setWieldedItem(item); + } + + } // action == 4 + + /* + Catch invalid actions + */ + else + { + infostream<<"WARNING: Server: Invalid action " + <clone(); m_unsent_map_edit_queue.push_back(e); } -Inventory* Server::getInventory(InventoryContext *c, std::string id) +Inventory* Server::getInventory(const InventoryLocation &loc) { - if(id == "current_player") + switch(loc.type){ + case InventoryLocation::UNDEFINED: + {} + break; + case InventoryLocation::CURRENT_PLAYER: + {} + break; + case InventoryLocation::PLAYER: { - assert(c->current_player); - return &(c->current_player->inventory); + Player *player = m_env->getPlayer(loc.name.c_str()); + if(!player) + return NULL; + return &player->inventory; } - - Strfnd fn(id); - std::string id0 = fn.next(":"); - - if(id0 == "nodemeta") + break; + case InventoryLocation::NODEMETA: { - v3s16 p; - p.X = stoi(fn.next(",")); - p.Y = stoi(fn.next(",")); - p.Z = stoi(fn.next(",")); - NodeMetadata *meta = m_env.getMap().getNodeMetadata(p); - if(meta) - return meta->getInventory(); - dstream<<"nodemeta at ("<getMap().getNodeMetadata(loc.p); + if(!meta) + return NULL; + return meta->getInventory(); + } + break; + default: + assert(0); } - - dstream<<__FUNCTION_NAME<<": unknown id "<current_player); - // Send inventory - UpdateCrafting(c->current_player->peer_id); - SendInventory(c->current_player->peer_id); - return; + return loc.name; } - - Strfnd fn(id); - std::string id0 = fn.next(":"); - - if(id0 == "nodemeta") + break; + case InventoryLocation::NODEMETA: { - v3s16 p; - p.X = stoi(fn.next(",")); - p.Y = stoi(fn.next(",")); - p.Z = stoi(fn.next(",")); - v3s16 blockpos = getNodeBlockPos(p); + NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p); + if(!meta) + return ""; + return meta->getOwner(); + } + break; + default: + assert(0); + } + return ""; +} +void Server::setInventoryModified(const InventoryLocation &loc) +{ + switch(loc.type){ + case InventoryLocation::UNDEFINED: + {} + break; + case InventoryLocation::PLAYER: + { + ServerRemotePlayer *srp = static_cast + (m_env->getPlayer(loc.name.c_str())); + if(!srp) + return; + srp->m_inventory_not_sent = true; + } + break; + case InventoryLocation::NODEMETA: + { + v3s16 blockpos = getNodeBlockPos(loc.p); - NodeMetadata *meta = m_env.getMap().getNodeMetadata(p); + NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p); if(meta) meta->inventoryModified(); - - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd()==false; i++) - { - RemoteClient *client = i.getNode()->getValue(); - client->SetBlockNotSent(blockpos); - } - - return; + + MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos); + if(block) + block->raiseModified(MOD_STATE_WRITE_NEEDED); + + setBlockNotSent(blockpos); + } + break; + default: + assert(0); } - - dstream<<__FUNCTION_NAME<<": unknown id "< Server::getPlayerInfo() @@ -3306,7 +3165,7 @@ core::list Server::getPlayerInfo() core::list list; - core::list players = m_env.getPlayers(); + core::list players = m_env->getPlayers(); core::list::Iterator i; for(i = players.begin(); @@ -3317,11 +3176,10 @@ core::list Server::getPlayerInfo() Player *player = *i; try{ - con::Peer *peer = m_con.GetPeer(player->peer_id); - // Copy info from peer to info struct - info.id = peer->id; - info.address = peer->address; - info.avg_rtt = peer->avg_rtt; + // 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) { @@ -3344,7 +3202,7 @@ core::list Server::getPlayerInfo() void Server::peerAdded(con::Peer *peer) { DSTACK(__FUNCTION_NAME); - dout_server<<"Server::peerAdded(): peer->id=" + infostream<<"Server::peerAdded(): peer->id=" <id<id=" + infostream<<"Server::deletingPeer(): peer->id=" <id<<", timeout="< stepped_blocks; - - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) - { - u16 peer_id = i.getNode()->getKey(); - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == peer_id); - - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - client->SendObjectData(this, dtime, stepped_blocks); - } + writeU16(os, TOCLIENT_DEATHSCREEN); + writeU8(os, set_camera_point_target); + writeV3F1000(os, camera_point_target); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + con.Send(peer_id, 0, data, true); } -void Server::SendPlayerInfos() +void Server::SendItemDef(con::Connection &con, u16 peer_id, + IItemDefManager *itemdef) { DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); - //JMutexAutoLock envlock(m_env_mutex); - - // Get connected players - core::list players = m_env.getPlayers(true); - - u32 player_count = players.getSize(); - u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count; + /* + u16 command + u32 length of the next item + zlib-compressed serialized ItemDefManager + */ + writeU16(os, TOCLIENT_ITEMDEF); + std::ostringstream tmp_os(std::ios::binary); + itemdef->serialize(tmp_os); + std::ostringstream tmp_os2(std::ios::binary); + compressZlib(tmp_os.str(), tmp_os2); + os< data(datasize); - writeU16(&data[0], TOCLIENT_PLAYERINFO); - - u32 start = 2; - core::list::Iterator i; - for(i = players.begin(); - i != players.end(); i++) - { - Player *player = *i; + // Make data buffer + std::string s = os.str(); + infostream<<"Server::SendItemDef(): Sending item definitions: size=" + < data((u8*)s.c_str(), s.size()); + // Send as reliable + con.Send(peer_id, 0, data, true); +} - /*dstream<<"Server sending player info for player with " - "peer_id="<peer_id<peer_id); - memset((char*)&data[start+2], 0, PLAYERNAME_SIZE); - snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName()); - start += 2+PLAYERNAME_SIZE; - } +void Server::SendNodeDef(con::Connection &con, u16 peer_id, + INodeDefManager *nodedef) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); - //JMutexAutoLock conlock(m_con_mutex); + /* + u16 command + u32 length of the next item + zlib-compressed serialized NodeDefManager + */ + writeU16(os, TOCLIENT_NODEDEF); + std::ostringstream tmp_os(std::ios::binary); + nodedef->serialize(tmp_os); + std::ostringstream tmp_os2(std::ios::binary); + compressZlib(tmp_os.str(), tmp_os2); + os<serialize(os, ver); + block->serialize(os, ver, false); std::string s = os.str(); SharedBuffer blockdata((u8*)s.c_str(), s.size()); @@ -3723,7 +3644,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) writeS16(&reply[6], p.Z); memcpy(&reply[8], *blockdata, blockdata.getSize()); - /*dstream<<"Server: Sending block ("< queue; s32 total_sending = 0; { - ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending"); + ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending"); for(core::map::Iterator i = m_clients.getIterator(); @@ -3755,6 +3676,11 @@ void Server::SendBlocks(float dtime) RemoteClient *client = i.getNode()->getValue(); assert(client->peer_id == i.getNode()->getKey()); + // If definitions and textures have not been sent, don't + // send MapBlocks either + if(!client->definitions_sent) + continue; + total_sending += client->SendingCount(); if(client->serialization_version == SER_FMT_VER_INVALID) @@ -3772,7 +3698,7 @@ void Server::SendBlocks(float dtime) for(u32 i=0; i= g_settings.getS32 + if(total_sending >= g_settings->getS32 ("max_simultaneous_block_sends_server_total")) break; @@ -3781,7 +3707,7 @@ void Server::SendBlocks(float dtime) MapBlock *block = NULL; try { - block = m_env.getMap().getBlockNoCreate(q.pos); + block = m_env->getMap().getBlockNoCreate(q.pos); } catch(InvalidPositionException &e) { @@ -3798,47 +3724,318 @@ void Server::SendBlocks(float dtime) } } -/* - Something random -*/ +void Server::PrepareTextures() { + DSTACK(__FUNCTION_NAME); -void Server::UpdateCrafting(u16 peer_id) -{ + infostream<<"Server::PrepareTextures(): Calculate sha1 sums of textures"<::Iterator i = m_mods.begin(); + i != m_mods.end(); i++){ + const ModSpec &mod = *i; + std::string texturepath = mod.path + DIR_DELIM + "textures"; + std::vector dirlist = fs::GetDirListing(texturepath); + for(u32 j=0; jm_Textures[tname] = TextureInformation(tpath,digest_string); + infostream<<"Server::PrepareTextures(): added sha1 for "<< tname < texture_announcements; + + for (std::map::iterator i = m_Textures.begin();i != m_Textures.end(); i++ ) { + + // Put in list + texture_announcements.push_back( + SendableTextureAnnouncement(i->first, i->second.sha1_digest)); + } + + //send announcements /* - Calculate crafting stuff + u16 command + u32 number of textures + for each texture { + u16 length of name + string name + u16 length of digest string + string sha1_digest + } */ - if(g_settings.getBool("creative_mode") == false) - { - InventoryList *clist = player->inventory.getList("craft"); - InventoryList *rlist = player->inventory.getList("craftresult"); + std::ostringstream os(std::ios_base::binary); - if(rlist->getUsedSlots() == 0) - player->craftresult_is_preview = true; + writeU16(os, TOCLIENT_ANNOUNCE_TEXTURES); + writeU16(os, texture_announcements.size()); - if(rlist && player->craftresult_is_preview) - { - rlist->clearItems(); + for(core::list::Iterator + j = texture_announcements.begin(); + j != texture_announcements.end(); j++){ + os<name); + os<sha1_digest); + } + + // Make data buffer + std::string s = os.str(); + infostream<<"Server::SendTextureAnnouncement(): Send to client"< data((u8*)s.c_str(), s.size()); + + // Send as reliable + m_con.Send(peer_id, 0, data, true); + +} + +struct SendableTexture +{ + std::string name; + std::string path; + std::string data; + + SendableTexture(const std::string &name_="", const std::string path_="", + const std::string &data_=""): + name(name_), + path(path_), + data(data_) + {} +}; + +void Server::SendTexturesRequested(u16 peer_id,core::list tosend) { + DSTACK(__FUNCTION_NAME); + + infostream<<"Server::SendTexturesRequested(): Sending textures to client"< > texture_bunches; + texture_bunches.push_back(core::list()); + + u32 texture_size_bunch_total = 0; + + for(core::list::Iterator i = tosend.begin(); i != tosend.end(); i++) { + if(m_Textures.find(i->name) == m_Textures.end()){ + errorstream<<"Server::SendTexturesRequested(): Client asked for " + <<"unknown texture \""<<(i->name)<<"\""<= bytes_per_bunch){ + texture_bunches.push_back(core::list()); + texture_size_bunch_total = 0; } - if(clist && rlist && player->craftresult_is_preview) + + } + + /* Create and send packets */ + + u32 num_bunches = texture_bunches.size(); + for(u32 i=0; igetItem(i); + /* + u16 command + u16 total number of texture bunches + u16 index of this bunch + u32 number of textures in this bunch + for each texture { + u16 length of name + string name + u32 length of data + data + } + */ + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_TEXTURES); + writeU16(os, num_bunches); + writeU16(os, i); + writeU32(os, texture_bunches[i].size()); + + for(core::list::Iterator + j = texture_bunches[i].begin(); + j != texture_bunches[i].end(); j++){ + os<name); + os<data); } - - // Get result of crafting grid - InventoryItem *result = craft_get_result(items); - if(result) - rlist->addItem(result); + + // Make data buffer + std::string s = os.str(); + infostream<<"Server::SendTexturesRequested(): bunch "<getName()<<" dies"<setHP(0); + + // Trigger scripted stuff + scriptapi_on_dieplayer(m_lua, srp); + + // Handle players that are not connected + if(player->peer_id == PEER_ID_INEXISTENT){ + RespawnPlayer(player); + return; + } + + SendPlayerHP(player); + SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0)); +} + +void Server::RespawnPlayer(Player *player) +{ + ServerRemotePlayer *srp = static_cast(player); + srp->setHP(20); + bool repositioned = scriptapi_on_respawnplayer(m_lua, srp); + if(!repositioned){ + v3f pos = findSpawnPos(m_env->getServerMap()); + player->setPosition(pos); + srp->m_last_good_position = pos; + srp->m_last_good_position_age = 0; + } + SendMovePlayer(player); + SendPlayerHP(player); +} + +void Server::UpdateCrafting(u16 peer_id) +{ + DSTACK(__FUNCTION_NAME); - } // if creative_mode == false + 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); + + // Put the new preview in + InventoryList *plist = player->inventory.getList("craftpreview"); + assert(plist); + assert(plist->getSize() >= 1); + plist->changeItem(0, preview); } RemoteClient* Server::getClient(u16 peer_id) @@ -3872,7 +4069,7 @@ std::wstring Server::getStatusString() if(client->serialization_version == SER_FMT_VER_INVALID) continue; // Get player - Player *player = m_env.getPlayer(client->peer_id); + Player *player = m_env->getPlayer(client->peer_id); // Get name of player std::wstring name = L"unknown"; if(player != NULL) @@ -3881,81 +4078,204 @@ std::wstring Server::getStatusString() os<isSavingEnabled() == false) - os<<" WARNING: Map saving is disabled."<getMap()))->isSavingEnabled() == false) + os<get("motd") != "") + os<get("motd")); return os.str(); } +void Server::setPlayerPassword(const std::string &name, const std::wstring &password) +{ + // Add player to auth manager + if(m_authmanager.exists(name) == false) + { + infostream<<"Server: adding player "<get("default_privs"))); + } + // Change password and save + m_authmanager.setPassword(name, translatePassword(name, password)); + m_authmanager.save(); +} + +// Saves g_settings to configpath given at initialization +void Server::saveConfig() +{ + if(m_configpath != "") + g_settings->updateConfigFile(m_configpath.c_str()); +} + +void Server::notifyPlayer(const char *name, const std::wstring msg) +{ + Player *player = m_env->getPlayer(name); + if(!player) + return; + SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg); +} + +void Server::notifyPlayers(const std::wstring msg) +{ + BroadcastChatMessage(msg); +} + +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); +} + +// IGameDef interface +// Under envlock +IItemDefManager* Server::getItemDefManager() +{ + return m_itemdef; +} +INodeDefManager* Server::getNodeDefManager() +{ + return m_nodedef; +} +ICraftDefManager* Server::getCraftDefManager() +{ + return m_craftdef; +} +ITextureSource* Server::getTextureSource() +{ + return NULL; +} +u16 Server::allocateUnknownNodeId(const std::string &name) +{ + return m_nodedef->allocateDummy(name); +} + +IWritableItemDefManager* Server::getWritableItemDefManager() +{ + return m_itemdef; +} +IWritableNodeDefManager* Server::getWritableNodeDefManager() +{ + return m_nodedef; +} +IWritableCraftDefManager* Server::getWritableCraftDefManager() +{ + return m_craftdef; +} + +const ModSpec* Server::getModSpec(const std::string &modname) +{ + for(core::list::Iterator i = m_mods.begin(); + i != m_mods.end(); i++){ + const ModSpec &mod = *i; + if(mod.name == modname) + return &mod; + } + return NULL; +} + v3f findSpawnPos(ServerMap &map) { //return v3f(50,50,50)*BS; + + v3s16 nodepos; - v2s16 nodepos; - s16 groundheight = 0; - +#if 0 + nodepos = v2s16(0,0); + groundheight = 20; +#endif + +#if 1 // 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 - nodepos = v2s16(-range + (myrand()%(range*2)), + v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)), -range + (myrand()%(range*2))); - v2s16 sectorpos = getNodeSectorPos(nodepos); - // Get sector (NOTE: Don't get because it's slow) - //m_env.getMap().emergeSector(sectorpos); + //v2s16 sectorpos = getNodeSectorPos(nodepos2d); // Get ground height at point (fallbacks to heightmap function) - groundheight = map.findGroundLevel(nodepos); + s16 groundheight = map.findGroundLevel(nodepos2d); // Don't go underwater if(groundheight < WATER_LEVEL) { - //dstream<<"-> Underwater"< Underwater"< WATER_LEVEL + 4) { - //dstream<<"-> Underwater"< Underwater"<= 2){ + is_good = true; + nodepos.Y -= 1; + break; + } + } + nodepos.Y++; + } + if(is_good){ + // Found a good place + //infostream<<"Searched through "<(m_env->getPlayer(name)); if(player != NULL) { // If player is already connected, cancel if(player->peer_id != 0) { - dstream<<"emergePlayer(): Player already connected"<peer_id = peer_id; + // Re-add player to environment + if(player->m_removed) + { + player->m_removed = false; + player->setId(0); + m_env->addActiveObject(player); + } + // Reset inventory to creative if in creative mode - if(g_settings.getBool("creative_mode")) + if(g_settings->getBool("creative_mode")) { - craft_set_creative_inventory(player); + // Warning: double code below + // Backup actual inventory + player->inventory_backup = new Inventory(m_itemdef); + *(player->inventory_backup) = player->inventory; + // Set creative inventory + player->resetInventory(); + scriptapi_get_creative_inventory(m_lua, player); } return player; @@ -3964,9 +4284,9 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id /* If player with the wanted peer_id already exists, cancel. */ - if(m_env.getPlayer(peer_id) != NULL) + if(m_env->getPlayer(peer_id) != NULL) { - dstream<<"emergePlayer(): Player with wrong name but same" + infostream<<"emergePlayer(): Player with wrong name but same" " peer_id already exists"<peer_id = c.peer_id; - //player->peer_id = PEER_ID_INEXISTENT; - player->peer_id = peer_id; - player->updateName(name); - m_authmanager.add(name); - m_authmanager.setPassword(name, password); - m_authmanager.setPrivs(name, - stringToPrivs(g_settings.get("default_privs"))); - - /* - Set player position - */ + /* Set player position */ - dstream<<"Server: Finding spawn place for player \"" - <getName()<<"\""<getServerMap()); - player->setPosition(pos); + player = new ServerRemotePlayer(m_env, pos, peer_id, name); + ServerRemotePlayer *srp = static_cast(player); - /* - Add player to environment - */ + /* Add player to environment */ + m_env->addPlayer(player); + m_env->addActiveObject(srp); - m_env.addPlayer(player); + /* Run scripts */ + scriptapi_on_newplayer(m_lua, srp); - /* - Add stuff to inventory - */ - - if(g_settings.getBool("creative_mode")) - { - craft_set_creative_inventory(player); - } - else if(g_settings.getBool("give_initial_stuff")) + /* Add stuff to inventory */ + if(g_settings->getBool("creative_mode")) { - craft_give_initial_stuff(player); + // Warning: double code above + // Backup actual inventory + player->inventory_backup = new Inventory(m_itemdef); + *(player->inventory_backup) = player->inventory; + // Set creative inventory + player->resetInventory(); + scriptapi_get_creative_inventory(m_lua, player); } return player; @@ -4066,37 +4375,65 @@ void Server::handlePeerChange(PeerChange &c) { // Get object u16 id = i.getNode()->getKey(); - ServerActiveObject* obj = m_env.getActiveObject(id); + ServerActiveObject* obj = m_env->getActiveObject(id); if(obj && obj->m_known_by_count > 0) obj->m_known_by_count--; } + ServerRemotePlayer* player = + static_cast(m_env->getPlayer(c.peer_id)); + // Collect information about leaving in chat std::wstring message; { - std::wstring name = L"unknown"; - Player *player = m_env.getPlayer(c.peer_id); if(player != NULL) - name = narrow_to_wide(player->getName()); - - message += L"*** "; - message += name; - message += L" left game"; - if(c.timeout) - message += L" (timed out)"; + { + std::wstring name = narrow_to_wide(player->getName()); + message += L"*** "; + message += name; + message += L" left game"; + if(c.timeout) + message += L" (timed out)"; + } } - - /*// Delete player - { - m_env.removePlayer(c.peer_id); - }*/ - + + // Remove from environment + if(player != NULL) + player->m_removed = true; + // Set player client disconnected + if(player != NULL) + player->peer_id = 0; + + /* + Print out action + */ { - Player *player = m_env.getPlayer(c.peer_id); if(player != NULL) - player->peer_id = 0; + { + std::ostringstream os(std::ios_base::binary); + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + assert(client->peer_id == i.getNode()->getKey()); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + // Get player + Player *player = m_env->getPlayer(client->peer_id); + if(!player) + continue; + // Get name of player + os<getName()<<" "; + } + + actionstream<getName()<<" " + <<(c.timeout?"times out.":"leaves game.") + <<" List of players: " + <get("name") == playername) { return PRIV_ALL; } @@ -4151,27 +4489,28 @@ void dedicated_server_loop(Server &server, bool &kill) { DSTACK(__FUNCTION_NAME); - dstream<getFloat("dedicated_server_step"); // This is kind of a hack but can be done like this // because server.step() is very light { - ScopeProfiler sp(&g_profiler, "dedicated server sleep"); - sleep_ms(30); + ScopeProfiler sp(g_profiler, "dedicated server sleep"); + sleep_ms((int)(steplen*1000.0)); } - server.step(0.030); + server.step(steplen); if(server.getShutdownRequested() || kill) { - dstream<getFloat("profiler_print_interval"); if(profiler_print_interval != 0) { - if(m_profiler_interval.step(0.030, profiler_print_interval)) + if(m_profiler_interval.step(steplen, profiler_print_interval)) { - dstream<<"Profiler:"<print(infostream); + g_profiler->clear(); } } @@ -4205,10 +4544,10 @@ void dedicated_server_loop(Server &server, bool &kill) u32 sum = PIChecksum(list); if(sum != sum_old) { - dstream<PrintLine(&dstream); + i->PrintLine(&infostream); } } sum_old = sum;