X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fserver.cpp;h=5646c0ac9c9dffdccff84b15f7e5ee00d83ce738;hb=cd563473fa298db3b910009f26ba263bacd84be9;hp=cd67bc7494c814ac8ee336a84f40ad9fcde8b7ac;hpb=704d8a62bf4c52c5ac0d9a85b0b902ea833a7e90;p=oweals%2Fminetest.git diff --git a/src/server.cpp b/src/server.cpp index cd67bc749..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" @@ -44,6 +45,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #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<<")" @@ -143,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"<m_env->getMap()); - //core::map changed_blocks; - //core::map lighting_invalidated_blocks; - MapBlock *block = NULL; bool got_block = true; core::map modified_blocks; - + /* - Fetch block from map or generate a single block + Try to fetch block from memory or disk. + If not found and asked to generate, initialize generator. */ + + bool started_generate = false; + mapgen::BlockMakeData data; + { JMutexAutoLock envlock(m_server->m_env_mutex); // Load sector if it isn't loaded if(map.getSectorNoGenerateNoEx(p2d) == NULL) - //map.loadSectorFull(p2d); map.loadSectorMeta(p2d); - + + // Attempt to load block block = map.getBlockNoCreateNoEx(p); if(!block || block->isDummy() || !block->isGenerated()) { if(enable_mapgen_debug_info) - infostream<<"EmergeThread: not in memory, loading"<isGenerated() == false) - { - if(enable_mapgen_debug_info) - infostream<<"EmergeThread: generating"<isGenerated() == false)){ if(enable_mapgen_debug_info) - infostream<<"EmergeThread: ended up with: " - <m_ignore_map_edit_events); - - // Activate objects and stuff - m_server->m_env->activateBlock(block, 3600); - } - } - else - { - /*if(block->getLightingExpired()){ - lighting_invalidated_blocks[block->getPos()] = block; - }*/ + map.initBlockMake(&data, p); } - - // TODO: Some additional checking and lighting updating, - // see emergeBlock } - {//envlock - JMutexAutoLock envlock(m_server->m_env_mutex); - - if(got_block) + /* + If generator was initialized, generate now when envlock is free. + */ + if(started_generate) { - /* - Collect a list of blocks that have been modified in - addition to the fetched one. - */ - -#if 0 - if(lighting_invalidated_blocks.size() > 0) { - /*infostream<<"lighting "<m_env_mutex); - // Update lighting without locking the environment mutex, - // add modified blocks to changed blocks - map.updateLighting(lighting_invalidated_blocks, modified_blocks); - } + ScopeProfiler sp(g_profiler, "EmergeThread: after " + "mapgen::make_block (envlock)", SPT_AVG); + + // Blit data back on map, update lighting, add mobs and + // whatever this does + map.finishBlockMake(&data, modified_blocks); + + // Get central block + block = map.getBlockNoCreateNoEx(p); + + /* + 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 */ @@ -346,6 +327,8 @@ void * EmergeThread::Thread() END_DEBUG_EXCEPTION_HANDLER(errorstream) + log_deregister_thread(); + return NULL; } @@ -688,7 +671,11 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, //if(server->m_emerge_queue.peerItemCount(peer_id) < 1) // Allow two blocks in queue per client //if(server->m_emerge_queue.peerItemCount(peer_id) < 2) - if(server->m_emerge_queue.peerItemCount(peer_id) < 25) + u32 max_emerge = 25; + // Make it more responsive when needing to generate stuff + if(surely_not_found_on_disk) + max_emerge = 5; + if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge) { //infostream<<"Adding block to emerge queue"< &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_=""): + ModSpec(const std::string &name_="", const std::string path_="", + const std::set &depends_=std::set()): name(name_), - path(path_) + path(path_), + depends(depends_), + unsatisfied_depends(depends_) {} }; +// Get a dependency-sorted list of ModSpecs static core::list getMods(core::list &modspaths) { - core::list mods; + 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; @@ -968,10 +967,55 @@ static core::list getMods(core::list &modspaths) continue; std::string modname = dirlist[j].name; std::string modpath = modspath + DIR_DELIM + modname; - mods.push_back(ModSpec(modname, modpath)); + std::set 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++){ @@ -1047,7 +1100,8 @@ Server::Server( // Initialize Environment - m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, this); + m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, + this, this); // Give environment reference to scripting api scriptapi_add_environment(m_lua, m_env); @@ -1065,6 +1119,11 @@ Server::Server( // Load players infostream<<"Server: Loading players"<deSerializePlayers(m_mapsavedir); + + /* + Add some test ActiveBlockModifiers to environment + */ + add_legacy_abms(m_env, m_nodedef); } Server::~Server() @@ -1149,6 +1208,8 @@ Server::~Server() delete m_toolmgr; delete m_nodedef; + delete m_craftdef; + delete m_craftitemdef; // Deinitialize scripting infostream<<"Server: Deinitializing scripting"<::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) { @@ -1836,22 +1963,8 @@ 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); @@ -1971,8 +2084,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) infostream<<"Server: Cannot negotiate " "serialization version with peer " <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; } } @@ -2147,6 +2268,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // 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); @@ -2237,6 +2361,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } Player *player = m_env->getPlayer(peer_id); + ServerRemotePlayer *srp = static_cast(player); if(player == NULL){ infostream<<"Server::ProcessData(): Cancelling: " @@ -2327,232 +2452,704 @@ 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"<getActiveObject(id); - - if(obj == NULL) + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + u8 buf[6]; + // Read stuff + is.read((char*)buf, 6); + v3s16 p = readV3S16(buf); + is.read((char*)buf, 2); + u16 textlen = readU16(buf); + std::string text; + for(u16 i=0; im_removed) + NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); + if(!meta) return; - - //TODO: Check that object is reasonably close - - // Get ServerRemotePlayer - ServerRemotePlayer *srp = (ServerRemotePlayer*)player; - // Update wielded item - srp->wieldItem(item_i); + meta->setText(text); - // Left click, pick/punch - if(button == 0) + actionstream<getName()<<" writes \""<getMap().getBlockNoCreateNoEx(blockpos); + if(block) { - actionstream<getName()<<" punches object " - <getId()<punch(srp); - -#if 0 + block->raiseModified(MOD_STATE_WRITE_NEEDED, + "sign node text"); + } + + setBlockNotSent(blockpos); + } + else if(command == TOSERVER_INVENTORY_ACTION) + { + /*// Ignore inventory changes if in creative mode + if(g_settings->getBool("creative_mode") == true) + { + infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode" + <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) - { - 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; - } + 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); - actionstream<getName()<<" punches object " - <getId()<changeItem(0, NULL); + mlist->addItem(item1); - ToolItem *titem = NULL; - std::string toolname = ""; + srp->m_inventory_not_sent = true; - 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(); - } + 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, player->getName()); + /* 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; } -#endif + + /* + Non-crafting move + */ + + // 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") + { + 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" + <to_inv != "current_player" + && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + { + Strfnd fn(ma->to_inv); + std::string id0 = fn.next(":"); + if(id0 == "nodemeta") + { + v3s16 p; + p.X = stoi(fn.next(",")); + p.Y = stoi(fn.next(",")); + p.Z = stoi(fn.next(",")); + NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); + if(meta->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) + { + delete a; + return; + } + // If player is not an admin, check for ownership + else if (da->from_inv != "current_player" + && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + { + 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) + { + /* + u16 command + u16 length + wstring message + */ + 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; igetName()); + + // 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); + + // 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""); + + ServerCommandContext *ctx = new ServerCommandContext( + str_split(message, L' '), + paramstring, + this, + m_env, + player, + privs); + + std::wstring reply(processServerCommand(ctx)); + send_to_sender = ctx->flags & SEND_TO_SENDER; + send_to_others = ctx->flags & SEND_TO_OTHERS; + + if (ctx->flags & SEND_NO_PREFIX) + line += reply; + else + line += L"Server: " + reply; + + delete ctx; + } - // Right click, do something with object - if(button == 1) + else { - actionstream<getName()<<" right clicks object " - <getId()< "; + line += message; + send_to_others = true; + } + else + { + line += L"Server: You are not allowed to shout"; + send_to_sender = true; + } + } + + if(line != L"") + { + if(send_to_others) + actionstream<<"CHAT: "<rightClick(srp); + /* + 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; + + // 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; + + 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); + if(g_settings->getBool("enable_damage")) + { + actionstream<getName()<<" damaged by " + <<(int)damage<<" hp at "<getPosition()/BS) + <peer_id); - SendInventory(player->peer_id); + + if(datasize != 2+PASSWORD_SIZE*2) + return; + /*char password[PASSWORD_SIZE]; + for(u32 i=0; igetName(); + + if(m_authmanager.exists(playername) == false) + { + infostream<<"Server: playername not found in authmanager"<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)<getMap().getNode(p_under); + pointed_pos = intToFloat(p_under, BS); } - catch(InvalidPositionException &e) + else if(pointed.type == POINTEDTHING_OBJECT) { - infostream<<"Server: Not punching: Node not found." - <<" Adding block to emerge queue." - <getBasePosition(); } - if(cannot_punch_node) + 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; + } + } - /* - Run script hook - */ - scriptapi_environment_on_punchnode(m_lua, p_under, n); + /* + Make sure the player is allowed to do it + */ + bool build_priv = (getPlayerPrivs(player) & PRIV_BUILD) != 0; + if(!build_priv) + { + infostream<<"Ignoring interaction from player "<getName() + <<" because privileges are "<getMap().getNode(p_under); + } + catch(InvalidPositionException &e) + { + infostream<<"Server: Not punching: Node not found." + <<" Adding block to emerge queue." + <m_removed) + return; + + actionstream<getName()<<" punches object " + <punch(srp); + } } // action == 0 /* - 2: stop digging + 1: stop digging */ - else if(action == 2) + else if(action == 1) { -#if 0 - RemoteClient *client = getClient(peer_id); - JMutexAutoLock digmutex(client->m_dig_mutex); - client->m_dig_tool_item = -1; -#endif - } + } // action == 1 /* - 3: Digging completed + 2: Digging completed */ - else if(action == 3) + else if(action == 2) { + // 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 = false; + bool cannot_remove_node = !build_priv; MapNode n(CONTENT_IGNORE); try @@ -2594,16 +3191,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<" Adding block to emerge queue." <getName()<<" cannot remove node" - <<" because privileges are "<deleteItem(item_i); } + + srp->m_inventory_not_sent = true; } } @@ -2704,16 +3294,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { // Add a item to inventory player->inventory.addItem("main", item); - - // Send inventory - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); + srp->m_inventory_not_sent = true; } item = NULL; if(mineral != MINERAL_NONE) - item = getDiggedMineralItem(mineral, this); + item = getDiggedMineralItem(mineral, this); // If not mineral if(item == NULL) @@ -2732,731 +3319,336 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { // Add a item to inventory player->inventory.addItem("main", item); - - // Send inventory - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); + 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); - } - - /* - 1: place block - */ - else if(action == 1) - { - - InventoryList *ilist = player->inventory.getList("main"); - if(ilist == NULL) - return; - - // Get item - InventoryItem *item = ilist->getItem(item_i); - - // If there is no item, it is not possible to add it anywhere - if(item == NULL) - return; - - /* - Handle material items - */ - if(std::string("MaterialItem") == item->getName()) - { - try{ - // Don't add a node if this is not a free space - MapNode n2 = m_env->getMap().getNode(p_over); - bool no_enough_privs = - ((getPlayerPrivs(player) & PRIV_BUILD)==0); - if(no_enough_privs) - infostream<<"Player "<getName()<<" cannot add node" - <<" because privileges are "<get(n2).buildable_to == false - || no_enough_privs) - { - // Client probably has wrong data. - // Set block not sent, so that client will get - // a valid one. - infostream<<"Client "<SetBlockNotSent(blockpos); - return; - } - } - catch(InvalidPositionException &e) - { - infostream<<"Server: Ignoring ADDNODE: Node not found" - <<" Adding block to emerge queue." - <m_time_from_building = 0.0; - - // Create node data - MaterialItem *mitem = (MaterialItem*)item; - MapNode n; - n.setContent(mitem->getMaterial()); - - actionstream<getName()<<" places material " - <<(int)mitem->getMaterial() - <<" at "<get(n).wall_mounted) - n.param2 = packDir(p_under - p_over); - - // Calculate the direction for furnaces and chests and stuff - if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE) - { - v3f playerpos = player->getPosition(); - v3f blockpos = intToFloat(p_over, BS) - playerpos; - blockpos = blockpos.normalize(); - n.param1 = 0; - if (fabs(blockpos.X) > fabs(blockpos.Z)) { - if (blockpos.X < 0) - n.param1 = 3; - else - n.param1 = 1; - } else { - if (blockpos.Z < 0) - n.param1 = 2; - else - n.param1 = 0; - } - } - - /* - Send to all close-by players - */ - core::list far_players; - sendAddNode(p_over, n, 0, &far_players, 30); - - /* - Handle inventory - */ - InventoryList *ilist = player->inventory.getList("main"); - if(g_settings->getBool("creative_mode") == false && ilist) - { - // Remove from inventory and send inventory - if(mitem->getCount() == 1) - ilist->deleteItem(item_i); - else - mitem->remove(1); - // Send inventory - UpdateCrafting(peer_id); - SendInventory(peer_id); - } - - /* - Add node. - - This takes some time so it is done after the quick stuff - */ - core::map modified_blocks; - { - MapEditEventIgnorer ign(&m_ignore_map_edit_events); - - std::string p_name = std::string(player->getName()); - m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name); - } - /* - Set blocks not sent to far players - */ - for(core::list::Iterator - i = far_players.begin(); - i != far_players.end(); i++) - { - u16 peer_id = *i; - RemoteClient *client = getClient(peer_id); - if(client==NULL) - continue; - client->SetBlocksNotSent(modified_blocks); - } - - /* - Run script hook - */ - scriptapi_environment_on_placenode(m_lua, p_over, n); + (this takes some time so it is done after the quick stuff) + */ + { + MapEditEventIgnorer ign(&m_ignore_map_edit_events); - /* - 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++) - { - - } - }*/ + m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks); } /* - Place other item (not a block) + Set blocks not sent to far players */ - else + for(core::list::Iterator + i = far_players.begin(); + i != far_players.end(); i++) { - 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"<SetBlocksNotSent(modified_blocks); + } - /* - If in creative mode, item dropping is disabled unless - player has build privileges - */ - if(g_settings->getBool("creative_mode") && - (getPlayerPrivs(player) & PRIV_BUILD) == 0) - { - infostream<<"Not allowing player to drop item: " - "creative mode and no build privs"<inventory.getList("main"); + if(ilist == NULL) return; - } - // Calculate a position for it - v3f pos = intToFloat(p_over, BS); - //pos.Y -= BS*0.45; - /*pos.Y -= BS*0.25; // let it drop a bit - // 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;*/ + // 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; /* - Create the object + Handle material items */ - ServerActiveObject *obj = item->createSAO(m_env, 0, pos); - - if(obj == NULL) - { - infostream<<"WARNING: item resulted in NULL object, " - <<"not placing onto map" - <getName()) { - actionstream<getName()<<" places "<getName() - <<" at "<addActiveObject(obj); - - infostream<<"Placed object"<getBool("creative_mode") == false) - { - // Delete the right amount of items from the slot - u16 dropcount = item->getDropCount(); - - // Delete item if all gone - if(item->getCount() <= dropcount) + 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) { - 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); + infostream<<"Client "<remove(dropcount); - - // Send inventory - UpdateCrafting(peer_id); - SendInventory(peer_id); } - } - } - - } // action == 1 + catch(InvalidPositionException &e) + { + infostream<<"Server: Ignoring ADDNODE: Node not found" + <<" Adding block to emerge queue." + <SetBlockNotSent(blockpos); + return; + } - NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); - if(!meta) - return; - if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL")) - return; - SignNodeMetadata *signmeta = (SignNodeMetadata*)meta; - signmeta->setText(text); - - actionstream<getName()<<" writes \""<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 "<getMap().getBlockNoCreateNoEx(blockpos); - if(block) - { - block->raiseModified(MOD_STATE_WRITE_NEEDED, - "sign node text"); - } + // Calculate direction for wall mounted stuff + if(m_nodedef->get(n).wall_mounted) + n.param2 = packDir(p_under - p_above); - setBlockNotSent(blockpos); - } - else if(command == TOSERVER_INVENTORY_ACTION) - { - /*// Ignore inventory changes if in creative mode - if(g_settings->getBool("creative_mode") == true) - { - infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode" - <to_inv == "current_player" && - ma->from_inv == "current_player") - { - InventoryList *rlist = player->inventory.getList("craftresult"); - assert(rlist); - InventoryList *clist = player->inventory.getList("craft"); - assert(clist); - InventoryList *mlist = player->inventory.getList("main"); - assert(mlist); /* - Craftresult is no longer preview if something - is moved into it + Send to all close-by players + */ + core::list far_players; + sendAddNode(p_above, n, 0, &far_players, 30); + + /* + 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() == LEGN(m_nodedef, "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() == LEGN(m_nodedef, "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 { @@ -3515,9 +3707,9 @@ void Server::inventoryModified(InventoryContext *c, std::string id) if(id == "current_player") { assert(c->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; } @@ -3536,7 +3728,7 @@ void Server::inventoryModified(InventoryContext *c, std::string id) if(meta) meta->inventoryModified(); - MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos); + MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos); if(block) block->raiseModified(MOD_STATE_WRITE_NEEDED); @@ -3718,6 +3910,31 @@ void Server::SendNodeDef(con::Connection &con, u16 peer_id, con.Send(peer_id, 0, data, true); } +void Server::SendCraftItemDef(con::Connection &con, u16 peer_id, + ICraftItemDefManager *craftitemdef) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + /* + u16 command + u32 length of the next item + serialized CraftItemDefManager + */ + writeU16(os, TOCLIENT_CRAFTITEMDEF); + std::ostringstream tmp_os(std::ios::binary); + craftitemdef->serialize(tmp_os); + os<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) - { - // Get result of crafting grid - - std::vector items; - for(u16 i=0; i<9; i++){ - if(clist->getItem(i) == NULL) - items.push_back(NULL); - else - items.push_back(clist->getItem(i)->clone()); - } - CraftPointerInput cpi(3, items); - - InventoryItem *result = m_craftdef->getCraftResult(cpi, this); - //InventoryItem *result = craft_get_result(items, this); - if(result) - rlist->addItem(result); + // If 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) @@ -4451,6 +4698,14 @@ 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() @@ -4465,6 +4720,10 @@ ICraftDefManager* Server::getCraftDefManager() { return m_craftdef; } +ICraftItemDefManager* Server::getCraftItemDefManager() +{ + return m_craftitemdef; +} ITextureSource* Server::getTextureSource() { return NULL; @@ -4486,6 +4745,10 @@ IWritableCraftDefManager* Server::getWritableCraftDefManager() { return m_craftdef; } +IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager() +{ + return m_craftitemdef; +} v3f findSpawnPos(ServerMap &map) { @@ -4602,9 +4865,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id m_authmanager.setPrivs(name, stringToPrivs(g_settings->get("default_privs"))); - /* - Set player position - */ + /* Set player position */ infostream<<"Server: Finding spawn place for player \"" <addPlayer(player); - /* - Add stuff to inventory - */ - + /* Run scripts */ + ServerRemotePlayer *srp = static_cast(player); + scriptapi_on_newplayer(m_lua, srp); + + /* Add stuff to inventory */ if(g_settings->getBool("creative_mode")) { // Warning: double code above @@ -4632,10 +4891,6 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id // Set creative inventory craft_set_creative_inventory(player, this); } - else if(g_settings->getBool("give_initial_stuff")) - { - craft_give_initial_stuff(player, this); - } return player;