X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fserver.cpp;h=2139df957388a8b05740cdaf4440374541fdf9ec;hb=81385682c8c1da90f2366452b10e20a93c0865fb;hp=c9881567dfd54f77fb00307f014c8d6a3ee25259;hpb=0dbb31afeb74c199bb66eaf95d3e23f2fa843386;p=minetest.git diff --git a/src/server.cpp b/src/server.cpp index c9881567d..2139df957 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -20,19 +20,17 @@ 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" #include "main.h" #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" @@ -42,8 +40,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "script.h" #include "scriptapi.h" #include "nodedef.h" -#include "tooldef.h" +#include "itemdef.h" #include "craftdef.h" +#include "mapgen.h" +#include "content_abm.h" +#include "mods.h" +#include "sha1.h" +#include "base64.h" +#include "tool.h" +#include "utility_string.h" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" @@ -104,6 +109,10 @@ void * ServerThread::Thread() { infostream<<"Server: PeerNotFoundException"<setAsyncFatalError(e.what()); + } } END_DEBUG_EXCEPTION_HANDLER(errorstream) @@ -143,12 +152,7 @@ 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; //infostream<<"EmergeThread::Thread(): running"<m_env->getMap()); - //core::map changed_blocks; - //core::map lighting_invalidated_blocks; - MapBlock *block = NULL; bool got_block = true; core::map modified_blocks; - + /* - Fetch block from map or generate a single block + 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.loadSectorFull(p2d); 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, loading"<isGenerated() == false) - { - if(enable_mapgen_debug_info) - infostream<<"EmergeThread: generating"<isGenerated() == false)){ 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, 3600); - } - } - else - { - /*if(block->getLightingExpired()){ - lighting_invalidated_blocks[block->getPos()] = block; - }*/ + map.initBlockMake(&data, p); } - - // TODO: Some additional checking and lighting updating, - // see emergeBlock } - {//envlock - JMutexAutoLock envlock(m_server->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 0 - if(lighting_invalidated_blocks.size() > 0) { - /*infostream<<"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); - } -#endif - } - // 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; + + /* + 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 */ @@ -346,6 +337,8 @@ void * EmergeThread::Thread() END_DEBUG_EXCEPTION_HANDLER(errorstream) + log_deregister_thread(); + return NULL; } @@ -766,105 +759,6 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, infostream<<"GetNextBlocks duration: "< &stepped_blocks - ) -{ - DSTACK(__FUNCTION_NAME); - - // Can't send anything without knowing version - if(serialization_version == SER_FMT_VER_INVALID) - { - infostream<<"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 (dummy, for compatibility) - */ - - // Write block count - writeU16(buf, 0); - os.write((char*)buf, 2); - - /* - Send data - */ - - //infostream<<"Server: Sending object data to "< data((u8*)s.c_str(), s.size()); - // Send as unreliable - server->m_con.Send(peer_id, 0, data, false); -} - void RemoteClient::GotBlock(v3s16 p) { if(m_blocks_sending.find(p) != NULL) @@ -935,63 +829,27 @@ void PlayerInfo::PrintLine(std::ostream *s) (*s)< &l) -{ - core::list::Iterator i; - u32 checksum = 1; - u32 a = 10; - for(i=l.begin(); i!=l.end(); i++) - { - checksum += a * (i->id+1); - checksum ^= 0x435aafcd; - a *= 10; - } - return checksum; -} - -struct ModSpec -{ - std::string name; - std::string path; - - ModSpec(const std::string &name_="", const std::string path_=""): - name(name_), - path(path_) - {} -}; - -static core::list getMods(core::list &modspaths) -{ - core::list mods; - for(core::list::Iterator i = modspaths.begin(); - i != modspaths.end(); i++){ - std::string modspath = *i; - std::vector dirlist = fs::GetDirListing(modspath); - for(u32 j=0; j::const_iterator i = m_gamespec.addon_paths.begin(); + i != m_gamespec.addon_paths.end(); i++) + infostream<<"- addons: "<<(*i)<::const_iterator i = m_gamespec.addon_paths.begin(); + i != m_gamespec.addon_paths.end(); i++) + m_modspaths.push_front((*i) + DIR_DELIM + "mods"); + + // Print out mod search paths + for(core::list::Iterator i = m_modspaths.begin(); + i != m_modspaths.end(); i++){ + std::string modspath = *i; + infostream<<"- mods: "<::Iterator i = m_mods.begin(); + i != m_mods.end(); i++){ + const ModSpec &mod = *i; + infostream< mods = getMods(m_modspaths); - for(core::list::Iterator i = mods.begin(); - i != mods.end(); i++){ - ModSpec mod = *i; - infostream<<"Server: Loading mod \""<::Iterator i = m_mods.begin(); + i != m_mods.end(); i++){ + const ModSpec &mod = *i; std::string scriptpath = mod.path + DIR_DELIM + "init.lua"; - bool success = script_load(m_lua, scriptpath.c_str()); + infostream<<" ["<updateAliases(m_itemdef); + // Initialize Environment - m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, this); + m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua, + this, this); // Give environment reference to scripting api scriptapi_add_environment(m_lua, m_env); @@ -1068,20 +980,25 @@ Server::Server( m_env->getMap().addEventReceiver(this); // If file exists, load environment metadata - if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt")) + if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt")) { infostream<<"Server: Loading environment metadata"<loadMeta(m_mapsavedir); + m_env->loadMeta(m_path_world); } // Load players infostream<<"Server: Loading players"<deSerializePlayers(m_mapsavedir); + m_env->deSerializePlayers(m_path_world); + + /* + Add some test ActiveBlockModifiers to environment + */ + add_legacy_abms(m_env, m_nodedef); } Server::~Server() { - infostream<<"Server::~Server()"<serializePlayers(m_mapsavedir); + m_env->serializePlayers(m_path_world); /* Save environment metadata */ infostream<<"Server: Saving environment metadata"<saveMeta(m_mapsavedir); + m_env->saveMeta(m_path_world); } /* @@ -1159,8 +1076,9 @@ Server::~Server() // Delete Environment delete m_env; - delete m_toolmgr; + delete m_itemdef; delete m_nodedef; + delete m_craftdef; // Deinitialize scripting infostream<<"Server: Deinitializing scripting"<__| \\___ >____ > |__| "<::Iterator i = m_clients.getIterator(); @@ -1351,11 +1282,21 @@ void Server::AsyncRunStep() { RemoteClient *client = i.getNode()->getValue(); ServerRemotePlayer *player = - (ServerRemotePlayer*)m_env->getPlayer(client->peer_id); + 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){ + if(player->m_last_good_position_age >= 1.0){ float age = player->m_last_good_position_age; v3f diff = (player->getPosition() - player->m_last_good_position); float d_vert = diff.Y; @@ -1375,6 +1316,32 @@ void Server::AsyncRunStep() } 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); + } } } @@ -1476,6 +1443,12 @@ void Server::AsyncRunStep() i.atEnd() == false; i++) { RemoteClient *client = i.getNode()->getValue(); + + // 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) { @@ -1573,7 +1546,7 @@ void Server::AsyncRunStep() // Send as reliable m_con.Send(client->peer_id, 0, reply, true); - infostream<<"Server: Sent object remove/add: " + verbosestream<<"Server: Sent object remove/add: " <= 4) disable_single_change_sending = true; - bool got_any_events = false; + int event_count = m_unsent_map_edit_queue.size(); // 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. @@ -1835,33 +1806,16 @@ void Server::AsyncRunStep() break;*/ } - if(got_any_events) - { + if(event_count >= 5){ infostream<<"Server: MapEditEvents:"<= g_settings->getFloat("objectdata_interval")) - { - JMutexAutoLock lock1(m_env_mutex); - JMutexAutoLock lock2(m_con_mutex); - - //ScopeProfiler sp(g_profiler, "Server: sending player positions"); - - SendObjectData(counter); - - counter = 0.0; - } - } - /* Trigger emergethread (it somehow gets to a non-triggered but bysy state sometimes) @@ -1884,6 +1838,7 @@ void Server::AsyncRunStep() if(counter >= g_settings->getFloat("server_map_save_interval")) { counter = 0.0; + JMutexAutoLock lock(m_env_mutex); ScopeProfiler sp(g_profiler, "Server: saving stuff"); @@ -1891,35 +1846,18 @@ void Server::AsyncRunStep() if(m_authmanager.isModified()) m_authmanager.save(); - //Bann stuff + //Ban stuff if(m_banmanager.isModified()) m_banmanager.save(); - // Map - JMutexAutoLock lock(m_env_mutex); - - /*// Unload unused data (delete from memory) - m_env->getMap().unloadUnusedData( - g_settings->getFloat("server_unload_unused_sectors_timeout")); - */ - /*u32 deleted_count = m_env->getMap().unloadUnusedData( - g_settings->getFloat("server_unload_unused_sectors_timeout")); - */ - - // Save only changed parts - m_env->getMap().save(true); - - /*if(deleted_count > 0) - { - infostream<<"Server: Unloaded "<getMap().save(MOD_STATE_WRITE_NEEDED); // Save players - m_env->serializePlayers(m_mapsavedir); + m_env->serializePlayers(m_path_world); // Save environment metadata - m_env->saveMeta(m_mapsavedir); + m_env->saveMeta(m_path_world); } } } @@ -1972,6 +1910,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); + ScopeProfiler sp(g_profiler, "Server::ProcessData"); + try{ Address address = m_con.GetPeerAddress(peer_id); @@ -1991,6 +1931,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <serialization_version; @@ -2012,7 +1954,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(datasize < 2+1+PLAYERNAME_SIZE) return; - infostream<<"Server: Got TOSERVER_INIT from " + verbosestream<<"Server: Got TOSERVER_INIT from " <getBool("strict_protocol_version_checking")) { - if(net_proto_version < PROTOCOL_VERSION) + if(net_proto_version != PROTOCOL_VERSION) { - SendAccessDenied(m_con, peer_id, - L"Your client is too old. Please upgrade."); + actionstream<<"Server: A mismatched client tried to connect" + <<" from "<get("default_password"); + std::wstring default_password = + narrow_to_wide(g_settings->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(); } - + + std::string checkpwd = m_authmanager.getPassword(playername); + /*infostream<<"Server: Client gave password '"<get("default_privs"))); - m_authmanager.save(); + + // Do not allow multiple players in simple singleplayer mode. + // This isn't a perfect way to do it, but will suffice for now. + if(m_simple_singleplayer_mode && m_clients.size() > 1){ + infostream<<"Server: Not allowing another client to connect in" + <<" simple singleplayer mode"<= g_settings->getU16("max_users") && (m_authmanager.getPrivs(playername) - & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 && + & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 && playername != g_settings->get("name")) { + actionstream<<"Server: "<hp == 0) + SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0)); + // Send time of day { SharedBuffer data = makePacket_TOCLIENT_TIME_OF_DAY( @@ -2234,21 +2220,25 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) m_con.Send(peer_id, 0, data, true); } - // Send information about server to player in chat - SendChatMessage(peer_id, getStatusString()); - - // Send information about joining in chat + // Note things in chat if not in simple singleplayer mode + if(!m_simple_singleplayer_mode) { - std::wstring name = L"unknown"; - Player *player = m_env->getPlayer(peer_id); - if(player != NULL) - name = narrow_to_wide(player->getName()); + // Send information about server to player in chat + SendChatMessage(peer_id, getStatusString()); - std::wstring message; - message += L"*** "; - message += name; - message += L" joined game"; - BroadcastChatMessage(message); + // Send information about joining in chat + { + std::wstring name = L"unknown"; + Player *player = m_env->getPlayer(peer_id); + if(player != NULL) + name = narrow_to_wide(player->getName()); + + std::wstring message; + message += L"*** "; + message += name; + message += L" joined game"; + BroadcastChatMessage(message); + } } // Warnings about protocol version can be issued here @@ -2257,11 +2247,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER"); } - /* - Check HP, respawn if necessary - */ - HandlePlayerHP(player, 0); - /* Print out action */ @@ -2299,6 +2284,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } Player *player = m_env->getPlayer(peer_id); + ServerRemotePlayer *srp = static_cast(player); if(player == NULL){ infostream<<"Server::ProcessData(): Cancelling: " @@ -2389,750 +2375,29 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } else if(command == TOSERVER_CLICK_ACTIVEOBJECT) { - 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[5]); - - ServerActiveObject *obj = m_env->getActiveObject(id); - - if(obj == NULL) - { - infostream<<"Server: CLICK_ACTIVEOBJECT: object not found" - <m_removed) - return; - - //TODO: Check that object is reasonably close - - // Get ServerRemotePlayer - ServerRemotePlayer *srp = (ServerRemotePlayer*)player; - - // Update wielded item - srp->wieldItem(item_i); - - // Left click, pick/punch - if(button == 0) - { - actionstream<getName()<<" punches object " - <getId()<punch(srp); - -#if 0 - /* - Try creating inventory item - */ - InventoryItem *item = obj->createPickedUpItem(); - - if(item) - { - InventoryList *ilist = player->inventory.getList("main"); - if(ilist != NULL) - { - actionstream<getName()<<" picked up " - <getName()<getBool("creative_mode") == false) - { - // Skip if inventory has no free space - if(ilist->roomForItem(item) == false) - { - infostream<<"Player inventory has no free space"<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. - */ - - actionstream<getName()<<" punches object " - <getId()<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(); - } - } - - v3f playerpos = player->getPosition(); - v3f objpos = obj->getBasePosition(); - v3f dir = (objpos - playerpos).normalize(); - - u16 wear = obj->punch(toolname, dir, player->getName()); - - if(titem) - { - bool weared_out = titem->addWear(wear); - if(weared_out) - mlist->deleteItem(item_i); - SendInventory(player->peer_id); - } - } -#endif - } - // Right click, do something with object - if(button == 1) - { - actionstream<getName()<<" right clicks object " - <getId()<rightClick(srp); - } - - /* - Update player state to client - */ - SendPlayerHP(player); - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); + infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<m_last_good_position.getDistanceFrom(np_f); - if(d > max_d){ - actionstream<<"Player "<getName() - <<" tried to access node from too far: " - <<"d="<SetBlockNotSent(blockpos); - // Do nothing else - return; - } - } - - /* - 0: start digging - */ - if(action == 0) - { - /* - NOTE: This can be used in the future to check if - somebody is cheating, by checking the timing. - */ - bool cannot_punch_node = false; - - MapNode n(CONTENT_IGNORE); - - try - { - n = m_env->getMap().getNode(p_under); - } - catch(InvalidPositionException &e) - { - infostream<<"Server: Not punching: Node not found." - <<" Adding block to emerge queue." - <m_dig_mutex); - client->m_dig_tool_item = -1; -#endif - } - - /* - 3: Digging completed - */ - else if(action == 3) - { - // Mandatory parameter; actually used for nothing - core::map modified_blocks; - - content_t material = CONTENT_IGNORE; - u8 mineral = MINERAL_NONE; - - bool cannot_remove_node = false; - - MapNode n(CONTENT_IGNORE); - try - { - n = m_env->getMap().getNode(p_under); - // Get mineral - mineral = n.getMineral(m_nodedef); - // Get material at position - material = n.getContent(); - // If not yet cancelled - if(cannot_remove_node == false) - { - // If it's not diggable, do nothing - if(m_nodedef->get(material).diggable == false) - { - infostream<<"Server: Not finishing digging: " - <<"Node not diggable" - <getMap().getNodeMetadata(p_under); - if(meta && meta->nodeRemovalDisabled() == true) - { - infostream<<"Server: Not finishing digging: " - <<"Node metadata disables removal" - <getName()<<" cannot remove node" - <<" because privileges are "<SetBlockNotSent(blockpos); - - return; - } - - actionstream<getName()<<" digs "< far_players; - sendRemoveNode(p_under, peer_id, &far_players, 30); - - /* - Update and send inventory - */ - - if(g_settings->getBool("creative_mode") == false) - { - /* - 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 - ToolDiggingProperties tp = - m_toolmgr->getDiggingProperties(toolname); - DiggingProperties prop = - getDiggingProperties(material, &tp, m_nodedef); - - if(prop.diggable == false) - { - infostream<<"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; - - if(mineral != MINERAL_NONE) - item = getDiggedMineralItem(mineral, this); - - // If not mineral - if(item == NULL) - { - const std::string &dug_s = m_nodedef->get(material).dug_item; - if(dug_s != "") - { - std::istringstream is(dug_s, std::ios::binary); - item = InventoryItem::deSerialize(is, this); - } - } - - if(item != NULL) - { - // Add a item to inventory - player->inventory.addItem("main", item); - - // Send inventory - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); - } - - item = NULL; - - if(mineral != MINERAL_NONE) - item = getDiggedMineralItem(mineral, this); - - // If not mineral - if(item == NULL) - { - const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item; - s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity; - if(extra_dug_s != "" && extra_rarity != 0 - && myrand() % extra_rarity == 0) - { - std::istringstream is(extra_dug_s, std::ios::binary); - item = InventoryItem::deSerialize(is, this); - } - } - - if(item != NULL) - { - // Add a item to inventory - player->inventory.addItem("main", item); - - // Send inventory - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); - } - } - - /* - Remove the node - (this takes some time so it is done after the quick stuff) - */ - { - MapEditEventIgnorer ign(&m_ignore_map_edit_events); - - m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks); - } - /* - 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); - } - - /* - Run script hook - */ - scriptapi_environment_on_dignode(m_lua, p_under, n, srp); - } - - /* - 1: place block - */ - else if(action == 1) - { - - 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) - infostream<<"Player "<getName()<<" cannot add node" - <<" because privileges are "<get(n2).buildable_to == false - || no_enough_privs) - { - // Client probably has wrong data. - // Set block not sent, so that client will get - // a valid one. - infostream<<"Client "<SetBlockNotSent(blockpos); - return; - } - } - catch(InvalidPositionException &e) - { - infostream<<"Server: Ignoring ADDNODE: Node not found" - <<" Adding block to emerge queue." - <m_time_from_building = 0.0; - - // Create node data - MaterialItem *mitem = (MaterialItem*)item; - MapNode n; - n.setContent(mitem->getMaterial()); - - actionstream<getName()<<" places material " - <<(int)mitem->getMaterial() - <<" at "<get(n).wall_mounted) - n.param2 = packDir(p_under - p_over); - - // Calculate the direction for furnaces and chests and stuff - if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE) - { - v3f playerpos = player->getPosition(); - v3f blockpos = intToFloat(p_over, BS) - playerpos; - blockpos = blockpos.normalize(); - n.param1 = 0; - if (fabs(blockpos.X) > fabs(blockpos.Z)) { - if (blockpos.X < 0) - n.param1 = 3; - else - n.param1 = 1; - } else { - if (blockpos.Z < 0) - n.param1 = 2; - else - n.param1 = 0; - } - } - - /* - Send to all close-by players - */ - core::list far_players; - sendAddNode(p_over, n, 0, &far_players, 30); - - /* - 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. - - This takes some time so it is done after the quick stuff - */ - core::map modified_blocks; - { - MapEditEventIgnorer ign(&m_ignore_map_edit_events); - - std::string p_name = std::string(player->getName()); - m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name); - } - /* - 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); - } - - /* - Run script hook - */ - scriptapi_environment_on_placenode(m_lua, p_over, n, srp); - - /* - Calculate special events - */ - - /*if(n.d == LEGN(m_nodedef, "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++) - { - - } - }*/ - } - /* - Place other item (not a block) - */ - else - { - 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) - { - infostream<<"Error while placing object: " - "block not found"<getBool("creative_mode") && - (getPlayerPrivs(player) & PRIV_BUILD) == 0) - { - infostream<<"Not allowing player to drop item: " - "creative mode and no build privs"<createSAO(m_env, 0, pos); - - if(obj == NULL) - { - infostream<<"WARNING: item resulted in NULL object, " - <<"not placing onto map" - <getName()<<" places "<getName() - <<" at "<addActiveObject(obj); - - infostream<<"Placed object"<getBool("creative_mode") == false) - { - // Delete the right amount of items from the slot - u16 dropcount = item->getDropCount(); - - // Delete item if all gone - if(item->getCount() <= dropcount) - { - if(item->getCount() < dropcount) - infostream<<"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 + infostream<<"Server: GROUND_ACTION not supported anymore"<getMap().getNodeMetadata(p); if(!meta) return; - if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL")) - return; - SignNodeMetadata *signmeta = (SignNodeMetadata*)meta; - signmeta->setText(text); + + meta->setText(text); - actionstream<getName()<<" writes \""<getName()<<" writes \""<getBool("creative_mode") == true) - { - infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode" - <getType() == IACTION_MOVE) + { + IMoveAction *ma = (IMoveAction*)a; + + ma->from_inv.applyCurrentPlayer(player->getName()); + ma->to_inv.applyCurrentPlayer(player->getName()); + + setInventoryModified(ma->from_inv); + setInventoryModified(ma->to_inv); + + bool from_inv_is_current_player = + (ma->from_inv.type == InventoryLocation::PLAYER) && + (ma->from_inv.name == player->getName()); + + bool to_inv_is_current_player = + (ma->to_inv.type == InventoryLocation::PLAYER) && + (ma->to_inv.name == player->getName()); /* - Handle craftresult specially if not in creative mode + Disable moving items out of craftpreview */ - bool disable_action = false; - if(a->getType() == IACTION_MOVE - && g_settings->getBool("creative_mode") == false) + if(ma->from_list == "craftpreview") { - IMoveAction *ma = (IMoveAction*)a; - if(ma->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); - - /* Print out action */ - InventoryList *list = - player->inventory.getList("craftresult"); - assert(list); - InventoryItem *item = list->getItem(0); - std::string itemname = "NULL"; - if(item) - itemname = item->getName(); - actionstream<getName()<<" crafts " - <to_list == "craftresult" - && ma->from_list == "craftresult") - { - disable_action = true; - - InventoryItem *item1 = rlist->changeItem(0, NULL); - mlist->addItem(item1); - } - } - // Disallow moving items if not allowed to build - else if((getPlayerPrivs(player) & PRIV_BUILD) == 0) + infostream<<"Ignoring IMoveAction from " + <<(ma->from_inv.dump())<<":"<from_list + <<" to "<<(ma->to_inv.dump())<<":"<to_list + <<" because src is "<from_list<to_list == "craftpreview" || ma->to_list == "craftresult") + { + infostream<<"Ignoring IMoveAction from " + <<(ma->from_inv.dump())<<":"<from_list + <<" to "<<(ma->to_inv.dump())<<":"<to_list + <<" because dst is "<to_list<from_inv); + if(owner_from != "" && owner_from != player->getName()) { + infostream<<"WARNING: "<getName() + <<" tried to access an inventory that" + <<" belongs to "<from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + + std::string owner_to = getInventoryOwner(ma->to_inv); + if(owner_to != "" && owner_to != player->getName()) { - Strfnd fn(ma->from_inv); - std::string id0 = fn.next(":"); - if(id0 == "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 && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) { - LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta; - if (lcm->getOwner() != player->getName()) - return; - } - } + infostream<<"WARNING: "<getName() + <<" tried to access an inventory that" + <<" belongs to "<to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + } + } + /* + Handle restrictions and special cases of the drop action + */ + else if(a->getType() == IACTION_DROP) + { + IDropAction *da = (IDropAction*)a; + + da->from_inv.applyCurrentPlayer(player->getName()); + + setInventoryModified(da->from_inv); + + // Disallow dropping items if not allowed to interact + if((getPlayerPrivs(player) & PRIV_INTERACT) == 0) + { + delete a; + return; + } + // If player is not an admin, check for ownership + else if((getPlayerPrivs(player) & PRIV_SERVER) == 0) + { + std::string owner_from = getInventoryOwner(da->from_inv); + if(owner_from != "" && owner_from != player->getName()) { - Strfnd fn(ma->to_inv); - std::string id0 = fn.next(":"); - if(id0 == "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 && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) { - LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta; - if (lcm->getOwner() != player->getName()) - return; - } - } + infostream<<"WARNING: "<getName() + <<" tried to access an inventory that" + <<" belongs to "<getType() == IACTION_CRAFT) + { + ICraftAction *ca = (ICraftAction*)a; + + ca->craft_inv.applyCurrentPlayer(player->getName()); + + setInventoryModified(ca->craft_inv); + + //bool craft_inv_is_current_player = + // (ca->craft_inv.type == InventoryLocation::PLAYER) && + // (ca->craft_inv.name == player->getName()); + + // Disallow crafting if not allowed to interact + if((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) { @@ -3352,6 +2643,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 @@ -3447,16 +2745,25 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) std::istringstream is(datastring, std::ios_base::binary); u8 damage = readU8(is); + ServerRemotePlayer *srp = static_cast(player); + if(g_settings->getBool("enable_damage")) { actionstream<getName()<<" damaged by " <<(int)damage<<" hp at "<getPosition()/BS) <setHP(srp->getHP() - damage); + + if(srp->getHP() == 0 && srp->m_hp_not_sent) + DiePlayer(srp); + + if(srp->m_hp_not_sent) + SendPlayerHP(player); } else { + // Force send (to correct the client's predicted HP) SendPlayerHP(player); } } @@ -3528,8 +2835,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; u16 item = readU16(&data[2]); - player->wieldItem(item); - SendWieldedItem(player); + srp->setWieldIndex(item); + SendWieldedItem(srp); } else if(command == TOSERVER_RESPAWN) { @@ -3540,6 +2847,293 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) actionstream<getName()<<" respawns at " <getPosition()/BS)< tosend; + u16 numtextures = readU16(is); + + infostream<<"Sending "<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); + + verbosestream<<"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) + { + verbosestream<<"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 * 14; // 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 " + <getDescription()<getWieldedItem(); + ToolCapabilities toolcap = + punchitem.getToolCapabilities(m_itemdef); + v3f dir = (pointed_object->getBasePosition() - + (srp->getPosition() + srp->getEyeOffset()) + ).normalize(); + pointed_object->punch(dir, &toolcap, 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 " + <getDescription()<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 " + <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(); - infostream<<"nodemeta at ("<getMap().getNodeMetadata(loc.p); + if(!meta) + return NULL; + return meta->getInventory(); + } + break; + default: + assert(0); } - - infostream<<__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(); @@ -3624,11 +3244,11 @@ void Server::inventoryModified(InventoryContext *c, std::string id) block->raiseModified(MOD_STATE_WRITE_NEEDED); setBlockNotSent(blockpos); - - return; } - - infostream<<__FUNCTION_NAME<<": unknown id "< Server::getPlayerInfo() @@ -3676,7 +3296,7 @@ core::list Server::getPlayerInfo() void Server::peerAdded(con::Peer *peer) { DSTACK(__FUNCTION_NAME); - infostream<<"Server::peerAdded(): peer->id=" + verbosestream<<"Server::peerAdded(): peer->id=" <id<id=" + verbosestream<<"Server::deletingPeer(): peer->id=" <id<<", timeout="<serialize(tmp_os); - os<serialize(tmp_os); + std::ostringstream tmp_os2(std::ios::binary); + compressZlib(tmp_os.str(), tmp_os2); + os<getName()); - start += 2+PLAYERNAME_SIZE; - } - - //JMutexAutoLock conlock(m_con_mutex); - - // Send as reliable - m_con.SendToAll(0, data, true); -} - void Server::SendInventory(u16 peer_id) { DSTACK(__FUNCTION_NAME); - Player* player = m_env->getPlayer(peer_id); + ServerRemotePlayer* player = + static_cast(m_env->getPlayer(peer_id)); assert(player); + player->m_inventory_not_sent = false; + /* Serialize it */ @@ -3883,34 +3452,24 @@ void Server::SendInventory(u16 peer_id) SharedBuffer 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); -} - -std::string getWieldedItemString(const Player *player) -{ - const InventoryItem *item = player->getWieldItem(); - if (item == NULL) - return std::string(""); - std::ostringstream os(std::ios_base::binary); - item->serialize(os); - return os.str(); + memcpy(&data[2], s.c_str(), s.size()); + + // Send as reliable + m_con.Send(peer_id, 0, data, true); } -void Server::SendWieldedItem(const Player* player) +void Server::SendWieldedItem(const ServerRemotePlayer* srp) { DSTACK(__FUNCTION_NAME); - assert(player); + assert(srp); std::ostringstream os(std::ios_base::binary); writeU16(os, TOCLIENT_PLAYERITEM); writeU16(os, 1); - writeU16(os, player->peer_id); - os<peer_id); + os<getWieldedItem().getItemString()); // Make data buffer std::string s = os.str(); @@ -3932,8 +3491,10 @@ void Server::SendPlayerItems() for(i = players.begin(); i != players.end(); ++i) { Player *p = *i; + ServerRemotePlayer *srp = + static_cast(p); writeU16(os, p->peer_id); - os<getWieldedItem().getItemString()); } // Make data buffer @@ -3992,6 +3553,7 @@ void Server::BroadcastChatMessage(const std::wstring &message) void Server::SendPlayerHP(Player *player) { SendHP(m_con, player->peer_id, player->hp); + static_cast(player)->m_hp_not_sent = false; } void Server::SendMovePlayer(Player *player) @@ -4008,7 +3570,7 @@ void Server::SendMovePlayer(Player *player) v3f pos = player->getPosition(); f32 pitch = player->getPitch(); f32 yaw = player->getYaw(); - infostream<<"Server sending TOCLIENT_MOVE_PLAYER" + verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER" <<" pos=("< queue; @@ -4208,6 +3770,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) @@ -4251,50 +3818,32 @@ void Server::SendBlocks(float dtime) } } -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::SendTextures(u16 peer_id) +void Server::PrepareTextures() { DSTACK(__FUNCTION_NAME); - infostream<<"Server::SendTextures(): Sending textures to client"< > texture_bunches; - texture_bunches.push_back(core::list()); - - u32 texture_size_bunch_total = 0; - core::list mods = getMods(m_modspaths); - for(core::list::Iterator i = mods.begin(); - i != mods.end(); i++){ - ModSpec mod = *i; + infostream<<"Server: Calculating texture checksums"<::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; j= bytes_per_bunch){ - texture_bunches.push_back(core::list()); - texture_size_bunch_total = 0; + this->m_Textures[tname] = TextureInformation(tpath,digest_string); + verbosestream<<"Server: sha1 for "< 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 + + /* + u16 command + u32 number of textures + for each texture { + u16 length of name + string name + u16 length of digest string + string sha1_digest + } + */ + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_ANNOUNCE_TEXTURES); + writeU16(os, texture_announcements.size()); + + 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(); + SharedBuffer 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); + + verbosestream<<"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; + } + } /* Create and send packets */ - - u32 num_bunches = texture_bunches.size(); - for(u32 i=0; i::Iterator + j = texture_bunches[i].begin(); + j != texture_bunches[i].end(); j++){ + os<name); + os<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); + // Make data buffer + std::string s = os.str(); + verbosestream<<"Server::SendTexturesRequested(): bunch " + <getName()<<" dies"<hp = 0; - - //TODO: Throw items around - - // Handle players that are not connected - if(player->peer_id == PEER_ID_INEXISTENT){ - RespawnPlayer(player); - return; - } + ServerRemotePlayer *srp = static_cast(player); - SendPlayerHP(player); - - RemoteClient *client = getClient(player->peer_id); - if(client->net_proto_version >= 3) - { - SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0)); - } - else - { - RespawnPlayer(player); - } + infostream<<"Server::DiePlayer(): Player " + <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) { - player->hp = 20; - ServerRemotePlayer *srp = (ServerRemotePlayer*)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()); @@ -4436,41 +4122,17 @@ void Server::UpdateCrafting(u16 peer_id) Player* player = m_env->getPlayer(peer_id); assert(player); - /* - Calculate crafting stuff - */ + // Get a preview for crafting + ItemStack preview; + // No crafting in creative mode if(g_settings->getBool("creative_mode") == false) - { - InventoryList *clist = player->inventory.getList("craft"); - InventoryList *rlist = player->inventory.getList("craftresult"); - - if(rlist && rlist->getUsedSlots() == 0) - player->craftresult_is_preview = true; + getCraftingResult(&player->inventory, preview, false, this); - if(rlist && player->craftresult_is_preview) - { - rlist->clearItems(); - } - if(clist && rlist && player->craftresult_is_preview) - { - // Get result of crafting grid - - std::vector items; - for(u16 i=0; i<9; i++){ - if(clist->getItem(i) == NULL) - items.push_back(NULL); - else - items.push_back(clist->getItem(i)->clone()); - } - CraftPointerInput cpi(3, items); - - InventoryItem *result = m_craftdef->getCraftResult(cpi, this); - //InventoryItem *result = craft_get_result(items, this); - if(result) - rlist->addItem(result); - } - - } // if creative_mode == false + // 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) @@ -4520,11 +4182,61 @@ std::wstring Server::getStatusString() return os.str(); } +u64 Server::getPlayerAuthPrivs(const std::string &name) +{ + try{ + return m_authmanager.getPrivs(name); + } + catch(AuthNotFoundException &e) + { + dstream<<"WARNING: Auth not found for "<get("name")) + return PRIV_ALL; + return getPlayerAuthPrivs(name); +} + +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()); + if(m_path_config != "") + g_settings->updateConfigFile(m_path_config.c_str()); } void Server::notifyPlayer(const char *name, const std::wstring msg) @@ -4540,11 +4252,19 @@ 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 -IToolDefManager* Server::getToolDefManager() +IItemDefManager* Server::getItemDefManager() { - return m_toolmgr; + return m_itemdef; } INodeDefManager* Server::getNodeDefManager() { @@ -4563,9 +4283,9 @@ u16 Server::allocateUnknownNodeId(const std::string &name) return m_nodedef->allocateDummy(name); } -IWritableToolDefManager* Server::getWritableToolDefManager() +IWritableItemDefManager* Server::getWritableItemDefManager() { - return m_toolmgr; + return m_itemdef; } IWritableNodeDefManager* Server::getWritableNodeDefManager() { @@ -4576,6 +4296,17 @@ 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; @@ -4639,12 +4370,13 @@ v3f findSpawnPos(ServerMap &map) return intToFloat(nodepos, BS); } -Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id) +ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id) { /* Try to get an existing player */ - Player *player = m_env->getPlayer(name); + ServerRemotePlayer *player = + static_cast(m_env->getPlayer(name)); if(player != NULL) { // If player is already connected, cancel @@ -4657,15 +4389,24 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id // Got one. player->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")) { // Warning: double code below // Backup actual inventory - player->inventory_backup = new Inventory(); + player->inventory_backup = new Inventory(m_itemdef); *(player->inventory_backup) = player->inventory; // Set creative inventory - craft_set_creative_inventory(player, this); + player->resetInventory(); + scriptapi_get_creative_inventory(m_lua, player); } return player; @@ -4685,12 +4426,6 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id Create a new player */ { - // Add authentication stuff - m_authmanager.add(name); - m_authmanager.setPassword(name, password); - m_authmanager.setPrivs(name, - stringToPrivs(g_settings->get("default_privs"))); - /* Set player position */ infostream<<"Server: Finding spawn place for player \"" @@ -4699,12 +4434,13 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id v3f pos = findSpawnPos(m_env->getServerMap()); player = new ServerRemotePlayer(m_env, pos, peer_id, name); + ServerRemotePlayer *srp = static_cast(player); /* Add player to environment */ m_env->addPlayer(player); + m_env->addActiveObject(srp); /* Run scripts */ - ServerRemotePlayer *srp = (ServerRemotePlayer*)player; scriptapi_on_newplayer(m_lua, srp); /* Add stuff to inventory */ @@ -4712,10 +4448,11 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id { // Warning: double code above // Backup actual inventory - player->inventory_backup = new Inventory(); + player->inventory_backup = new Inventory(m_itemdef); *(player->inventory_backup) = player->inventory; // Set creative inventory - craft_set_creative_inventory(player, this); + player->resetInventory(); + scriptapi_get_creative_inventory(m_lua, player); } return player; @@ -4775,10 +4512,12 @@ void Server::handlePeerChange(PeerChange &c) obj->m_known_by_count--; } + ServerRemotePlayer* player = + static_cast(m_env->getPlayer(c.peer_id)); + // Collect information about leaving in chat std::wstring message; { - Player *player = m_env->getPlayer(c.peer_id); if(player != NULL) { std::wstring name = narrow_to_wide(player->getName()); @@ -4789,21 +4528,19 @@ void Server::handlePeerChange(PeerChange &c) 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; - - /* - Print out action - */ if(player != NULL) { std::ostringstream os(std::ios_base::binary); @@ -4835,10 +4572,11 @@ void Server::handlePeerChange(PeerChange &c) m_clients.remove(c.peer_id); // Send player info to all remaining clients - SendPlayerInfos(); + //SendPlayerInfos(); // Send leave chat message to all remaining clients - BroadcastChatMessage(message); + if(message.length() != 0) + BroadcastChatMessage(message); } // PEER_REMOVED else @@ -4853,7 +4591,7 @@ void Server::handlePeerChanges() { PeerChange c = m_peer_change_queue.pop_front(); - infostream<<"Server: Handling peer change: " + verbosestream<<"Server: Handling peer change: " <<"id="<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); + sleep_ms((int)(steplen*1000.0)); } - server.step(0.030); + server.step(steplen); if(server.getShutdownRequested() || kill) { - infostream<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)) { infostream<<"Profiler:"<print(infostream); g_profiler->clear(); } } - - /* - Player info - */ - static int counter = 0; - counter--; - if(counter <= 0) - { - counter = 10; - - core::list list = server.getPlayerInfo(); - core::list::Iterator i; - static u32 sum_old = 0; - u32 sum = PIChecksum(list); - if(sum != sum_old) - { - infostream<PrintLine(&infostream); - } - } - sum_old = sum; - } } }