#include "content_craft.h"
#include "content_nodemeta.h"
#include "mapblock.h"
+#include "serverobject.h"
#define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
DSTACK(__FUNCTION_NAME);
- //bool debug=false;
-
BEGIN_DEBUG_EXCEPTION_HANDLER
+ bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
+
/*
Get block info from queue, emerge them and send them
to clients.
Also decrement the emerge queue count in clients.
*/
- bool optional = true;
+ bool only_from_disk = true;
{
core::map<u16, u8>::Iterator i;
// Check flags
u8 flags = i.getNode()->getValue();
if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
- optional = false;
+ only_from_disk = false;
}
}
-
- /*dstream<<"EmergeThread: p="
- <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
- <<"optional="<<optional<<std::endl;*/
+
+ if(enable_mapgen_debug_info)
+ dstream<<"EmergeThread: p="
+ <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
+ <<"only_from_disk="<<only_from_disk<<std::endl;
ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
bool got_block = true;
core::map<v3s16, MapBlock*> modified_blocks;
- bool only_from_disk = false;
-
- if(optional)
- only_from_disk = true;
-
/*
Fetch block from map or generate a single block
*/
block = map.getBlockNoCreateNoEx(p);
if(!block || block->isDummy() || !block->isGenerated())
{
+ if(enable_mapgen_debug_info)
+ dstream<<"EmergeThread: not in memory, loading"<<std::endl;
+
// Get, load or create sector
/*ServerMapSector *sector =
(ServerMapSector*)map.createSector(p2d);*/
lighting_invalidated_blocks);*/
block = map.loadBlock(p);
+
+ if(only_from_disk == false)
+ {
+ if(block == NULL || block->isGenerated() == false)
+ {
+ if(enable_mapgen_debug_info)
+ dstream<<"EmergeThread: generating"<<std::endl;
+ block = map.generateBlock(p, modified_blocks);
+ }
+ }
- if(block == NULL && only_from_disk == false)
- block = map.generateBlock(p, modified_blocks);
- //block = map.generateBlock(p, changed_blocks);
- /*block = map.generateBlock(p, block, sector, changed_blocks,
- lighting_invalidated_blocks);*/
+ if(enable_mapgen_debug_info)
+ dstream<<"EmergeThread: ended up with: "
+ <<analyze_block(block)<<std::endl;
if(block == NULL)
{
v3s16 center = getNodeBlockPos(center_nodepos);
// Camera position and direction
- v3f camera_pos =
- playerpos + v3f(0, BS+BS/2, 0);
+ v3f camera_pos = player->getEyePosition();
v3f camera_dir = v3f(0,0,1);
camera_dir.rotateYZBy(player->getPitch());
camera_dir.rotateXZBy(player->getYaw());
bool block_is_invalid = false;
if(block != NULL)
{
+ // Reset usage timer, this block will be of use in the future.
+ block->resetUsageTimer();
+
// Block is dummy if data doesn't exist.
// It means it has been not found from disk and not generated
if(block->isDummy())
*/
Server::Server(
- std::string mapsavedir
+ std::string mapsavedir,
+ std::string configpath
):
m_env(new ServerMap(mapsavedir), this),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_authmanager(mapsavedir+"/auth.txt"),
+ m_banmanager(mapsavedir+"/ipban.txt"),
m_thread(this),
m_emergethread(this),
m_time_counter(0),
m_time_of_day_send_timer(0),
m_uptime(0),
m_mapsavedir(mapsavedir),
+ m_configpath(configpath),
m_shutdown_requested(false),
m_ignore_map_edit_events(false),
m_ignore_map_edit_events_peer_id(0)
}
{
- // Step environment
- // This also runs Map's timers
JMutexAutoLock lock(m_env_mutex);
+ // Step environment
ScopeProfiler sp(&g_profiler, "Server: environment step");
m_env.step(dtime);
}
+
+ const float map_timer_and_unload_dtime = 5.15;
+ if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
+ {
+ JMutexAutoLock lock(m_env_mutex);
+ // Run Map's timers and unload unused data
+ ScopeProfiler sp(&g_profiler, "Server: map timer and unload");
+ m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
+ g_settings.getFloat("server_unload_unused_data_timeout"));
+ }
/*
Do background stuff
if(m_unsent_map_edit_queue.size() >= 4)
disable_single_change_sending = true;
+ bool got_any_events = false;
+
+ // We'll log the amount of each
+ Profiler prof;
+
while(m_unsent_map_edit_queue.size() != 0)
{
+ got_any_events = true;
+
MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
// Players far away from the change are stored here.
if(event->type == MEET_ADDNODE)
{
- dstream<<"Server: MEET_ADDNODE"<<std::endl;
+ //dstream<<"Server: MEET_ADDNODE"<<std::endl;
+ prof.add("MEET_ADDNODE", 1);
if(disable_single_change_sending)
sendAddNode(event->p, event->n, event->already_known_by_peer,
&far_players, 5);
}
else if(event->type == MEET_REMOVENODE)
{
- dstream<<"Server: MEET_REMOVENODE"<<std::endl;
+ //dstream<<"Server: MEET_REMOVENODE"<<std::endl;
+ prof.add("MEET_REMOVENODE", 1);
if(disable_single_change_sending)
sendRemoveNode(event->p, event->already_known_by_peer,
&far_players, 5);
else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
{
dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
+ prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
setBlockNotSent(event->p);
}
else if(event->type == MEET_OTHER)
{
+ prof.add("MEET_OTHER", 1);
dstream<<"WARNING: Server: MEET_OTHER not implemented"
<<std::endl;
}
else
{
+ prof.add("unknown", 1);
dstream<<"WARNING: Server: Unknown MapEditEvent "
<<((u32)event->type)<<std::endl;
}
*/
if(far_players.size() > 0)
{
+ // Convert list format to that wanted by SetBlocksNotSent
core::map<v3s16, MapBlock*> modified_blocks2;
for(core::map<v3s16, bool>::Iterator
i = event->modified_blocks.getIterator();
modified_blocks2.insert(p,
m_env.getMap().getBlockNoCreateNoEx(p));
}
+ // Set blocks not sent
for(core::list<u16>::Iterator
i = far_players.begin();
i != far_players.end(); i++)
if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
break;*/
}
+
+ if(got_any_events)
+ {
+ dstream<<"Server: MapEditEvents:"<<std::endl;
+ prof.print(dstream);
+ }
+
}
/*
}
}
- /*
- Step node metadata
- TODO: Move to ServerEnvironment and utilize active block stuff
- */
- /*{
- //TimeTaker timer("Step node metadata");
-
- JMutexAutoLock envlock(m_env_mutex);
- JMutexAutoLock conlock(m_con_mutex);
-
- ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
-
- core::map<v3s16, MapBlock*> changed_blocks;
- m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
-
- // Use setBlockNotSent
-
- for(core::map<v3s16, MapBlock*>::Iterator
- i = changed_blocks.getIterator();
- i.atEnd() == false; i++)
- {
- MapBlock *block = i.getNode()->getValue();
-
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd()==false; i++)
- {
- RemoteClient *client = i.getNode()->getValue();
- client->SetBlockNotSent(block->getPos());
- }
- }
- }*/
-
/*
Trigger emergethread (it somehow gets to a non-triggered but
bysy state sometimes)
// Auth stuff
if(m_authmanager.isModified())
m_authmanager.save();
+
+ //Bann stuff
+ if(m_banmanager.isModified())
+ m_banmanager.save();
// Map
JMutexAutoLock lock(m_env_mutex);
- if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
- {
- // Save only changed parts
- m_env.getMap().save(true);
- // Delete unused sectors
- u32 deleted_count = m_env.getMap().unloadUnusedData(
- g_settings.getFloat("server_unload_unused_sectors_timeout"));
- if(deleted_count > 0)
- {
- dout_server<<"Server: Unloaded "<<deleted_count
- <<" sectors from memory"<<std::endl;
- }
+ /*// 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 players
- m_env.serializePlayers(m_mapsavedir);
-
- // Save environment metadata
- m_env.saveMeta(m_mapsavedir);
- }
+ // Save only changed parts
+ m_env.getMap().save(true);
+
+ /*if(deleted_count > 0)
+ {
+ dout_server<<"Server: Unloaded "<<deleted_count
+ <<" blocks from memory"<<std::endl;
+ }*/
+
+ // Save players
+ m_env.serializePlayers(m_mapsavedir);
+
+ // Save environment metadata
+ m_env.saveMeta(m_mapsavedir);
}
}
}
<<peer_id<<" not found"<<std::endl;
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;
//peer->serialization_version = deployed;
getClient(peer->id)->pending_serialization_version = deployed;
-
+
if(deployed == SER_FMT_VER_INVALID)
{
derr_server<<DTIME<<"Server: Cannot negotiate "
"serialization version with peer "
<<peer_id<<std::endl;
+ SendAccessDenied(m_con, peer_id,
+ L"Your client is too old (map format)");
+ return;
+ }
+
+ /*
+ Read and check network protocol version
+ */
+
+ u16 net_proto_version = 0;
+ if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
+ {
+ net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
+ }
+
+ 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.");
return;
}
// Get password
char password[PASSWORD_SIZE];
- if(datasize == 2+1+PLAYERNAME_SIZE)
+ if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
{
// old version - assume blank password
password[0] = 0;
checkpwd = g_settings.get("default_password");
}
- if(password != checkpwd && checkpwd != "")
+ /*dstream<<"Server: Client gave password '"<<password
+ <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
+
+ if(password != checkpwd && m_authmanager.exists(playername))
{
derr_server<<DTIME<<"Server: peer_id="<<peer_id
<<": supplied invalid password for "
UpdateCrafting(peer->id);
SendInventory(peer->id);
+ // Send player items to all players
+ SendPlayerItems();
+
// Send HP
{
Player *player = m_env.getPlayer(peer_id);
message += L" joined game";
BroadcastChatMessage(message);
}
+
+ // Warnings about protocol version can be issued here
+ /*if(getClient(peer->id)->net_proto_version == 0)
+ {
+ SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
+ }*/
return;
}
return;
}
+ // Skip if object has been removed
+ if(obj->m_removed)
+ return;
+
//TODO: Check that object is reasonably close
// Left click, pick object up (usually)
if(button == 0)
{
- InventoryList *ilist = player->inventory.getList("main");
- if(g_settings.getBool("creative_mode") == false && ilist != NULL)
- {
+ /*
+ Try creating inventory item
+ */
+ InventoryItem *item = obj->createPickedUpItem();
- // Skip if inventory has no free space
- if(ilist->getUsedSlots() == ilist->getSize())
+ if(item)
+ {
+ InventoryList *ilist = player->inventory.getList("main");
+ if(ilist != NULL)
{
- dout_server<<"Player inventory has no free space"<<std::endl;
- return;
- }
+ if(g_settings.getBool("creative_mode") == false)
+ {
+ // Skip if inventory has no free space
+ if(ilist->getUsedSlots() == ilist->getSize())
+ {
+ dout_server<<"Player inventory has no free space"<<std::endl;
+ return;
+ }
- // Skip if object has been removed
- if(obj->m_removed)
- return;
-
- /*
- Create the inventory item
- */
- InventoryItem *item = obj->createPickedUpItem();
-
- if(item)
- {
- // Add to inventory and send inventory
- ilist->addItem(item);
- UpdateCrafting(player->peer_id);
- SendInventory(player->peer_id);
+ // Add to inventory and send inventory
+ ilist->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.
- */
+ }
+ else
+ {
+ /*
+ Item cannot be picked up. Punch it instead.
+ */
- ToolItem *titem = NULL;
- std::string toolname = "";
+ ToolItem *titem = NULL;
+ std::string toolname = "";
- 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();
- }
- }
-
- u16 wear = obj->punch(toolname);
-
- if(titem)
+ InventoryList *mlist = player->inventory.getList("main");
+ if(mlist != NULL)
+ {
+ InventoryItem *item = mlist->getItem(item_i);
+ if(item && (std::string)item->getName() == "ToolItem")
{
- bool weared_out = titem->addWear(wear);
- if(weared_out)
- mlist->deleteItem(item_i);
- SendInventory(player->peer_id);
+ 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);
+
+ if(titem)
+ {
+ bool weared_out = titem->addWear(wear);
+ if(weared_out)
+ mlist->deleteItem(item_i);
+ SendInventory(player->peer_id);
+ }
+ }
+ }
+ // Right click, do something with object
+ if(button == 1)
+ {
+ // Track hp changes super-crappily
+ u16 oldhp = player->hp;
+
+ // Do stuff
+ obj->rightClick(player);
+
+ // Send back stuff
+ if(player->hp != oldhp)
+ {
+ SendPlayerHP(player);
}
}
}
// Mandatory parameter; actually used for nothing
core::map<v3s16, MapBlock*> modified_blocks;
- u8 material = CONTENT_IGNORE;
+ content_t material = CONTENT_IGNORE;
u8 mineral = MINERAL_NONE;
bool cannot_remove_node = false;
// Get mineral
mineral = n.getMineral();
// Get material at position
- material = n.d;
+ material = n.getContent();
// If not yet cancelled
if(cannot_remove_node == false)
{
<<" because privileges are "<<getPlayerPrivs(player)
<<std::endl;
- if(content_buildable_to(n2.d) == false
+ if(content_features(n2).buildable_to == false
|| no_enough_privs)
{
// Client probably has wrong data.
// Create node data
MaterialItem *mitem = (MaterialItem*)item;
MapNode n;
- n.d = mitem->getMaterial();
- if(content_features(n.d).wall_mounted)
- n.dir = packDir(p_under - p_over);
-
+ n.setContent(mitem->getMaterial());
+
+ // Calculate direction for wall mounted stuff
+ if(content_features(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)
+ {
+ 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
*/
line += L"Server: ";
message = message.substr(commandprefix.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,
newpwd += c;
}
+ dstream<<"Server: Client requests a password change from "
+ <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
+
std::string playername = player->getName();
if(m_authmanager.exists(playername) == false)
}
std::string checkpwd = m_authmanager.getPassword(playername);
-
+
if(oldpwd != checkpwd)
{
dstream<<"Server: invalid old password"<<std::endl;
<<std::endl;
SendChatMessage(peer_id, L"Password change successful");
}
+ else if (command == TOSERVER_PLAYERITEM)
+ {
+ if (datasize < 2+2)
+ return;
+
+ u16 item = readU16(&data[2]);
+ player->wieldItem(item);
+ SendWieldedItem(player);
+ }
else
{
derr_server<<"WARNING: Server::ProcessData(): Ignoring "
void Server::onMapEditEvent(MapEditEvent *event)
{
- dstream<<"Server::onMapEditEvent()"<<std::endl;
+ //dstream<<"Server::onMapEditEvent()"<<std::endl;
if(m_ignore_map_edit_events)
return;
MapEditEvent *e = event->clone();
m_con.Send(peer_id, 0, data, true);
}
+std::string getWieldedItemString(const Player *player)
+{
+ const InventoryItem *item = player->getWieldItem();
+ if (item == NULL)
+ return std::string("");
+ std::ostringstream os(std::ios_base::binary);
+ item->serialize(os);
+ return os.str();
+}
+
+void Server::SendWieldedItem(const Player* player)
+{
+ DSTACK(__FUNCTION_NAME);
+
+ assert(player);
+
+ std::ostringstream os(std::ios_base::binary);
+
+ writeU16(os, TOCLIENT_PLAYERITEM);
+ writeU16(os, 1);
+ writeU16(os, player->peer_id);
+ os<<serializeString(getWieldedItemString(player));
+
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+
+ m_con.SendToAll(0, data, true);
+}
+
+void Server::SendPlayerItems()
+{
+ DSTACK(__FUNCTION_NAME);
+
+ std::ostringstream os(std::ios_base::binary);
+ core::list<Player *> players = m_env.getPlayers(true);
+
+ writeU16(os, TOCLIENT_PLAYERITEM);
+ writeU16(os, players.size());
+ core::list<Player *>::Iterator i;
+ for(i = players.begin(); i != players.end(); ++i)
+ {
+ Player *p = *i;
+ writeU16(os, p->peer_id);
+ os<<serializeString(getWieldedItemString(p));
+ }
+
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+
+ m_con.SendToAll(0, data, true);
+}
+
void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
{
DSTACK(__FUNCTION_NAME);
}
os<<L"}";
if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
- os<<" WARNING: Map saving is disabled."<<std::endl;
+ os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
+ if(g_settings.get("motd") != "")
+ os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings.get("motd"));
return os.str();
}
// Reset inventory to creative if in creative mode
if(g_settings.getBool("creative_mode"))
{
+ // Warning: double code below
+ // Backup actual inventory
+ player->inventory_backup = new Inventory();
+ *(player->inventory_backup) = player->inventory;
+ // Set creative inventory
craft_set_creative_inventory(player);
}
if(g_settings.getBool("creative_mode"))
{
+ // Warning: double code above
+ // Backup actual inventory
+ player->inventory_backup = new Inventory();
+ *(player->inventory_backup) = player->inventory;
+ // Set creative inventory
craft_set_creative_inventory(player);
}
else if(g_settings.getBool("give_initial_stuff"))