X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fserver.cpp;h=5646c0ac9c9dffdccff84b15f7e5ee00d83ce738;hb=cd563473fa298db3b910009f26ba263bacd84be9;hp=3e22a023aded7a77cd05e7b395dfbda301fad514;hpb=143dce6672972a864a427f34f6513e5faeeaeb48;p=oweals%2Fminetest.git diff --git a/src/server.cpp b/src/server.cpp index 3e22a023a..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" @@ -39,6 +40,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #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<<")" @@ -138,12 +147,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"<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); + + /* + 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 */ @@ -341,6 +327,8 @@ void * EmergeThread::Thread() END_DEBUG_EXCEPTION_HANDLER(errorstream) + log_deregister_thread(); + return NULL; } @@ -354,11 +342,10 @@ 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; } @@ -372,7 +359,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, //TimeTaker timer("RemoteClient::GetNextBlocks"); - Player *player = server->m_env.getPlayer(peer_id); + Player *player = server->m_env->getPlayer(peer_id); assert(player != NULL); @@ -410,17 +397,13 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, /*infostream<<"m_nearest_unsent_reset_timer=" < 10.0) + // Reset periodically to workaround for some bugs or stuff + if(m_nearest_unsent_reset_timer > 20.0) { m_nearest_unsent_reset_timer = 0; m_nearest_unsent_d = 0; - /*infostream<<"Resetting m_nearest_unsent_d for " - <getPlayerName(peer_id)<getPlayerName(peer_id)<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;*/ //infostream<<"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; - /*infostream<<"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(); @@ -842,31 +828,9 @@ 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); - // Write block count writeU16(buf, 0); os.write((char*)buf, 2); @@ -968,6 +932,92 @@ u32 PIChecksum(core::list &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")) { infostream<<"Server: Loading environment metadata"<loadMeta(m_mapsavedir); } // Load players infostream<<"Server: Loading players"<deSerializePlayers(m_mapsavedir); + + /* + Add some test ActiveBlockModifiers to environment + */ + add_legacy_abms(m_env, m_nodedef); } Server::~Server() @@ -1057,13 +1166,13 @@ Server::~Server() Save players */ infostream<<"Server: Saving players"<serializePlayers(m_mapsavedir); /* Save environment metadata */ infostream<<"Server: Saving environment metadata"<saveMeta(m_mapsavedir); } /* @@ -1086,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); @@ -1150,14 +1273,15 @@ 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); //infostream<<"Server steps "<setTimeOfDay((m_env->getTimeOfDay() + units) % 24000); //infostream<<"Server: m_time_of_day = "<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); } @@ -1233,27 +1356,94 @@ 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, + m_env->getMap().timerUpdate(map_timer_and_unload_dtime, g_settings->getFloat("server_unload_unused_data_timeout")); } /* Do background stuff */ - + /* - Transform liquids + Handle players */ + { + JMutexAutoLock lock(m_env_mutex); + JMutexAutoLock lock2(m_con_mutex); + + //float player_max_speed = BS * 4.0; // Normal speed + float player_max_speed = BS * 20; // Fast speed + float player_max_speed_up = BS * 20; + + player_max_speed *= 2.5; // Tolerance + player_max_speed_up *= 2.5; + + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + ServerRemotePlayer *player = + static_cast + (m_env->getPlayer(client->peer_id)); + if(player==NULL) + continue; + + /* + Check player movements + + NOTE: Actually the server should handle player physics like the + client does and compare player's position to what is calculated + on our side. This is required when eg. players fly due to an + explosion. + */ + player->m_last_good_position_age += dtime; + if(player->m_last_good_position_age >= 2.0){ + float age = player->m_last_good_position_age; + v3f diff = (player->getPosition() - player->m_last_good_position); + float d_vert = diff.Y; + diff.Y = 0; + float d_horiz = diff.getLength(); + /*infostream<getName()<<"'s horizontal speed is " + <<(d_horiz/age)<m_last_good_position = player->getPosition(); + } else { + actionstream<<"Player "<getName() + <<" moved too fast; resetting position" + <setPosition(player->m_last_good_position); + SendMovePlayer(player); + } + player->m_last_good_position_age = 0; + } + + /* + 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) { @@ -1264,13 +1454,13 @@ void Server::AsyncRunStep() ScopeProfiler sp(g_profiler, "Server: liquid transform"); core::map modified_blocks; - m_env.getMap().transformLiquids(modified_blocks); + m_env->getMap().transformLiquids(modified_blocks); #if 0 /* Update lighting */ core::map lighting_modified_blocks; - ServerMap &map = ((ServerMap&)m_env.getMap()); + ServerMap &map = ((ServerMap&)m_env->getMap()); map.updateLighting(modified_blocks, lighting_modified_blocks); // Add blocks modified by lighting to modified_blocks @@ -1320,7 +1510,7 @@ void Server::AsyncRunStep() { //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; infostream<<"* "<getName()<<"\t"; @@ -1340,7 +1530,7 @@ void Server::AsyncRunStep() JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - ScopeProfiler sp(g_profiler, "Server: checking added and deleted objects"); + ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs"); // Radius inside which objects are active s16 radius = g_settings->getS16("active_object_send_range_blocks"); @@ -1351,7 +1541,7 @@ 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); if(player==NULL) { // This can happen if the client timeouts somehow @@ -1364,9 +1554,9 @@ void Server::AsyncRunStep() core::map 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 @@ -1389,7 +1579,7 @@ void Server::AsyncRunStep() { // Get object u16 id = i.getNode()->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()); @@ -1411,7 +1601,7 @@ 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; @@ -1477,7 +1667,7 @@ void Server::AsyncRunStep() } } - m_env.setKnownActiveObjects(whatever); + m_env->setKnownActiveObjects(whatever); #endif } @@ -1489,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 @@ -1498,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; @@ -1687,7 +1877,7 @@ void Server::AsyncRunStep() { v3s16 p = i.getNode()->getKey(); modified_blocks2.insert(p, - m_env.getMap().getBlockNoCreateNoEx(p)); + m_env->getMap().getBlockNoCreateNoEx(p)); } // Set blocks not sent for(core::list::Iterator @@ -1720,7 +1910,6 @@ void Server::AsyncRunStep() /* Send object positions - TODO: Get rid of MapBlockObjects */ { float &counter = m_objectdata_timer; @@ -1730,7 +1919,7 @@ void Server::AsyncRunStep() 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); @@ -1774,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) - { - infostream<<"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); } } } @@ -1803,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 @@ -1838,7 +2012,7 @@ void Server::Receive() <<" has apparently closed connection. " <<"Removing player."<removePlayer(peer_id);*/ } } @@ -1849,9 +2023,18 @@ 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) { @@ -1860,17 +2043,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; } - // drop player if is ip is banned - if(m_banmanager.isIpBanned(peer->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 { @@ -1891,7 +2064,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; infostream<<"Server: Got TOSERVER_INIT from " - <id<serialization_version = deployed; - getClient(peer->id)->pending_serialization_version = deployed; + getClient(peer_id)->pending_serialization_version = deployed; if(deployed == SER_FMT_VER_INVALID) { infostream<<"Server: Cannot negotiate " "serialization version with peer " <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; } - /* Uhh... this should actually be a warning but let's do it like this */ if(g_settings->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."); + 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; } } @@ -2054,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); @@ -2071,27 +2252,39 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(command == TOSERVER_INIT2) { infostream<<"Server: Got TOSERVER_INIT2 from " - <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); + Player *player = m_env->getPlayer(peer_id); // Send HP SendPlayerHP(player); @@ -2099,8 +2292,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // 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 @@ -2109,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()); @@ -2121,7 +2314,7 @@ 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 < PROTOCOL_VERSION) + if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION) { SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER"); } @@ -2145,7 +2338,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) 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); if(!player) continue; // Get name of player @@ -2167,7 +2360,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; } - Player *player = m_env.getPlayer(peer_id); + Player *player = m_env->getPlayer(peer_id); + ServerRemotePlayer *srp = static_cast(player); if(player == NULL){ infostream<<"Server::ProcessData(): Cancelling: " @@ -2258,1090 +2452,1203 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } else if(command == TOSERVER_CLICK_ACTIVEOBJECT) { - if(datasize < 7) - return; + infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<m_removed) + NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); + if(!meta) return; + + meta->setText(text); - //TODO: Check that object is reasonably close - - // Left click, pick object up (usually) - if(button == 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()); + actionstream<getName()<<" writes \""<addWear(wear); - if(weared_out) - mlist->deleteItem(item_i); - SendInventory(player->peer_id); - } - } - } - // Right click, do something with object - if(button == 1) + v3s16 blockpos = getNodeBlockPos(p); + MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos); + if(block) { - actionstream<getName()<<" right clicks object " - <getId()<hp; - - // Do stuff - obj->rightClick(player); - - // Send back stuff - if(player->hp != oldhp) - { - SendPlayerHP(player); - } + block->raiseModified(MOD_STATE_WRITE_NEEDED, + "sign node text"); } + + setBlockNotSent(blockpos); } - else if(command == TOSERVER_GROUND_ACTION) + else if(command == TOSERVER_INVENTORY_ACTION) { - if(datasize < 17) - return; - /* - length: 17 - [0] u16 command - [2] u8 action - [3] v3s16 nodepos_undersurface - [9] v3s16 nodepos_abovesurface - [15] u16 item - actions: - 0: start digging - 1: place block - 2: stop digging (all parameters ignored) - 3: digging completed - */ - u8 action = readU8(&data[2]); - v3s16 p_under; - p_under.X = readS16(&data[3]); - p_under.Y = readS16(&data[5]); - p_under.Z = readS16(&data[7]); - v3s16 p_over; - p_over.X = readS16(&data[9]); - p_over.Y = readS16(&data[11]); - p_over.Z = readS16(&data[13]); - u16 item_i = readU16(&data[15]); - - //TODO: Check that target is reasonably close - - /* - 0: start digging - */ - if(action == 0) + /*// Ignore inventory changes if in creative mode + if(g_settings->getBool("creative_mode") == true) { - /* - NOTE: This can be used in the future to check if - somebody is cheating, by checking the timing. - */ - } // action == 0 - - /* - 2: stop digging - */ - else if(action == 2) + infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode" + <getType() == IACTION_MOVE + && g_settings->getBool("creative_mode") == false) { - // 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 - { - 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) - { - // If it's not diggable, do nothing - if(content_diggable(material) == false) - { - infostream<<"Server: Not finishing digging: " - <<"Node not diggable" - <nodeRemovalDisabled() == true) - { - infostream<<"Server: Not finishing digging: " - <<"Node metadata disables removal" - <inventory.getList("craftresult"); + assert(rlist); + InventoryList *clist = player->inventory.getList("craft"); + assert(clist); + InventoryList *mlist = player->inventory.getList("main"); + assert(mlist); - // Make sure the player is allowed to do it - if((getPlayerPrivs(player) & PRIV_BUILD) == 0) - { - infostream<<"Player "<getName()<<" cannot remove node" - <<" because privileges are "<to_inv == "current_player" + && ma->to_list == "craftresult" + && (ma->from_inv != "current_player" + || ma->from_list != "craftresult")) { - infostream<<"Server: Not finishing digging."<SetBlockNotSent(blockpos); - + infostream<<"Ignoring IMoveAction from " + <from_inv<<":"<from_list + <<" to "<to_inv<<":"<to_list + <<" because dst is craftresult" + <<" and src isn't craftresult"<getName()<<" digs "< far_players; - sendRemoveNode(p_under, peer_id, &far_players, 30); - + /* - Update and send inventory + Handle crafting (source is craftresult, which is preview) */ - - if(g_settings->getBool("creative_mode") == false) + if(ma->from_inv == "current_player" + && ma->from_list == "craftresult" + && player->craftresult_is_preview) { /* - Wear out tool + If the craftresult is placed on itself, crafting takes + place and result is moved into main list */ - InventoryList *mlist = player->inventory.getList("main"); - if(mlist != NULL) + if(ma->to_inv == "current_player" + && ma->to_list == "craftresult") { - InventoryItem *item = mlist->getItem(item_i); - if(item && (std::string)item->getName() == "ToolItem") - { - ToolItem *titem = (ToolItem*)item; - std::string toolname = titem->getToolName(); + // 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); - // Get digging properties for material and tool - DiggingProperties prop = - getDiggingProperties(material, toolname); + InventoryItem *item1 = rlist->changeItem(0, NULL); + mlist->addItem(item1); - if(prop.diggable == false) - { - infostream<<"Server: WARNING: Player digged" - <<" with impossible material + tool" - <<" combination"<addWear(prop.wear); + srp->m_inventory_not_sent = true; - if(weared_out) - { - mlist->deleteItem(item_i); - } - } + delete a; + return; } - /* - Add dug item to inventory + 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"<craftresult_is_preview = false; + clist->decrementMaterials(1); - // If not mineral - if(item == NULL) - { - std::string &dug_s = content_features(material).dug_item; - if(dug_s != "") - { - std::istringstream is(dug_s, std::ios::binary); - item = InventoryItem::deSerialize(is); - } - } + /* 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(item != NULL) - { - // Add a item to inventory - player->inventory.addItem("main", item); - - // Send inventory - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); - } - - item = NULL; + delete a; + return; + } - if(mineral != MINERAL_NONE) - item = getDiggedMineralItem(mineral); + /* + Non-crafting move + */ - // If not mineral - if(item == NULL) + // 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")) + { + infostream<<"Cannot move outside of player's inventory: " + <<"No build privilege"<from_inv != "current_player" + && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + { + Strfnd fn(ma->from_inv); + std::string id0 = fn.next(":"); + if(id0 == "nodemeta") { - std::string &extra_dug_s = content_features(material).extra_dug_item; - s32 extra_rarity = content_features(material).extra_dug_item_rarity; - if(extra_dug_s != "" && extra_rarity != 0 - && myrand() % extra_rarity == 0) + 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(extra_dug_s, std::ios::binary); - item = InventoryItem::deSerialize(is); + 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") { - // Add a item to inventory - player->inventory.addItem("main", item); - - // Send inventory - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); + 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()) + { + 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) { - MapEditEventIgnorer ign(&m_ignore_map_edit_events); - - m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks); + delete a; + return; } - /* - Set blocks not sent to far players - */ - for(core::list::Iterator - i = far_players.begin(); - i != far_players.end(); i++) + // If player is not an admin, check for ownership + else if (da->from_inv != "current_player" + && (getPlayerPrivs(player) & PRIV_SERVER) == 0) { - u16 peer_id = *i; - RemoteClient *client = getClient(peer_id); - if(client==NULL) - continue; - client->SetBlocksNotSent(modified_blocks); + Strfnd fn(da->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->getOwner() != "" && + meta->getOwner() != player->getName()) + { + infostream<<"Cannot move item: " + "not owner of metadata" + <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) - infostream<<"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. - infostream<<"Client "<SetBlockNotSent(blockpos); - return; - } - } - catch(InvalidPositionException &e) - { - infostream<<"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()); - - actionstream<getName()<<" places material " - <<(int)mitem->getMaterial() - <<" at "<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; - 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); - } + 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) - { - 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); + /* + 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) - { - infostream<<"WARNING: item resulted in NULL object, " - <<"not placing onto map" - <getName()<<" places "<getName() - <<" at "<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) - 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); - } - } + 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) + { + infostream<<"Server: playername not found in authmanager"<getName()<<" changes password"<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); - - actionstream<getName()<<" writes \""<setChangedFlag(); + v3f pointed_pos = player_pos; + if(pointed.type == POINTEDTHING_NODE) + { + pointed_pos = intToFloat(p_under, BS); + } + else if(pointed.type == POINTEDTHING_OBJECT) + { + pointed_pos = pointed_object->getBasePosition(); + } + + 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; + } } - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd()==false; i++) + /* + Make sure the player is allowed to do it + */ + bool build_priv = (getPlayerPrivs(player) & PRIV_BUILD) != 0; + if(!build_priv) { - RemoteClient *client = i.getNode()->getValue(); - client->SetBlockNotSent(blockpos); + infostream<<"Ignoring interaction from player "<getName() + <<" because privileges are "<getBool("creative_mode") == true) + + /* + 0: start digging or punch object + */ + if(action == 0) { - infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode" - <m_removed) + return; + + actionstream<getName()<<" punches object " + <punch(srp); + } + + } // action == 0 + + /* + 1: stop digging + */ + else if(action == 1) + { + } // action == 1 + + /* + 2: Digging completed + */ + else if(action == 2) { - // Create context - InventoryContext c; - c.current_player = player; + // Only complete digging of nodes + if(pointed.type != POINTEDTHING_NODE) + return; + + // Mandatory parameter; actually used for nothing + core::map modified_blocks; + + content_t material = CONTENT_IGNORE; + u8 mineral = MINERAL_NONE; + + 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); + } /* - Handle craftresult specially if not in creative mode + Run script hook */ - bool disable_action = false; - if(a->getType() == IACTION_MOVE - && g_settings->getBool("creative_mode") == false) + scriptapi_environment_on_dignode(m_lua, p_under, n, srp); + } // action == 2 + + /* + 3: place block or right-click object + */ + else if(action == 3) + { + if(pointed.type == POINTEDTHING_NODE) { - IMoveAction *ma = (IMoveAction*)a; - if(ma->to_inv == "current_player" && - ma->from_inv == "current_player") + 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()) { - InventoryList *rlist = player->inventory.getList("craftresult"); - assert(rlist); - InventoryList *clist = player->inventory.getList("craft"); - assert(clist); - InventoryList *mlist = player->inventory.getList("main"); - assert(mlist); + 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); + /* - Craftresult is no longer preview if something - is moved into it + Handle inventory */ - if(ma->to_list == "craftresult" - && ma->from_list != "craftresult") + InventoryList *ilist = player->inventory.getList("main"); + if(g_settings->getBool("creative_mode") == false && ilist) { - // If it currently is a preview, remove - // its contents - if(player->craftresult_is_preview) - { - rlist->deleteItem(0); - } - player->craftresult_is_preview = false; + // Remove from inventory and send inventory + if(mitem->getCount() <= 1) + ilist->deleteItem(item_i); + else + mitem->remove(1); + srp->m_inventory_not_sent = true; } + /* - Crafting takes place if this condition is true. + Add node. + + This takes some time so it is done after the quick stuff */ - if(player->craftresult_is_preview && - ma->from_list == "craftresult") + core::map modified_blocks; { - 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 " - <getName()); + m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name); } /* - If the craftresult is placed on itself, move it to - main inventory instead of doing the action + Set blocks not sent to far players */ - if(ma->to_list == "craftresult" - && ma->from_list == "craftresult") + for(core::list::Iterator + i = far_players.begin(); + i != far_players.end(); i++) { - disable_action = true; - - InventoryItem *item1 = rlist->changeItem(0, NULL); - mlist->addItem(item1); + u16 peer_id = *i; + RemoteClient *client = getClient(peer_id); + if(client==NULL) + continue; + client->SetBlocksNotSent(modified_blocks); } - } - // Disallow moving items if not allowed to build - else if((getPlayerPrivs(player) & PRIV_BUILD) == 0) - { - return; - } - // if it's a locking chest, only allow the owner or server admins to move items - else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0) - { - Strfnd fn(ma->from_inv); - std::string id0 = fn.next(":"); - if(id0 == "nodemeta") + + /* + Run script hook + */ + scriptapi_environment_on_placenode(m_lua, p_above, n, srp); + + /* + Calculate special events + */ + + /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE")) { - 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() == CONTENT_LOCKABLE_CHEST) { - LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta; - if (lcm->getOwner() != player->getName()) - return; + 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 (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + /* + Place other item (not a block) + */ + else { - Strfnd fn(ma->to_inv); - std::string id0 = fn.next(":"); - if(id0 == "nodemeta") + if(!build_priv) { - 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() == CONTENT_LOCKABLE_CHEST) { - LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta; - if (lcm->getOwner() != player->getName()) - return; - } + infostream<<"Not allowing player to place item: " + "no build privileges"<apply(&c, this); - // Eat the action - delete a; - } - else - { - // Send inventory - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); - } - } - else - { - infostream<<"TOSERVER_INVENTORY_ACTION: " - <<"InventoryAction::deSerialize() returned NULL" - <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'/') - { - 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""); + // Calculate a position for it + v3f pos = player_pos; + if(pointed.type == POINTEDTHING_NOTHING) + { + infostream<<"Not allowing player to place item: " + "pointing to nothing"<getBasePosition(); - ServerCommandContext *ctx = new ServerCommandContext( - str_split(message, L' '), - paramstring, - this, - &m_env, - player, - privs); + // 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; + } - std::wstring reply(processServerCommand(ctx)); - send_to_sender = ctx->flags & SEND_TO_SENDER; - send_to_others = ctx->flags & SEND_TO_OTHERS; + //pos.Y -= BS*0.45; + //pos.Y -= BS*0.25; // let it drop a bit - if (ctx->flags & SEND_NO_PREFIX) - line += reply; - else - line += L"Server: " + reply; + /* + 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 "< "; - line += message; - send_to_others = true; + /* + Place the item + */ + bool remove = item->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 + else if(pointed.type == POINTEDTHING_OBJECT) { - line += L"Server: You are not allowed to shout"; - send_to_sender = true; - } - } - - if(line != L"") - { - if(send_to_others) - actionstream<<"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; + if(!build_priv) + 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; + // Skip if object has been removed + if(pointed_object->m_removed) + return; - SendChatMessage(client->peer_id, line); + actionstream<getName()<<" right-clicks object " + <rightClick(srp); } - } - } - 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); - if(g_settings->getBool("enable_damage")) - { - actionstream<getName()<<" damaged by " - <<(int)damage<<" hp at "<getPosition()/BS) - <inventory.getList("main"); + if(ilist == NULL) + return; - infostream<<"Server: Client requests a password change from " - <<"'"<getItem(item_i); + + // If there is no item, it is not possible to add it anywhere + if(item == NULL) + return; - std::string playername = player->getName(); + // 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; + } + } - if(oldpwd != checkpwd) + } // action == 4 + + /* + Catch invalid actions + */ + else { - infostream<<"Server: invalid old password"<getName()<<" changes password"<wieldItem(item); - SendWieldedItem(player); - } - else if(command == TOSERVER_RESPAWN) - { - if(player->hp != 0) - return; - - RespawnPlayer(player); - - actionstream<getName()<<" respawns at " - <getPosition()/BS)<completeAddToInventoryLater(item_i); } else { @@ -3384,7 +3691,7 @@ 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(); infostream<<"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; } @@ -3417,17 +3724,15 @@ 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; } @@ -3443,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(); @@ -3454,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) { @@ -3556,6 +3860,81 @@ void Server::SendDeathscreen(con::Connection &con, u16 peer_id, 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< > 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; + } + } + } + + /* 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); + } + + // Make data buffer + std::string s = os.str(); + infostream<<"Server::SendTextures(): bunch "<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) { - InventoryList *clist = player->inventory.getList("craft"); - InventoryList *rlist = player->inventory.getList("craftresult"); - - if(rlist && rlist->getUsedSlots() == 0) - player->craftresult_is_preview = true; + // 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 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(rlist && player->craftresult_is_preview) - { - rlist->clearItems(); - } - if(clist && rlist && player->craftresult_is_preview) - { - 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); + // 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()); } - - } // if creative_mode == false + 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) @@ -4125,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) @@ -4134,7 +4671,7 @@ std::wstring Server::getStatusString() os<isSavingEnabled() == false) + if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false) os<get("motd") != "") os<get("motd")); @@ -4148,12 +4685,76 @@ void Server::saveConfig() 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); @@ -4166,13 +4767,11 @@ 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) { @@ -4185,22 +4784,33 @@ v3f findSpawnPos(ServerMap &map) //infostream<<"-> 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 @@ -4229,7 +4839,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id 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; @@ -4238,7 +4848,7 @@ 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) { 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"))); - /* - Set player position - */ + /* Set player position */ infostream<<"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 - */ - + /* Add stuff to inventory */ if(g_settings->getBool("creative_mode")) { // Warning: double code above @@ -4287,11 +4889,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id 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; @@ -4345,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--; @@ -4354,7 +4952,7 @@ void Server::handlePeerChange(PeerChange &c) // Collect information about leaving in chat std::wstring message; { - Player *player = m_env.getPlayer(c.peer_id); + Player *player = m_env->getPlayer(c.peer_id); if(player != NULL) { std::wstring name = narrow_to_wide(player->getName()); @@ -4368,12 +4966,12 @@ void Server::handlePeerChange(PeerChange &c) /*// 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; @@ -4392,7 +4990,7 @@ void Server::handlePeerChange(PeerChange &c) 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); if(!player) continue; // Get name of player