#include "log.h"
#include "script.h"
#include "scriptapi.h"
+#include "nodedef.h"
+#include "tooldef.h"
+#include "craftdef.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
//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"<<std::endl;
return checksum;
}
+struct ModSpec
+{
+ std::string name;
+ std::string path;
+
+ ModSpec(const std::string &name_="", const std::string path_=""):
+ name(name_),
+ path(path_)
+ {}
+};
+
+static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
+{
+ core::list<ModSpec> mods;
+ for(core::list<std::string>::Iterator i = modspaths.begin();
+ i != modspaths.end(); i++){
+ std::string modspath = *i;
+ std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
+ for(u32 j=0; j<dirlist.size(); j++){
+ if(!dirlist[j].dir)
+ continue;
+ std::string modname = dirlist[j].name;
+ std::string modpath = modspath + DIR_DELIM + modname;
+ mods.push_back(ModSpec(modname, modpath));
+ }
+ }
+ return mods;
+}
+
/*
Server
*/
m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
m_lua(NULL),
- //m_scriptapi(NULL),
+ m_toolmgr(createToolDefManager()),
+ m_nodedef(createNodeDefManager()),
+ m_craftdef(createCraftDefManager()),
m_thread(this),
m_emergethread(this),
m_time_counter(0),
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
+
+ infostream<<"m_nodedef="<<m_nodedef<<std::endl;
+
+ // Initialize default node definitions
+ content_mapnode_init(m_nodedef);
+ // Path to builtin.lua
+ std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
+ // Add default global mod path
+ m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
+
// Initialize scripting
infostream<<"Server: Initializing scripting"<<std::endl;
assert(m_lua);
// Export API
scriptapi_export(m_lua, this);
- // Load and run scripts
- std::string defaultscript = porting::path_data + DIR_DELIM
- + "scripts" + DIR_DELIM + "default.lua";
- bool success = script_load(m_lua, defaultscript.c_str());
+ // Load and run builtin.lua
+ infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
+ <<"\""<<std::endl;
+ bool success = script_load(m_lua, builtinpath.c_str());
if(!success){
errorstream<<"Server: Failed to load and run "
- <<defaultscript<<std::endl;
+ <<builtinpath<<std::endl;
assert(0);
}
+ // Load and run "mod" scripts
+ core::list<ModSpec> mods = getMods(m_modspaths);
+ for(core::list<ModSpec>::Iterator i = mods.begin();
+ i != mods.end(); i++){
+ ModSpec mod = *i;
+ infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
+ std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
+ bool success = script_load(m_lua, scriptpath.c_str());
+ if(!success){
+ errorstream<<"Server: Failed to load and run "
+ <<scriptpath<<std::endl;
+ assert(0);
+ }
+ }
// Initialize Environment
- m_env = new ServerEnvironment(new ServerMap(mapsavedir), m_lua);
+ m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, this);
// Give environment reference to scripting api
scriptapi_add_environment(m_lua, m_env);
// Delete Environment
delete m_env;
+
+ delete m_toolmgr;
+ delete m_nodedef;
// Deinitialize scripting
infostream<<"Server: Deinitializing scripting"<<std::endl;
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);
/*
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 textures
+ SendTextures(peer_id);
// Send player info to all players
SendPlayerInfos();
NOTE: This can be used in the future to check if
somebody is cheating, by checking the timing.
*/
+ bool cannot_punch_node = false;
+
+ MapNode n(CONTENT_IGNORE);
+
+ try
+ {
+ n = m_env->getMap().getNode(p_under);
+ }
+ catch(InvalidPositionException &e)
+ {
+ infostream<<"Server: Not punching: Node not found."
+ <<" Adding block to emerge queue."
+ <<std::endl;
+ m_emerge_queue.addBlock(peer_id,
+ getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
+ cannot_punch_node = true;
+ }
+
+ if(cannot_punch_node)
+ return;
+
+ /*
+ Run script hook
+ */
+ ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
+ scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
+
} // action == 0
/*
u8 mineral = MINERAL_NONE;
bool cannot_remove_node = false;
-
+
+ MapNode n(CONTENT_IGNORE);
try
{
- MapNode n = m_env->getMap().getNode(p_under);
+ n = m_env->getMap().getNode(p_under);
// Get mineral
- mineral = n.getMineral();
+ 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(content_diggable(material) == false)
+ if(m_nodedef->get(material).diggable == false)
{
infostream<<"Server: Not finishing digging: "
<<"Node not diggable"
std::string toolname = titem->getToolName();
// Get digging properties for material and tool
+ ToolDiggingProperties tp =
+ m_toolmgr->getDiggingProperties(toolname);
DiggingProperties prop =
- getDiggingProperties(material, toolname);
+ getDiggingProperties(material, &tp, m_nodedef);
if(prop.diggable == false)
{
InventoryItem *item = NULL;
if(mineral != MINERAL_NONE)
- item = getDiggedMineralItem(mineral);
+ item = getDiggedMineralItem(mineral, this);
// If not mineral
if(item == NULL)
{
- std::string &dug_s = content_features(material).dug_item;
+ 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);
+ item = InventoryItem::deSerialize(is, this);
}
}
item = NULL;
if(mineral != MINERAL_NONE)
- item = getDiggedMineralItem(mineral);
+ item = getDiggedMineralItem(mineral, this);
// If not mineral
if(item == NULL)
{
- std::string &extra_dug_s = content_features(material).extra_dug_item;
- s32 extra_rarity = content_features(material).extra_dug_item_rarity;
+ 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);
+ 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);
+ // Add a item to inventory
+ player->inventory.addItem("main", item);
// Send inventory
UpdateCrafting(player->peer_id);
continue;
client->SetBlocksNotSent(modified_blocks);
}
+
+ /*
+ Run script hook
+ */
+ ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
+ scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
}
/*
<<" because privileges are "<<getPlayerPrivs(player)
<<std::endl;
- if(content_features(n2).buildable_to == false
+ if(m_nodedef->get(n2).buildable_to == false
|| no_enough_privs)
{
// Client probably has wrong data.
<<" at "<<PP(p_under)<<std::endl;
// Calculate direction for wall mounted stuff
- if(content_features(n).wall_mounted)
+ if(m_nodedef->get(n).wall_mounted)
n.param2 = packDir(p_under - p_over);
// Calculate the direction for furnaces and chests and stuff
- if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
+ if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
{
v3f playerpos = player->getPosition();
v3f blockpos = intToFloat(p_over, BS) - playerpos;
client->SetBlocksNotSent(modified_blocks);
}
+ /*
+ Run script hook
+ */
+ ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
+ scriptapi_environment_on_placenode(m_lua, p_over, n, srp);
+
/*
Calculate special events
*/
- /*if(n.d == CONTENT_MESE)
+ /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
{
u32 count = 0;
for(s16 z=-1; z<=1; z++)
NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
if(!meta)
return;
- if(meta->typeId() != CONTENT_SIGN_WALL)
+ if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL"))
return;
SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
signmeta->setText(text);
MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
if(block)
{
- block->setChangedFlag();
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "sign node text");
}
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd()==false; i++)
- {
- RemoteClient *client = i.getNode()->getValue();
- client->SetBlockNotSent(blockpos);
- }
+ setBlockNotSent(blockpos);
}
else if(command == TOSERVER_INVENTORY_ACTION)
{
p.Y = stoi(fn.next(","));
p.Z = stoi(fn.next(","));
NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
- if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
+ if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
if (lcm->getOwner() != player->getName())
return;
p.Y = stoi(fn.next(","));
p.Z = stoi(fn.next(","));
NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
- if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
+ if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
if (lcm->getOwner() != player->getName())
return;
NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
if(meta)
meta->inventoryModified();
-
- for(core::map<u16, RemoteClient*>::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;
}
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<<serializeLongString(tmp_os.str());
+
+ // Make data buffer
+ std::string s = os.str();
+ infostream<<"Server::SendToolDef(): Sending tool definitions: size="
+ <<s.size()<<std::endl;
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ con.Send(peer_id, 0, data, true);
+}
+
+void Server::SendNodeDef(con::Connection &con, u16 peer_id,
+ INodeDefManager *nodedef)
+{
+ DSTACK(__FUNCTION_NAME);
+ std::ostringstream os(std::ios_base::binary);
+
+ /*
+ u16 command
+ u32 length of the next item
+ serialized NodeDefManager
+ */
+ writeU16(os, TOCLIENT_NODEDEF);
+ std::ostringstream tmp_os(std::ios::binary);
+ nodedef->serialize(tmp_os);
+ os<<serializeLongString(tmp_os.str());
+
+ // Make data buffer
+ std::string s = os.str();
+ infostream<<"Server::SendNodeDef(): Sending node definitions: size="
+ <<s.size()<<std::endl;
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ con.Send(peer_id, 0, data, true);
+}
+
/*
Non-static send methods
*/
}
}
+struct SendableTexture
+{
+ std::string name;
+ std::string path;
+ std::string data;
+
+ SendableTexture(const std::string &name_="", const std::string path_="",
+ const std::string &data_=""):
+ name(name_),
+ path(path_),
+ data(data_)
+ {}
+};
+
+void Server::SendTextures(u16 peer_id)
+{
+ DSTACK(__FUNCTION_NAME);
+
+ infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
+
+ /* Read textures */
+
+ // Put 5kB in one bunch (this is not accurate)
+ u32 bytes_per_bunch = 5000;
+
+ core::array< core::list<SendableTexture> > texture_bunches;
+ texture_bunches.push_back(core::list<SendableTexture>());
+
+ u32 texture_size_bunch_total = 0;
+ core::list<ModSpec> mods = getMods(m_modspaths);
+ for(core::list<ModSpec>::Iterator i = mods.begin();
+ i != mods.end(); i++){
+ ModSpec mod = *i;
+ std::string texturepath = mod.path + DIR_DELIM + "textures";
+ std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
+ for(u32 j=0; j<dirlist.size(); j++){
+ if(dirlist[j].dir) // Ignode dirs
+ continue;
+ std::string tname = dirlist[j].name;
+ std::string tpath = texturepath + DIR_DELIM + tname;
+ // Read data
+ std::ifstream fis(tpath.c_str(), std::ios_base::binary);
+ if(fis.good() == false){
+ errorstream<<"Server::SendTextures(): Could not open \""
+ <<tname<<"\" for reading"<<std::endl;
+ continue;
+ }
+ std::ostringstream tmp_os(std::ios_base::binary);
+ bool bad = false;
+ for(;;){
+ char buf[1024];
+ fis.read(buf, 1024);
+ std::streamsize len = fis.gcount();
+ tmp_os.write(buf, len);
+ texture_size_bunch_total += len;
+ if(fis.eof())
+ break;
+ if(!fis.good()){
+ bad = true;
+ break;
+ }
+ }
+ if(bad){
+ errorstream<<"Server::SendTextures(): Failed to read \""
+ <<tname<<"\""<<std::endl;
+ continue;
+ }
+ /*infostream<<"Server::SendTextures(): Loaded \""
+ <<tname<<"\""<<std::endl;*/
+ // Put in list
+ texture_bunches[texture_bunches.size()-1].push_back(
+ SendableTexture(tname, tpath, tmp_os.str()));
+
+ // Start next bunch if got enough data
+ if(texture_size_bunch_total >= bytes_per_bunch){
+ texture_bunches.push_back(core::list<SendableTexture>());
+ texture_size_bunch_total = 0;
+ }
+ }
+ }
+
+ /* Create and send packets */
+
+ u32 num_bunches = texture_bunches.size();
+ for(u32 i=0; i<num_bunches; i++)
+ {
+ /*
+ u16 command
+ u16 total number of texture bunches
+ u16 index of this bunch
+ u32 number of textures in this bunch
+ for each texture {
+ u16 length of name
+ string name
+ u32 length of data
+ data
+ }
+ */
+ std::ostringstream os(std::ios_base::binary);
+
+ writeU16(os, TOCLIENT_TEXTURES);
+ writeU16(os, num_bunches);
+ writeU16(os, i);
+ writeU32(os, texture_bunches[i].size());
+
+ for(core::list<SendableTexture>::Iterator
+ j = texture_bunches[i].begin();
+ j != texture_bunches[i].end(); j++){
+ os<<serializeString(j->name);
+ os<<serializeLongString(j->data);
+ }
+
+ // Make data buffer
+ std::string s = os.str();
+ infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
+ <<" textures="<<texture_bunches[i].size()
+ <<" size=" <<s.size()<<std::endl;
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ m_con.Send(peer_id, 0, data, true);
+ }
+}
+
/*
Something random
*/
}
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
+
+ std::vector<InventoryItem*> 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);
- // Get result of crafting grid
- InventoryItem *result = craft_get_result(items);
+ InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
+ //InventoryItem *result = craft_get_result(items, this);
if(result)
rlist->addItem(result);
}
BroadcastChatMessage(msg);
}
+// IGameDef interface
+// Under envlock
+IToolDefManager* Server::getToolDefManager()
+{
+ return m_toolmgr;
+}
+INodeDefManager* Server::getNodeDefManager()
+{
+ return m_nodedef;
+}
+ICraftDefManager* Server::getCraftDefManager()
+{
+ return m_craftdef;
+}
+ITextureSource* Server::getTextureSource()
+{
+ return NULL;
+}
+u16 Server::allocateUnknownNodeId(const std::string &name)
+{
+ return m_nodedef->allocateDummy(name);
+}
+
+IWritableToolDefManager* Server::getWritableToolDefManager()
+{
+ return m_toolmgr;
+}
+IWritableNodeDefManager* Server::getWritableNodeDefManager()
+{
+ return m_nodedef;
+}
+IWritableCraftDefManager* Server::getWritableCraftDefManager()
+{
+ return m_craftdef;
+}
+
v3f findSpawnPos(ServerMap &map)
{
//return v3f(50,50,50)*BS;
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;
player->inventory_backup = new Inventory();
*(player->inventory_backup) = player->inventory;
// Set creative inventory
- craft_set_creative_inventory(player);
+ craft_set_creative_inventory(player, this);
}
else if(g_settings->getBool("give_initial_stuff"))
{
- craft_give_initial_stuff(player);
+ craft_give_initial_stuff(player, this);
}
return player;