X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fserver.cpp;h=5646c0ac9c9dffdccff84b15f7e5ee00d83ce738;hb=cd563473fa298db3b910009f26ba263bacd84be9;hp=97eaa3a3ea48f12c4f876e917b8014fda07f7869;hpb=4acad897cf895e7ddf0810c35e4054588453f913;p=oweals%2Fminetest.git diff --git a/src/server.cpp b/src/server.cpp index 97eaa3a3e..5646c0ac9 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" @@ -36,6 +37,19 @@ with this program; if not, write to the Free Software Foundation, Inc., #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 "tooldef.h" +#include "craftdef.h" +#include "craftitemdef.h" +#include "mapgen.h" +#include "content_abm.h" + +#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0) @@ -68,6 +82,8 @@ void * ServerThread::Thread() { ThreadStarted(); + log_register_thread("ServerThread"); + DSTACK(__FUNCTION_NAME); BEGIN_DEBUG_EXCEPTION_HANDLER @@ -82,7 +98,7 @@ void * ServerThread::Thread() m_server->AsyncRunStep(); } - //dout_server<<"Running m_server->Receive()"<Receive()"<Receive(); } catch(con::NoIncomingDataException &e) @@ -90,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 @@ -129,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"<isGenerated() == false) - { - if(enable_mapgen_debug_info) - dstream<<"EmergeThread: generating"<isGenerated() == false)){ if(enable_mapgen_debug_info) - dstream<<"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) { - /*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); + + /* + Do some post-generate stuff + */ - // 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); + 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); } -#endif - } - // If we got no block, there should be no invalidated blocks - else - { - //assert(lighting_invalidated_blocks.size() == 0); } - }//envlock - + if(block == NULL) + got_block = false; + /* Set sent status of modified blocks on clients */ @@ -330,7 +325,9 @@ void * EmergeThread::Thread() } - END_DEBUG_EXCEPTION_HANDLER + END_DEBUG_EXCEPTION_HANDLER(errorstream) + + log_deregister_thread(); return NULL; } @@ -345,25 +342,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); @@ -385,7 +381,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, 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 @@ -450,27 +442,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="< players = server->m_env.getPlayers(true); + core::list players = server->m_env->getPlayers(true); // Write player count u16 playercount = players.size(); @@ -830,137 +828,18 @@ void RemoteClient::SendObjectData( } /* - 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 + Get and write object data (dummy, for compatibility) */ - 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); + writeU16(buf, 0); os.write((char*)buf, 2); - // Write block objects - os< &l) return checksum; } +/* + Mods +*/ + +struct ModSpec +{ + std::string name; + std::string path; + std::set depends; + std::set unsatisfied_depends; + + ModSpec(const std::string &name_="", const std::string path_="", + const std::set &depends_=std::set()): + name(name_), + path(path_), + depends(depends_), + unsatisfied_depends(depends_) + {} +}; + +// Get a dependency-sorted list of ModSpecs +static core::list getMods(core::list &modspaths) +{ + std::queue mods_satisfied; + core::list mods_unsorted; + core::list mods_sorted; + 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 depends; + std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(), + std::ios_base::binary); + while(is.good()){ + std::string dep; + std::getline(is, dep); + dep = trim(dep); + if(dep != "") + depends.insert(dep); + } + ModSpec spec(modname, modpath, depends); + mods_unsorted.push_back(spec); + if(depends.empty()) + mods_satisfied.push(spec); + } + } + // Sort by depencencies + while(!mods_satisfied.empty()){ + ModSpec mod = mods_satisfied.front(); + mods_satisfied.pop(); + mods_sorted.push_back(mod); + for(core::list::Iterator i = mods_unsorted.begin(); + i != mods_unsorted.end(); i++){ + ModSpec &mod2 = *i; + if(mod2.unsatisfied_depends.empty()) + continue; + mod2.unsatisfied_depends.erase(mod.name); + if(!mod2.unsatisfied_depends.empty()) + continue; + mods_satisfied.push(mod2); + } + } + // Check unsatisfied dependencies + for(core::list::Iterator i = mods_unsorted.begin(); + i != mods_unsorted.end(); i++){ + ModSpec &mod = *i; + if(mod.unsatisfied_depends.empty()) + continue; + errorstream<<"mod \""<::iterator + i = mod.unsatisfied_depends.begin(); + i != mod.unsatisfied_depends.end(); i++){ + errorstream<<" \""<<(*i)<<"\""; + } + errorstream<<". Loading nevertheless."< mods = getMods(m_modspaths); + for(core::list::Iterator i = mods.begin(); + i != mods.end(); i++){ + ModSpec mod = *i; + infostream<<"Server: Loading mod \""<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); + + /* + 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 */ @@ -1167,13 +1195,25 @@ 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_toolmgr; + delete m_nodedef; + delete m_craftdef; + delete m_craftitemdef; + + // Deinitialize scripting + infostream<<"Server: Deinitializing scripting"<add("Server::AsyncRunStep (num)", 1); + float dtime; { JMutexAutoLock lock1(m_step_dtime_mutex); @@ -1229,17 +1273,18 @@ void Server::AsyncRunStep() } { - ScopeProfiler sp(&g_profiler, "Server: selecting and sending " - "blocks to clients"); + ScopeProfiler sp(g_profiler, "Server: sel and send 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); @@ -1299,10 +1343,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); } @@ -1312,64 +1356,131 @@ void Server::AsyncRunStep() { JMutexAutoLock lock(m_env_mutex); // Step environment - ScopeProfiler sp(&g_profiler, "Server: environment step"); - m_env.step(dtime); + 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 = 5.15; + const float map_timer_and_unload_dtime = 2.92; if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) { JMutexAutoLock lock(m_env_mutex); // Run Map's timers and unload unused data - 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")); + 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 */ - m_liquid_transform_timer += dtime; - if(m_liquid_transform_timer >= 1.00) { - m_liquid_transform_timer -= 1.00; - JMutexAutoLock lock(m_env_mutex); + JMutexAutoLock lock2(m_con_mutex); - ScopeProfiler sp(&g_profiler, "Server: liquid transform"); - - core::map modified_blocks; - m_env.getMap().transformLiquids(modified_blocks); -#if 0 - /* - Update lighting - */ - core::map lighting_modified_blocks; - ServerMap &map = ((ServerMap&)m_env.getMap()); - map.updateLighting(modified_blocks, lighting_modified_blocks); + //float player_max_speed = BS * 4.0; // Normal speed + float player_max_speed = BS * 20; // Fast speed + float player_max_speed_up = BS * 20; - // Add blocks modified by lighting to modified_blocks - for(core::map::Iterator - i = lighting_modified_blocks.getIterator(); - i.atEnd() == false; i++) - { - MapBlock *block = i.getNode()->getValue(); - modified_blocks.insert(block->getPos(), block); - } -#endif - /* - Set the modified blocks unsent for all the clients - */ - - JMutexAutoLock lock2(m_con_mutex); + player_max_speed *= 2.5; // Tolerance + player_max_speed_up *= 2.5; for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + 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; + } + + /* + 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); + } + } + } + + /* Transform liquids */ + m_liquid_transform_timer += dtime; + if(m_liquid_transform_timer >= 1.00) + { + m_liquid_transform_timer -= 1.00; + + JMutexAutoLock lock(m_env_mutex); + + ScopeProfiler sp(g_profiler, "Server: liquid transform"); + + core::map modified_blocks; + m_env->getMap().transformLiquids(modified_blocks); +#if 0 + /* + Update lighting + */ + core::map lighting_modified_blocks; + ServerMap &map = ((ServerMap&)m_env->getMap()); + map.updateLighting(modified_blocks, lighting_modified_blocks); + + // Add blocks modified by lighting to modified_blocks + for(core::map::Iterator + i = lighting_modified_blocks.getIterator(); + i.atEnd() == false; i++) + { + MapBlock *block = i.getNode()->getValue(); + modified_blocks.insert(block->getPos(), block); + } +#endif + /* + Set the modified blocks unsent for all the clients + */ + + JMutexAutoLock lock2(m_con_mutex); + + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) { RemoteClient *client = i.getNode()->getValue(); @@ -1390,48 +1501,51 @@ 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); + 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()); @@ -1487,12 +1601,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(); @@ -1524,7 +1638,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 } @@ -1565,7 +1679,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 @@ -1574,7 +1688,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; @@ -1657,7 +1771,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: "<type == MEET_ADDNODE) { - //dstream<<"Server: MEET_ADDNODE"<p, event->n, event->already_known_by_peer, @@ -1716,7 +1830,7 @@ void Server::AsyncRunStep() } else if(event->type == MEET_REMOVENODE) { - //dstream<<"Server: MEET_REMOVENODE"<p, event->already_known_by_peer, @@ -1727,13 +1841,13 @@ void Server::AsyncRunStep() } else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED) { - dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<p); } else if(event->type == MEET_OTHER) { - dstream<<"Server: MEET_OTHER"<::Iterator i = event->modified_blocks.getIterator(); @@ -1746,7 +1860,7 @@ void Server::AsyncRunStep() else { prof.add("unknown", 1); - dstream<<"WARNING: Server: Unknown MapEditEvent " + infostream<<"WARNING: Server: Unknown MapEditEvent " <<((u32)event->type)<getKey(); modified_blocks2.insert(p, - m_env.getMap().getBlockNoCreateNoEx(p)); + m_env->getMap().getBlockNoCreateNoEx(p)); } // Set blocks not sent for(core::list::Iterator @@ -1788,25 +1902,24 @@ void Server::AsyncRunStep() if(got_any_events) { - dstream<<"Server: MapEditEvents:"<= g_settings.getFloat("objectdata_interval")) + 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"); + //ScopeProfiler sp(g_profiler, "Server: sending player positions"); SendObjectData(counter); @@ -1833,11 +1946,11 @@ void Server::AsyncRunStep() { float &counter = m_savemap_timer; counter += dtime; - if(counter >= g_settings.getFloat("server_map_save_interval")) + if(counter >= g_settings->getFloat("server_map_save_interval")) { counter = 0.0; - ScopeProfiler sp(&g_profiler, "Server: saving stuff"); + ScopeProfiler sp(g_profiler, "Server: saving stuff"); // Auth stuff if(m_authmanager.isModified()) @@ -1850,28 +1963,14 @@ void Server::AsyncRunStep() // 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) - { - dout_server<<"Server: Unloaded "<getMap().save(MOD_STATE_WRITE_NEEDED); // Save players - m_env.serializePlayers(m_mapsavedir); + m_env->serializePlayers(m_mapsavedir); // Save environment metadata - m_env.saveMeta(m_mapsavedir); + m_env->saveMeta(m_mapsavedir); } } } @@ -1879,14 +1978,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 @@ -1897,7 +1995,7 @@ void Server::Receive() } catch(con::InvalidIncomingDataException &e) { - derr_server<<"Server::Receive(): " + infostream<<"Server::Receive(): " "InvalidIncomingDataException: what()=" <removePlayer(peer_id);*/ } } @@ -1925,28 +2023,27 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - con::Peer *peer; 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<address.serializeString())){ - SendAccessDenied(m_con, peer_id, - L"Your ip is banned. Banned name was " - +narrow_to_wide(m_banmanager.getBanName( - peer->address.serializeString()))); - m_con.deletePeer(peer_id, false); - return; - } - - u8 peer_ser_ver = getClient(peer->id)->serialization_version; + u8 peer_ser_ver = getClient(peer_id)->serialization_version; try { @@ -1966,8 +2063,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<id)->net_proto_version = net_proto_version; + getClient(peer_id)->net_proto_version = net_proto_version; if(net_proto_version == 0) { - SendAccessDenied(m_con, peer_id, - L"Your client is too old. Please upgrade."); + 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"." + ); + return; + } + } /* Set up player @@ -2025,7 +2141,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(playername[0]=='\0') { - derr_server<get("default_password"); } - /*dstream<<"Server: Client gave password '"<get("default_privs"))); m_authmanager.save(); } + + // Enforce user limit. + // Don't enforce for users that have some admin right + if(m_clients.size() >= g_settings->getU16("max_users") && + (m_authmanager.getPrivs(playername) + & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 && + playername != g_settings->get("name")) + { + SendAccessDenied(m_con, peer_id, L"Too many users."); + return; + } // Get player Player *player = emergePlayer(playername, password, peer_id); - - /*{ - // DEBUG: Test serialization - std::ostringstream test_os; - player->serialize(test_os); - dstream<<"Player serialization test: \""<deSerialize(test_is); - }*/ - // 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 */ @@ -2145,7 +2235,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); @@ -2161,38 +2251,49 @@ 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 tool definitions + SendToolDef(m_con, peer_id, m_toolmgr); + + // Send node definitions + SendNodeDef(m_con, peer_id, m_nodedef); + + // Send CraftItem definitions + SendCraftItemDef(m_con, peer_id, m_craftitemdef); + + // Send textures + SendTextures(peer_id); // Send player info to all players 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); // 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 @@ -2201,7 +2302,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()); @@ -2213,26 +2314,57 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } // Warnings about protocol version can be issued here - /*if(getClient(peer->id)->net_proto_version == 0) + if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION) { - SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER"); - }*/ + 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 + */ + { + 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()<<" joins game. List of players: " + <GotBlock(p); @@ -2306,7 +2439,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); @@ -2314,1123 +2447,1219 @@ 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) + /*// Ignore inventory changes if in creative mode + if(g_settings->getBool("creative_mode") == true) + { + infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode" + <m_removed) - return; - - //TODO: Check that object is reasonably close - - // Left click, pick object up (usually) - if(button == 0) + /* + Handle restrictions and special cases of the move action + */ + if(a->getType() == IACTION_MOVE + && g_settings->getBool("creative_mode") == false) { + InventoryList *rlist = player->inventory.getList("craftresult"); + assert(rlist); + InventoryList *clist = player->inventory.getList("craft"); + assert(clist); + InventoryList *mlist = player->inventory.getList("main"); + assert(mlist); + + IMoveAction *ma = (IMoveAction*)a; + /* - Try creating inventory item + Disable moving items into craftresult from elsewhere */ - InventoryItem *item = obj->createPickedUpItem(); - - if(item) + if(ma->to_inv == "current_player" + && ma->to_list == "craftresult" + && (ma->from_inv != "current_player" + || ma->from_list != "craftresult")) { - InventoryList *ilist = player->inventory.getList("main"); - if(ilist != NULL) - { - if(g_settings.getBool("creative_mode") == false) - { - // Skip if inventory has no free space - if(ilist->getUsedSlots() == ilist->getSize()) - { - dout_server<<"Player inventory has no free space"<addItem(item); - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); - } - - // Remove object from environment - obj->m_removed = true; - } + infostream<<"Ignoring IMoveAction from " + <from_inv<<":"<from_list + <<" to "<to_inv<<":"<to_list + <<" because dst is craftresult" + <<" and src isn't craftresult"<from_inv == "current_player" + && ma->from_list == "craftresult" + && player->craftresult_is_preview) { /* - Item cannot be picked up. Punch it instead. + If the craftresult is placed on itself, crafting takes + place and result is moved into main list */ + if(ma->to_inv == "current_player" + && ma->to_list == "craftresult") + { + // Except if main list doesn't have free slots + if(mlist->getFreeSlots() == 0){ + infostream<<"Cannot craft: Main list doesn't have" + <<" free slots"<craftresult_is_preview = false; + clist->decrementMaterials(1); - ToolItem *titem = NULL; - std::string toolname = ""; + InventoryItem *item1 = rlist->changeItem(0, NULL); + mlist->addItem(item1); - 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(); - } + srp->m_inventory_not_sent = true; + + delete a; + return; } + /* + Disable action if there are no free slots in + destination + + If the item is placed on an item that is not of the + same kind, the existing item will be first moved to + craftresult and immediately moved to the free slot. + */ + do{ + Inventory *inv_to = getInventory(&c, ma->to_inv); + if(!inv_to) break; + InventoryList *list_to = inv_to->getList(ma->to_list); + if(!list_to) break; + if(list_to->getFreeSlots() == 0){ + infostream<<"Cannot craft: Destination doesn't have" + <<" free slots"<getPosition(); - v3f objpos = obj->getBasePosition(); - v3f dir = (objpos - playerpos).normalize(); + /* + Ok, craft normally. + */ + player->craftresult_is_preview = false; + clist->decrementMaterials(1); - u16 wear = obj->punch(toolname, dir); + /* Print out action */ + InventoryItem *item = rlist->getItem(0); + std::string itemstring = "NULL"; + if(item) + itemstring = item->getItemString(); + actionstream<getName()<<" crafts " + <apply(&c, this, m_env); - if(titem) - { - bool weared_out = titem->addWear(wear); - if(weared_out) - mlist->deleteItem(item_i); - SendInventory(player->peer_id); - } + delete a; + return; } - } - // Right click, do something with object - if(button == 1) - { - // Track hp changes super-crappily - u16 oldhp = player->hp; - - // Do stuff - obj->rightClick(player); + + /* + Non-crafting move + */ - // Send back stuff - if(player->hp != oldhp) + // Disallow moving items in elsewhere than player's inventory + // if not allowed to build + if((getPlayerPrivs(player) & PRIV_BUILD) == 0 + && (ma->from_inv != "current_player" + || ma->to_inv != "current_player")) { - SendPlayerHP(player); + infostream<<"Cannot move outside of player's inventory: " + <<"No build privilege"<id); - JMutexAutoLock digmutex(client->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; - try + // If player is not an admin, check for ownership of src + if(ma->from_inv != "current_player" + && (getPlayerPrivs(player) & PRIV_SERVER) == 0) { - MapNode n = m_env.getMap().getNode(p_under); - // Get mineral - mineral = n.getMineral(); - // Get material at position - material = n.getContent(); - // If not yet cancelled - if(cannot_remove_node == false) + Strfnd fn(ma->from_inv); + std::string id0 = fn.next(":"); + if(id0 == "nodemeta") { - // If it's not diggable, do nothing - if(content_diggable(material) == false) + 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->getOwner() != "" && + meta->getOwner() != player->getName()) { - derr_server<<"Server: Not finishing digging: " - <<"Node not diggable" + infostream<<"Cannot move item: " + "not owner of metadata" <to_inv != "current_player" + && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + { + Strfnd fn(ma->to_inv); + std::string id0 = fn.next(":"); + if(id0 == "nodemeta") { - // Get node metadata - NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under); - if(meta && meta->nodeRemovalDisabled() == true) + 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->getOwner() != "" && + meta->getOwner() != player->getName()) { - derr_server<<"Server: Not finishing digging: " - <<"Node metadata disables removal" + infostream<<"Cannot move item: " + "not owner of metadata" <getType() == IACTION_DROP) + { + IDropAction *da = (IDropAction*)a; + // Disallow dropping items if not allowed to build if((getPlayerPrivs(player) & PRIV_BUILD) == 0) { - dstream<<"Player "<getName()<<" cannot remove node" - <<" because privileges are "<SetBlockNotSent(blockpos); - + delete a; return; } - - /* - Send the removal to all close-by players. - - If other player is close, send REMOVENODE - - Otherwise set blocks not sent - */ - core::list far_players; - sendRemoveNode(p_under, peer_id, &far_players, 30); - - /* - Update and send inventory - */ - - if(g_settings.getBool("creative_mode") == false) + // If player is not an admin, check for ownership + else if (da->from_inv != "current_player" + && (getPlayerPrivs(player) & PRIV_SERVER) == 0) { - /* - 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; - - if(mineral != MINERAL_NONE) - item = getDiggedMineralItem(mineral); - - // If not mineral - if(item == NULL) + Strfnd fn(da->from_inv); + std::string id0 = fn.next(":"); + if(id0 == "nodemeta") { - std::string &dug_s = content_features(material).dug_item; - if(dug_s != "") + 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->getOwner() != "" && + meta->getOwner() != player->getName()) { - std::istringstream is(dug_s, std::ios::binary); - item = InventoryItem::deSerialize(is); + infostream<<"Cannot move item: " + "not owner of metadata" + <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); } } + // Do the action + a->apply(&c, this, m_env); + // Eat the action + delete a; + } + else if(command == TOSERVER_CHAT_MESSAGE) + { /* - 1: place block + u16 command + u16 length + wstring message */ - else if(action == 1) + u8 buf[6]; + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + // Read stuff + is.read((char*)buf, 2); + u16 len = readU16(buf); + + std::wstring message; + for(u16 i=0; iinventory.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 "<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 + bool send_to_sender = false; + // Whether to send to other players + bool send_to_others = false; + + // Local player gets all privileges regardless of + // what's set on their account. + u64 privs = getPlayerPrivs(player); - if(content_features(n2).buildable_to == false - || no_enough_privs) - { - // Client probably has wrong data. - // Set block not sent, so that client will get - // a valid one. - dstream<<"Client "<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.setContent(mitem->getMaterial()); + // Parse commands + if(message[0] == L'/') + { + size_t strip_size = 1; + if (message[1] == L'#') // support old-style commans + ++strip_size; + message = message.substr(strip_size); - // Calculate direction for wall mounted stuff - if(content_features(n).wall_mounted) - n.param2 = packDir(p_under - p_over); + WStrfnd f1(message); + f1.next(L" "); // Skip over /#whatever + std::wstring paramstring = f1.next(L""); - // Calculate the direction for furnaces and chests and stuff - if(content_features(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; - } - } + ServerCommandContext *ctx = new ServerCommandContext( + str_split(message, L' '), + paramstring, + this, + m_env, + player, + privs); - /* - 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. + std::wstring reply(processServerCommand(ctx)); + send_to_sender = ctx->flags & SEND_TO_SENDER; + send_to_others = ctx->flags & SEND_TO_OTHERS; - This takes some time so it is done after the quick stuff - */ - core::map modified_blocks; - { - MapEditEventIgnorer ign(&m_ignore_map_edit_events); + if (ctx->flags & SEND_NO_PREFIX) + line += reply; + else + line += L"Server: " + reply; - m_env.getMap().addNodeAndUpdate(p_over, n, 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); - } + delete ctx; - /* - 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++) - { - - } - }*/ + } + else + { + if(privs & PRIV_SHOUT) + { + line += L"<"; + line += name; + line += L"> "; + line += message; + send_to_others = true; } - /* - 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) - { - derr_server<<"Error while placing object: " - "block not found"<createSAO(&m_env, 0, pos); + /* + Send the message to clients + */ + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + // Get client and check that it is valid + RemoteClient *client = i.getNode()->getValue(); + assert(client->peer_id == i.getNode()->getKey()); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; - if(obj == NULL) - { - derr_server<<"WARNING: item resulted in NULL object, " - <<"not placing onto map" - <peer_id); + if(sender_selected == true && send_to_sender == false) + continue; + if(sender_selected == false && send_to_others == false) + continue; - if(g_settings.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) - 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); - } - } + SendChatMessage(client->peer_id, line); } + } + } + else if(command == TOSERVER_DAMAGE) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + u8 damage = readU8(is); - } // action == 1 - - /* - Catch invalid actions - */ + if(g_settings->getBool("enable_damage")) + { + actionstream<getName()<<" damaged by " + <<(int)damage<<" hp at "<getPosition()/BS) + <getName(); + + if(m_authmanager.exists(playername) == false) { - derr_server<<"Error while setting sign text: " - "block not found"<getObject(id); - if(obj == NULL) + std::string checkpwd = m_authmanager.getPassword(playername); + + if(oldpwd != checkpwd) { - derr_server<<"Error while setting sign text: " - "object not found"<getName()<<" changes password"<getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN) - { - derr_server<<"Error while setting sign text: " - "object is not a sign"<setText(text); - - obj->getBlock()->setChangedFlag(); + u16 item = readU16(&data[2]); + player->wieldItem(item); + SendWieldedItem(player); } - else if(command == TOSERVER_SIGNNODETEXT) + else if(command == TOSERVER_RESPAWN) { - if((getPlayerPrivs(player) & PRIV_BUILD) == 0) + if(player->hp != 0) return; - /* - u16 command - v3s16 p - u16 textlen - textdata - */ + + RespawnPlayer(player); + + actionstream<getName()<<" respawns at " + <getPosition()/BS)<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(); - } + /* + [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); - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd()==false; i++) + infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<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); - } - } + pointed_pos = intToFloat(p_under, BS); } - - if(disable_action == false) + else if(pointed.type == POINTEDTHING_OBJECT) { - // Feed action to player inventory - a->apply(&c, this); - // Eat the action - delete a; + pointed_pos = pointed_object->getBasePosition(); } - else - { - // Send inventory - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); + + float d = player_pos.getDistanceFrom(pointed_pos); + 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; } } - else - { - dstream<<"TOSERVER_INVENTORY_ACTION: " - <<"InventoryAction::deSerialize() returned NULL" - <getName() + <<" because privileges are "<getName()); - - // Line to send to players - std::wstring line; - // Whether to send to the player that sent the line - bool send_to_sender = false; - // Whether to send to other players - bool send_to_others = false; - - // Local player gets all privileges regardless of - // what's set on their account. - u64 privs = getPlayerPrivs(player); - - // Parse commands - if(message[0] == L'/') + /* + 0: start digging or punch object + */ + if(action == 0) { - size_t strip_size = 1; - if (message[1] == L'#') // support old-style commans - ++strip_size; - message = message.substr(strip_size); - - WStrfnd f1(message); - f1.next(L" "); // Skip over /#whatever - std::wstring paramstring = f1.next(L""); - - ServerCommandContext *ctx = new ServerCommandContext( - str_split(message, L' '), - paramstring, - this, - &m_env, - player, - privs); + if(pointed.type == POINTEDTHING_NODE) + { + /* + NOTE: This can be used in the future to check if + somebody is cheating, by checking the timing. + */ + bool cannot_punch_node = !build_priv; - std::wstring reply(processServerCommand(ctx)); - send_to_sender = ctx->flags & SEND_TO_SENDER; - send_to_others = ctx->flags & SEND_TO_OTHERS; + MapNode n(CONTENT_IGNORE); - if (ctx->flags & SEND_NO_PREFIX) - line += reply; - else - line += L"Server: " + reply; + try + { + n = m_env->getMap().getNode(p_under); + } + catch(InvalidPositionException &e) + { + infostream<<"Server: Not punching: Node not found." + <<" Adding block to emerge queue." + < "; - line += message; - send_to_others = true; + /* + Run script hook + */ + scriptapi_environment_on_punchnode(m_lua, p_under, n, srp); } - else + else if(pointed.type == POINTEDTHING_OBJECT) { - line += L"Server: You are not allowed to shout"; - send_to_sender = true; - } - } - - if(line != L"") - { - dstream<<"CHAT: "<::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) - { - // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; + // Skip if object has been removed + if(pointed_object->m_removed) + return; - // Filter recipient - bool sender_selected = (peer_id == client->peer_id); - if(sender_selected == true && send_to_sender == false) - continue; - if(sender_selected == false && send_to_others == false) - continue; + actionstream<getName()<<" punches object " + <peer_id, line); - } - } - } - else if(command == TOSERVER_DAMAGE) - { - 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; + // Do stuff + pointed_object->punch(srp); } - else - { - player->hp = 0; - dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies" - <setPosition(pos); - player->hp = 20; - SendMovePlayer(player); - SendPlayerHP(player); - - //TODO: Throw items around - } - } + } // action == 0 - SendPlayerHP(player); - } - else if(command == TOSERVER_PASSWORD) - { /* - [0] u16 TOSERVER_PASSWORD - [2] u8[28] old password - [30] u8[28] new password + 1: stop digging */ - - if(datasize != 2+PASSWORD_SIZE*2) - return; - /*char password[PASSWORD_SIZE]; - for(u32 i=0; i modified_blocks; - std::string playername = player->getName(); + content_t material = CONTENT_IGNORE; + u8 mineral = MINERAL_NONE; - if(m_authmanager.exists(playername) == false) + bool cannot_remove_node = !build_priv; + + 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" + <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); + } + + srp->m_inventory_not_sent = true; + } + } + + /* + 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); + srp->m_inventory_not_sent = true; + } + + 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); + srp->m_inventory_not_sent = true; + } + } + + /* + 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); + } // action == 2 + + /* + 3: place block or right-click object + */ + else if(action == 3) { - dstream<<"Server: playername not found in authmanager"<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()) + { + bool cannot_place_node = !build_priv; + + try{ + // Don't add a node if this is not a free space + MapNode n2 = m_env->getMap().getNode(p_above); + if(m_nodedef->get(n2).buildable_to == false) + { + infostream<<"Client "<SetBlockNotSent(blockpos); + return; + } + + // Reset build time counter + getClient(peer_id)->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_above); + + // 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_above, 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_above, 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); + srp->m_inventory_not_sent = true; + } + + /* + 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_above, 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_above, 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 + { + if(!build_priv) + { + infostream<<"Not allowing player to place item: " + "no build privileges"<getBasePosition(); + + // Randomize a bit + pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0; + pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0; + } + + //pos.Y -= BS*0.45; + //pos.Y -= BS*0.25; // let it drop a bit + + /* + Check that the block is loaded so that the item + can properly be added to the static list too + */ + v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS)); + MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos); + if(block==NULL) + { + infostream<<"Error while placing item: " + "block not found"<getName()<<" places "<getName() + <<" at "<dropOrPlace(m_env, srp, pos, true, -1); + if(remove && g_settings->getBool("creative_mode") == false) + { + InventoryList *ilist = player->inventory.getList("main"); + if(ilist){ + // Remove from inventory and send inventory + ilist->deleteItem(item_i); + srp->m_inventory_not_sent = true; + } + } + } + } + else if(pointed.type == POINTEDTHING_OBJECT) + { + // Right click object + + if(!build_priv) + return; + + // Skip if object has been removed + if(pointed_object->m_removed) + return; + + actionstream<getName()<<" right-clicks object " + <rightClick(srp); + } + + } // action == 3 + + /* + 4: use + */ + else if(action == 4) + { + 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; + + // Requires build privs + if(!build_priv) + { + infostream<<"Not allowing player to use item: " + "no build privileges"<getName()<<" uses "<getName() + <<", pointing at "<use(m_env, srp, pointed); + + if(remove && g_settings->getBool("creative_mode") == false) + { + InventoryList *ilist = player->inventory.getList("main"); + if(ilist){ + // Remove from inventory and send inventory + ilist->deleteItem(item_i); + srp->m_inventory_not_sent = true; + } + } + + } // action == 4 + + /* + Catch invalid actions + */ + else { - dstream<<"Server: invalid old password"<wieldItem(item); - SendWieldedItem(player); + // Complete add_to_inventory_later + srp->completeAddToInventoryLater(item_i); } else { - derr_server<<"WARNING: Server::ProcessData(): Ignoring " + infostream<<"Server::ProcessData(): Ignoring " "unknown command "<clone(); @@ -3462,15 +3691,15 @@ Inventory* Server::getInventory(InventoryContext *c, std::string id) p.X = stoi(fn.next(",")); p.Y = stoi(fn.next(",")); p.Z = stoi(fn.next(",")); - NodeMetadata *meta = m_env.getMap().getNodeMetadata(p); + NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); if(meta) return meta->getInventory(); - dstream<<"nodemeta at ("<current_player); - // Send inventory - UpdateCrafting(c->current_player->peer_id); - SendInventory(c->current_player->peer_id); + ServerRemotePlayer *srp = + static_cast(c->current_player); + srp->m_inventory_not_sent = true; return; } @@ -3495,22 +3724,20 @@ void Server::inventoryModified(InventoryContext *c, std::string id) p.Z = stoi(fn.next(",")); v3s16 blockpos = getNodeBlockPos(p); - NodeMetadata *meta = m_env.getMap().getNodeMetadata(p); + NodeMetadata *meta = m_env->getMap().getNodeMetadata(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); - } + + MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos); + if(block) + block->raiseModified(MOD_STATE_WRITE_NEEDED); + + setBlockNotSent(blockpos); return; } - dstream<<__FUNCTION_NAME<<": unknown id "< Server::getPlayerInfo() @@ -3521,7 +3748,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(); @@ -3532,11 +3759,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) { @@ -3559,7 +3785,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="< data((u8*)s.c_str(), s.size()); + // Send as reliable + con.Send(peer_id, 0, data, true); +} + +void Server::SendToolDef(con::Connection &con, u16 peer_id, + IToolDefManager *tooldef) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + /* + u16 command + u32 length of the next item + serialized ToolDefManager + */ + writeU16(os, TOCLIENT_TOOLDEF); + std::ostringstream tmp_os(std::ios::binary); + tooldef->serialize(tmp_os); + os<::Iterator i = m_clients.getIterator(); @@ -4041,7 +4362,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; @@ -4050,7 +4371,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) { @@ -4067,47 +4388,247 @@ void Server::SendBlocks(float dtime) } } -/* - Something random -*/ +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::UpdateCrafting(u16 peer_id) +void Server::SendTextures(u16 peer_id) { 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; + 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; + } + } + } - /* - Calculate crafting stuff - */ - if(g_settings.getBool("creative_mode") == false) + /* Create and send packets */ + + u32 num_bunches = texture_bunches.size(); + for(u32 i=0; iinventory.getList("craft"); - InventoryList *rlist = player->inventory.getList("craftresult"); + /* + 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); + } + + // Make data buffer + std::string s = os.str(); + infostream<<"Server::SendTextures(): 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; + } - if(rlist && player->craftresult_is_preview) + SendPlayerHP(player); + + RemoteClient *client = getClient(player->peer_id); + if(client->net_proto_version >= 3) { - rlist->clearItems(); + SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0)); } - if(clist && rlist && player->craftresult_is_preview) + else { - InventoryItem *items[9]; - for(u16 i=0; i<9; i++) - { - items[i] = clist->getItem(i); - } - - // Get result of crafting grid - InventoryItem *result = craft_get_result(items); - if(result) - rlist->addItem(result); + RespawnPlayer(player); } + } +} + +void Server::RespawnPlayer(Player *player) +{ + player->hp = 20; + ServerRemotePlayer *srp = static_cast(player); + 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); + + Player* player = m_env->getPlayer(peer_id); + assert(player); + ServerRemotePlayer *srp = static_cast(player); + + // No crafting in creative mode + if(g_settings->getBool("creative_mode")) + return; + + // Get the InventoryLists of the player in which we will operate + InventoryList *clist = player->inventory.getList("craft"); + assert(clist); + InventoryList *rlist = player->inventory.getList("craftresult"); + assert(rlist); + InventoryList *mlist = player->inventory.getList("main"); + assert(mlist); + + // If the result list is not a preview and is not empty, try to + // throw the item into main list + if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0) + { + // Grab item out of craftresult + InventoryItem *item = rlist->changeItem(0, NULL); + // Try to put in main + InventoryItem *leftover = mlist->addItem(item); + // If there are leftovers, put them back to craftresult and + // delete leftovers + delete rlist->addItem(leftover); + // Inventory was modified + srp->m_inventory_not_sent = true; + } - } // if creative_mode == false + // If result list is empty, we will make it preview what would be + // crafted + if(rlist->getUsedSlots() == 0) + player->craftresult_is_preview = true; + + // If it is a preview, clear the possible old preview in it + if(player->craftresult_is_preview) + rlist->clearItems(); + + // If it is a preview, find out what is the crafting result + // and put it in + if(player->craftresult_is_preview) + { + // Mangle crafting grid to an another format + 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); + + // Find out what is crafted and add it to result item slot + InventoryItem *result = m_craftdef->getCraftResult(cpi, this); + if(result) + rlist->addItem(result); + } } RemoteClient* Server::getClient(u16 peer_id) @@ -4141,7 +4662,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) @@ -4150,19 +4671,90 @@ std::wstring Server::getStatusString() os<isSavingEnabled() == false) + if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false) os<get("motd") != "") + os<get("motd")); return os.str(); } +// 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 +IToolDefManager* Server::getToolDefManager() +{ + return m_toolmgr; +} +INodeDefManager* Server::getNodeDefManager() +{ + return m_nodedef; +} +ICraftDefManager* Server::getCraftDefManager() +{ + return m_craftdef; +} +ICraftItemDefManager* Server::getCraftItemDefManager() +{ + return m_craftitemdef; +} +ITextureSource* Server::getTextureSource() +{ + return NULL; +} +u16 Server::allocateUnknownNodeId(const std::string &name) +{ + return m_nodedef->allocateDummy(name); +} + +IWritableToolDefManager* Server::getWritableToolDefManager() +{ + return m_toolmgr; +} +IWritableNodeDefManager* Server::getWritableNodeDefManager() +{ + return m_nodedef; +} +IWritableCraftDefManager* Server::getWritableCraftDefManager() +{ + return m_craftdef; +} +IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager() +{ + return m_craftitemdef; +} + v3f findSpawnPos(ServerMap &map) { //return v3f(50,50,50)*BS; - v2s16 nodepos; - s16 groundheight = 0; + v3s16 nodepos; #if 0 nodepos = v2s16(0,0); @@ -4175,41 +4767,50 @@ v3f findSpawnPos(ServerMap &map) { 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 "<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; // Reset inventory to creative if in creative mode - if(g_settings.getBool("creative_mode")) + if(g_settings->getBool("creative_mode")) { // Warning: double code below // Backup actual inventory player->inventory_backup = new Inventory(); *(player->inventory_backup) = player->inventory; // Set creative inventory - craft_set_creative_inventory(player); + craft_set_creative_inventory(player, this); } return player; @@ -4247,9 +4848,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); + // Add authentication stuff m_authmanager.add(name); m_authmanager.setPassword(name, password); m_authmanager.setPrivs(name, - stringToPrivs(g_settings.get("default_privs"))); + 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); - /* - Add player to environment - */ + /* Add player to environment */ + m_env->addPlayer(player); - m_env.addPlayer(player); + /* Run scripts */ + ServerRemotePlayer *srp = static_cast(player); + scriptapi_on_newplayer(m_lua, srp); - /* - Add stuff to inventory - */ - - if(g_settings.getBool("creative_mode")) + /* Add stuff to inventory */ + if(g_settings->getBool("creative_mode")) { // Warning: double code above // Backup actual inventory player->inventory_backup = new Inventory(); *(player->inventory_backup) = player->inventory; // Set creative inventory - craft_set_creative_inventory(player); - } - else if(g_settings.getBool("give_initial_stuff")) - { - craft_give_initial_stuff(player); + craft_set_creative_inventory(player, this); } return player; @@ -4354,7 +4943,7 @@ 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--; @@ -4363,28 +4952,56 @@ void Server::handlePeerChange(PeerChange &c) // Collect information about leaving in chat std::wstring message; { - std::wstring name = L"unknown"; - Player *player = m_env.getPlayer(c.peer_id); + 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); + m_env->removePlayer(c.peer_id); }*/ // Set player client disconnected { - Player *player = m_env.getPlayer(c.peer_id); + 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); + 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; } @@ -4439,11 +5056,11 @@ void dedicated_server_loop(Server &server, bool &kill) { DSTACK(__FUNCTION_NAME); - dstream<getFloat("profiler_print_interval"); if(profiler_print_interval != 0) { if(m_profiler_interval.step(0.030, profiler_print_interval)) { - dstream<<"Profiler:"<print(infostream); + g_profiler->clear(); } } @@ -4493,10 +5110,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;