+/*
+Minetest-c55
+Copyright (C) 2010 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
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
/*
(c) 2010 Perttu Ahola <celeron55@gmail.com>
*/
#include "jmutexautolock.h"
#include "main.h"
#include "constants.h"
+#include "voxel.h"
void * ServerThread::Thread()
{
v3s16 &p = q->pos;
//derr_server<<"EmergeThread::Thread(): running"<<std::endl;
+
+ //TimeTaker timer("block emerge", g_device);
/*
Try to emerge it from somewhere.
dout_server<<std::endl;
}
+ /*
+ Update water pressure
+ */
+
+ m_server->UpdateBlockWaterPressure(block, modified_blocks);
+
+ for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
+ i.atEnd() == false; i++)
+ {
+ MapBlock *block = i.getNode()->getValue();
+ m_server->UpdateBlockWaterPressure(block, modified_blocks);
+ //v3s16 p = i.getNode()->getKey();
+ //m_server->UpdateBlockWaterPressure(p, modified_blocks);
+ }
+
/*
Collect a list of blocks that have been modified in
addition to the fetched one.
*/
- // Add all the "changed blocks"
+ // Add all the "changed blocks" to modified_blocks
for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
i.atEnd() == false; i++)
{
modified_blocks.insert(block->getPos(), block);
}
- //TimeTaker timer("** updateLighting", g_device);
+ /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
+ <<" blocks"<<std::endl;
+ TimeTaker timer("** updateLighting", g_device);*/
+
// Update lighting without locking the environment mutex,
// add modified blocks to changed blocks
map.updateLighting(lighting_invalidated_blocks, modified_blocks);
// Remove block from sent history
client->SetBlocksNotSent(modified_blocks);
}
-
- if(q->peer_ids.find(client->peer_id) != NULL)
- {
- // Decrement emerge queue count of client
- client->BlockEmerged();
- }
}
}
return NULL;
}
-void RemoteClient::SendBlocks(Server *server, float dtime)
+void RemoteClient::GetNextBlocks(Server *server, float dtime,
+ core::array<PrioritySortedBlockTransfer> &dest)
{
DSTACK(__FUNCTION_NAME);
- /*
- Find what blocks to send to the client next, and send them.
-
- Throttling is based on limiting the amount of blocks "flying"
- at a given time.
- */
-
- // Can't send anything without knowing version
- if(serialization_version == SER_FMT_VER_INVALID)
+
+ // Increment timers
{
- dstream<<"RemoteClient::SendBlocks(): Not sending, no version."
- <<std::endl;
- return;
+ JMutexAutoLock lock(m_blocks_sent_mutex);
+ m_nearest_unsent_reset_timer += dtime;
}
+ // Won't send anything if already sending
{
JMutexAutoLock lock(m_blocks_sending_mutex);
- if(m_blocks_sending.size() >= MAX_SIMULTANEOUS_BLOCK_SENDS)
+ if(m_blocks_sending.size() >= g_settings.getU16
+ ("max_simultaneous_block_sends_per_client"))
{
//dstream<<"Not sending any blocks, Queue full."<<std::endl;
return;
v3s16 center = getNodeBlockPos(center_nodepos);
/*
- Find out what block the player is going to next and set
- center to it.
-
- Don't react to speeds under the initial value of highest_speed
+ Get the starting value of the block finder radius.
*/
- /*f32 highest_speed = 0.1 * BS;
- v3s16 dir(0,0,0);
- if(abs(playerspeed.X) > highest_speed)
- {
- highest_speed = playerspeed.X;
- if(playerspeed.X > 0)
- dir = v3s16(1,0,0);
- else
- dir = v3s16(-1,0,0);
- }
- if(abs(playerspeed.Y) > highest_speed)
- {
- highest_speed = playerspeed.Y;
- if(playerspeed.Y > 0)
- dir = v3s16(0,1,0);
- else
- dir = v3s16(0,-1,0);
- }
- if(abs(playerspeed.Z) > highest_speed)
- {
- highest_speed = playerspeed.Z;
- if(playerspeed.Z > 0)
- dir = v3s16(0,0,1);
- else
- dir = v3s16(0,0,-1);
- }
-
- center += dir;*/
-
- /*
- Calculate the starting value of the block finder radius.
-
- The radius shall be the last used value minus the
- maximum moved distance.
- */
- /*s16 d_start = m_last_block_find_d;
- if(max_moved >= d_start)
- {
- d_start = 0;
- }
- else
- {
- d_start -= max_moved;
- }*/
-
s16 last_nearest_unsent_d;
s16 d_start;
{
m_last_center = center;
}
- static float reset_counter = 0;
- reset_counter += dtime;
- if(reset_counter > 5.0)
+ /*dstream<<"m_nearest_unsent_reset_timer="
+ <<m_nearest_unsent_reset_timer<<std::endl;*/
+ if(m_nearest_unsent_reset_timer > 5.0)
{
- reset_counter = 0;
+ m_nearest_unsent_reset_timer = 0;
m_nearest_unsent_d = 0;
+ //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
}
last_nearest_unsent_d = m_nearest_unsent_d;
d_start = m_nearest_unsent_d;
}
- u16 maximum_simultaneous_block_sends = MAX_SIMULTANEOUS_BLOCK_SENDS;
+ u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
+ ("max_simultaneous_block_sends_per_client");
+ u16 maximum_simultaneous_block_sends =
+ maximum_simultaneous_block_sends_setting;
+ /*
+ Check the time from last addNode/removeNode.
+
+ Decrease send rate if player is building stuff.
+ */
{
SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
m_time_from_building.m_value += dtime;
- /*
- Check the time from last addNode/removeNode.
- Decrease send rate if player is building stuff.
- */
if(m_time_from_building.m_value
< FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)
{
= LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
}
}
+
+ u32 num_blocks_selected;
+ {
+ JMutexAutoLock lock(m_blocks_sending_mutex);
+ num_blocks_selected = m_blocks_sending.size();
+ }
+
+ /*
+ next time d will be continued from the d from which the nearest
+ unsent block was found this time.
+
+ This is because not necessarily any of the blocks found this
+ time are actually sent.
+ */
+ s32 new_nearest_unsent_d = -1;
// Serialization version used
//u8 ser_version = serialization_version;
//bool has_incomplete_blocks = false;
- /*
- TODO: Get this from somewhere
- TODO: Values more than 7 make placing and removing blocks very
- sluggish when the map is being generated. This is
- because d is looped every time from 0 to d_max if no
- blocks are found for sending.
- */
- //s16 d_max = 7;
- s16 d_max = 8;
-
- //TODO: Get this from somewhere (probably a bigger value)
- s16 d_max_gen = 5;
+ s16 d_max = g_settings.getS16("max_block_send_distance");
+ s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
//dstream<<"Starting from "<<d_start<<std::endl;
if(m_nearest_unsent_d != last_nearest_unsent_d)
{
d = m_nearest_unsent_d;
+ last_nearest_unsent_d = m_nearest_unsent_d;
}
- else
- {
- m_nearest_unsent_d = d;
- }
- last_nearest_unsent_d = m_nearest_unsent_d;
}
/*
/*
Send throttling
- Don't allow too many simultaneous transfers
+ - EXCEPT when the blocks are very close
Also, don't send blocks that are already flying.
*/
+
+ u16 maximum_simultaneous_block_sends_now =
+ maximum_simultaneous_block_sends;
+
+ if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
+ {
+ maximum_simultaneous_block_sends_now =
+ maximum_simultaneous_block_sends_setting;
+ }
+
{
JMutexAutoLock lock(m_blocks_sending_mutex);
- if(m_blocks_sending.size()
- >= maximum_simultaneous_block_sends)
+ // 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;*/
- return;
+ goto queue_full;
}
if(m_blocks_sending.find(p) != NULL)
if(m_blocks_sent.find(p) != NULL)
continue;
}
-
+
/*
Check if map has this block
*/
/*
If block has been marked to not exist on disk (dummy)
- and generating new ones is not wanted, skip block. TODO
+ and generating new ones is not wanted, skip block.
*/
if(generate == false && surely_not_found_on_disk == true)
{
continue;
}
+ /*
+ Record the lowest d from which a a block has been
+ found being not sent and possibly to exist
+ */
+ if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
+ {
+ new_nearest_unsent_d = d;
+ }
+
/*
Add inexistent block to emerge queue.
*/
if(block == NULL || surely_not_found_on_disk)
{
- // Block not found.
- SharedPtr<JMutexAutoLock> lock
- (m_num_blocks_in_emerge_queue.getLock());
+ /*SharedPtr<JMutexAutoLock> lock
+ (m_num_blocks_in_emerge_queue.getLock());*/
//TODO: Get value from somewhere
- //TODO: Balance between clients
- //if(server->m_emerge_queue.size() < 1)
-
// Allow only one block in emerge queue
- if(m_num_blocks_in_emerge_queue.m_value == 0)
+ if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
{
// Add it to the emerge queue and trigger the thread
if(generate == false)
flags |= TOSERVER_GETBLOCK_FLAG_OPTIONAL;
- {
- m_num_blocks_in_emerge_queue.m_value++;
- }
-
server->m_emerge_queue.addBlock(peer_id, p, flags);
server->m_emergethread.trigger();
}
}
/*
- Send block
+ Add block to queue
*/
-
- /*dstream<<"RemoteClient::SendBlocks(): d="<<d<<", p="
- <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
- <<" sending queue size: "<<m_blocks_sending.size()<<std::endl;*/
- server->SendBlockNoLock(peer_id, block, serialization_version);
-
- /*
- Add to history
- */
- SentBlock(p);
+ PrioritySortedBlockTransfer q((float)d, p, peer_id);
+
+ dest.push_back(q);
+
+ num_blocks_selected += 1;
}
}
+queue_full:
- // Don't add anything here. The loop breaks by returning.
+ if(new_nearest_unsent_d != -1)
+ {
+ JMutexAutoLock lock(m_blocks_sent_mutex);
+ m_nearest_unsent_d = new_nearest_unsent_d;
+ }
}
void RemoteClient::SendObjectData(
in memory):
- Set blocks changed
- Add blocks to emerge queue if they are not found
+
+ SUGGESTION: These could be ignored from the backside of the player
+
+ TODO: Keep track of total size of packet and stop when it is too big
*/
Player *player = server->m_env.getPlayer(peer_id);
v3s16 center_nodepos = floatToInt(playerpos);
v3s16 center = getNodeBlockPos(center_nodepos);
- s16 d_max = ACTIVE_OBJECT_D_BLOCKS;
+ //s16 d_max = ACTIVE_OBJECT_D_BLOCKS;
+ s16 d_max = g_settings.getS16("active_object_range");
+
+ // Number of blocks whose objects were written to bos
+ u16 blockcount = 0;
- core::map<v3s16, MapBlock*> blocks;
+ //core::map<v3s16, MapBlock*> blocks;
+ std::ostringstream bos(std::ios_base::binary);
for(s16 d = 0; d <= d_max; d++)
{
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);
+ // Skip block if there are no objects
+ if(block->getObjectCount() == 0)
+ continue;
+
// Step block if not in stepped_blocks and add to stepped_blocks
if(stepped_blocks.find(p) == NULL)
{
stepped_blocks.insert(p, true);
block->setChangedFlag();
}
-
- // Add block to queue
- blocks.insert(p, block);
+
+ /*
+ Write objects
+ */
+
+ // Write blockpos
+ writeV3S16(buf, p);
+ bos.write((char*)buf, 6);
+
+ // Write objects
+ block->serializeObjects(bos, serialization_version);
+
+ 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)
// Fetch the block only if it is on disk.
// Grab and increment counter
- SharedPtr<JMutexAutoLock> lock
+ /*SharedPtr<JMutexAutoLock> lock
(m_num_blocks_in_emerge_queue.getLock());
- m_num_blocks_in_emerge_queue.m_value++;
+ m_num_blocks_in_emerge_queue.m_value++;*/
// Add to queue as an anonymous fetch from disk
u8 flags = TOSERVER_GETBLOCK_FLAG_OPTIONAL;
}
}
- /*
- Write objects
- */
-
- u16 blockcount = blocks.size();
+skip_subsequent:
// Write block count
writeU16(buf, blockcount);
os.write((char*)buf, 2);
-
- for(core::map<v3s16, MapBlock*>::Iterator
- i = blocks.getIterator();
- i.atEnd() == false; i++)
- {
- v3s16 p = i.getNode()->getKey();
- // Write blockpos
- writeV3S16(buf, p);
- os.write((char*)buf, 6);
- // Write objects
- MapBlock *block = i.getNode()->getValue();
- block->serializeObjects(os, serialization_version);
- }
-
+
+ // Write block objects
+ os<<bos.str();
+
/*
Send data
*/
+ //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
+
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
}
}
-void RemoteClient::BlockEmerged()
+/*void RemoteClient::BlockEmerged()
{
SharedPtr<JMutexAutoLock> lock(m_num_blocks_in_emerge_queue.getLock());
assert(m_num_blocks_in_emerge_queue.m_value > 0);
m_num_blocks_in_emerge_queue.m_value--;
-}
+}*/
/*void RemoteClient::RunSendingTimeouts(float dtime, float timeout)
{
Server::Server(
std::string mapsavedir,
- bool creative_mode,
- MapgenParams mapgen_params
+ HMParams hm_params,
+ MapParams map_params
):
- m_env(new ServerMap(mapsavedir, mapgen_params), dout_server),
+ m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_thread(this),
- m_emergethread(this),
- m_creative_mode(creative_mode)
+ m_emergethread(this)
{
+ m_flowwater_timer = 0.0;
+ m_print_info_timer = 0.0;
+ m_objectdata_timer = 0.0;
+ m_emergethread_trigger_timer = 0.0;
+ m_savemap_timer = 0.0;
+
m_env_mutex.Init();
m_con_mutex.Init();
m_step_dtime_mutex.Init();
m_thread.stop();
// Initialize connection
- m_con.setTimeoutMs(50);
+ m_con.setTimeoutMs(30);
m_con.Serve(port);
// Start thread
void Server::AsyncRunStep()
{
DSTACK(__FUNCTION_NAME);
+
float dtime;
{
JMutexAutoLock lock1(m_step_dtime_mutex);
dtime = m_step_dtime;
- if(dtime < 0.001)
- return;
- m_step_dtime = 0.0;
}
+ // Send blocks to clients
+ SendBlocks(dtime);
+
+ if(dtime < 0.001)
+ return;
+
+ {
+ JMutexAutoLock lock1(m_step_dtime_mutex);
+ m_step_dtime -= dtime;
+ }
+
//dstream<<"Server steps "<<dtime<<std::endl;
//dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
/*
Do background stuff
*/
+
+ /*
+ Flow water
+ */
+ {
+ float interval;
+
+ if(g_settings.getBool("endless_water") == false)
+ interval = 1.0;
+ else
+ interval = 0.25;
+
+ float &counter = m_flowwater_timer;
+ counter += dtime;
+ if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
+ {
+
+ counter = 0.0;
+
+ core::map<v3s16, MapBlock*> modified_blocks;
+
+ {
+
+ JMutexAutoLock envlock(m_env_mutex);
+
+ MapVoxelManipulator v(&m_env.getMap());
+ v.m_disable_water_climb =
+ g_settings.getBool("disable_water_climb");
+
+ if(g_settings.getBool("endless_water") == false)
+ v.flowWater(m_flow_active_nodes, 0, false, 250);
+ else
+ v.flowWater(m_flow_active_nodes, 0, false, 50);
+
+ v.blitBack(modified_blocks);
+
+ ServerMap &map = ((ServerMap&)m_env.getMap());
+
+ // Update lighting
+ core::map<v3s16, MapBlock*> lighting_modified_blocks;
+ map.updateLighting(modified_blocks, lighting_modified_blocks);
+
+ // Add blocks modified by lighting to modified_blocks
+ for(core::map<v3s16, MapBlock*>::Iterator
+ i = lighting_modified_blocks.getIterator();
+ i.atEnd() == false; i++)
+ {
+ MapBlock *block = i.getNode()->getValue();
+ modified_blocks.insert(block->getPos(), block);
+ }
+ } // envlock
+
+ /*
+ Set the modified blocks unsent for all the clients
+ */
+
+ JMutexAutoLock lock2(m_con_mutex);
+
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator();
+ i.atEnd() == false; i++)
+ {
+ RemoteClient *client = i.getNode()->getValue();
+
+ if(modified_blocks.size() > 0)
+ {
+ // Remove block from sent history
+ client->SetBlocksNotSent(modified_blocks);
+ }
+ }
+
+ } // interval counter
+ }
// Periodically print some info
{
- static float counter = 0.0;
+ float &counter = m_print_info_timer;
counter += dtime;
if(counter >= 30.0)
{
}
}
- // Run time- and client- related stuff
- // NOTE: If you intend to add something here, check that it
- // doesn't fit in RemoteClient::SendBlocks for exampel.
- /*{
- // Clients are behind connection lock
- JMutexAutoLock lock(m_con_mutex);
+ /*
+ Update digging
+
+ NOTE: Some of this could be moved to RemoteClient
+ */
+
+ {
+ JMutexAutoLock envlock(m_env_mutex);
+ JMutexAutoLock conlock(m_con_mutex);
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
i.atEnd() == false; i++)
{
RemoteClient *client = i.getNode()->getValue();
- //con::Peer *peer = m_con.GetPeer(client->peer_id);
- //client->RunSendingTimeouts(dtime, peer->resend_timeout);
+ Player *player = m_env.getPlayer(client->peer_id);
+
+ JMutexAutoLock digmutex(client->m_dig_mutex);
+
+ if(client->m_dig_tool_item == -1)
+ continue;
+
+ client->m_dig_time_remaining -= dtime;
+
+ if(client->m_dig_time_remaining > 0)
+ continue;
+
+ v3s16 p_under = client->m_dig_position;
+
+ // Mandatory parameter; actually used for nothing
+ core::map<v3s16, MapBlock*> modified_blocks;
+
+ u8 material;
+
+ try
+ {
+ // Get material at position
+ material = m_env.getMap().getNode(p_under).d;
+ // If it's not diggable, do nothing
+ if(content_diggable(material) == false)
+ {
+ derr_server<<"Server: Not finishing digging: Node not diggable"
+ <<std::endl;
+ client->m_dig_tool_item = -1;
+ break;
+ }
+ }
+ catch(InvalidPositionException &e)
+ {
+ derr_server<<"Server: Not finishing digging: Node not found"
+ <<std::endl;
+ client->m_dig_tool_item = -1;
+ break;
+ }
+
+ // Create packet
+ u32 replysize = 8;
+ SharedBuffer<u8> reply(replysize);
+ writeU16(&reply[0], TOCLIENT_REMOVENODE);
+ writeS16(&reply[2], p_under.X);
+ writeS16(&reply[4], p_under.Y);
+ writeS16(&reply[6], p_under.Z);
+ // Send as reliable
+ m_con.SendToAll(0, reply, true);
+
+ if(g_settings.getBool("creative_mode") == false)
+ {
+ // Add to inventory and send inventory
+ InventoryItem *item = new MaterialItem(material, 1);
+ player->inventory.addItem(item);
+ SendInventory(player->peer_id);
+ }
+
+ /*
+ Remove the node
+ (this takes some time so it is done after the quick stuff)
+ */
+ m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
+
+ /*
+ Update water
+ */
+
+ // Update water pressure around modification
+ // This also adds it to m_flow_active_nodes if appropriate
+
+ MapVoxelManipulator v(&m_env.getMap());
+ v.m_disable_water_climb =
+ g_settings.getBool("disable_water_climb");
+
+ VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
+
+ try
+ {
+ v.updateAreaWaterPressure(area, m_flow_active_nodes);
+ }
+ catch(ProcessingLimitException &e)
+ {
+ dstream<<"Processing limit reached (1)"<<std::endl;
+ }
+
+ v.blitBack(modified_blocks);
}
- }*/
+ }
- // Send blocks to clients
- SendBlocks(dtime);
-
// Send object positions
{
- static float counter = 0.0;
+ float &counter = m_objectdata_timer;
counter += dtime;
- //TODO: Get value from somewhere
- if(counter >= 0.1)
+ if(counter >= g_settings.getFloat("objectdata_interval"))
{
JMutexAutoLock lock1(m_env_mutex);
JMutexAutoLock lock2(m_con_mutex);
counter = 0.0;
}
}
+
+ // Trigger emergethread (it gets somehow gets to a
+ // non-triggered but bysy state sometimes)
+ {
+ float &counter = m_emergethread_trigger_timer;
+ counter += dtime;
+ if(counter >= 2.0)
+ {
+ counter = 0.0;
+
+ m_emergethread.trigger();
+ }
+ }
+ // Save map
{
- // Save map
- static float counter = 0.0;
+ float &counter = m_savemap_timer;
counter += dtime;
if(counter >= SERVER_MAP_SAVE_INTERVAL)
{
// Left click
if(button == 0)
{
- if(m_creative_mode == false)
+ if(g_settings.getBool("creative_mode") == false)
{
// Skip if inventory has no free space
block->removeObject(id);
}
}
- else if(command == TOSERVER_CLICK_GROUND)
+ else if(command == TOSERVER_GROUND_ACTION)
{
if(datasize < 17)
return;
/*
length: 17
[0] u16 command
- [2] u8 button (0=left, 1=right)
+ [2] u8 action
[3] v3s16 nodepos_undersurface
[9] v3s16 nodepos_abovesurface
[15] u16 item
+ actions:
+ 0: start digging
+ 1: place block
+ 2: stop digging (all parameters ignored)
*/
- u8 button = readU8(&data[2]);
+ u8 action = readU8(&data[2]);
v3s16 p_under;
p_under.X = readS16(&data[3]);
p_under.Y = readS16(&data[5]);
//TODO: Check that target is reasonably close
/*
- Left button digs ground
+ 0: start digging
*/
- if(button == 0)
+ if(action == 0)
{
- core::map<v3s16, MapBlock*> modified_blocks;
-
- u8 material;
+ u8 content;
try
{
- // Get material at position
- material = m_env.getMap().getNode(p_under).d;
- // If it's air, do nothing
- if(material == MATERIAL_AIR)
+ // Get content at position
+ content = m_env.getMap().getNode(p_under).d;
+ // If it's not diggable, do nothing
+ if(content_diggable(content) == false)
{
return;
}
- // Otherwise remove it
- m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
}
catch(InvalidPositionException &e)
{
- derr_server<<"Server: Ignoring REMOVENODE: Node not found"
+ derr_server<<"Server: Not starting digging: Node not found"
<<std::endl;
return;
}
+ /*
+ Set stuff in RemoteClient
+ */
+ RemoteClient *client = getClient(peer->id);
+ JMutexAutoLock(client->m_dig_mutex);
+ client->m_dig_tool_item = 0;
+ client->m_dig_position = p_under;
+ float dig_time = 0.5;
+ if(content == CONTENT_STONE)
+ {
+ dig_time = 1.5;
+ }
+ else if(content == CONTENT_TORCH)
+ {
+ dig_time = 0.0;
+ }
+ client->m_dig_time_remaining = dig_time;
+
// Reset build time counter
getClient(peer->id)->m_time_from_building.set(0.0);
- // Create packet
- u32 replysize = 8;
- SharedBuffer<u8> reply(replysize);
- writeU16(&reply[0], TOCLIENT_REMOVENODE);
- writeS16(&reply[2], p_under.X);
- writeS16(&reply[4], p_under.Y);
- writeS16(&reply[6], p_under.Z);
- // Send as reliable
- m_con.SendToAll(0, reply, true);
-
- if(m_creative_mode == false)
- {
- // Add to inventory and send inventory
- InventoryItem *item = new MaterialItem(material, 1);
- player->inventory.addItem(item);
- SendInventory(player->peer_id);
- }
+ } // action == 0
- } // button == 0
/*
- Right button places blocks and stuff
+ 2: stop digging
*/
- else if(button == 1)
+ else if(action == 2)
+ {
+ RemoteClient *client = getClient(peer->id);
+ JMutexAutoLock digmutex(client->m_dig_mutex);
+ client->m_dig_tool_item = -1;
+ }
+
+ /*
+ 1: place block
+ */
+ else if(action == 1)
{
// Get item
*/
if(std::string("MaterialItem") == item->getName())
{
- MaterialItem *mitem = (MaterialItem*)item;
-
- MapNode n;
- n.d = mitem->getMaterial();
-
try{
- // Don't add a node if there isn't air
+ // Don't add a node if this is not a free space
MapNode n2 = m_env.getMap().getNode(p_over);
- if(n2.d != MATERIAL_AIR)
+ if(content_buildable_to(n2.d) == false)
return;
-
- core::map<v3s16, MapBlock*> modified_blocks;
- m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
}
catch(InvalidPositionException &e)
{
// Reset build time counter
getClient(peer->id)->m_time_from_building.set(0.0);
- if(m_creative_mode == false)
- {
- // Remove from inventory and send inventory
- if(mitem->getCount() == 1)
- player->inventory.deleteItem(item_i);
- else
- mitem->remove(1);
- // Send inventory
- SendInventory(peer_id);
- }
-
+ // Create node data
+ MaterialItem *mitem = (MaterialItem*)item;
+ MapNode n;
+ n.d = mitem->getMaterial();
+ if(content_directional(n.d))
+ n.dir = packDir(p_under - p_over);
+
+#if 1
// Create packet
u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
SharedBuffer<u8> reply(replysize);
n.serialize(&reply[8], peer_ser_ver);
// Send as reliable
m_con.SendToAll(0, reply, true);
+
+ /*
+ Handle inventory
+ */
+ if(g_settings.getBool("creative_mode") == false)
+ {
+ // Remove from inventory and send inventory
+ if(mitem->getCount() == 1)
+ player->inventory.deleteItem(item_i);
+ else
+ mitem->remove(1);
+ // Send inventory
+ SendInventory(peer_id);
+ }
+
+ /*
+ Add node.
+
+ This takes some time so it is done after the quick stuff
+ */
+ core::map<v3s16, MapBlock*> modified_blocks;
+ m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
+#endif
+#if 0
+ /*
+ Handle inventory
+ */
+ if(g_settings.getBool("creative_mode") == false)
+ {
+ // Remove from inventory and send inventory
+ if(mitem->getCount() == 1)
+ player->inventory.deleteItem(item_i);
+ else
+ mitem->remove(1);
+ // Send inventory
+ SendInventory(peer_id);
+ }
+
+ /*
+ Add node.
+
+ This takes some time so it is done after the quick stuff
+ */
+ core::map<v3s16, MapBlock*> modified_blocks;
+ m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
+
+ /*
+ Set the modified blocks unsent for all the clients
+ */
+
+ //JMutexAutoLock lock2(m_con_mutex);
+
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator();
+ i.atEnd() == false; i++)
+ {
+ RemoteClient *client = i.getNode()->getValue();
+
+ if(modified_blocks.size() > 0)
+ {
+ // Remove block from sent history
+ client->SetBlocksNotSent(modified_blocks);
+ }
+ }
+#endif
+
+ /*
+ Update water
+ */
+
+ // Update water pressure around modification
+ // This also adds it to m_flow_active_nodes if appropriate
+
+ MapVoxelManipulator v(&m_env.getMap());
+ v.m_disable_water_climb =
+ g_settings.getBool("disable_water_climb");
+
+ VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
+
+ try
+ {
+ v.updateAreaWaterPressure(area, m_flow_active_nodes);
+ }
+ catch(ProcessingLimitException &e)
+ {
+ dstream<<"Processing limit reached (1)"<<std::endl;
+ }
+
+ v.blitBack(modified_blocks);
}
/*
Handle block object items
//dout_server<<"Placed object"<<std::endl;
- if(m_creative_mode == false)
+ if(g_settings.getBool("creative_mode") == false)
{
// Remove from inventory and send inventory
player->inventory.deleteItem(item_i);
}
}
- } // button == 1
+ } // action == 1
/*
- Catch invalid buttons
+ Catch invalid actions
*/
else
{
- derr_server<<"WARNING: Server: Invalid button "
- <<button<<std::endl;
+ derr_server<<"WARNING: Server: Invalid action "
+ <<action<<std::endl;
}
}
+#if 0
else if(command == TOSERVER_RELEASE)
{
if(datasize < 3)
[0] u16 command
[2] u8 button
*/
- //TODO
+ dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
}
+#endif
else if(command == TOSERVER_SIGNTEXT)
{
/*
writeS16(&reply[4], p.Y);
writeS16(&reply[6], p.Z);
memcpy(&reply[8], *blockdata, blockdata.getSize());
+
+ /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+ <<": \tpacket size: "<<replysize<<std::endl;*/
/*
Send packet
m_con.Send(peer_id, 1, reply, true);
}
-/*void Server::SendBlock(u16 peer_id, MapBlock *block, u8 ver)
-{
- JMutexAutoLock conlock(m_con_mutex);
-
- SendBlockNoLock(peer_id, block, ver);
-}*/
-
-#if 0
-void Server::SendSectorMeta(u16 peer_id, core::list<v2s16> ps, u8 ver)
-{
- DSTACK(__FUNCTION_NAME);
- dstream<<"Server sending sector meta of "
- <<ps.getSize()<<" sectors"<<std::endl;
-
- core::list<v2s16>::Iterator i = ps.begin();
- core::list<v2s16> sendlist;
- for(;;)
- {
- if(sendlist.size() == 255 || i == ps.end())
- {
- if(sendlist.size() == 0)
- break;
- /*
- [0] u16 command
- [2] u8 sector count
- [3...] v2s16 pos + sector metadata
- */
- std::ostringstream os(std::ios_base::binary);
- u8 buf[4];
-
- writeU16(buf, TOCLIENT_SECTORMETA);
- os.write((char*)buf, 2);
-
- writeU8(buf, sendlist.size());
- os.write((char*)buf, 1);
-
- for(core::list<v2s16>::Iterator
- j = sendlist.begin();
- j != sendlist.end(); j++)
- {
- // Write position
- writeV2S16(buf, *j);
- os.write((char*)buf, 4);
-
- /*
- Write ClientMapSector metadata
- */
-
- /*
- [0] u8 serialization version
- [1] s16 corners[0]
- [3] s16 corners[1]
- [5] s16 corners[2]
- [7] s16 corners[3]
- size = 9
-
- In which corners are in these positions
- v2s16(0,0),
- v2s16(1,0),
- v2s16(1,1),
- v2s16(0,1),
- */
-
- // Write version
- writeU8(buf, ver);
- os.write((char*)buf, 1);
-
- // Write corners
- // TODO: Get real values
- s16 corners[4];
- ((ServerMap&)m_env.getMap()).getSectorCorners(*j, corners);
-
- writeS16(buf, corners[0]);
- os.write((char*)buf, 2);
- writeS16(buf, corners[1]);
- os.write((char*)buf, 2);
- writeS16(buf, corners[2]);
- os.write((char*)buf, 2);
- writeS16(buf, corners[3]);
- os.write((char*)buf, 2);
- }
-
- SharedBuffer<u8> data((u8*)os.str().c_str(), os.str().size());
-
- /*dstream<<"Server::SendSectorMeta(): sending packet"
- " with "<<sendlist.size()<<" sectors"<<std::endl;*/
-
- m_con.Send(peer_id, 1, data, true);
-
- if(i == ps.end())
- break;
-
- sendlist.clear();
- }
-
- sendlist.push_back(*i);
- i++;
- }
-}
-#endif
-
core::list<PlayerInfo> Server::getPlayerInfo()
{
DSTACK(__FUNCTION_NAME);
// The player shouldn't already exist
assert(player == NULL);
- player = new RemotePlayer();
+ player = new ServerRemotePlayer();
player->peer_id = peer->id;
/*
Set player position
*/
-
+
+ // We're going to throw the player to this position
+ //v2s16 nodepos(29990,29990);
+ //v2s16 nodepos(9990,9990);
+ v2s16 nodepos(0,0);
+ v2s16 sectorpos = getNodeSectorPos(nodepos);
// Get zero sector (it could have been unloaded to disk)
- m_env.getMap().emergeSector(v2s16(0,0));
+ m_env.getMap().emergeSector(sectorpos);
// Get ground height at origin
- f32 groundheight = m_env.getMap().getGroundHeight(v2s16(0,0), true);
- // The zero sector should have been generated
+ f32 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
+ // The sector should have been generated -> groundheight exists
assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
// Don't go underwater
if(groundheight < WATER_LEVEL)
groundheight = WATER_LEVEL;
player->setPosition(intToFloat(v3s16(
- 0,
+ nodepos.X,
groundheight + 1,
- 0
+ nodepos.Y
)));
/*
Add stuff to inventory
*/
- if(m_creative_mode)
+ if(g_settings.getBool("creative_mode"))
{
// Give all materials
- assert(USEFUL_MATERIAL_COUNT <= PLAYER_INVENTORY_SIZE);
- for(u16 i=0; i<USEFUL_MATERIAL_COUNT; i++)
+ assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
+ for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
{
+ // Skip some materials
+ if(i == CONTENT_OCEAN)
+ continue;
+
InventoryItem *item = new MaterialItem(i, 1);
player->inventory.addItem(item);
}
bool r = player->inventory.addItem(item);
assert(r == true);
}
- // Rat
+ /*// Rat
{
InventoryItem *item = new MapBlockObjectItem("Rat");
bool r = player->inventory.addItem(item);
assert(r == true);
- }
+ }*/
}
else
{
void Server::SendBlocks(float dtime)
{
DSTACK(__FUNCTION_NAME);
- //dstream<<"Server::SendBlocks(): BEGIN"<<std::endl;
JMutexAutoLock envlock(m_env_mutex);
- JMutexAutoLock conlock(m_con_mutex);
+
+ core::array<PrioritySortedBlockTransfer> queue;
+
+ s32 total_sending = 0;
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
{
RemoteClient *client = i.getNode()->getValue();
assert(client->peer_id == i.getNode()->getKey());
+
+ total_sending += client->SendingCount();
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
-
- //dstream<<"Server::SendBlocks(): sending blocks for client "<<client->peer_id<<std::endl;
- //u16 peer_id = client->peer_id;
- client->SendBlocks(this, dtime);
+ client->GetNextBlocks(this, dtime, queue);
}
- //dstream<<"Server::SendBlocks(): END"<<std::endl;
+ // Sort.
+ // Lowest priority number comes first.
+ // Lowest is most important.
+ queue.sort();
+
+ JMutexAutoLock conlock(m_con_mutex);
+
+ for(u32 i=0; i<queue.size(); i++)
+ {
+ //TODO: Calculate limit dynamically
+ if(total_sending >= g_settings.getS32
+ ("max_simultaneous_block_sends_server_total"))
+ break;
+
+ PrioritySortedBlockTransfer q = queue[i];
+
+ MapBlock *block = NULL;
+ try
+ {
+ block = m_env.getMap().getBlockNoCreate(q.pos);
+ }
+ catch(InvalidPositionException &e)
+ {
+ continue;
+ }
+
+ RemoteClient *client = getClient(q.peer_id);
+
+ SendBlockNoLock(q.peer_id, block, client->serialization_version);
+
+ client->SentBlock(q.pos);
+
+ total_sending++;
+ }
}
+
RemoteClient* Server::getClient(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
return n->getValue();
}
+void Server::UpdateBlockWaterPressure(MapBlock *block,
+ core::map<v3s16, MapBlock*> &modified_blocks)
+{
+ MapVoxelManipulator v(&m_env.getMap());
+ v.m_disable_water_climb =
+ g_settings.getBool("disable_water_climb");
+
+ VoxelArea area(block->getPosRelative(),
+ block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
+
+ try
+ {
+ v.updateAreaWaterPressure(area, m_flow_active_nodes);
+ }
+ catch(ProcessingLimitException &e)
+ {
+ dstream<<"Processing limit reached (1)"<<std::endl;
+ }
+
+ v.blitBack(modified_blocks);
+}
+
+