3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
29 #include "materials.h"
32 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
34 void * ServerThread::Thread()
38 DSTACK(__FUNCTION_NAME);
40 BEGIN_DEBUG_EXCEPTION_HANDLER
45 //TimeTaker timer("AsyncRunStep() + Receive()");
48 //TimeTaker timer("AsyncRunStep()");
49 m_server->AsyncRunStep();
52 //dout_server<<"Running m_server->Receive()"<<std::endl;
55 catch(con::NoIncomingDataException &e)
58 catch(con::PeerNotFoundException &e)
60 dout_server<<"Server: PeerNotFoundException"<<std::endl;
64 END_DEBUG_EXCEPTION_HANDLER
69 void * EmergeThread::Thread()
73 DSTACK(__FUNCTION_NAME);
77 BEGIN_DEBUG_EXCEPTION_HANDLER
80 Get block info from queue, emerge them and send them
83 After queue is empty, exit.
87 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
91 SharedPtr<QueuedBlockEmerge> q(qptr);
95 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
97 //TimeTaker timer("block emerge");
100 Try to emerge it from somewhere.
102 If it is only wanted as optional, only loading from disk
107 Check if any peer wants it as non-optional. In that case it
110 Also decrement the emerge queue count in clients.
113 bool optional = true;
116 core::map<u16, u8>::Iterator i;
117 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
119 //u16 peer_id = i.getNode()->getKey();
122 u8 flags = i.getNode()->getValue();
123 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
129 /*dstream<<"EmergeThread: p="
130 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
131 <<"optional="<<optional<<std::endl;*/
133 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
135 core::map<v3s16, MapBlock*> changed_blocks;
136 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
138 MapBlock *block = NULL;
139 bool got_block = true;
140 core::map<v3s16, MapBlock*> modified_blocks;
144 //TimeTaker envlockwaittimer("block emerge envlock wait time");
147 JMutexAutoLock envlock(m_server->m_env_mutex);
149 //envlockwaittimer.stop();
151 //TimeTaker timer("block emerge (while env locked)");
154 bool only_from_disk = false;
157 only_from_disk = true;
159 // First check if the block already exists
160 //block = map.getBlockNoCreate(p);
164 //dstream<<"Calling emergeBlock"<<std::endl;
165 block = map.emergeBlock(
169 lighting_invalidated_blocks);
172 While we're at it, generate some other blocks too
180 lighting_invalidated_blocks);
185 lighting_invalidated_blocks);
187 catch(InvalidPositionException &e)
192 // If it is a dummy, block was not found on disk
195 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
198 if(only_from_disk == false)
200 dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
205 catch(InvalidPositionException &e)
208 // This happens when position is over limit.
214 if(debug && changed_blocks.size() > 0)
216 dout_server<<DTIME<<"Got changed_blocks: ";
217 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
218 i.atEnd() == false; i++)
220 MapBlock *block = i.getNode()->getValue();
221 v3s16 p = block->getPos();
222 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
224 dout_server<<std::endl;
228 Collect a list of blocks that have been modified in
229 addition to the fetched one.
232 if(lighting_invalidated_blocks.size() > 0)
233 dstream<<"lighting "<<lighting_invalidated_blocks.size()
234 <<" blocks"<<std::endl;
236 // 50-100ms for single block generation
237 //TimeTaker timer("** EmergeThread updateLighting");
239 // Update lighting without locking the environment mutex,
240 // add modified blocks to changed blocks
241 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
243 // Add all from changed_blocks to modified_blocks
244 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
245 i.atEnd() == false; i++)
247 MapBlock *block = i.getNode()->getValue();
248 modified_blocks.insert(block->getPos(), block);
251 // If we got no block, there should be no invalidated blocks
254 assert(lighting_invalidated_blocks.size() == 0);
260 Set sent status of modified blocks on clients
263 // NOTE: Server's clients are also behind the connection mutex
264 JMutexAutoLock lock(m_server->m_con_mutex);
267 Add the originally fetched block to the modified list
271 modified_blocks.insert(p, block);
275 Set the modified blocks unsent for all the clients
278 for(core::map<u16, RemoteClient*>::Iterator
279 i = m_server->m_clients.getIterator();
280 i.atEnd() == false; i++)
282 RemoteClient *client = i.getNode()->getValue();
284 if(modified_blocks.size() > 0)
286 // Remove block from sent history
287 client->SetBlocksNotSent(modified_blocks);
293 END_DEBUG_EXCEPTION_HANDLER
298 void RemoteClient::GetNextBlocks(Server *server, float dtime,
299 core::array<PrioritySortedBlockTransfer> &dest)
301 DSTACK(__FUNCTION_NAME);
304 m_nearest_unsent_reset_timer += dtime;
306 // Won't send anything if already sending
307 if(m_blocks_sending.size() >= g_settings.getU16
308 ("max_simultaneous_block_sends_per_client"))
310 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
314 Player *player = server->m_env.getPlayer(peer_id);
316 assert(player != NULL);
318 v3f playerpos = player->getPosition();
319 v3f playerspeed = player->getSpeed();
321 v3s16 center_nodepos = floatToInt(playerpos, BS);
323 v3s16 center = getNodeBlockPos(center_nodepos);
325 // Camera position and direction
327 playerpos + v3f(0, BS+BS/2, 0);
328 v3f camera_dir = v3f(0,0,1);
329 camera_dir.rotateYZBy(player->getPitch());
330 camera_dir.rotateXZBy(player->getYaw());
333 Get the starting value of the block finder radius.
335 s16 last_nearest_unsent_d;
338 if(m_last_center != center)
340 m_nearest_unsent_d = 0;
341 m_last_center = center;
344 /*dstream<<"m_nearest_unsent_reset_timer="
345 <<m_nearest_unsent_reset_timer<<std::endl;*/
346 if(m_nearest_unsent_reset_timer > 5.0)
348 m_nearest_unsent_reset_timer = 0;
349 m_nearest_unsent_d = 0;
350 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
353 last_nearest_unsent_d = m_nearest_unsent_d;
355 d_start = m_nearest_unsent_d;
357 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
358 ("max_simultaneous_block_sends_per_client");
359 u16 maximum_simultaneous_block_sends =
360 maximum_simultaneous_block_sends_setting;
363 Check the time from last addNode/removeNode.
365 Decrease send rate if player is building stuff.
367 m_time_from_building += dtime;
368 if(m_time_from_building < g_settings.getFloat(
369 "full_block_send_enable_min_time_from_building"))
371 maximum_simultaneous_block_sends
372 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
375 u32 num_blocks_selected = m_blocks_sending.size();
378 next time d will be continued from the d from which the nearest
379 unsent block was found this time.
381 This is because not necessarily any of the blocks found this
382 time are actually sent.
384 s32 new_nearest_unsent_d = -1;
386 s16 d_max = g_settings.getS16("max_block_send_distance");
387 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
389 //dstream<<"Starting from "<<d_start<<std::endl;
391 for(s16 d = d_start; d <= d_max; d++)
393 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
396 If m_nearest_unsent_d was changed by the EmergeThread
397 (it can change it to 0 through SetBlockNotSent),
399 Else update m_nearest_unsent_d
401 if(m_nearest_unsent_d != last_nearest_unsent_d)
403 d = m_nearest_unsent_d;
404 last_nearest_unsent_d = m_nearest_unsent_d;
408 Get the border/face dot coordinates of a "d-radiused"
411 core::list<v3s16> list;
412 getFacePositions(list, d);
414 core::list<v3s16>::Iterator li;
415 for(li=list.begin(); li!=list.end(); li++)
417 v3s16 p = *li + center;
421 - Don't allow too many simultaneous transfers
422 - EXCEPT when the blocks are very close
424 Also, don't send blocks that are already flying.
427 u16 maximum_simultaneous_block_sends_now =
428 maximum_simultaneous_block_sends;
430 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
432 maximum_simultaneous_block_sends_now =
433 maximum_simultaneous_block_sends_setting;
436 // Limit is dynamically lowered when building
437 if(num_blocks_selected
438 >= maximum_simultaneous_block_sends_now)
440 /*dstream<<"Not sending more blocks. Queue full. "
441 <<m_blocks_sending.size()
446 if(m_blocks_sending.find(p) != NULL)
452 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
453 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
454 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
455 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
456 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
457 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
460 // If this is true, inexistent block will be made from scratch
461 bool generate = d <= d_max_gen;
464 /*// Limit the generating area vertically to 2/3
465 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
468 // Limit the send area vertically to 2/3
469 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
475 If block is far away, don't generate it unless it is
478 NOTE: We can't know the ground level this way with the
484 MapSector *sector = NULL;
487 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
489 catch(InvalidPositionException &e)
495 // Get center ground height in nodes
496 f32 gh = sector->getGroundHeight(
497 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
498 // Block center y in nodes
499 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
500 // If differs a lot, don't generate
501 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
508 Don't generate or send if not in sight
511 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
517 Don't send already sent blocks
520 if(m_blocks_sent.find(p) != NULL)
525 Check if map has this block
527 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
529 bool surely_not_found_on_disk = false;
530 bool block_is_invalid = false;
535 surely_not_found_on_disk = true;
538 if(block->isValid() == false)
540 block_is_invalid = true;
544 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
545 v2s16 chunkpos = map->sector_to_chunk(p2d);
546 if(map->chunkNonVolatile(chunkpos) == false)
547 block_is_invalid = true;
551 If block has been marked to not exist on disk (dummy)
552 and generating new ones is not wanted, skip block.
554 if(generate == false && surely_not_found_on_disk == true)
561 Record the lowest d from which a a block has been
562 found being not sent and possibly to exist
564 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
566 new_nearest_unsent_d = d;
570 Add inexistent block to emerge queue.
572 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
574 //TODO: Get value from somewhere
575 // Allow only one block in emerge queue
576 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
577 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
579 //dstream<<"Adding block to emerge queue"<<std::endl;
581 // Add it to the emerge queue and trigger the thread
584 if(generate == false)
585 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
587 server->m_emerge_queue.addBlock(peer_id, p, flags);
588 server->m_emergethread.trigger();
596 Add block to send queue
599 PrioritySortedBlockTransfer q((float)d, p, peer_id);
603 num_blocks_selected += 1;
608 if(new_nearest_unsent_d != -1)
610 m_nearest_unsent_d = new_nearest_unsent_d;
614 void RemoteClient::SendObjectData(
617 core::map<v3s16, bool> &stepped_blocks
620 DSTACK(__FUNCTION_NAME);
622 // Can't send anything without knowing version
623 if(serialization_version == SER_FMT_VER_INVALID)
625 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
631 Send a TOCLIENT_OBJECTDATA packet.
635 u16 number of player positions
646 std::ostringstream os(std::ios_base::binary);
650 writeU16(buf, TOCLIENT_OBJECTDATA);
651 os.write((char*)buf, 2);
654 Get and write player data
657 // Get connected players
658 core::list<Player*> players = server->m_env.getPlayers(true);
660 // Write player count
661 u16 playercount = players.size();
662 writeU16(buf, playercount);
663 os.write((char*)buf, 2);
665 core::list<Player*>::Iterator i;
666 for(i = players.begin();
667 i != players.end(); i++)
671 v3f pf = player->getPosition();
672 v3f sf = player->getSpeed();
674 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
675 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
676 s32 pitch_i (player->getPitch() * 100);
677 s32 yaw_i (player->getYaw() * 100);
679 writeU16(buf, player->peer_id);
680 os.write((char*)buf, 2);
681 writeV3S32(buf, position_i);
682 os.write((char*)buf, 12);
683 writeV3S32(buf, speed_i);
684 os.write((char*)buf, 12);
685 writeS32(buf, pitch_i);
686 os.write((char*)buf, 4);
687 writeS32(buf, yaw_i);
688 os.write((char*)buf, 4);
692 Get and write object data
698 For making players to be able to build to their nearby
699 environment (building is not possible on blocks that are not
702 - Add blocks to emerge queue if they are not found
704 SUGGESTION: These could be ignored from the backside of the player
707 Player *player = server->m_env.getPlayer(peer_id);
711 v3f playerpos = player->getPosition();
712 v3f playerspeed = player->getSpeed();
714 v3s16 center_nodepos = floatToInt(playerpos, BS);
715 v3s16 center = getNodeBlockPos(center_nodepos);
717 s16 d_max = g_settings.getS16("active_object_range");
719 // Number of blocks whose objects were written to bos
722 std::ostringstream bos(std::ios_base::binary);
724 for(s16 d = 0; d <= d_max; d++)
726 core::list<v3s16> list;
727 getFacePositions(list, d);
729 core::list<v3s16>::Iterator li;
730 for(li=list.begin(); li!=list.end(); li++)
732 v3s16 p = *li + center;
735 Ignore blocks that haven't been sent to the client
738 if(m_blocks_sent.find(p) == NULL)
742 // Try stepping block and add it to a send queue
747 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
750 Step block if not in stepped_blocks and add to stepped_blocks.
752 if(stepped_blocks.find(p) == NULL)
754 block->stepObjects(dtime, true, server->getDayNightRatio());
755 stepped_blocks.insert(p, true);
756 block->setChangedFlag();
759 // Skip block if there are no objects
760 if(block->getObjectCount() == 0)
769 bos.write((char*)buf, 6);
772 block->serializeObjects(bos, serialization_version);
777 Stop collecting objects if data is already too big
779 // Sum of player and object data sizes
780 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
781 // break out if data too big
782 if(sum > MAX_OBJECTDATA_SIZE)
784 goto skip_subsequent;
788 catch(InvalidPositionException &e)
791 // Add it to the emerge queue and trigger the thread.
792 // Fetch the block only if it is on disk.
794 // Grab and increment counter
795 /*SharedPtr<JMutexAutoLock> lock
796 (m_num_blocks_in_emerge_queue.getLock());
797 m_num_blocks_in_emerge_queue.m_value++;*/
799 // Add to queue as an anonymous fetch from disk
800 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
801 server->m_emerge_queue.addBlock(0, p, flags);
802 server->m_emergethread.trigger();
810 writeU16(buf, blockcount);
811 os.write((char*)buf, 2);
813 // Write block objects
820 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
823 std::string s = os.str();
824 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
825 // Send as unreliable
826 server->m_con.Send(peer_id, 0, data, false);
829 void RemoteClient::GotBlock(v3s16 p)
831 if(m_blocks_sending.find(p) != NULL)
832 m_blocks_sending.remove(p);
835 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
836 " m_blocks_sending"<<std::endl;*/
837 m_excess_gotblocks++;
839 m_blocks_sent.insert(p, true);
842 void RemoteClient::SentBlock(v3s16 p)
844 if(m_blocks_sending.find(p) == NULL)
845 m_blocks_sending.insert(p, 0.0);
847 dstream<<"RemoteClient::SentBlock(): Sent block"
848 " already in m_blocks_sending"<<std::endl;
851 void RemoteClient::SetBlockNotSent(v3s16 p)
853 m_nearest_unsent_d = 0;
855 if(m_blocks_sending.find(p) != NULL)
856 m_blocks_sending.remove(p);
857 if(m_blocks_sent.find(p) != NULL)
858 m_blocks_sent.remove(p);
861 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
863 m_nearest_unsent_d = 0;
865 for(core::map<v3s16, MapBlock*>::Iterator
866 i = blocks.getIterator();
867 i.atEnd()==false; i++)
869 v3s16 p = i.getNode()->getKey();
871 if(m_blocks_sending.find(p) != NULL)
872 m_blocks_sending.remove(p);
873 if(m_blocks_sent.find(p) != NULL)
874 m_blocks_sent.remove(p);
882 PlayerInfo::PlayerInfo()
887 void PlayerInfo::PrintLine(std::ostream *s)
890 (*s)<<"\""<<name<<"\" ("
891 <<(position.X/10)<<","<<(position.Y/10)
892 <<","<<(position.Z/10)<<") ";
894 (*s)<<" avg_rtt="<<avg_rtt;
898 u32 PIChecksum(core::list<PlayerInfo> &l)
900 core::list<PlayerInfo>::Iterator i;
903 for(i=l.begin(); i!=l.end(); i++)
905 checksum += a * (i->id+1);
906 checksum ^= 0x435aafcd;
917 std::string mapsavedir
919 m_env(new ServerMap(mapsavedir), this),
920 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
922 m_emergethread(this),
925 m_time_of_day_send_timer(0),
927 m_mapsavedir(mapsavedir),
928 m_shutdown_requested(false),
929 m_ignore_map_edit_events(false),
930 m_ignore_map_edit_events_peer_id(0)
932 m_liquid_transform_timer = 0.0;
933 m_print_info_timer = 0.0;
934 m_objectdata_timer = 0.0;
935 m_emergethread_trigger_timer = 0.0;
936 m_savemap_timer = 0.0;
940 m_step_dtime_mutex.Init();
943 m_env.getMap().addEventReceiver(this);
946 m_env.deSerializePlayers(m_mapsavedir);
952 Send shutdown message
955 JMutexAutoLock conlock(m_con_mutex);
957 std::wstring line = L"*** Server shutting down";
960 Send the message to clients
962 for(core::map<u16, RemoteClient*>::Iterator
963 i = m_clients.getIterator();
964 i.atEnd() == false; i++)
966 // Get client and check that it is valid
967 RemoteClient *client = i.getNode()->getValue();
968 assert(client->peer_id == i.getNode()->getKey());
969 if(client->serialization_version == SER_FMT_VER_INVALID)
972 SendChatMessage(client->peer_id, line);
979 m_env.serializePlayers(m_mapsavedir);
990 JMutexAutoLock clientslock(m_con_mutex);
992 for(core::map<u16, RemoteClient*>::Iterator
993 i = m_clients.getIterator();
994 i.atEnd() == false; i++)
997 // NOTE: These are removed by env destructor
999 u16 peer_id = i.getNode()->getKey();
1000 JMutexAutoLock envlock(m_env_mutex);
1001 m_env.removePlayer(peer_id);
1005 delete i.getNode()->getValue();
1010 void Server::start(unsigned short port)
1012 DSTACK(__FUNCTION_NAME);
1013 // Stop thread if already running
1016 // Initialize connection
1017 m_con.setTimeoutMs(30);
1021 m_thread.setRun(true);
1024 dout_server<<"Server: Started on port "<<port<<std::endl;
1029 DSTACK(__FUNCTION_NAME);
1031 // Stop threads (set run=false first so both start stopping)
1032 m_thread.setRun(false);
1033 m_emergethread.setRun(false);
1035 m_emergethread.stop();
1037 dout_server<<"Server: Threads stopped"<<std::endl;
1039 dout_server<<"Server: Saving players"<<std::endl;
1041 // FIXME: Apparently this does not do anything here
1042 //m_env.serializePlayers(m_mapsavedir);
1045 void Server::step(float dtime)
1047 DSTACK(__FUNCTION_NAME);
1052 JMutexAutoLock lock(m_step_dtime_mutex);
1053 m_step_dtime += dtime;
1057 void Server::AsyncRunStep()
1059 DSTACK(__FUNCTION_NAME);
1063 JMutexAutoLock lock1(m_step_dtime_mutex);
1064 dtime = m_step_dtime;
1067 // Send blocks to clients
1073 //dstream<<"Server steps "<<dtime<<std::endl;
1074 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1077 JMutexAutoLock lock1(m_step_dtime_mutex);
1078 m_step_dtime -= dtime;
1085 m_uptime.set(m_uptime.get() + dtime);
1089 Update m_time_of_day
1092 m_time_counter += dtime;
1093 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1094 u32 units = (u32)(m_time_counter*speed);
1095 m_time_counter -= (f32)units / speed;
1096 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1098 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1101 Send to clients at constant intervals
1104 m_time_of_day_send_timer -= dtime;
1105 if(m_time_of_day_send_timer < 0.0)
1107 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1109 //JMutexAutoLock envlock(m_env_mutex);
1110 JMutexAutoLock conlock(m_con_mutex);
1112 for(core::map<u16, RemoteClient*>::Iterator
1113 i = m_clients.getIterator();
1114 i.atEnd() == false; i++)
1116 RemoteClient *client = i.getNode()->getValue();
1117 //Player *player = m_env.getPlayer(client->peer_id);
1119 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1120 m_time_of_day.get());
1122 m_con.Send(client->peer_id, 0, data, true);
1128 // Process connection's timeouts
1129 JMutexAutoLock lock2(m_con_mutex);
1130 m_con.RunTimeouts(dtime);
1134 // This has to be called so that the client list gets synced
1135 // with the peer list of the connection
1136 handlePeerChanges();
1141 // This also runs Map's timers
1142 JMutexAutoLock lock(m_env_mutex);
1153 m_liquid_transform_timer += dtime;
1154 if(m_liquid_transform_timer >= 1.00)
1156 m_liquid_transform_timer -= 1.00;
1158 JMutexAutoLock lock(m_env_mutex);
1160 core::map<v3s16, MapBlock*> modified_blocks;
1161 m_env.getMap().transformLiquids(modified_blocks);
1166 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1167 ServerMap &map = ((ServerMap&)m_env.getMap());
1168 map.updateLighting(modified_blocks, lighting_modified_blocks);
1170 // Add blocks modified by lighting to modified_blocks
1171 for(core::map<v3s16, MapBlock*>::Iterator
1172 i = lighting_modified_blocks.getIterator();
1173 i.atEnd() == false; i++)
1175 MapBlock *block = i.getNode()->getValue();
1176 modified_blocks.insert(block->getPos(), block);
1180 Set the modified blocks unsent for all the clients
1183 JMutexAutoLock lock2(m_con_mutex);
1185 for(core::map<u16, RemoteClient*>::Iterator
1186 i = m_clients.getIterator();
1187 i.atEnd() == false; i++)
1189 RemoteClient *client = i.getNode()->getValue();
1191 if(modified_blocks.size() > 0)
1193 // Remove block from sent history
1194 client->SetBlocksNotSent(modified_blocks);
1199 // Periodically print some info
1201 float &counter = m_print_info_timer;
1207 JMutexAutoLock lock2(m_con_mutex);
1209 for(core::map<u16, RemoteClient*>::Iterator
1210 i = m_clients.getIterator();
1211 i.atEnd() == false; i++)
1213 //u16 peer_id = i.getNode()->getKey();
1214 RemoteClient *client = i.getNode()->getValue();
1215 client->PrintInfo(std::cout);
1220 if(g_settings.getBool("enable_experimental"))
1224 Check added and deleted active objects
1227 JMutexAutoLock envlock(m_env_mutex);
1228 JMutexAutoLock conlock(m_con_mutex);
1230 // Radius inside which objects are active
1233 for(core::map<u16, RemoteClient*>::Iterator
1234 i = m_clients.getIterator();
1235 i.atEnd() == false; i++)
1237 RemoteClient *client = i.getNode()->getValue();
1238 Player *player = m_env.getPlayer(client->peer_id);
1241 v3s16 pos = floatToInt(player->getPosition(), BS);
1243 core::map<u16, bool> removed_objects;
1244 core::map<u16, bool> added_objects;
1245 m_env.getRemovedActiveObjects(pos, radius,
1246 client->m_known_objects, removed_objects);
1247 m_env.getAddedActiveObjects(pos, radius,
1248 client->m_known_objects, added_objects);
1250 // Ignore if nothing happened
1251 if(removed_objects.size() == 0 && added_objects.size() == 0)
1254 std::string data_buffer;
1258 // Handle removed objects
1259 writeU16((u8*)buf, removed_objects.size());
1260 data_buffer.append(buf, 2);
1261 for(core::map<u16, bool>::Iterator
1262 i = removed_objects.getIterator();
1263 i.atEnd()==false; i++)
1266 u16 id = i.getNode()->getKey();
1267 ServerActiveObject* obj = m_env.getActiveObject(id);
1269 // Add to data buffer for sending
1270 writeU16((u8*)buf, i.getNode()->getKey());
1271 data_buffer.append(buf, 2);
1273 // Remove from known objects
1274 client->m_known_objects.remove(i.getNode()->getKey());
1276 if(obj && obj->m_known_by_count > 0)
1277 obj->m_known_by_count--;
1280 // Handle added objects
1281 writeU16((u8*)buf, added_objects.size());
1282 data_buffer.append(buf, 2);
1283 for(core::map<u16, bool>::Iterator
1284 i = added_objects.getIterator();
1285 i.atEnd()==false; i++)
1288 u16 id = i.getNode()->getKey();
1289 ServerActiveObject* obj = m_env.getActiveObject(id);
1292 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1294 dstream<<"WARNING: "<<__FUNCTION_NAME
1295 <<": NULL object"<<std::endl;
1297 type = obj->getType();
1299 // Add to data buffer for sending
1300 writeU16((u8*)buf, id);
1301 data_buffer.append(buf, 2);
1302 writeU8((u8*)buf, type);
1303 data_buffer.append(buf, 1);
1305 data_buffer.append(serializeLongString(
1306 obj->getClientInitializationData()));
1308 // Add to known objects
1309 client->m_known_objects.insert(i.getNode()->getKey(), false);
1312 obj->m_known_by_count++;
1316 SharedBuffer<u8> reply(2 + data_buffer.size());
1317 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1318 memcpy((char*)&reply[2], data_buffer.c_str(),
1319 data_buffer.size());
1321 m_con.Send(client->peer_id, 0, reply, true);
1323 dstream<<"INFO: Server: Sent object remove/add: "
1324 <<removed_objects.size()<<" removed, "
1325 <<added_objects.size()<<" added, "
1326 <<"packet size is "<<reply.getSize()<<std::endl;
1331 Send object messages
1334 JMutexAutoLock envlock(m_env_mutex);
1335 JMutexAutoLock conlock(m_con_mutex);
1338 // Value = data sent by object
1339 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1341 // Get active object messages from environment
1344 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1348 core::list<ActiveObjectMessage>* message_list = NULL;
1349 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1350 n = buffered_messages.find(aom.id);
1353 message_list = new core::list<ActiveObjectMessage>;
1354 buffered_messages.insert(aom.id, message_list);
1358 message_list = n->getValue();
1360 message_list->push_back(aom);
1363 // Route data to every client
1364 for(core::map<u16, RemoteClient*>::Iterator
1365 i = m_clients.getIterator();
1366 i.atEnd()==false; i++)
1368 RemoteClient *client = i.getNode()->getValue();
1369 std::string reliable_data;
1370 std::string unreliable_data;
1371 // Go through all objects in message buffer
1372 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1373 j = buffered_messages.getIterator();
1374 j.atEnd()==false; j++)
1376 // If object is not known by client, skip it
1377 u16 id = j.getNode()->getKey();
1378 if(client->m_known_objects.find(id) == NULL)
1380 // Get message list of object
1381 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1382 // Go through every message
1383 for(core::list<ActiveObjectMessage>::Iterator
1384 k = list->begin(); k != list->end(); k++)
1386 // Compose the full new data with header
1387 ActiveObjectMessage aom = *k;
1388 std::string new_data;
1391 writeU16((u8*)&buf[0], aom.id);
1392 new_data.append(buf, 2);
1394 new_data += serializeString(aom.datastring);
1395 // Add data to buffer
1397 reliable_data += new_data;
1399 unreliable_data += new_data;
1403 reliable_data and unreliable_data are now ready.
1406 if(reliable_data.size() > 0)
1408 SharedBuffer<u8> reply(2 + reliable_data.size());
1409 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1410 memcpy((char*)&reply[2], reliable_data.c_str(),
1411 reliable_data.size());
1413 m_con.Send(client->peer_id, 0, reply, true);
1415 if(unreliable_data.size() > 0)
1417 SharedBuffer<u8> reply(2 + unreliable_data.size());
1418 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1419 memcpy((char*)&reply[2], unreliable_data.c_str(),
1420 unreliable_data.size());
1421 // Send as unreliable
1422 m_con.Send(client->peer_id, 0, reply, false);
1425 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1427 dstream<<"INFO: Server: Size of object message data: "
1428 <<"reliable: "<<reliable_data.size()
1429 <<", unreliable: "<<unreliable_data.size()
1434 // Clear buffered_messages
1435 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1436 i = buffered_messages.getIterator();
1437 i.atEnd()==false; i++)
1439 delete i.getNode()->getValue();
1443 } // enable_experimental
1446 Send queued-for-sending map edit events.
1449 while(m_unsent_map_edit_queue.size() != 0)
1451 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1453 if(event->type == MEET_ADDNODE)
1455 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1456 sendAddNode(event->p, event->n, event->already_known_by_peer);
1458 else if(event->type == MEET_REMOVENODE)
1460 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1461 sendRemoveNode(event->p, event->already_known_by_peer);
1463 else if(event->type == MEET_OTHER)
1465 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1470 dstream<<"WARNING: Server: Unknown MapEditEvent "
1471 <<((u32)event->type)<<std::endl;
1479 Send object positions
1480 TODO: Get rid of MapBlockObjects
1483 float &counter = m_objectdata_timer;
1485 if(counter >= g_settings.getFloat("objectdata_interval"))
1487 JMutexAutoLock lock1(m_env_mutex);
1488 JMutexAutoLock lock2(m_con_mutex);
1489 SendObjectData(counter);
1496 Trigger emergethread (it somehow gets to a non-triggered but
1497 bysy state sometimes)
1500 float &counter = m_emergethread_trigger_timer;
1506 m_emergethread.trigger();
1512 float &counter = m_savemap_timer;
1514 if(counter >= g_settings.getFloat("server_map_save_interval"))
1518 JMutexAutoLock lock(m_env_mutex);
1520 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1522 // Save only changed parts
1523 m_env.getMap().save(true);
1525 // Delete unused sectors
1526 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1527 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1528 if(deleted_count > 0)
1530 dout_server<<"Server: Unloaded "<<deleted_count
1531 <<" sectors from memory"<<std::endl;
1535 m_env.serializePlayers(m_mapsavedir);
1541 void Server::Receive()
1543 DSTACK(__FUNCTION_NAME);
1544 u32 data_maxsize = 10000;
1545 Buffer<u8> data(data_maxsize);
1550 JMutexAutoLock conlock(m_con_mutex);
1551 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1554 // This has to be called so that the client list gets synced
1555 // with the peer list of the connection
1556 handlePeerChanges();
1558 ProcessData(*data, datasize, peer_id);
1560 catch(con::InvalidIncomingDataException &e)
1562 derr_server<<"Server::Receive(): "
1563 "InvalidIncomingDataException: what()="
1564 <<e.what()<<std::endl;
1566 catch(con::PeerNotFoundException &e)
1568 //NOTE: This is not needed anymore
1570 // The peer has been disconnected.
1571 // Find the associated player and remove it.
1573 /*JMutexAutoLock envlock(m_env_mutex);
1575 dout_server<<"ServerThread: peer_id="<<peer_id
1576 <<" has apparently closed connection. "
1577 <<"Removing player."<<std::endl;
1579 m_env.removePlayer(peer_id);*/
1583 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1585 DSTACK(__FUNCTION_NAME);
1586 // Environment is locked first.
1587 JMutexAutoLock envlock(m_env_mutex);
1588 JMutexAutoLock conlock(m_con_mutex);
1592 peer = m_con.GetPeer(peer_id);
1594 catch(con::PeerNotFoundException &e)
1596 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1597 <<peer_id<<" not found"<<std::endl;
1601 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1609 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1611 if(command == TOSERVER_INIT)
1613 // [0] u16 TOSERVER_INIT
1614 // [2] u8 SER_FMT_VER_HIGHEST
1615 // [3] u8[20] player_name
1620 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1621 <<peer->id<<std::endl;
1623 // First byte after command is maximum supported
1624 // serialization version
1625 u8 client_max = data[2];
1626 u8 our_max = SER_FMT_VER_HIGHEST;
1627 // Use the highest version supported by both
1628 u8 deployed = core::min_(client_max, our_max);
1629 // If it's lower than the lowest supported, give up.
1630 if(deployed < SER_FMT_VER_LOWEST)
1631 deployed = SER_FMT_VER_INVALID;
1633 //peer->serialization_version = deployed;
1634 getClient(peer->id)->pending_serialization_version = deployed;
1636 if(deployed == SER_FMT_VER_INVALID)
1638 derr_server<<DTIME<<"Server: Cannot negotiate "
1639 "serialization version with peer "
1640 <<peer_id<<std::endl;
1649 const u32 playername_size = 20;
1650 char playername[playername_size];
1651 for(u32 i=0; i<playername_size-1; i++)
1653 playername[i] = data[3+i];
1655 playername[playername_size-1] = 0;
1658 Player *player = emergePlayer(playername, "", peer_id);
1659 //Player *player = m_env.getPlayer(peer_id);
1662 // DEBUG: Test serialization
1663 std::ostringstream test_os;
1664 player->serialize(test_os);
1665 dstream<<"Player serialization test: \""<<test_os.str()
1667 std::istringstream test_is(test_os.str());
1668 player->deSerialize(test_is);
1671 // If failed, cancel
1674 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1675 <<": failed to emerge player"<<std::endl;
1680 // If a client is already connected to the player, cancel
1681 if(player->peer_id != 0)
1683 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1684 <<" tried to connect to "
1685 "an already connected player (peer_id="
1686 <<player->peer_id<<")"<<std::endl;
1689 // Set client of player
1690 player->peer_id = peer_id;
1693 // Check if player doesn't exist
1695 throw con::InvalidIncomingDataException
1696 ("Server::ProcessData(): INIT: Player doesn't exist");
1698 /*// update name if it was supplied
1699 if(datasize >= 20+3)
1702 player->updateName((const char*)&data[3]);
1705 // Now answer with a TOCLIENT_INIT
1707 SharedBuffer<u8> reply(2+1+6+8);
1708 writeU16(&reply[0], TOCLIENT_INIT);
1709 writeU8(&reply[2], deployed);
1710 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1711 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1713 m_con.Send(peer_id, 0, reply, true);
1717 if(command == TOSERVER_INIT2)
1719 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1720 <<peer->id<<std::endl;
1723 getClient(peer->id)->serialization_version
1724 = getClient(peer->id)->pending_serialization_version;
1727 Send some initialization data
1730 // Send player info to all players
1733 // Send inventory to player
1734 SendInventory(peer->id);
1738 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1739 m_time_of_day.get());
1740 m_con.Send(peer->id, 0, data, true);
1743 // Send information about server to player in chat
1744 SendChatMessage(peer_id, getStatusString());
1746 // Send information about joining in chat
1748 std::wstring name = L"unknown";
1749 Player *player = m_env.getPlayer(peer_id);
1751 name = narrow_to_wide(player->getName());
1753 std::wstring message;
1756 message += L" joined game";
1757 BroadcastChatMessage(message);
1763 if(peer_ser_ver == SER_FMT_VER_INVALID)
1765 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1766 " serialization format invalid or not initialized."
1767 " Skipping incoming command="<<command<<std::endl;
1771 Player *player = m_env.getPlayer(peer_id);
1774 derr_server<<"Server::ProcessData(): Cancelling: "
1775 "No player for peer_id="<<peer_id
1779 if(command == TOSERVER_PLAYERPOS)
1781 if(datasize < 2+12+12+4+4)
1785 v3s32 ps = readV3S32(&data[start+2]);
1786 v3s32 ss = readV3S32(&data[start+2+12]);
1787 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1788 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1789 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1790 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1791 pitch = wrapDegrees(pitch);
1792 yaw = wrapDegrees(yaw);
1793 player->setPosition(position);
1794 player->setSpeed(speed);
1795 player->setPitch(pitch);
1796 player->setYaw(yaw);
1798 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1799 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1800 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1802 else if(command == TOSERVER_GOTBLOCKS)
1815 u16 count = data[2];
1816 for(u16 i=0; i<count; i++)
1818 if((s16)datasize < 2+1+(i+1)*6)
1819 throw con::InvalidIncomingDataException
1820 ("GOTBLOCKS length is too short");
1821 v3s16 p = readV3S16(&data[2+1+i*6]);
1822 /*dstream<<"Server: GOTBLOCKS ("
1823 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1824 RemoteClient *client = getClient(peer_id);
1825 client->GotBlock(p);
1828 else if(command == TOSERVER_DELETEDBLOCKS)
1841 u16 count = data[2];
1842 for(u16 i=0; i<count; i++)
1844 if((s16)datasize < 2+1+(i+1)*6)
1845 throw con::InvalidIncomingDataException
1846 ("DELETEDBLOCKS length is too short");
1847 v3s16 p = readV3S16(&data[2+1+i*6]);
1848 /*dstream<<"Server: DELETEDBLOCKS ("
1849 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1850 RemoteClient *client = getClient(peer_id);
1851 client->SetBlockNotSent(p);
1854 else if(command == TOSERVER_CLICK_OBJECT)
1861 [2] u8 button (0=left, 1=right)
1866 u8 button = readU8(&data[2]);
1868 p.X = readS16(&data[3]);
1869 p.Y = readS16(&data[5]);
1870 p.Z = readS16(&data[7]);
1871 s16 id = readS16(&data[9]);
1872 //u16 item_i = readU16(&data[11]);
1874 MapBlock *block = NULL;
1877 block = m_env.getMap().getBlockNoCreate(p);
1879 catch(InvalidPositionException &e)
1881 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1885 MapBlockObject *obj = block->getObject(id);
1889 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1893 //TODO: Check that object is reasonably close
1898 InventoryList *ilist = player->inventory.getList("main");
1899 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1902 // Skip if inventory has no free space
1903 if(ilist->getUsedSlots() == ilist->getSize())
1905 dout_server<<"Player inventory has no free space"<<std::endl;
1910 Create the inventory item
1912 InventoryItem *item = NULL;
1913 // If it is an item-object, take the item from it
1914 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1916 item = ((ItemObject*)obj)->createInventoryItem();
1918 // Else create an item of the object
1921 item = new MapBlockObjectItem
1922 (obj->getInventoryString());
1925 // Add to inventory and send inventory
1926 ilist->addItem(item);
1927 SendInventory(player->peer_id);
1930 // Remove from block
1931 block->removeObject(id);
1934 else if(command == TOSERVER_GROUND_ACTION)
1942 [3] v3s16 nodepos_undersurface
1943 [9] v3s16 nodepos_abovesurface
1948 2: stop digging (all parameters ignored)
1949 3: digging completed
1951 u8 action = readU8(&data[2]);
1953 p_under.X = readS16(&data[3]);
1954 p_under.Y = readS16(&data[5]);
1955 p_under.Z = readS16(&data[7]);
1957 p_over.X = readS16(&data[9]);
1958 p_over.Y = readS16(&data[11]);
1959 p_over.Z = readS16(&data[13]);
1960 u16 item_i = readU16(&data[15]);
1962 //TODO: Check that target is reasonably close
1970 NOTE: This can be used in the future to check if
1971 somebody is cheating, by checking the timing.
1978 else if(action == 2)
1981 RemoteClient *client = getClient(peer->id);
1982 JMutexAutoLock digmutex(client->m_dig_mutex);
1983 client->m_dig_tool_item = -1;
1988 3: Digging completed
1990 else if(action == 3)
1992 // Mandatory parameter; actually used for nothing
1993 core::map<v3s16, MapBlock*> modified_blocks;
1996 u8 mineral = MINERAL_NONE;
2000 MapNode n = m_env.getMap().getNode(p_under);
2001 // Get material at position
2003 // If it's not diggable, do nothing
2004 if(content_diggable(material) == false)
2006 derr_server<<"Server: Not finishing digging: Node not diggable"
2009 // Client probably has wrong data.
2010 // Set block not sent, so that client will get
2012 dstream<<"Client "<<peer_id<<" tried to dig "
2013 <<"node from invalid position; setting"
2014 <<" MapBlock not sent."<<std::endl;
2015 RemoteClient *client = getClient(peer_id);
2016 v3s16 blockpos = getNodeBlockPos(p_under);
2017 client->SetBlockNotSent(blockpos);
2022 mineral = n.getMineral();
2024 catch(InvalidPositionException &e)
2026 derr_server<<"Server: Not finishing digging: Node not found."
2027 <<" Adding block to emerge queue."
2029 m_emerge_queue.addBlock(peer_id,
2030 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2035 Send the removal to all other clients
2037 sendRemoveNode(p_over, peer_id);
2040 Update and send inventory
2043 if(g_settings.getBool("creative_mode") == false)
2048 InventoryList *mlist = player->inventory.getList("main");
2051 InventoryItem *item = mlist->getItem(item_i);
2052 if(item && (std::string)item->getName() == "ToolItem")
2054 ToolItem *titem = (ToolItem*)item;
2055 std::string toolname = titem->getToolName();
2057 // Get digging properties for material and tool
2058 DiggingProperties prop =
2059 getDiggingProperties(material, toolname);
2061 if(prop.diggable == false)
2063 derr_server<<"Server: WARNING: Player digged"
2064 <<" with impossible material + tool"
2065 <<" combination"<<std::endl;
2068 bool weared_out = titem->addWear(prop.wear);
2072 mlist->deleteItem(item_i);
2078 Add dug item to inventory
2081 InventoryItem *item = NULL;
2083 if(mineral != MINERAL_NONE)
2084 item = getDiggedMineralItem(mineral);
2089 std::string &dug_s = content_features(material).dug_item;
2092 std::istringstream is(dug_s, std::ios::binary);
2093 item = InventoryItem::deSerialize(is);
2099 // Add a item to inventory
2100 player->inventory.addItem("main", item);
2103 SendInventory(player->peer_id);
2109 (this takes some time so it is done after the quick stuff)
2111 m_ignore_map_edit_events = true;
2112 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2113 m_ignore_map_edit_events = false;
2119 else if(action == 1)
2122 InventoryList *ilist = player->inventory.getList("main");
2127 InventoryItem *item = ilist->getItem(item_i);
2129 // If there is no item, it is not possible to add it anywhere
2134 Handle material items
2136 if(std::string("MaterialItem") == item->getName())
2139 // Don't add a node if this is not a free space
2140 MapNode n2 = m_env.getMap().getNode(p_over);
2141 if(content_buildable_to(n2.d) == false)
2143 // Client probably has wrong data.
2144 // Set block not sent, so that client will get
2146 dstream<<"Client "<<peer_id<<" tried to place"
2147 <<" node in invalid position; setting"
2148 <<" MapBlock not sent."<<std::endl;
2149 RemoteClient *client = getClient(peer_id);
2150 v3s16 blockpos = getNodeBlockPos(p_over);
2151 client->SetBlockNotSent(blockpos);
2155 catch(InvalidPositionException &e)
2157 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2158 <<" Adding block to emerge queue."
2160 m_emerge_queue.addBlock(peer_id,
2161 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2165 // Reset build time counter
2166 getClient(peer->id)->m_time_from_building = 0.0;
2169 MaterialItem *mitem = (MaterialItem*)item;
2171 n.d = mitem->getMaterial();
2172 if(content_features(n.d).wall_mounted)
2173 n.dir = packDir(p_under - p_over);
2178 sendAddNode(p_over, n, 0);
2183 InventoryList *ilist = player->inventory.getList("main");
2184 if(g_settings.getBool("creative_mode") == false && ilist)
2186 // Remove from inventory and send inventory
2187 if(mitem->getCount() == 1)
2188 ilist->deleteItem(item_i);
2192 SendInventory(peer_id);
2198 This takes some time so it is done after the quick stuff
2200 core::map<v3s16, MapBlock*> modified_blocks;
2201 m_ignore_map_edit_events = true;
2202 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2203 m_ignore_map_edit_events = false;
2206 Calculate special events
2209 /*if(n.d == CONTENT_MESE)
2212 for(s16 z=-1; z<=1; z++)
2213 for(s16 y=-1; y<=1; y++)
2214 for(s16 x=-1; x<=1; x++)
2225 v3s16 blockpos = getNodeBlockPos(p_over);
2227 MapBlock *block = NULL;
2230 block = m_env.getMap().getBlockNoCreate(blockpos);
2232 catch(InvalidPositionException &e)
2234 derr_server<<"Error while placing object: "
2235 "block not found"<<std::endl;
2239 v3s16 block_pos_i_on_map = block->getPosRelative();
2240 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2242 v3f pos = intToFloat(p_over, BS);
2243 pos -= block_pos_f_on_map;
2245 /*dout_server<<"pos="
2246 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2249 MapBlockObject *obj = NULL;
2252 Handle block object items
2254 if(std::string("MBOItem") == item->getName())
2256 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2258 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2259 "inventorystring=\""
2260 <<oitem->getInventoryString()
2261 <<"\""<<std::endl;*/
2263 obj = oitem->createObject
2264 (pos, player->getYaw(), player->getPitch());
2271 dout_server<<"Placing a miscellaneous item on map"
2274 Create an ItemObject that contains the item.
2276 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2277 std::ostringstream os(std::ios_base::binary);
2278 item->serialize(os);
2279 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2280 iobj->setItemString(os.str());
2286 derr_server<<"WARNING: item resulted in NULL object, "
2287 <<"not placing onto map"
2292 block->addObject(obj);
2294 dout_server<<"Placed object"<<std::endl;
2296 InventoryList *ilist = player->inventory.getList("main");
2297 if(g_settings.getBool("creative_mode") == false && ilist)
2299 // Remove from inventory and send inventory
2300 ilist->deleteItem(item_i);
2302 SendInventory(peer_id);
2310 Catch invalid actions
2314 derr_server<<"WARNING: Server: Invalid action "
2315 <<action<<std::endl;
2319 else if(command == TOSERVER_RELEASE)
2328 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2331 else if(command == TOSERVER_SIGNTEXT)
2340 std::string datastring((char*)&data[2], datasize-2);
2341 std::istringstream is(datastring, std::ios_base::binary);
2344 is.read((char*)buf, 6);
2345 v3s16 blockpos = readV3S16(buf);
2346 is.read((char*)buf, 2);
2347 s16 id = readS16(buf);
2348 is.read((char*)buf, 2);
2349 u16 textlen = readU16(buf);
2351 for(u16 i=0; i<textlen; i++)
2353 is.read((char*)buf, 1);
2354 text += (char)buf[0];
2357 MapBlock *block = NULL;
2360 block = m_env.getMap().getBlockNoCreate(blockpos);
2362 catch(InvalidPositionException &e)
2364 derr_server<<"Error while setting sign text: "
2365 "block not found"<<std::endl;
2369 MapBlockObject *obj = block->getObject(id);
2372 derr_server<<"Error while setting sign text: "
2373 "object not found"<<std::endl;
2377 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2379 derr_server<<"Error while setting sign text: "
2380 "object is not a sign"<<std::endl;
2384 ((SignObject*)obj)->setText(text);
2386 obj->getBlock()->setChangedFlag();
2388 else if(command == TOSERVER_INVENTORY_ACTION)
2390 /*// Ignore inventory changes if in creative mode
2391 if(g_settings.getBool("creative_mode") == true)
2393 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2397 // Strip command and create a stream
2398 std::string datastring((char*)&data[2], datasize-2);
2399 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2400 std::istringstream is(datastring, std::ios_base::binary);
2402 InventoryAction *a = InventoryAction::deSerialize(is);
2406 Handle craftresult specially if not in creative mode
2408 bool disable_action = false;
2409 if(a->getType() == IACTION_MOVE
2410 && g_settings.getBool("creative_mode") == false)
2412 IMoveAction *ma = (IMoveAction*)a;
2413 // Don't allow moving anything to craftresult
2414 if(ma->to_name == "craftresult")
2417 disable_action = true;
2419 // When something is removed from craftresult
2420 if(ma->from_name == "craftresult")
2422 disable_action = true;
2423 // Remove stuff from craft
2424 InventoryList *clist = player->inventory.getList("craft");
2427 u16 count = ma->count;
2430 clist->decrementMaterials(count);
2433 // Feed action to player inventory
2434 a->apply(&player->inventory);
2437 // If something appeared in craftresult, throw it
2439 InventoryList *rlist = player->inventory.getList("craftresult");
2440 InventoryList *mlist = player->inventory.getList("main");
2441 if(rlist && mlist && rlist->getUsedSlots() == 1)
2443 InventoryItem *item1 = rlist->changeItem(0, NULL);
2444 mlist->addItem(item1);
2448 if(disable_action == false)
2450 // Feed action to player inventory
2451 a->apply(&player->inventory);
2456 SendInventory(player->peer_id);
2460 dstream<<"TOSERVER_INVENTORY_ACTION: "
2461 <<"InventoryAction::deSerialize() returned NULL"
2465 else if(command == TOSERVER_CHAT_MESSAGE)
2473 std::string datastring((char*)&data[2], datasize-2);
2474 std::istringstream is(datastring, std::ios_base::binary);
2477 is.read((char*)buf, 2);
2478 u16 len = readU16(buf);
2480 std::wstring message;
2481 for(u16 i=0; i<len; i++)
2483 is.read((char*)buf, 2);
2484 message += (wchar_t)readU16(buf);
2487 // Get player name of this client
2488 std::wstring name = narrow_to_wide(player->getName());
2490 // Line to send to players
2492 // Whether to send to the player that sent the line
2493 bool send_to_sender = false;
2494 // Whether to send to other players
2495 bool send_to_others = false;
2498 std::wstring commandprefix = L"/#";
2499 if(message.substr(0, commandprefix.size()) == commandprefix)
2501 line += L"Server: ";
2503 message = message.substr(commandprefix.size());
2504 // Get player name as narrow string
2505 std::string name_s = player->getName();
2506 // Convert message to narrow string
2507 std::string message_s = wide_to_narrow(message);
2508 // Operator is the single name defined in config.
2509 std::string operator_name = g_settings.get("name");
2510 bool is_operator = (operator_name != "" &&
2511 wide_to_narrow(name) == operator_name);
2512 bool valid_command = false;
2513 if(message_s == "help")
2515 line += L"-!- Available commands: ";
2519 line += L"shutdown setting ";
2524 send_to_sender = true;
2525 valid_command = true;
2527 else if(message_s == "status")
2529 line = getStatusString();
2530 send_to_sender = true;
2531 valid_command = true;
2533 else if(is_operator)
2535 if(message_s == "shutdown")
2537 dstream<<DTIME<<" Server: Operator requested shutdown."
2539 m_shutdown_requested.set(true);
2541 line += L"*** Server shutting down (operator request)";
2542 send_to_sender = true;
2543 valid_command = true;
2545 else if(message_s.substr(0,8) == "setting ")
2547 std::string confline = message_s.substr(8);
2548 g_settings.parseConfigLine(confline);
2549 line += L"-!- Setting changed.";
2550 send_to_sender = true;
2551 valid_command = true;
2555 if(valid_command == false)
2557 line += L"-!- Invalid command: " + message;
2558 send_to_sender = true;
2569 send_to_others = true;
2574 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2577 Send the message to clients
2579 for(core::map<u16, RemoteClient*>::Iterator
2580 i = m_clients.getIterator();
2581 i.atEnd() == false; i++)
2583 // Get client and check that it is valid
2584 RemoteClient *client = i.getNode()->getValue();
2585 assert(client->peer_id == i.getNode()->getKey());
2586 if(client->serialization_version == SER_FMT_VER_INVALID)
2590 bool sender_selected = (peer_id == client->peer_id);
2591 if(sender_selected == true && send_to_sender == false)
2593 if(sender_selected == false && send_to_others == false)
2596 SendChatMessage(client->peer_id, line);
2602 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2603 "unknown command "<<command<<std::endl;
2607 catch(SendFailedException &e)
2609 derr_server<<"Server::ProcessData(): SendFailedException: "
2615 void Server::onMapEditEvent(MapEditEvent *event)
2617 dstream<<"Server::onMapEditEvent()"<<std::endl;
2618 if(m_ignore_map_edit_events)
2620 MapEditEvent *e = event->clone();
2621 m_unsent_map_edit_queue.push_back(e);
2624 core::list<PlayerInfo> Server::getPlayerInfo()
2626 DSTACK(__FUNCTION_NAME);
2627 JMutexAutoLock envlock(m_env_mutex);
2628 JMutexAutoLock conlock(m_con_mutex);
2630 core::list<PlayerInfo> list;
2632 core::list<Player*> players = m_env.getPlayers();
2634 core::list<Player*>::Iterator i;
2635 for(i = players.begin();
2636 i != players.end(); i++)
2640 Player *player = *i;
2643 con::Peer *peer = m_con.GetPeer(player->peer_id);
2644 // Copy info from peer to info struct
2646 info.address = peer->address;
2647 info.avg_rtt = peer->avg_rtt;
2649 catch(con::PeerNotFoundException &e)
2651 // Set dummy peer info
2653 info.address = Address(0,0,0,0,0);
2657 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2658 info.position = player->getPosition();
2660 list.push_back(info);
2667 void Server::peerAdded(con::Peer *peer)
2669 DSTACK(__FUNCTION_NAME);
2670 dout_server<<"Server::peerAdded(): peer->id="
2671 <<peer->id<<std::endl;
2674 c.type = PEER_ADDED;
2675 c.peer_id = peer->id;
2677 m_peer_change_queue.push_back(c);
2680 void Server::deletingPeer(con::Peer *peer, bool timeout)
2682 DSTACK(__FUNCTION_NAME);
2683 dout_server<<"Server::deletingPeer(): peer->id="
2684 <<peer->id<<", timeout="<<timeout<<std::endl;
2687 c.type = PEER_REMOVED;
2688 c.peer_id = peer->id;
2689 c.timeout = timeout;
2690 m_peer_change_queue.push_back(c);
2693 void Server::SendObjectData(float dtime)
2695 DSTACK(__FUNCTION_NAME);
2697 core::map<v3s16, bool> stepped_blocks;
2699 for(core::map<u16, RemoteClient*>::Iterator
2700 i = m_clients.getIterator();
2701 i.atEnd() == false; i++)
2703 u16 peer_id = i.getNode()->getKey();
2704 RemoteClient *client = i.getNode()->getValue();
2705 assert(client->peer_id == peer_id);
2707 if(client->serialization_version == SER_FMT_VER_INVALID)
2710 client->SendObjectData(this, dtime, stepped_blocks);
2714 void Server::SendPlayerInfos()
2716 DSTACK(__FUNCTION_NAME);
2718 //JMutexAutoLock envlock(m_env_mutex);
2720 // Get connected players
2721 core::list<Player*> players = m_env.getPlayers(true);
2723 u32 player_count = players.getSize();
2724 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2726 SharedBuffer<u8> data(datasize);
2727 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2730 core::list<Player*>::Iterator i;
2731 for(i = players.begin();
2732 i != players.end(); i++)
2734 Player *player = *i;
2736 /*dstream<<"Server sending player info for player with "
2737 "peer_id="<<player->peer_id<<std::endl;*/
2739 writeU16(&data[start], player->peer_id);
2740 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2741 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2742 start += 2+PLAYERNAME_SIZE;
2745 //JMutexAutoLock conlock(m_con_mutex);
2748 m_con.SendToAll(0, data, true);
2752 Craft checking system
2770 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2776 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2782 enum ItemSpecType type;
2783 // Only other one of these is used
2789 items: a pointer to an array of 9 pointers to items
2790 specs: a pointer to an array of 9 ItemSpecs
2792 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2794 u16 items_min_x = 100;
2795 u16 items_max_x = 100;
2796 u16 items_min_y = 100;
2797 u16 items_max_y = 100;
2798 for(u16 y=0; y<3; y++)
2799 for(u16 x=0; x<3; x++)
2801 if(items[y*3 + x] == NULL)
2803 if(items_min_x == 100 || x < items_min_x)
2805 if(items_min_y == 100 || y < items_min_y)
2807 if(items_max_x == 100 || x > items_max_x)
2809 if(items_max_y == 100 || y > items_max_y)
2812 // No items at all, just return false
2813 if(items_min_x == 100)
2816 u16 items_w = items_max_x - items_min_x + 1;
2817 u16 items_h = items_max_y - items_min_y + 1;
2819 u16 specs_min_x = 100;
2820 u16 specs_max_x = 100;
2821 u16 specs_min_y = 100;
2822 u16 specs_max_y = 100;
2823 for(u16 y=0; y<3; y++)
2824 for(u16 x=0; x<3; x++)
2826 if(specs[y*3 + x].type == ITEM_NONE)
2828 if(specs_min_x == 100 || x < specs_min_x)
2830 if(specs_min_y == 100 || y < specs_min_y)
2832 if(specs_max_x == 100 || x > specs_max_x)
2834 if(specs_max_y == 100 || y > specs_max_y)
2837 // No specs at all, just return false
2838 if(specs_min_x == 100)
2841 u16 specs_w = specs_max_x - specs_min_x + 1;
2842 u16 specs_h = specs_max_y - specs_min_y + 1;
2845 if(items_w != specs_w || items_h != specs_h)
2848 for(u16 y=0; y<specs_h; y++)
2849 for(u16 x=0; x<specs_w; x++)
2851 u16 items_x = items_min_x + x;
2852 u16 items_y = items_min_y + y;
2853 u16 specs_x = specs_min_x + x;
2854 u16 specs_y = specs_min_y + y;
2855 InventoryItem *item = items[items_y * 3 + items_x];
2856 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2858 if(spec.type == ITEM_NONE)
2860 // Has to be no item
2866 // There should be an item
2870 std::string itemname = item->getName();
2872 if(spec.type == ITEM_MATERIAL)
2874 if(itemname != "MaterialItem")
2876 MaterialItem *mitem = (MaterialItem*)item;
2877 if(mitem->getMaterial() != spec.num)
2880 else if(spec.type == ITEM_CRAFT)
2882 if(itemname != "CraftItem")
2884 CraftItem *mitem = (CraftItem*)item;
2885 if(mitem->getSubName() != spec.name)
2888 else if(spec.type == ITEM_TOOL)
2890 // Not supported yet
2893 else if(spec.type == ITEM_MBO)
2895 // Not supported yet
2900 // Not supported yet
2908 void Server::SendInventory(u16 peer_id)
2910 DSTACK(__FUNCTION_NAME);
2912 Player* player = m_env.getPlayer(peer_id);
2915 Calculate crafting stuff
2917 if(g_settings.getBool("creative_mode") == false)
2919 InventoryList *clist = player->inventory.getList("craft");
2920 InventoryList *rlist = player->inventory.getList("craftresult");
2923 rlist->clearItems();
2927 InventoryItem *items[9];
2928 for(u16 i=0; i<9; i++)
2930 items[i] = clist->getItem(i);
2939 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2940 if(checkItemCombination(items, specs))
2942 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2951 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2952 if(checkItemCombination(items, specs))
2954 rlist->addItem(new CraftItem("Stick", 4));
2963 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2964 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2965 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2966 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2967 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2968 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2969 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2970 if(checkItemCombination(items, specs))
2972 rlist->addItem(new MapBlockObjectItem("Sign"));
2981 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2982 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2983 if(checkItemCombination(items, specs))
2985 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2994 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2995 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2996 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2997 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2998 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2999 if(checkItemCombination(items, specs))
3001 rlist->addItem(new ToolItem("WPick", 0));
3010 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3011 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3012 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3013 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3014 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3015 if(checkItemCombination(items, specs))
3017 rlist->addItem(new ToolItem("STPick", 0));
3026 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3027 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3028 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3029 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3030 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3031 if(checkItemCombination(items, specs))
3033 rlist->addItem(new ToolItem("MesePick", 0));
3038 } // if creative_mode == false
3044 std::ostringstream os;
3045 //os.imbue(std::locale("C"));
3047 player->inventory.serialize(os);
3049 std::string s = os.str();
3051 SharedBuffer<u8> data(s.size()+2);
3052 writeU16(&data[0], TOCLIENT_INVENTORY);
3053 memcpy(&data[2], s.c_str(), s.size());
3056 m_con.Send(peer_id, 0, data, true);
3059 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3061 DSTACK(__FUNCTION_NAME);
3063 std::ostringstream os(std::ios_base::binary);
3067 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3068 os.write((char*)buf, 2);
3071 writeU16(buf, message.size());
3072 os.write((char*)buf, 2);
3075 for(u32 i=0; i<message.size(); i++)
3079 os.write((char*)buf, 2);
3083 std::string s = os.str();
3084 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3086 m_con.Send(peer_id, 0, data, true);
3089 void Server::BroadcastChatMessage(const std::wstring &message)
3091 for(core::map<u16, RemoteClient*>::Iterator
3092 i = m_clients.getIterator();
3093 i.atEnd() == false; i++)
3095 // Get client and check that it is valid
3096 RemoteClient *client = i.getNode()->getValue();
3097 assert(client->peer_id == i.getNode()->getKey());
3098 if(client->serialization_version == SER_FMT_VER_INVALID)
3101 SendChatMessage(client->peer_id, message);
3105 void Server::sendRemoveNode(v3s16 p, u16 ignore_id)
3109 SharedBuffer<u8> reply(replysize);
3110 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3111 writeS16(&reply[2], p.X);
3112 writeS16(&reply[4], p.Y);
3113 writeS16(&reply[6], p.Z);
3115 for(core::map<u16, RemoteClient*>::Iterator
3116 i = m_clients.getIterator();
3117 i.atEnd() == false; i++)
3119 // Get client and check that it is valid
3120 RemoteClient *client = i.getNode()->getValue();
3121 assert(client->peer_id == i.getNode()->getKey());
3122 if(client->serialization_version == SER_FMT_VER_INVALID)
3125 // Don't send if it's the same one
3126 if(client->peer_id == ignore_id)
3130 m_con.Send(client->peer_id, 0, reply, true);
3134 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id)
3136 for(core::map<u16, RemoteClient*>::Iterator
3137 i = m_clients.getIterator();
3138 i.atEnd() == false; i++)
3140 // Get client and check that it is valid
3141 RemoteClient *client = i.getNode()->getValue();
3142 assert(client->peer_id == i.getNode()->getKey());
3143 if(client->serialization_version == SER_FMT_VER_INVALID)
3146 // Don't send if it's the same one
3147 if(client->peer_id == ignore_id)
3151 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3152 SharedBuffer<u8> reply(replysize);
3153 writeU16(&reply[0], TOCLIENT_ADDNODE);
3154 writeS16(&reply[2], p.X);
3155 writeS16(&reply[4], p.Y);
3156 writeS16(&reply[6], p.Z);
3157 n.serialize(&reply[8], client->serialization_version);
3160 m_con.Send(client->peer_id, 0, reply, true);
3164 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3166 DSTACK(__FUNCTION_NAME);
3168 Create a packet with the block in the right format
3171 std::ostringstream os(std::ios_base::binary);
3172 block->serialize(os, ver);
3173 std::string s = os.str();
3174 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3176 u32 replysize = 8 + blockdata.getSize();
3177 SharedBuffer<u8> reply(replysize);
3178 v3s16 p = block->getPos();
3179 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3180 writeS16(&reply[2], p.X);
3181 writeS16(&reply[4], p.Y);
3182 writeS16(&reply[6], p.Z);
3183 memcpy(&reply[8], *blockdata, blockdata.getSize());
3185 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3186 <<": \tpacket size: "<<replysize<<std::endl;*/
3191 m_con.Send(peer_id, 1, reply, true);
3194 void Server::SendBlocks(float dtime)
3196 DSTACK(__FUNCTION_NAME);
3198 JMutexAutoLock envlock(m_env_mutex);
3199 JMutexAutoLock conlock(m_con_mutex);
3201 //TimeTaker timer("Server::SendBlocks");
3203 core::array<PrioritySortedBlockTransfer> queue;
3205 s32 total_sending = 0;
3207 for(core::map<u16, RemoteClient*>::Iterator
3208 i = m_clients.getIterator();
3209 i.atEnd() == false; i++)
3211 RemoteClient *client = i.getNode()->getValue();
3212 assert(client->peer_id == i.getNode()->getKey());
3214 total_sending += client->SendingCount();
3216 if(client->serialization_version == SER_FMT_VER_INVALID)
3219 client->GetNextBlocks(this, dtime, queue);
3223 // Lowest priority number comes first.
3224 // Lowest is most important.
3227 for(u32 i=0; i<queue.size(); i++)
3229 //TODO: Calculate limit dynamically
3230 if(total_sending >= g_settings.getS32
3231 ("max_simultaneous_block_sends_server_total"))
3234 PrioritySortedBlockTransfer q = queue[i];
3236 MapBlock *block = NULL;
3239 block = m_env.getMap().getBlockNoCreate(q.pos);
3241 catch(InvalidPositionException &e)
3246 RemoteClient *client = getClient(q.peer_id);
3248 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3250 client->SentBlock(q.pos);
3257 RemoteClient* Server::getClient(u16 peer_id)
3259 DSTACK(__FUNCTION_NAME);
3260 //JMutexAutoLock lock(m_con_mutex);
3261 core::map<u16, RemoteClient*>::Node *n;
3262 n = m_clients.find(peer_id);
3263 // A client should exist for all peers
3265 return n->getValue();
3268 std::wstring Server::getStatusString()
3270 std::wostringstream os(std::ios_base::binary);
3273 os<<L"uptime="<<m_uptime.get();
3274 // Information about clients
3276 for(core::map<u16, RemoteClient*>::Iterator
3277 i = m_clients.getIterator();
3278 i.atEnd() == false; i++)
3280 // Get client and check that it is valid
3281 RemoteClient *client = i.getNode()->getValue();
3282 assert(client->peer_id == i.getNode()->getKey());
3283 if(client->serialization_version == SER_FMT_VER_INVALID)
3286 Player *player = m_env.getPlayer(client->peer_id);
3287 // Get name of player
3288 std::wstring name = L"unknown";
3290 name = narrow_to_wide(player->getName());
3291 // Add name to information string
3295 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3296 os<<" WARNING: Map saving is disabled."<<std::endl;
3301 void setCreativeInventory(Player *player)
3303 player->resetInventory();
3305 // Give some good picks
3307 InventoryItem *item = new ToolItem("STPick", 0);
3308 void* r = player->inventory.addItem("main", item);
3312 InventoryItem *item = new ToolItem("MesePick", 0);
3313 void* r = player->inventory.addItem("main", item);
3320 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3323 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3324 player->inventory.addItem("main", item);
3327 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3329 // Skip some materials
3330 if(i == CONTENT_WATER || i == CONTENT_TORCH
3331 || i == CONTENT_COALSTONE)
3334 InventoryItem *item = new MaterialItem(i, 1);
3335 player->inventory.addItem("main", item);
3339 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3340 void* r = player->inventory.addItem("main", item);
3345 Player *Server::emergePlayer(const char *name, const char *password,
3349 Try to get an existing player
3351 Player *player = m_env.getPlayer(name);
3354 // If player is already connected, cancel
3355 if(player->peer_id != 0)
3357 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3362 player->peer_id = peer_id;
3364 // Reset inventory to creative if in creative mode
3365 if(g_settings.getBool("creative_mode"))
3367 setCreativeInventory(player);
3374 If player with the wanted peer_id already exists, cancel.
3376 if(m_env.getPlayer(peer_id) != NULL)
3378 dstream<<"emergePlayer(): Player with wrong name but same"
3379 " peer_id already exists"<<std::endl;
3387 player = new ServerRemotePlayer();
3388 //player->peer_id = c.peer_id;
3389 //player->peer_id = PEER_ID_INEXISTENT;
3390 player->peer_id = peer_id;
3391 player->updateName(name);
3397 dstream<<"Server: Finding spawn place for player \""
3398 <<player->getName()<<"\""<<std::endl;
3402 player->setPosition(intToFloat(v3s16(
3409 s16 groundheight = 0;
3411 // Try to find a good place a few times
3412 for(s32 i=0; i<500; i++)
3415 // We're going to try to throw the player to this position
3416 nodepos = v2s16(-range + (myrand()%(range*2)),
3417 -range + (myrand()%(range*2)));
3418 v2s16 sectorpos = getNodeSectorPos(nodepos);
3420 Ignore position if it is near a chunk edge.
3421 Otherwise it would cause excessive loading time at
3425 if(m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(1,1))
3426 != m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(-1,-1)))
3430 m_env.getMap().emergeSector(sectorpos);
3431 // Get ground height at point
3432 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3433 // Don't go underwater
3434 if(groundheight < WATER_LEVEL)
3436 //dstream<<"-> Underwater"<<std::endl;
3439 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3440 // Get block at point
3442 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3443 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3444 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3445 // Don't go inside ground
3447 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3448 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3449 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3450 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3451 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3452 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3454 dstream<<"-> Inside ground"<<std::endl;
3458 }catch(InvalidPositionException &e)
3460 dstream<<"-> Invalid position"<<std::endl;
3461 // Ignore invalid position
3465 // Found a good place
3466 dstream<<"Searched through "<<i<<" places."<<std::endl;
3471 // If no suitable place was not found, go above water at least.
3472 if(groundheight < WATER_LEVEL)
3473 groundheight = WATER_LEVEL;
3475 player->setPosition(intToFloat(v3s16(
3483 Add player to environment
3486 m_env.addPlayer(player);
3489 Add stuff to inventory
3492 if(g_settings.getBool("creative_mode"))
3494 setCreativeInventory(player);
3499 InventoryItem *item = new ToolItem("WPick", 32000);
3500 void* r = player->inventory.addItem("main", item);
3504 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3505 void* r = player->inventory.addItem("main", item);
3509 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3510 void* r = player->inventory.addItem("main", item);
3514 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3515 void* r = player->inventory.addItem("main", item);
3519 InventoryItem *item = new CraftItem("Stick", 4);
3520 void* r = player->inventory.addItem("main", item);
3524 InventoryItem *item = new ToolItem("WPick", 32000);
3525 void* r = player->inventory.addItem("main", item);
3529 InventoryItem *item = new ToolItem("STPick", 32000);
3530 void* r = player->inventory.addItem("main", item);
3533 /*// Give some lights
3535 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3536 bool r = player->inventory.addItem("main", item);
3540 for(u16 i=0; i<4; i++)
3542 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3543 bool r = player->inventory.addItem("main", item);
3546 /*// Give some other stuff
3548 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3549 bool r = player->inventory.addItem("main", item);
3556 } // create new player
3560 void Server::UpdateBlockWaterPressure(MapBlock *block,
3561 core::map<v3s16, MapBlock*> &modified_blocks)
3563 MapVoxelManipulator v(&m_env.getMap());
3564 v.m_disable_water_climb =
3565 g_settings.getBool("disable_water_climb");
3567 VoxelArea area(block->getPosRelative(),
3568 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3572 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3574 catch(ProcessingLimitException &e)
3576 dstream<<"Processing limit reached (1)"<<std::endl;
3579 v.blitBack(modified_blocks);
3583 void Server::handlePeerChange(PeerChange &c)
3585 JMutexAutoLock envlock(m_env_mutex);
3586 JMutexAutoLock conlock(m_con_mutex);
3588 if(c.type == PEER_ADDED)
3595 core::map<u16, RemoteClient*>::Node *n;
3596 n = m_clients.find(c.peer_id);
3597 // The client shouldn't already exist
3601 RemoteClient *client = new RemoteClient();
3602 client->peer_id = c.peer_id;
3603 m_clients.insert(client->peer_id, client);
3606 else if(c.type == PEER_REMOVED)
3613 core::map<u16, RemoteClient*>::Node *n;
3614 n = m_clients.find(c.peer_id);
3615 // The client should exist
3618 // Collect information about leaving in chat
3619 std::wstring message;
3621 std::wstring name = L"unknown";
3622 Player *player = m_env.getPlayer(c.peer_id);
3624 name = narrow_to_wide(player->getName());
3628 message += L" left game";
3630 message += L" (timed out)";
3635 m_env.removePlayer(c.peer_id);
3638 // Set player client disconnected
3640 Player *player = m_env.getPlayer(c.peer_id);
3642 player->peer_id = 0;
3646 delete m_clients[c.peer_id];
3647 m_clients.remove(c.peer_id);
3649 // Send player info to all remaining clients
3652 // Send leave chat message to all remaining clients
3653 BroadcastChatMessage(message);
3662 void Server::handlePeerChanges()
3664 while(m_peer_change_queue.size() > 0)
3666 PeerChange c = m_peer_change_queue.pop_front();
3668 dout_server<<"Server: Handling peer change: "
3669 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3672 handlePeerChange(c);
3676 void dedicated_server_loop(Server &server, bool &kill)
3678 DSTACK(__FUNCTION_NAME);
3680 std::cout<<DTIME<<std::endl;
3681 std::cout<<"========================"<<std::endl;
3682 std::cout<<"Running dedicated server"<<std::endl;
3683 std::cout<<"========================"<<std::endl;
3684 std::cout<<std::endl;
3688 // This is kind of a hack but can be done like this
3689 // because server.step() is very light
3693 if(server.getShutdownRequested() || kill)
3695 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
3699 static int counter = 0;
3705 core::list<PlayerInfo> list = server.getPlayerInfo();
3706 core::list<PlayerInfo>::Iterator i;
3707 static u32 sum_old = 0;
3708 u32 sum = PIChecksum(list);
3711 std::cout<<DTIME<<"Player info:"<<std::endl;
3712 for(i=list.begin(); i!=list.end(); i++)
3714 i->PrintLine(&std::cout);