#include "content_nodemeta.h"
#include "mapblock.h"
#include "serverobject.h"
+#include "settings.h"
+#include "profiler.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)
{
}
// Won't send anything if already sending
- if(m_blocks_sending.size() >= g_settings.getU16
+ if(m_blocks_sending.size() >= g_settings->getU16
("max_simultaneous_block_sends_per_client"))
{
//dstream<<"Not sending any blocks, Queue full."<<std::endl;
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());
//dstream<<"d_start="<<d_start<<std::endl;
- u16 max_simul_sends_setting = g_settings.getU16
+ u16 max_simul_sends_setting = g_settings->getU16
("max_simultaneous_block_sends_per_client");
u16 max_simul_sends_usually = max_simul_sends_setting;
Decrease send rate if player is building stuff.
*/
m_time_from_building += dtime;
- if(m_time_from_building < g_settings.getFloat(
+ if(m_time_from_building < g_settings->getFloat(
"full_block_send_enable_min_time_from_building"))
{
max_simul_sends_usually
*/
s32 new_nearest_unsent_d = -1;
- s16 d_max = g_settings.getS16("max_block_send_distance");
- s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
+ s16 d_max = g_settings->getS16("max_block_send_distance");
+ s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
// Don't loop very much at a time
if(d_max > d_start+1)
/*
Don't generate or send if not in sight
+ FIXME This only works if the client uses a small enough
+ FOV setting. The default of 72 degrees is fine.
*/
- if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
+ float camera_fov = (72.0*PI/180) * 4./3.;
+ if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
{
continue;
}
{
m_nothing_to_send_counter++;
if((s16)m_nothing_to_send_counter >=
- g_settings.getS16("max_block_send_distance"))
+ g_settings->getS16("max_block_send_distance"))
{
// Pause time in seconds
m_nothing_to_send_pause_timer = 1.0;
u16 command
u16 number of player positions
for each player:
+ u16 peer_id
v3s32 position*100
v3s32 speed*100
s32 pitch*100
v3s16 center_nodepos = floatToInt(playerpos, BS);
v3s16 center = getNodeBlockPos(center_nodepos);
- s16 d_max = g_settings.getS16("active_object_range");
-
- // Number of blocks whose objects were written to bos
- u16 blockcount = 0;
-
- std::ostringstream bos(std::ios_base::binary);
-
- for(s16 d = 0; d <= d_max; d++)
- {
- core::list<v3s16> list;
- getFacePositions(list, d);
-
- core::list<v3s16>::Iterator li;
- for(li=list.begin(); li!=list.end(); li++)
- {
- v3s16 p = *li + center;
-
- /*
- Ignore blocks that haven't been sent to the client
- */
- {
- if(m_blocks_sent.find(p) == NULL)
- continue;
- }
-
- // Try stepping block and add it to a send queue
- try
- {
-
- // Get block
- MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
-
- /*
- Step block if not in stepped_blocks and add to stepped_blocks.
- */
- if(stepped_blocks.find(p) == NULL)
- {
- block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
- stepped_blocks.insert(p, true);
- block->setChangedFlag();
- }
-
- // Skip block if there are no objects
- if(block->getObjectCount() == 0)
- continue;
-
- /*
- Write objects
- */
-
- // Write blockpos
- writeV3S16(buf, p);
- bos.write((char*)buf, 6);
-
- // Write objects
- //block->serializeObjects(bos, serialization_version); // DEPRECATED
- // count=0
- writeU16(bos, 0);
-
- blockcount++;
-
- /*
- Stop collecting objects if data is already too big
- */
- // Sum of player and object data sizes
- s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
- // break out if data too big
- if(sum > MAX_OBJECTDATA_SIZE)
- {
- goto skip_subsequent;
- }
-
- } //try
- catch(InvalidPositionException &e)
- {
- // Not in memory
- // Add it to the emerge queue and trigger the thread.
- // Fetch the block only if it is on disk.
-
- // Grab and increment counter
- /*SharedPtr<JMutexAutoLock> lock
- (m_num_blocks_in_emerge_queue.getLock());
- m_num_blocks_in_emerge_queue.m_value++;*/
-
- // Add to queue as an anonymous fetch from disk
- u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
- server->m_emerge_queue.addBlock(0, p, flags);
- server->m_emergethread.trigger();
- }
- }
- }
-
-skip_subsequent:
-
// Write block count
- writeU16(buf, blockcount);
+ writeU16(buf, 0);
os.write((char*)buf, 2);
- // Write block objects
- os<<bos.str();
-
/*
Send data
*/
*/
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)
{}
}
}
+
+ {
+ JMutexAutoLock envlock(m_env_mutex);
- /*
- Save players
- */
- dstream<<"Server: Saving players"<<std::endl;
- m_env.serializePlayers(m_mapsavedir);
+ /*
+ Save players
+ */
+ dstream<<"Server: Saving players"<<std::endl;
+ m_env.serializePlayers(m_mapsavedir);
- /*
- Save environment metadata
- */
- dstream<<"Server: Saving environment metadata"<<std::endl;
- m_env.saveMeta(m_mapsavedir);
-
+ /*
+ Save environment metadata
+ */
+ dstream<<"Server: Saving environment metadata"<<std::endl;
+ m_env.saveMeta(m_mapsavedir);
+ }
+
/*
Stop threads
*/
void Server::stop()
{
DSTACK(__FUNCTION_NAME);
+
+ dout_server<<"Server: Stopping and waiting threads"<<std::endl;
// Stop threads (set run=false first so both start stopping)
m_thread.setRun(false);
}
{
- ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
+ ScopeProfiler sp(g_profiler, "Server: selecting and sending "
"blocks to clients");
// Send blocks to clients
SendBlocks(dtime);
m_uptime.set(m_uptime.get() + dtime);
}
+ {
+ // Process connection's timeouts
+ JMutexAutoLock lock2(m_con_mutex);
+ ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
+ m_con.RunTimeouts(dtime);
+ }
+
+ {
+ // This has to be called so that the client list gets synced
+ // with the peer list of the connection
+ ScopeProfiler sp(g_profiler, "Server: peer change handling");
+ handlePeerChanges();
+ }
+
/*
Update m_time_of_day and overall game time
*/
JMutexAutoLock envlock(m_env_mutex);
m_time_counter += dtime;
- f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
+ f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
u32 units = (u32)(m_time_counter*speed);
m_time_counter -= (f32)units / speed;
m_time_of_day_send_timer -= dtime;
if(m_time_of_day_send_timer < 0.0)
{
- m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
+ m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
//JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
}
}
- {
- // Process connection's timeouts
- JMutexAutoLock lock2(m_con_mutex);
- ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
- m_con.RunTimeouts(dtime);
- }
-
- {
- // This has to be called so that the client list gets synced
- // with the peer list of the connection
- ScopeProfiler sp(&g_profiler, "Server: peer change handling");
- handlePeerChanges();
- }
-
{
JMutexAutoLock lock(m_env_mutex);
// Step environment
- ScopeProfiler sp(&g_profiler, "Server: environment step");
+ ScopeProfiler sp(g_profiler, "Server: environment step");
m_env.step(dtime);
}
{
JMutexAutoLock lock(m_env_mutex);
// Run Map's timers and unload unused data
- ScopeProfiler sp(&g_profiler, "Server: map timer and unload");
+ 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"));
+ g_settings->getFloat("server_unload_unused_data_timeout"));
}
/*
JMutexAutoLock lock(m_env_mutex);
- ScopeProfiler sp(&g_profiler, "Server: liquid transform");
+ ScopeProfiler sp(g_profiler, "Server: liquid transform");
core::map<v3s16, MapBlock*> modified_blocks;
m_env.getMap().transformLiquids(modified_blocks);
}
}
- //if(g_settings.getBool("enable_experimental"))
+ //if(g_settings->getBool("enable_experimental"))
{
/*
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 objects");
// Radius inside which objects are active
- s16 radius = 32;
+ s16 radius = g_settings->getS16("active_object_send_range_blocks");
+ radius *= MAP_BLOCKSIZE;
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
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
}
else if(event->type == MEET_OTHER)
{
+ dstream<<"Server: MEET_OTHER"<<std::endl;
prof.add("MEET_OTHER", 1);
- dstream<<"WARNING: Server: MEET_OTHER not implemented"
- <<std::endl;
+ for(core::map<v3s16, bool>::Iterator
+ i = event->modified_blocks.getIterator();
+ i.atEnd()==false; i++)
+ {
+ v3s16 p = i.getNode()->getKey();
+ setBlockNotSent(p);
+ }
}
else
{
{
float &counter = m_objectdata_timer;
counter += dtime;
- if(counter >= g_settings.getFloat("objectdata_interval"))
+ if(counter >= g_settings->getFloat("objectdata_interval"))
{
JMutexAutoLock lock1(m_env_mutex);
JMutexAutoLock lock2(m_con_mutex);
- ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
+ ScopeProfiler sp(g_profiler, "Server: sending mbo positions");
SendObjectData(counter);
{
float &counter = m_savemap_timer;
counter += dtime;
- if(counter >= g_settings.getFloat("server_map_save_interval"))
+ if(counter >= g_settings->getFloat("server_map_save_interval"))
{
counter = 0.0;
- ScopeProfiler sp(&g_profiler, "Server: saving stuff");
+ ScopeProfiler sp(g_profiler, "Server: saving stuff");
// Auth stuff
if(m_authmanager.isModified())
m_authmanager.save();
+
+ //Bann stuff
+ if(m_banmanager.isModified())
+ m_banmanager.save();
// Map
JMutexAutoLock lock(m_env_mutex);
/*// Unload unused data (delete from memory)
m_env.getMap().unloadUnusedData(
- g_settings.getFloat("server_unload_unused_sectors_timeout"));
+ g_settings->getFloat("server_unload_unused_sectors_timeout"));
*/
/*u32 deleted_count = m_env.getMap().unloadUnusedData(
- g_settings.getFloat("server_unload_unused_sectors_timeout"));
+ g_settings->getFloat("server_unload_unused_sectors_timeout"));
*/
// Save only changed parts
<<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;
+ }
+
+ /* 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)
+ {
+ SendAccessDenied(m_con, peer_id,
+ L"Your client is too old. Please upgrade.");
+ return;
+ }
+ }
/*
Set up player
// 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;
}
else
{
- checkpwd = g_settings.get("default_password");
+ 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 "
m_authmanager.add(playername);
m_authmanager.setPassword(playername, checkpwd);
m_authmanager.setPrivs(playername,
- stringToPrivs(g_settings.get("default_privs")));
+ stringToPrivs(g_settings->get("default_privs")));
m_authmanager.save();
}
+
+ // Enforce user limit.
+ // Don't enforce for users that have some admin right
+ if(m_clients.size() >= g_settings->getU16("max_users") &&
+ (m_authmanager.getPrivs(playername)
+ & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
+ playername != g_settings->get("name"))
+ {
+ SendAccessDenied(m_con, peer_id, L"Too many users.");
+ return;
+ }
// Get player
Player *player = emergePlayer(playername, password, peer_id);
-
- /*{
- // DEBUG: Test serialization
- std::ostringstream test_os;
- player->serialize(test_os);
- dstream<<"Player serialization test: \""<<test_os.str()
- <<"\""<<std::endl;
- std::istringstream test_is(test_os.str());
- player->deSerialize(test_is);
- }*/
-
// If failed, cancel
if(player == NULL)
{
return;
}
- /*
- // If a client is already connected to the player, cancel
- if(player->peer_id != 0)
- {
- derr_server<<DTIME<<"Server: peer_id="<<peer_id
- <<" tried to connect to "
- "an already connected player (peer_id="
- <<player->peer_id<<")"<<std::endl;
- return;
- }
- // Set client of player
- player->peer_id = peer_id;
- */
-
- // Check if player doesn't exist
- if(player == NULL)
- throw con::InvalidIncomingDataException
- ("Server::ProcessData(): INIT: Player doesn't exist");
-
- /*// update name if it was supplied
- if(datasize >= 20+3)
- {
- data[20+3-1] = 0;
- player->updateName((const char*)&data[3]);
- }*/
-
/*
Answer with a TOCLIENT_INIT
*/
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 < PROTOCOL_VERSION)
+ {
+ SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
+ }
+
+ /*
+ Check HP, respawn if necessary
+ */
+ {
+ Player *player = m_env.getPlayer(peer_id);
+ HandlePlayerHP(player, 0);
+ }
return;
}
}
else if(command == TOSERVER_CLICK_OBJECT)
{
- if(datasize < 13)
- return;
-
- if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
- return;
-
- /*
- [0] u16 command
- [2] u8 button (0=left, 1=right)
- [3] v3s16 block
- [9] s16 id
- [11] u16 item
- */
- u8 button = readU8(&data[2]);
- v3s16 p;
- p.X = readS16(&data[3]);
- p.Y = readS16(&data[5]);
- p.Z = readS16(&data[7]);
- s16 id = readS16(&data[9]);
- //u16 item_i = readU16(&data[11]);
-
- MapBlock *block = NULL;
- try
- {
- block = m_env.getMap().getBlockNoCreate(p);
- }
- catch(InvalidPositionException &e)
- {
- derr_server<<"CLICK_OBJECT block not found"<<std::endl;
- return;
- }
-
- MapBlockObject *obj = block->getObject(id);
-
- if(obj == NULL)
- {
- derr_server<<"CLICK_OBJECT object not found"<<std::endl;
- return;
- }
-
- //TODO: Check that object is reasonably close
-
- // Left click
- if(button == 0)
- {
- InventoryList *ilist = player->inventory.getList("main");
- if(g_settings.getBool("creative_mode") == false && ilist != NULL)
- {
-
- // Skip if inventory has no free space
- if(ilist->getUsedSlots() == ilist->getSize())
- {
- dout_server<<"Player inventory has no free space"<<std::endl;
- return;
- }
-
- /*
- Create the inventory item
- */
- InventoryItem *item = NULL;
- // If it is an item-object, take the item from it
- if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
- {
- item = ((ItemObject*)obj)->createInventoryItem();
- }
- // Else create an item of the object
- else
- {
- item = new MapBlockObjectItem
- (obj->getInventoryString());
- }
-
- // Add to inventory and send inventory
- ilist->addItem(item);
- UpdateCrafting(player->peer_id);
- SendInventory(player->peer_id);
- }
-
- // Remove from block
- block->removeObject(id);
- }
+ derr_server<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
+ return;
}
else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
{
*/
u8 button = readU8(&data[2]);
u16 id = readS16(&data[3]);
- u16 item_i = readU16(&data[11]);
+ u16 item_i = readU16(&data[5]);
ServerActiveObject *obj = m_env.getActiveObject(id);
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->roomForItem(item) == false)
+ {
+ 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)
+ InventoryList *mlist = player->inventory.getList("main");
+ if(mlist != NULL)
+ {
+ InventoryItem *item = mlist->getItem(item_i);
+ if(item && (std::string)item->getName() == "ToolItem")
{
- InventoryItem *item = mlist->getItem(item_i);
- if(item && (std::string)item->getName() == "ToolItem")
- {
- titem = (ToolItem*)item;
- toolname = titem->getToolName();
- }
+ 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);
- }
+ v3f playerpos = player->getPosition();
+ v3f objpos = obj->getBasePosition();
+ v3f dir = (objpos - playerpos).normalize();
+
+ u16 wear = obj->punch(toolname, dir, player->getName());
+
+ 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);
+ }
+ }
}
else if(command == TOSERVER_GROUND_ACTION)
{
Update and send inventory
*/
- if(g_settings.getBool("creative_mode") == false)
+ if(g_settings->getBool("creative_mode") == false)
{
/*
Wear out tool
UpdateCrafting(player->peer_id);
SendInventory(player->peer_id);
}
+
+ item = NULL;
+
+ if(mineral != MINERAL_NONE)
+ item = getDiggedMineralItem(mineral);
+
+ // 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;
+ if(extra_dug_s != "" && extra_rarity != 0
+ && myrand() % extra_rarity == 0)
+ {
+ std::istringstream is(extra_dug_s, std::ios::binary);
+ item = InventoryItem::deSerialize(is);
+ }
+ }
+
+ if(item != NULL)
+ {
+ // Add a item to inventory
+ player->inventory.addItem("main", item);
+
+ // Send inventory
+ UpdateCrafting(player->peer_id);
+ SendInventory(player->peer_id);
+ }
}
/*
Handle inventory
*/
InventoryList *ilist = player->inventory.getList("main");
- if(g_settings.getBool("creative_mode") == false && ilist)
+ if(g_settings->getBool("creative_mode") == false && ilist)
{
// Remove from inventory and send inventory
if(mitem->getCount() == 1)
{
MapEditEventIgnorer ign(&m_ignore_map_edit_events);
- m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
+ 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
return;
}
+ /*
+ If in creative mode, item dropping is disabled unless
+ player has build privileges
+ */
+ if(g_settings->getBool("creative_mode") &&
+ (getPlayerPrivs(player) & PRIV_BUILD) == 0)
+ {
+ derr_server<<"Not allowing player to drop item: "
+ "creative mode and no build privs"<<std::endl;
+ return;
+ }
+
dout_server<<"Placing a miscellaneous item on map"
<<std::endl;
dout_server<<"Placed object"<<std::endl;
- if(g_settings.getBool("creative_mode") == false)
+ if(g_settings->getBool("creative_mode") == false)
{
// Delete the right amount of items from the slot
u16 dropcount = item->getDropCount();
#endif
else if(command == TOSERVER_SIGNTEXT)
{
- if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
- return;
- /*
- u16 command
- v3s16 blockpos
- s16 id
- u16 textlen
- textdata
- */
- 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 blockpos = readV3S16(buf);
- is.read((char*)buf, 2);
- s16 id = readS16(buf);
- is.read((char*)buf, 2);
- u16 textlen = readU16(buf);
- std::string text;
- for(u16 i=0; i<textlen; i++)
- {
- is.read((char*)buf, 1);
- text += (char)buf[0];
- }
-
- MapBlock *block = NULL;
- try
- {
- block = m_env.getMap().getBlockNoCreate(blockpos);
- }
- catch(InvalidPositionException &e)
- {
- derr_server<<"Error while setting sign text: "
- "block not found"<<std::endl;
- return;
- }
-
- MapBlockObject *obj = block->getObject(id);
- if(obj == NULL)
- {
- derr_server<<"Error while setting sign text: "
- "object not found"<<std::endl;
- return;
- }
-
- if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
- {
- derr_server<<"Error while setting sign text: "
- "object is not a sign"<<std::endl;
- return;
- }
-
- ((SignObject*)obj)->setText(text);
-
- obj->getBlock()->setChangedFlag();
+ derr_server<<"Server: TOSERVER_SIGNTEXT not supported anymore"
+ <<std::endl;
+ return;
}
else if(command == TOSERVER_SIGNNODETEXT)
{
else if(command == TOSERVER_INVENTORY_ACTION)
{
/*// Ignore inventory changes if in creative mode
- if(g_settings.getBool("creative_mode") == true)
+ if(g_settings->getBool("creative_mode") == true)
{
dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
<<std::endl;
*/
bool disable_action = false;
if(a->getType() == IACTION_MOVE
- && g_settings.getBool("creative_mode") == false)
+ && g_settings->getBool("creative_mode") == false)
{
IMoveAction *ma = (IMoveAction*)a;
if(ma->to_inv == "current_player" &&
mlist->addItem(item1);
}
}
+ // 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")
+ {
+ 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;
+ }
+ }
+ }
+ else if (ma->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 && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
+ LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
+ if (lcm->getOwner() != player->getName())
+ return;
+ }
+ }
+ }
}
if(disable_action == false)
u64 privs = getPlayerPrivs(player);
// Parse commands
- std::wstring commandprefix = L"/#";
- if(message.substr(0, commandprefix.size()) == commandprefix)
+ if(message[0] == L'/')
{
- line += L"Server: ";
+ size_t strip_size = 1;
+ if (message[1] == L'#') // support old-style commans
+ ++strip_size;
+ message = message.substr(strip_size);
- 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,
privs);
- line += processServerCommand(ctx);
- send_to_sender = ctx->flags & 1;
- send_to_others = ctx->flags & 2;
+ 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;
}
}
else if(command == TOSERVER_DAMAGE)
{
- if(g_settings.getBool("enable_damage"))
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
- u8 damage = readU8(is);
- if(player->hp > damage)
- {
- player->hp -= damage;
- }
- else
- {
- player->hp = 0;
+ std::string datastring((char*)&data[2], datasize-2);
+ std::istringstream is(datastring, std::ios_base::binary);
+ u8 damage = readU8(is);
- dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
- <<std::endl;
-
- v3f pos = findSpawnPos(m_env.getServerMap());
- player->setPosition(pos);
- player->hp = 20;
- SendMovePlayer(player);
- SendPlayerHP(player);
-
- //TODO: Throw items around
- }
+ if(g_settings->getBool("enable_damage"))
+ {
+ HandlePlayerHP(player, damage);
+ }
+ else
+ {
+ SendPlayerHP(player);
}
-
- SendPlayerHP(player);
}
else if(command == TOSERVER_PASSWORD)
{
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 if(command == TOSERVER_RESPAWN)
+ {
+ if(player->hp != 0)
+ return;
+
+ RespawnPlayer(player);
+ }
else
{
derr_server<<"WARNING: Server::ProcessData(): Ignoring "
con.Send(peer_id, 0, data, true);
}
+void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
+ bool set_camera_point_target, v3f camera_point_target)
+{
+ DSTACK(__FUNCTION_NAME);
+ std::ostringstream os(std::ios_base::binary);
+
+ writeU16(os, TOCLIENT_DEATHSCREEN);
+ writeU8(os, set_camera_point_target);
+ writeV3F1000(os, camera_point_target);
+
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ con.Send(peer_id, 0, data, true);
+}
+
/*
Non-static send methods
*/
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);
s32 total_sending = 0;
{
- ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
+ ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
for(u32 i=0; i<queue.size(); i++)
{
//TODO: Calculate limit dynamically
- if(total_sending >= g_settings.getS32
+ if(total_sending >= g_settings->getS32
("max_simultaneous_block_sends_server_total"))
break;
Something random
*/
+void Server::HandlePlayerHP(Player *player, s16 damage)
+{
+ if(player->hp > damage)
+ {
+ player->hp -= damage;
+ SendPlayerHP(player);
+ }
+ else
+ {
+ dstream<<"Server::HandlePlayerHP(): Player "
+ <<player->getName()<<" dies"<<std::endl;
+
+ player->hp = 0;
+
+ //TODO: Throw items around
+
+ // Handle players that are not connected
+ if(player->peer_id == PEER_ID_INEXISTENT){
+ RespawnPlayer(player);
+ return;
+ }
+
+ SendPlayerHP(player);
+
+ RemoteClient *client = getClient(player->peer_id);
+ if(client->net_proto_version >= 3)
+ {
+ SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
+ }
+ else
+ {
+ RespawnPlayer(player);
+ }
+ }
+}
+
+void Server::RespawnPlayer(Player *player)
+{
+ v3f pos = findSpawnPos(m_env.getServerMap());
+ player->setPosition(pos);
+ player->hp = 20;
+ SendMovePlayer(player);
+ SendPlayerHP(player);
+}
+
void Server::UpdateCrafting(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
/*
Calculate crafting stuff
*/
- if(g_settings.getBool("creative_mode") == false)
+ if(g_settings->getBool("creative_mode") == false)
{
InventoryList *clist = player->inventory.getList("craft");
InventoryList *rlist = player->inventory.getList("craftresult");
- if(rlist->getUsedSlots() == 0)
+ if(rlist && rlist->getUsedSlots() == 0)
player->craftresult_is_preview = true;
if(rlist && player->craftresult_is_preview)
}
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();
}
+// Saves g_settings to configpath given at initialization
+void Server::saveConfig()
+{
+ if(m_configpath != "")
+ g_settings->updateConfigFile(m_configpath.c_str());
+}
+
v3f findSpawnPos(ServerMap &map)
{
//return v3f(50,50,50)*BS;
player->peer_id = peer_id;
// Reset inventory to creative if in creative mode
- if(g_settings.getBool("creative_mode"))
+ if(g_settings->getBool("creative_mode"))
{
+ // Warning: double code below
+ // Backup actual inventory
+ player->inventory_backup = new Inventory();
+ *(player->inventory_backup) = player->inventory;
+ // Set creative inventory
craft_set_creative_inventory(player);
}
m_authmanager.add(name);
m_authmanager.setPassword(name, password);
m_authmanager.setPrivs(name,
- stringToPrivs(g_settings.get("default_privs")));
+ stringToPrivs(g_settings->get("default_privs")));
/*
Set player position
Add stuff to inventory
*/
- if(g_settings.getBool("creative_mode"))
+ 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"))
+ else if(g_settings->getBool("give_initial_stuff"))
{
craft_give_initial_stuff(player);
}
// Collect information about leaving in chat
std::wstring message;
{
- std::wstring name = L"unknown";
Player *player = m_env.getPlayer(c.peer_id);
if(player != NULL)
- name = narrow_to_wide(player->getName());
-
- message += L"*** ";
- message += name;
- message += L" left game";
- if(c.timeout)
- message += L" (timed out)";
+ {
+ std::wstring name = narrow_to_wide(player->getName());
+ message += L"*** ";
+ message += name;
+ message += L" left game";
+ if(c.timeout)
+ message += L" (timed out)";
+ }
}
/*// Delete player
std::string playername = player->getName();
// Local player gets all privileges regardless of
// what's set on their account.
- if(g_settings.get("name") == playername)
+ if(g_settings->get("name") == playername)
{
return PRIV_ALL;
}
// This is kind of a hack but can be done like this
// because server.step() is very light
{
- ScopeProfiler sp(&g_profiler, "dedicated server sleep");
+ ScopeProfiler sp(g_profiler, "dedicated server sleep");
sleep_ms(30);
}
server.step(0.030);
Profiler
*/
float profiler_print_interval =
- g_settings.getFloat("profiler_print_interval");
+ g_settings->getFloat("profiler_print_interval");
if(profiler_print_interval != 0)
{
if(m_profiler_interval.step(0.030, profiler_print_interval))
{
dstream<<"Profiler:"<<std::endl;
- g_profiler.print(dstream);
- g_profiler.clear();
+ g_profiler->print(dstream);
+ g_profiler->clear();
}
}