51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-/*
-(c) 2010 Perttu Ahola <celeron55@gmail.com>
-*/
-
#include "server.h"
#include "utility.h"
#include <iostream>
only_from_disk,
changed_blocks,
lighting_invalidated_blocks);
+
+#if 0
+ /*
+ While we're at it, generate some other blocks too
+ */
+ try
+ {
+ map.emergeBlock(
+ p+v3s16(0,1,0),
+ only_from_disk,
+ changed_blocks,
+ lighting_invalidated_blocks);
+ map.emergeBlock(
+ p+v3s16(0,-1,0),
+ only_from_disk,
+ changed_blocks,
+ lighting_invalidated_blocks);
+ }
+ catch(InvalidPositionException &e)
+ {
+ }
+#endif
}
// If it is a dummy, block was not found on disk
Collect a list of blocks that have been modified in
addition to the fetched one.
*/
-
- // Add all the "changed blocks" to modified_blocks
+
+ if(lighting_invalidated_blocks.size() > 0)
+ {
+ /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
+ <<" blocks"<<std::endl;*/
+
+ // 50-100ms for single block generation
+ //TimeTaker timer("** EmergeThread updateLighting");
+
+ // Update lighting without locking the environment mutex,
+ // add modified blocks to changed blocks
+ map.updateLighting(lighting_invalidated_blocks, modified_blocks);
+ }
+
+ // Add all from changed_blocks to modified_blocks
for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
modified_blocks.insert(block->getPos(), block);
}
-
- /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
- <<" blocks"<<std::endl;*/
-
- //TimeTaker timer("** updateLighting");
-
- // Update lighting without locking the environment mutex,
- // add modified blocks to changed blocks
- map.updateLighting(lighting_invalidated_blocks, modified_blocks);
}
// If we got no block, there should be no invalidated blocks
else
block_is_invalid = true;
}
+ /*if(block->isFullyGenerated() == false)
+ {
+ block_is_invalid = true;
+ }*/
+
v2s16 p2d(p.X, p.Z);
ServerMap *map = (ServerMap*)(&server->m_env.getMap());
v2s16 chunkpos = map->sector_to_chunk(p2d);
{
//TODO: Get value from somewhere
// Allow only one block in emerge queue
- if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
+ //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
+ if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
{
//dstream<<"Adding block to emerge queue"<<std::endl;
Server::Server(
std::string mapsavedir
):
- m_env(new ServerMap(mapsavedir)),
+ m_env(new ServerMap(mapsavedir), this),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_thread(this),
m_emergethread(this),
m_time_of_day_send_timer(0),
m_uptime(0),
m_mapsavedir(mapsavedir),
- m_shutdown_requested(false)
+ m_shutdown_requested(false),
+ m_ignore_map_edit_events(false),
+ m_ignore_map_edit_events_peer_id(0)
{
- //m_flowwater_timer = 0.0;
m_liquid_transform_timer = 0.0;
m_print_info_timer = 0.0;
m_objectdata_timer = 0.0;
m_step_dtime_mutex.Init();
m_step_dtime = 0.0;
+ m_env.getMap().addEventReceiver(this);
+
// Load players
m_env.deSerializePlayers(m_mapsavedir);
}
m_thread.setRun(true);
m_thread.Start();
- dout_server<<"Server started on port "<<port<<std::endl;
+ dout_server<<"Server: Started on port "<<port<<std::endl;
}
void Server::stop()
{
DSTACK(__FUNCTION_NAME);
+
// Stop threads (set run=false first so both start stopping)
m_thread.setRun(false);
m_emergethread.setRun(false);
m_thread.stop();
m_emergethread.stop();
- dout_server<<"Server threads stopped"<<std::endl;
+ dout_server<<"Server: Threads stopped"<<std::endl;
+
+ dout_server<<"Server: Saving players"<<std::endl;
+ // Save players
+ // FIXME: Apparently this does not do anything here
+ //m_env.serializePlayers(m_mapsavedir);
}
void Server::step(float dtime)
}
}
+ if(g_settings.getBool("enable_experimental"))
+ {
+
/*
Check added and deleted active objects
*/
{
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
+
+ // Radius inside which objects are active
+ s16 radius = 32;
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
{
RemoteClient *client = i.getNode()->getValue();
Player *player = m_env.getPlayer(client->peer_id);
+ if(player==NULL)
+ continue;
v3s16 pos = floatToInt(player->getPosition(), BS);
- s16 radius = 32;
core::map<u16, bool> removed_objects;
core::map<u16, bool> added_objects;
writeU8((u8*)buf, type);
data_buffer.append(buf, 1);
+ data_buffer.append(serializeLongString(
+ obj->getClientInitializationData()));
+
// Add to known objects
client->m_known_objects.insert(i.getNode()->getKey(), false);
// Compose the full new data with header
ActiveObjectMessage aom = *k;
std::string new_data;
- // Add header (object id + length)
- char header[4];
- writeU16((u8*)&header[0], aom.id);
- writeU16((u8*)&header[2], aom.datastring.size());
- new_data.append(header, 4);
+ // Add object id
+ char buf[2];
+ writeU16((u8*)&buf[0], aom.id);
+ new_data.append(buf, 2);
// Add data
- new_data += aom.datastring;
+ new_data += serializeString(aom.datastring);
// Add data to buffer
if(aom.reliable)
reliable_data += new_data;
// Send as unreliable
m_con.Send(client->peer_id, 0, reply, false);
}
- if(reliable_data.size() > 0 || unreliable_data.size() > 0)
+
+ /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
{
dstream<<"INFO: Server: Size of object message data: "
<<"reliable: "<<reliable_data.size()
<<", unreliable: "<<unreliable_data.size()
<<std::endl;
- }
+ }*/
}
// Clear buffered_messages
}
}
+ } // enable_experimental
+
+ /*
+ Send queued-for-sending map edit events.
+ */
+ {
+ while(m_unsent_map_edit_queue.size() != 0)
+ {
+ MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
+
+ if(event->type == MEET_ADDNODE)
+ {
+ dstream<<"Server: MEET_ADDNODE"<<std::endl;
+ sendAddNode(event->p, event->n, event->already_known_by_peer);
+ }
+ else if(event->type == MEET_REMOVENODE)
+ {
+ dstream<<"Server: MEET_REMOVENODE"<<std::endl;
+ sendRemoveNode(event->p, event->already_known_by_peer);
+ }
+ else if(event->type == MEET_OTHER)
+ {
+ dstream<<"WARNING: Server: MEET_OTHER not implemented"
+ <<std::endl;
+ }
+ else
+ {
+ dstream<<"WARNING: Server: Unknown MapEditEvent "
+ <<((u32)event->type)<<std::endl;
+ }
+
+ delete event;
+ }
+ }
+
/*
Send object positions
+ TODO: Get rid of MapBlockObjects
*/
{
float &counter = m_objectdata_timer;
// Now answer with a TOCLIENT_INIT
- SharedBuffer<u8> reply(2+1+6);
+ SharedBuffer<u8> reply(2+1+6+8);
writeU16(&reply[0], TOCLIENT_INIT);
writeU8(&reply[2], deployed);
- writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
+ writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
+ //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
+
// Send as reliable
m_con.Send(peer_id, 0, reply, true);
/*
Send the removal to all other clients
*/
-
- // 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);
-
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
- {
- // Get client and check that it is valid
- RemoteClient *client = i.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
- if(client->serialization_version == SER_FMT_VER_INVALID)
- continue;
-
- // Don't send if it's the same one
- if(peer_id == client->peer_id)
- continue;
-
- // Send as reliable
- m_con.Send(client->peer_id, 0, reply, true);
- }
+ sendRemoveNode(p_under, peer_id);
/*
Update and send inventory
Remove the node
(this takes some time so it is done after the quick stuff)
*/
+ m_ignore_map_edit_events = true;
m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
-
-#if 0
- /*
- 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);
-#endif
+ m_ignore_map_edit_events = false;
}
/*
n.d = mitem->getMaterial();
if(content_features(n.d).wall_mounted)
n.dir = packDir(p_under - p_over);
-
- // Create packet
- u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
- SharedBuffer<u8> reply(replysize);
- writeU16(&reply[0], TOCLIENT_ADDNODE);
- writeS16(&reply[2], p_over.X);
- writeS16(&reply[4], p_over.Y);
- writeS16(&reply[6], p_over.Z);
- n.serialize(&reply[8], peer_ser_ver);
- // Send as reliable
- m_con.SendToAll(0, reply, true);
+
+ /*
+ Send to all players
+ */
+ sendAddNode(p_over, n, 0);
/*
Handle inventory
This takes some time so it is done after the quick stuff
*/
core::map<v3s16, MapBlock*> modified_blocks;
+ m_ignore_map_edit_events = true;
m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
+ m_ignore_map_edit_events = false;
/*
Calculate special events
}
}
-/*void Server::Send(u16 peer_id, u16 channelnum,
- SharedBuffer<u8> data, bool reliable)
-{
- JMutexAutoLock lock(m_con_mutex);
- m_con.Send(peer_id, channelnum, data, reliable);
-}*/
-
-void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
+void Server::onMapEditEvent(MapEditEvent *event)
{
- DSTACK(__FUNCTION_NAME);
- /*
- Create a packet with the block in the right format
- */
-
- std::ostringstream os(std::ios_base::binary);
- block->serialize(os, ver);
- std::string s = os.str();
- SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
-
- 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);
- 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);
+ dstream<<"Server::onMapEditEvent()"<<std::endl;
+ if(m_ignore_map_edit_events)
+ return;
+ MapEditEvent *e = event->clone();
+ m_unsent_map_edit_queue.push_back(e);
}
core::list<PlayerInfo> Server::getPlayerInfo()
m_con.SendToAll(0, data, true);
}
+/*
+ Craft checking system
+*/
+
enum ItemSpecType
{
ITEM_NONE,
}
}
+void Server::sendRemoveNode(v3s16 p, u16 ignore_id)
+{
+ // Create packet
+ u32 replysize = 8;
+ SharedBuffer<u8> reply(replysize);
+ writeU16(&reply[0], TOCLIENT_REMOVENODE);
+ writeS16(&reply[2], p.X);
+ writeS16(&reply[4], p.Y);
+ writeS16(&reply[6], p.Z);
+
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator();
+ i.atEnd() == false; i++)
+ {
+ // Get client and check that it is valid
+ RemoteClient *client = i.getNode()->getValue();
+ assert(client->peer_id == i.getNode()->getKey());
+ if(client->serialization_version == SER_FMT_VER_INVALID)
+ continue;
+
+ // Don't send if it's the same one
+ if(client->peer_id == ignore_id)
+ continue;
+
+ // Send as reliable
+ m_con.Send(client->peer_id, 0, reply, true);
+ }
+}
+
+void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id)
+{
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator();
+ i.atEnd() == false; i++)
+ {
+ // Get client and check that it is valid
+ RemoteClient *client = i.getNode()->getValue();
+ assert(client->peer_id == i.getNode()->getKey());
+ if(client->serialization_version == SER_FMT_VER_INVALID)
+ continue;
+
+ // Don't send if it's the same one
+ if(client->peer_id == ignore_id)
+ continue;
+
+ // Create packet
+ u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
+ SharedBuffer<u8> reply(replysize);
+ writeU16(&reply[0], TOCLIENT_ADDNODE);
+ writeS16(&reply[2], p.X);
+ writeS16(&reply[4], p.Y);
+ writeS16(&reply[6], p.Z);
+ n.serialize(&reply[8], client->serialization_version);
+
+ // Send as reliable
+ m_con.Send(client->peer_id, 0, reply, true);
+ }
+}
+
+void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
+{
+ DSTACK(__FUNCTION_NAME);
+ /*
+ Create a packet with the block in the right format
+ */
+
+ std::ostringstream os(std::ios_base::binary);
+ block->serialize(os, ver);
+ std::string s = os.str();
+ SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
+
+ 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);
+ 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::SendBlocks(float dtime)
{
DSTACK(__FUNCTION_NAME);
/*
Give materials
*/
+
+ // CONTENT_IGNORE-terminated list
+ u8 material_items[] = {
+ CONTENT_TORCH,
+ CONTENT_MUD,
+ CONTENT_STONE,
+ CONTENT_SAND,
+ CONTENT_TREE,
+ CONTENT_LEAVES,
+ CONTENT_MESE,
+ CONTENT_WATERSOURCE,
+ CONTENT_CLOUD,
+ CONTENT_FURNACE,
+ CONTENT_SIGN_WALL,
+ CONTENT_IGNORE
+ };
+
+ u8 *mip = material_items;
+ for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
+ {
+ if(*mip == CONTENT_IGNORE)
+ break;
+
+ InventoryItem *item = new MaterialItem(*mip, 1);
+ player->inventory.addItem("main", item);
+
+ mip++;
+ }
+
+#if 0
assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
// add torch first
InventoryItem *item = new MaterialItem(i, 1);
player->inventory.addItem("main", item);
}
+#endif
+
// Sign
{
InventoryItem *item = new MapBlockObjectItem("Sign Example text");
<<player->getName()<<"\""<<std::endl;
v2s16 nodepos;
-#if 1
+#if 0
player->setPosition(intToFloat(v3s16(
0,
45, //64,
0
), BS));
#endif
-#if 0
- f32 groundheight = 0;
-#if 0
+#if 1
+ s16 groundheight = 0;
+#if 1
// Try to find a good place a few times
- for(s32 i=0; i<500; i++)
+ for(s32 i=0; i<1000; i++)
{
s32 range = 1 + i;
// We're going to try to throw the player to this position
nodepos = v2s16(-range + (myrand()%(range*2)),
-range + (myrand()%(range*2)));
v2s16 sectorpos = getNodeSectorPos(nodepos);
- // Get sector
- m_env.getMap().emergeSector(sectorpos);
- // Get ground height at point
- groundheight = m_env.getMap().getGroundHeight(nodepos, true);
- // The sector should have been generated -> groundheight exists
- assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
+ // Get sector (NOTE: Don't get because it's slow)
+ //m_env.getMap().emergeSector(sectorpos);
+ // Get ground height at point (fallbacks to heightmap function)
+ groundheight = m_env.getServerMap().findGroundLevel(nodepos);
// Don't go underwater
if(groundheight < WATER_LEVEL)
{
//dstream<<"-> Underwater"<<std::endl;
continue;
}
-#if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
+ // Don't go to high places
+ if(groundheight > WATER_LEVEL + 4)
+ {
+ //dstream<<"-> Underwater"<<std::endl;
+ continue;
+ }
+
+#if 0
+// Doesn't work, generating blocks is a bit too complicated for doing here
// Get block at point
v3s16 nodepos3d;
nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
continue;
}
#endif
+
// Found a good place
dstream<<"Searched through "<<i<<" places."<<std::endl;
break;
player->setPosition(intToFloat(v3s16(
nodepos.X,
- //groundheight + 1,
- groundheight + 15,
+ groundheight + 5, // Accomodate mud
nodepos.Y
- )));
+ ), BS));
#endif
/*