/*
Minetest-c55
-Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include "voxel.h"
#include "materials.h"
#include "mineral.h"
+#include "config.h"
+#include "servercommand.h"
+#include "filesys.h"
#define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
{
DSTACK(__FUNCTION_NAME);
+ /*u32 timer_result;
+ TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
+
// Increment timers
m_nearest_unsent_reset_timer += dtime;
+ m_nothing_to_send_pause_timer -= dtime;
+
+ if(m_nothing_to_send_pause_timer >= 0)
+ return;
// Won't send anything if already sending
if(m_blocks_sending.size() >= g_settings.getU16
/*
Get the starting value of the block finder radius.
*/
- s16 last_nearest_unsent_d;
- s16 d_start;
if(m_last_center != center)
{
//dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
}
- last_nearest_unsent_d = m_nearest_unsent_d;
-
- d_start = m_nearest_unsent_d;
+ //s16 last_nearest_unsent_d = m_nearest_unsent_d;
+ s16 d_start = m_nearest_unsent_d;
- u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
+ //dstream<<"d_start="<<d_start<<std::endl;
+
+ u16 max_simul_sends_setting = g_settings.getU16
("max_simultaneous_block_sends_per_client");
- u16 maximum_simultaneous_block_sends =
- maximum_simultaneous_block_sends_setting;
+ u16 max_simul_sends_usually = max_simul_sends_setting;
/*
Check the time from last addNode/removeNode.
if(m_time_from_building < g_settings.getFloat(
"full_block_send_enable_min_time_from_building"))
{
- maximum_simultaneous_block_sends
+ max_simul_sends_usually
= LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
}
+ /*
+ Number of blocks sending + number of blocks selected for sending
+ */
u32 num_blocks_selected = m_blocks_sending.size();
/*
//dstream<<"Starting from "<<d_start<<std::endl;
+ bool sending_something = false;
+
for(s16 d = d_start; d <= d_max; d++)
{
//dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
update our d to it.
Else update m_nearest_unsent_d
*/
- if(m_nearest_unsent_d != last_nearest_unsent_d)
+ /*if(m_nearest_unsent_d != last_nearest_unsent_d)
{
d = m_nearest_unsent_d;
last_nearest_unsent_d = m_nearest_unsent_d;
- }
+ }*/
/*
Get the border/face dot coordinates of a "d-radiused"
Also, don't send blocks that are already flying.
*/
- u16 maximum_simultaneous_block_sends_now =
- maximum_simultaneous_block_sends;
+ // Start with the usual maximum
+ u16 max_simul_dynamic = max_simul_sends_usually;
+ // If block is very close, allow full maximum
if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
- {
- maximum_simultaneous_block_sends_now =
- maximum_simultaneous_block_sends_setting;
- }
+ max_simul_dynamic = max_simul_sends_setting;
- // Limit is dynamically lowered when building
- if(num_blocks_selected
- >= maximum_simultaneous_block_sends_now)
- {
- /*dstream<<"Not sending more blocks. Queue full. "
- <<m_blocks_sending.size()
- <<std::endl;*/
+ // Don't select too many blocks for sending
+ if(num_blocks_selected >= max_simul_dynamic)
goto queue_full;
- }
-
+
+ // Don't send blocks that are currently being transferred
if(m_blocks_sending.find(p) != NULL)
continue;
continue;
}
-#if 0
+#if 1
/*
If block is far away, don't generate it unless it is
- near ground level
-
- NOTE: We can't know the ground level this way with the
- new generator.
+ near ground level.
*/
- if(d > 4)
+ if(d >= 4)
{
- v2s16 p2d(p.X, p.Z);
- MapSector *sector = NULL;
- try
- {
- sector = server->m_env.getMap().getSectorNoGenerate(p2d);
- }
- catch(InvalidPositionException &e)
- {
- }
-
- if(sector != NULL)
- {
- // Get center ground height in nodes
- f32 gh = sector->getGroundHeight(
- v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
- // Block center y in nodes
- f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
- // If differs a lot, don't generate
- if(fabs(gh - y) > MAP_BLOCKSIZE*2)
- generate = false;
- }
+ #if 1
+ // Block center y in nodes
+ f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
+ // Don't generate if it's very high or very low
+ if(y < -64 || y > 64)
+ generate = false;
+ #endif
+ #if 0
+ v2s16 p2d_nodes_center(
+ MAP_BLOCKSIZE*p.X,
+ MAP_BLOCKSIZE*p.Z);
+
+ // Get ground height in nodes
+ s16 gh = server->m_env.getServerMap().findGroundLevel(
+ p2d_nodes_center);
+
+ // If differs a lot, don't generate
+ if(fabs(gh - y) > MAP_BLOCKSIZE*2)
+ generate = false;
+ // Actually, don't even send it
+ //continue;
+ #endif
}
#endif
v2s16 chunkpos = map->sector_to_chunk(p2d);
if(map->chunkNonVolatile(chunkpos) == false)
block_is_invalid = true;
+#if 1
+ /*
+ If block is not close, don't send it unless it is near
+ ground level.
+
+ Block is not near ground level if night-time mesh
+ doesn't differ from day-time mesh.
+ */
+ if(d > 3)
+ {
+ if(block->dayNightDiffed() == false)
+ continue;
+ }
+#endif
}
/*
*/
if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
{
- new_nearest_unsent_d = d;
+ if(generate == true)
+ new_nearest_unsent_d = d;
}
/*
dest.push_back(q);
num_blocks_selected += 1;
+ sending_something = true;
}
}
queue_full:
{
m_nearest_unsent_d = new_nearest_unsent_d;
}
+
+ if(sending_something == false)
+ {
+ m_nothing_to_send_counter++;
+ if(m_nothing_to_send_counter >= 3)
+ {
+ // Pause time in seconds
+ m_nothing_to_send_pause_timer = 2.0;
+ }
+ }
+ else
+ {
+ m_nothing_to_send_counter = 0;
+ }
+
+ /*timer_result = timer.stop(true);
+ if(timer_result != 0)
+ dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
}
void RemoteClient::SendObjectData(
*/
if(stepped_blocks.find(p) == NULL)
{
- block->stepObjects(dtime, true, server->getDayNightRatio());
+ block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
stepped_blocks.insert(p, true);
block->setChangedFlag();
}
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_thread(this),
m_emergethread(this),
- m_time_of_day(9000),
m_time_counter(0),
m_time_of_day_send_timer(0),
m_uptime(0),
m_con_mutex.Init();
m_step_dtime_mutex.Init();
m_step_dtime = 0.0;
-
+
+ // Register us to receive map edit events
m_env.getMap().addEventReceiver(this);
+ // If file exists, load environment metadata
+ if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
+ {
+ dstream<<"Server: Loading environment metadata"<<std::endl;
+ m_env.loadMeta(m_mapsavedir);
+ }
+
// Load players
+ dstream<<"Server: Loading players"<<std::endl;
m_env.deSerializePlayers(m_mapsavedir);
}
*/
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);
/*
Stop threads
}
/*
- Update m_time_of_day
+ 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);
u32 units = (u32)(m_time_counter*speed);
m_time_counter -= (f32)units / speed;
- m_time_of_day.set((m_time_of_day.get() + units) % 24000);
+
+ m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
//dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
//Player *player = m_env.getPlayer(client->peer_id);
SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
- m_time_of_day.get());
+ m_env.getTimeOfDay());
// Send as reliable
m_con.Send(client->peer_id, 0, data, true);
}
// Save players
m_env.serializePlayers(m_mapsavedir);
+
+ // Save environment metadata
+ m_env.saveMeta(m_mapsavedir);
}
}
}
// [0] u16 TOSERVER_INIT
// [2] u8 SER_FMT_VER_HIGHEST
// [3] u8[20] player_name
+ // [23] u8[28] password <--- can be sent without this, from old versions
- if(datasize < 3)
+ if(datasize < 2+1+PLAYERNAME_SIZE)
return;
derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
*/
// Get player name
- const u32 playername_size = 20;
- char playername[playername_size];
- for(u32 i=0; i<playername_size-1; i++)
+ char playername[PLAYERNAME_SIZE];
+ for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
{
playername[i] = data[3+i];
}
- playername[playername_size-1] = 0;
-
+ playername[PLAYERNAME_SIZE-1] = 0;
+
+ // Get password
+ char password[PASSWORD_SIZE];
+ if(datasize == 2+1+PLAYERNAME_SIZE)
+ {
+ // old version - assume blank password
+ password[0] = 0;
+ }
+ else
+ {
+ for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+ {
+ password[i] = data[23+i];
+ }
+ password[PASSWORD_SIZE-1] = 0;
+ }
+ Player *checkplayer = m_env.getPlayer(playername);
+ if(checkplayer != NULL && strcmp(checkplayer->getPassword(),password))
+ {
+ derr_server<<DTIME<<"Server: peer_id="<<peer_id
+ <<": supplied invalid password for "
+ <<playername<<std::endl;
+ SendAccessDenied(m_con, peer_id);
+ return;
+ }
+
// Get player
- Player *player = emergePlayer(playername, "", peer_id);
- //Player *player = m_env.getPlayer(peer_id);
+ Player *player = emergePlayer(playername, password, peer_id);
+
/*{
// DEBUG: Test serialization
// Send time of day
{
SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
- m_time_of_day.get());
+ m_env.getTimeOfDay());
m_con.Send(peer->id, 0, data, true);
}
if(datasize < 13)
return;
+ if((player->privs & PRIV_BUILD) == 0)
+ return;
+
/*
[0] u16 command
[2] u8 button (0=left, 1=right)
if(datasize < 7)
return;
+ if((player->privs & PRIV_BUILD) == 0)
+ return;
+
/*
length: 7
[0] u16 command
// Mandatory parameter; actually used for nothing
core::map<v3s16, MapBlock*> modified_blocks;
- u8 material;
+ u8 material = CONTENT_IGNORE;
u8 mineral = MINERAL_NONE;
bool cannot_remove_node = false;
cannot_remove_node = true;
}
+ // Make sure the player is allowed to do it
+ if((player->privs & PRIV_BUILD) == 0)
+ cannot_remove_node = true;
+
/*
If node can't be removed, set block to be re-sent to
client and quit.
try{
// Don't add a node if this is not a free space
MapNode n2 = m_env.getMap().getNode(p_over);
- if(content_buildable_to(n2.d) == false)
+ if(content_buildable_to(n2.d) == false
+ || (player->privs & PRIV_BUILD) ==0)
{
// Client probably has wrong data.
// Set block not sent, so that client will get
#endif
else if(command == TOSERVER_SIGNTEXT)
{
+ if((player->privs & PRIV_BUILD) == 0)
+ return;
/*
u16 command
v3s16 blockpos
}
else if(command == TOSERVER_SIGNNODETEXT)
{
+ if((player->privs & PRIV_BUILD) == 0)
+ return;
/*
u16 command
v3s16 p
// 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 = player->privs;
+ if(g_settings.get("name") == player->getName())
+ privs = PRIV_ALL;
+
// Parse commands
std::wstring commandprefix = L"/#";
if(message.substr(0, commandprefix.size()) == commandprefix)
line += L"Server: ";
message = message.substr(commandprefix.size());
- // Get player name as narrow string
- std::string name_s = player->getName();
- // Convert message to narrow string
- std::string message_s = wide_to_narrow(message);
- // Operator is the single name defined in config.
- std::string operator_name = g_settings.get("name");
- bool is_operator = (operator_name != "" &&
- wide_to_narrow(name) == operator_name);
- bool valid_command = false;
- if(message_s == "help")
- {
- line += L"-!- Available commands: ";
- line += L"status ";
- if(is_operator)
- {
- line += L"shutdown setting ";
- }
- else
- {
- }
- send_to_sender = true;
- valid_command = true;
- }
- else if(message_s == "status")
- {
- line = getStatusString();
- send_to_sender = true;
- valid_command = true;
- }
- else if(is_operator)
+
+ ServerCommandContext *ctx = new ServerCommandContext(
+ str_split(message, L' '),
+ this,
+ &m_env,
+ player,
+ privs);
+
+ line += processServerCommand(ctx);
+ send_to_sender = ctx->flags & 1;
+ send_to_others = ctx->flags & 2;
+ delete ctx;
+
+ }
+ else
+ {
+ if(privs & PRIV_SHOUT)
{
- if(message_s == "shutdown")
- {
- dstream<<DTIME<<" Server: Operator requested shutdown."
- <<std::endl;
- m_shutdown_requested.set(true);
-
- line += L"*** Server shutting down (operator request)";
- send_to_sender = true;
- valid_command = true;
- }
- else if(message_s.substr(0,8) == "setting ")
- {
- std::string confline = message_s.substr(8);
- g_settings.parseConfigLine(confline);
- line += L"-!- Setting changed.";
- send_to_sender = true;
- valid_command = true;
- }
+ line += L"<";
+ line += name;
+ line += L"> ";
+ line += message;
+ send_to_others = true;
}
-
- if(valid_command == false)
+ else
{
- line += L"-!- Invalid command: " + message;
+ line += L"Server: You are not allowed to shout";
send_to_sender = true;
}
}
- else
- {
- line += L"<";
- /*if(is_operator)
- line += L"@";*/
- line += name;
- line += L"> ";
- line += message;
- send_to_others = true;
- }
if(line != L"")
{
SendPlayerHP(player);
}
+ else if(command == TOSERVER_PASSWORD)
+ {
+ /*
+ [0] u16 TOSERVER_PASSWORD
+ [2] u8[28] old password
+ [30] u8[28] new password
+ */
+
+ if(datasize != 2+PASSWORD_SIZE*2)
+ return;
+ char password[PASSWORD_SIZE];
+ for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+ password[i] = data[2+i];
+ password[PASSWORD_SIZE-1] = 0;
+ if(strcmp(player->getPassword(),password))
+ {
+ // Wrong old password supplied!!
+ SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
+ return;
+ }
+ for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+ password[i] = data[30+i];
+ player->updatePassword(password);
+ SendChatMessage(peer_id, L"Password change successful");
+ }
else
{
derr_server<<"WARNING: Server::ProcessData(): Ignoring "
con.Send(peer_id, 0, data, true);
}
+void Server::SendAccessDenied(con::Connection &con, u16 peer_id)
+{
+ DSTACK(__FUNCTION_NAME);
+ std::ostringstream os(std::ios_base::binary);
+
+ writeU16(os, TOCLIENT_ACCESS_DENIED);
+
+ // 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
*/
void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
{
DSTACK(__FUNCTION_NAME);
+
+ v3s16 p = block->getPos();
+
+#if 0
+ // Analyze it a bit
+ bool completely_air = true;
+ for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
+ for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+ for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+ {
+ if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
+ {
+ completely_air = false;
+ x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
+ }
+ }
+
+ // Print result
+ dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
+ if(completely_air)
+ dstream<<"[completely air] ";
+ dstream<<std::endl;
+#endif
+
/*
Create a packet with the block in the right format
*/
u32 replysize = 8 + blockdata.getSize();
SharedBuffer<u8> reply(replysize);
- v3s16 p = block->getPos();
writeU16(&reply[0], TOCLIENT_BLOCKDATA);
writeS16(&reply[2], p.X);
writeS16(&reply[4], p.Y);
}
}
+ // Fence
+ if(!found)
+ {
+ ItemSpec specs[9];
+ specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
+ specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
+ specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
+ specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
+ specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
+ specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
+ if(checkItemCombination(items, specs))
+ {
+ rlist->addItem(new MaterialItem(CONTENT_FENCE, 2));
+ found = true;
+ }
+ }
+
// Sign
if(!found)
{
}
}
+ // Rail
+ if(!found)
+ {
+ ItemSpec specs[9];
+ specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+ specs[1] = ItemSpec(ITEM_CRAFT, "Stick");
+ specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+ specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+ specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
+ specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+ specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+ specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
+ specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+ if(checkItemCombination(items, specs))
+ {
+ rlist->addItem(new MaterialItem(CONTENT_RAIL, 15));
+ found = true;
+ }
+ }
+
// Chest
if(!found)
{
found = true;
}
}
+
+ // Sandstone
+ if(!found)
+ {
+ ItemSpec specs[9];
+ specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
+ specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
+ specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
+ specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
+ if(checkItemCombination(items, specs))
+ {
+ rlist->addItem(new MaterialItem(CONTENT_SANDSTONE, 1));
+ found = true;
+ }
+ }
+
+ // Clay
+ if(!found)
+ {
+ ItemSpec specs[9];
+ specs[3] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
+ specs[4] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
+ specs[6] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
+ specs[7] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
+ if(checkItemCombination(items, specs))
+ {
+ rlist->addItem(new MaterialItem(CONTENT_CLAY, 1));
+ found = true;
+ }
+ }
+
+ // Brick
+ if(!found)
+ {
+ ItemSpec specs[9];
+ specs[3] = ItemSpec(ITEM_CRAFT, "clay_brick");
+ specs[4] = ItemSpec(ITEM_CRAFT, "clay_brick");
+ specs[6] = ItemSpec(ITEM_CRAFT, "clay_brick");
+ specs[7] = ItemSpec(ITEM_CRAFT, "clay_brick");
+ if(checkItemCombination(items, specs))
+ {
+ rlist->addItem(new MaterialItem(CONTENT_BRICK, 1));
+ found = true;
+ }
+ }
+
+ // Paper
+ if(!found)
+ {
+ ItemSpec specs[9];
+ specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_PAPYRUS);
+ specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_PAPYRUS);
+ specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_PAPYRUS);
+ if(checkItemCombination(items, specs))
+ {
+ rlist->addItem(new CraftItem("paper", 1));
+ found = true;
+ }
+ }
+
+ // Book
+ if(!found)
+ {
+ ItemSpec specs[9];
+ specs[1] = ItemSpec(ITEM_CRAFT, "paper");
+ specs[4] = ItemSpec(ITEM_CRAFT, "paper");
+ specs[7] = ItemSpec(ITEM_CRAFT, "paper");
+ if(checkItemCombination(items, specs))
+ {
+ rlist->addItem(new CraftItem("book", 1));
+ found = true;
+ }
+ }
+
+ // Book shelf
+ if(!found)
+ {
+ ItemSpec specs[9];
+ specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+ specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+ specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+ specs[3] = ItemSpec(ITEM_CRAFT, "book");
+ specs[4] = ItemSpec(ITEM_CRAFT, "book");
+ specs[5] = ItemSpec(ITEM_CRAFT, "book");
+ specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+ specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+ specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+ if(checkItemCombination(items, specs))
+ {
+ rlist->addItem(new MaterialItem(CONTENT_BOOKSHELF, 1));
+ found = true;
+ }
+ }
}
} // if creative_mode == false
CONTENT_MUD,
CONTENT_STONE,
CONTENT_SAND,
+ CONTENT_SANDSTONE,
+ CONTENT_CLAY,
+ CONTENT_BRICK,
CONTENT_TREE,
CONTENT_LEAVES,
+ CONTENT_CACTUS,
+ CONTENT_PAPYRUS,
+ CONTENT_BOOKSHELF,
+ CONTENT_GLASS,
+ CONTENT_FENCE,
+ CONTENT_RAIL,
CONTENT_MESE,
CONTENT_WATERSOURCE,
CONTENT_CLOUD,
v3f findSpawnPos(ServerMap &map)
{
+ //return v3f(50,50,50)*BS;
+
v2s16 nodepos;
s16 groundheight = 0;
), BS);
}
-Player *Server::emergePlayer(const char *name, const char *password,
- u16 peer_id)
+Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
{
/*
Try to get an existing player
//player->peer_id = PEER_ID_INEXISTENT;
player->peer_id = peer_id;
player->updateName(name);
+ player->updatePassword(password);
+
+ if(g_settings.exists("default_privs"))
+ player->privs = g_settings.getU64("default_privs");
/*
Set player position
// The client should exist
assert(n != NULL);
+ /*
+ Mark objects to be not known by the client
+ */
+ RemoteClient *client = n->getValue();
+ // Handle objects
+ for(core::map<u16, bool>::Iterator
+ i = client->m_known_objects.getIterator();
+ i.atEnd()==false; i++)
+ {
+ // Get object
+ u16 id = i.getNode()->getKey();
+ ServerActiveObject* obj = m_env.getActiveObject(id);
+
+ if(obj && obj->m_known_by_count > 0)
+ obj->m_known_by_count--;
+ }
+
// Collect information about leaving in chat
std::wstring message;
{