3 Copyright (C) 2010-2011 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 #include "servercommand.h"
34 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
36 void * ServerThread::Thread()
40 DSTACK(__FUNCTION_NAME);
42 BEGIN_DEBUG_EXCEPTION_HANDLER
47 //TimeTaker timer("AsyncRunStep() + Receive()");
50 //TimeTaker timer("AsyncRunStep()");
51 m_server->AsyncRunStep();
54 //dout_server<<"Running m_server->Receive()"<<std::endl;
57 catch(con::NoIncomingDataException &e)
60 catch(con::PeerNotFoundException &e)
62 dout_server<<"Server: PeerNotFoundException"<<std::endl;
66 END_DEBUG_EXCEPTION_HANDLER
71 void * EmergeThread::Thread()
75 DSTACK(__FUNCTION_NAME);
79 BEGIN_DEBUG_EXCEPTION_HANDLER
82 Get block info from queue, emerge them and send them
85 After queue is empty, exit.
89 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
93 SharedPtr<QueuedBlockEmerge> q(qptr);
99 Do not generate over-limit
101 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
102 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
103 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
104 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
105 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
106 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
109 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
111 //TimeTaker timer("block emerge");
114 Try to emerge it from somewhere.
116 If it is only wanted as optional, only loading from disk
121 Check if any peer wants it as non-optional. In that case it
124 Also decrement the emerge queue count in clients.
127 bool optional = true;
130 core::map<u16, u8>::Iterator i;
131 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
133 //u16 peer_id = i.getNode()->getKey();
136 u8 flags = i.getNode()->getValue();
137 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
143 /*dstream<<"EmergeThread: p="
144 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
145 <<"optional="<<optional<<std::endl;*/
147 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
149 core::map<v3s16, MapBlock*> changed_blocks;
150 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
152 MapBlock *block = NULL;
153 bool got_block = true;
154 core::map<v3s16, MapBlock*> modified_blocks;
156 bool only_from_disk = false;
159 only_from_disk = true;
161 v2s16 chunkpos = map.sector_to_chunk(p2d);
163 bool generate_chunk = false;
164 if(only_from_disk == false)
166 JMutexAutoLock envlock(m_server->m_env_mutex);
167 if(map.chunkNonVolatile(chunkpos) == false)
168 generate_chunk = true;
175 JMutexAutoLock envlock(m_server->m_env_mutex);
176 map.initChunkMake(data, chunkpos);
182 JMutexAutoLock envlock(m_server->m_env_mutex);
183 map.finishChunkMake(data, changed_blocks);
188 Fetch block from map or generate a single block
191 JMutexAutoLock envlock(m_server->m_env_mutex);
193 // Load sector if it isn't loaded
194 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
195 map.loadSectorFull(p2d);
197 block = map.getBlockNoCreateNoEx(p);
198 if(!block || block->isDummy())
206 // Get, load or create sector
207 ServerMapSector *sector =
208 (ServerMapSector*)map.createSector(p2d);
210 block = map.generateBlock(p, block, sector, changed_blocks,
211 lighting_invalidated_blocks);
218 if(block->getLightingExpired()){
219 lighting_invalidated_blocks[block->getPos()] = block;
223 // TODO: Some additional checking and lighting updating,
228 JMutexAutoLock envlock(m_server->m_env_mutex);
233 Collect a list of blocks that have been modified in
234 addition to the fetched one.
237 if(lighting_invalidated_blocks.size() > 0)
239 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
240 <<" blocks"<<std::endl;*/
242 // 50-100ms for single block generation
243 //TimeTaker timer("** EmergeThread updateLighting");
245 // Update lighting without locking the environment mutex,
246 // add modified blocks to changed blocks
247 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
250 // Add all from changed_blocks to modified_blocks
251 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
252 i.atEnd() == false; i++)
254 MapBlock *block = i.getNode()->getValue();
255 modified_blocks.insert(block->getPos(), block);
258 // If we got no block, there should be no invalidated blocks
261 assert(lighting_invalidated_blocks.size() == 0);
267 Set sent status of modified blocks on clients
270 // NOTE: Server's clients are also behind the connection mutex
271 JMutexAutoLock lock(m_server->m_con_mutex);
274 Add the originally fetched block to the modified list
278 modified_blocks.insert(p, block);
282 Set the modified blocks unsent for all the clients
285 for(core::map<u16, RemoteClient*>::Iterator
286 i = m_server->m_clients.getIterator();
287 i.atEnd() == false; i++)
289 RemoteClient *client = i.getNode()->getValue();
291 if(modified_blocks.size() > 0)
293 // Remove block from sent history
294 client->SetBlocksNotSent(modified_blocks);
300 END_DEBUG_EXCEPTION_HANDLER
305 void RemoteClient::GetNextBlocks(Server *server, float dtime,
306 core::array<PrioritySortedBlockTransfer> &dest)
308 DSTACK(__FUNCTION_NAME);
311 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
314 m_nearest_unsent_reset_timer += dtime;
315 m_nothing_to_send_pause_timer -= dtime;
317 if(m_nothing_to_send_pause_timer >= 0)
320 // Won't send anything if already sending
321 if(m_blocks_sending.size() >= g_settings.getU16
322 ("max_simultaneous_block_sends_per_client"))
324 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
328 Player *player = server->m_env.getPlayer(peer_id);
330 assert(player != NULL);
332 v3f playerpos = player->getPosition();
333 v3f playerspeed = player->getSpeed();
335 v3s16 center_nodepos = floatToInt(playerpos, BS);
337 v3s16 center = getNodeBlockPos(center_nodepos);
339 // Camera position and direction
341 playerpos + v3f(0, BS+BS/2, 0);
342 v3f camera_dir = v3f(0,0,1);
343 camera_dir.rotateYZBy(player->getPitch());
344 camera_dir.rotateXZBy(player->getYaw());
347 Get the starting value of the block finder radius.
350 if(m_last_center != center)
352 m_nearest_unsent_d = 0;
353 m_last_center = center;
356 /*dstream<<"m_nearest_unsent_reset_timer="
357 <<m_nearest_unsent_reset_timer<<std::endl;*/
358 if(m_nearest_unsent_reset_timer > 5.0)
360 m_nearest_unsent_reset_timer = 0;
361 m_nearest_unsent_d = 0;
362 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
365 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
366 s16 d_start = m_nearest_unsent_d;
368 //dstream<<"d_start="<<d_start<<std::endl;
370 u16 max_simul_sends_setting = g_settings.getU16
371 ("max_simultaneous_block_sends_per_client");
372 u16 max_simul_sends_usually = max_simul_sends_setting;
375 Check the time from last addNode/removeNode.
377 Decrease send rate if player is building stuff.
379 m_time_from_building += dtime;
380 if(m_time_from_building < g_settings.getFloat(
381 "full_block_send_enable_min_time_from_building"))
383 max_simul_sends_usually
384 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
388 Number of blocks sending + number of blocks selected for sending
390 u32 num_blocks_selected = m_blocks_sending.size();
393 next time d will be continued from the d from which the nearest
394 unsent block was found this time.
396 This is because not necessarily any of the blocks found this
397 time are actually sent.
399 s32 new_nearest_unsent_d = -1;
401 s16 d_max = g_settings.getS16("max_block_send_distance");
402 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
404 //dstream<<"Starting from "<<d_start<<std::endl;
406 bool sending_something = false;
408 for(s16 d = d_start; d <= d_max; d++)
410 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
413 If m_nearest_unsent_d was changed by the EmergeThread
414 (it can change it to 0 through SetBlockNotSent),
416 Else update m_nearest_unsent_d
418 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
420 d = m_nearest_unsent_d;
421 last_nearest_unsent_d = m_nearest_unsent_d;
425 Get the border/face dot coordinates of a "d-radiused"
428 core::list<v3s16> list;
429 getFacePositions(list, d);
431 core::list<v3s16>::Iterator li;
432 for(li=list.begin(); li!=list.end(); li++)
434 v3s16 p = *li + center;
438 - Don't allow too many simultaneous transfers
439 - EXCEPT when the blocks are very close
441 Also, don't send blocks that are already flying.
444 // Start with the usual maximum
445 u16 max_simul_dynamic = max_simul_sends_usually;
447 // If block is very close, allow full maximum
448 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
449 max_simul_dynamic = max_simul_sends_setting;
451 // Don't select too many blocks for sending
452 if(num_blocks_selected >= max_simul_dynamic)
455 // Don't send blocks that are currently being transferred
456 if(m_blocks_sending.find(p) != NULL)
462 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
463 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
464 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
465 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
466 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
467 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
470 // If this is true, inexistent block will be made from scratch
471 bool generate = d <= d_max_gen;
474 /*// Limit the generating area vertically to 2/3
475 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
478 // Limit the send area vertically to 2/3
479 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
485 If block is far away, don't generate it unless it is
491 // Block center y in nodes
492 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
493 // Don't generate if it's very high or very low
494 if(y < -64 || y > 64)
498 v2s16 p2d_nodes_center(
502 // Get ground height in nodes
503 s16 gh = server->m_env.getServerMap().findGroundLevel(
506 // If differs a lot, don't generate
507 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
509 // Actually, don't even send it
516 Don't generate or send if not in sight
519 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
525 Don't send already sent blocks
528 if(m_blocks_sent.find(p) != NULL)
533 Check if map has this block
535 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
537 bool surely_not_found_on_disk = false;
538 bool block_is_invalid = false;
543 surely_not_found_on_disk = true;
546 if(block->isValid() == false)
548 block_is_invalid = true;
551 /*if(block->isFullyGenerated() == false)
553 block_is_invalid = true;
557 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
558 v2s16 chunkpos = map->sector_to_chunk(p2d);
559 if(map->chunkNonVolatile(chunkpos) == false)
560 block_is_invalid = true;
563 If block is not close, don't send it unless it is near
566 Block is not near ground level if night-time mesh
567 doesn't differ from day-time mesh.
571 if(block->dayNightDiffed() == false)
578 If block has been marked to not exist on disk (dummy)
579 and generating new ones is not wanted, skip block.
581 if(generate == false && surely_not_found_on_disk == true)
588 Record the lowest d from which a a block has been
589 found being not sent and possibly to exist
591 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
594 new_nearest_unsent_d = d;
598 Add inexistent block to emerge queue.
600 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
602 //TODO: Get value from somewhere
603 // Allow only one block in emerge queue
604 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
605 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
607 //dstream<<"Adding block to emerge queue"<<std::endl;
609 // Add it to the emerge queue and trigger the thread
612 if(generate == false)
613 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
615 server->m_emerge_queue.addBlock(peer_id, p, flags);
616 server->m_emergethread.trigger();
624 Add block to send queue
627 PrioritySortedBlockTransfer q((float)d, p, peer_id);
631 num_blocks_selected += 1;
632 sending_something = true;
637 if(new_nearest_unsent_d != -1)
639 m_nearest_unsent_d = new_nearest_unsent_d;
642 if(sending_something == false)
644 m_nothing_to_send_counter++;
645 if(m_nothing_to_send_counter >= 3)
647 // Pause time in seconds
648 m_nothing_to_send_pause_timer = 2.0;
653 m_nothing_to_send_counter = 0;
656 /*timer_result = timer.stop(true);
657 if(timer_result != 0)
658 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
661 void RemoteClient::SendObjectData(
664 core::map<v3s16, bool> &stepped_blocks
667 DSTACK(__FUNCTION_NAME);
669 // Can't send anything without knowing version
670 if(serialization_version == SER_FMT_VER_INVALID)
672 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
678 Send a TOCLIENT_OBJECTDATA packet.
682 u16 number of player positions
693 std::ostringstream os(std::ios_base::binary);
697 writeU16(buf, TOCLIENT_OBJECTDATA);
698 os.write((char*)buf, 2);
701 Get and write player data
704 // Get connected players
705 core::list<Player*> players = server->m_env.getPlayers(true);
707 // Write player count
708 u16 playercount = players.size();
709 writeU16(buf, playercount);
710 os.write((char*)buf, 2);
712 core::list<Player*>::Iterator i;
713 for(i = players.begin();
714 i != players.end(); i++)
718 v3f pf = player->getPosition();
719 v3f sf = player->getSpeed();
721 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
722 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
723 s32 pitch_i (player->getPitch() * 100);
724 s32 yaw_i (player->getYaw() * 100);
726 writeU16(buf, player->peer_id);
727 os.write((char*)buf, 2);
728 writeV3S32(buf, position_i);
729 os.write((char*)buf, 12);
730 writeV3S32(buf, speed_i);
731 os.write((char*)buf, 12);
732 writeS32(buf, pitch_i);
733 os.write((char*)buf, 4);
734 writeS32(buf, yaw_i);
735 os.write((char*)buf, 4);
739 Get and write object data
745 For making players to be able to build to their nearby
746 environment (building is not possible on blocks that are not
749 - Add blocks to emerge queue if they are not found
751 SUGGESTION: These could be ignored from the backside of the player
754 Player *player = server->m_env.getPlayer(peer_id);
758 v3f playerpos = player->getPosition();
759 v3f playerspeed = player->getSpeed();
761 v3s16 center_nodepos = floatToInt(playerpos, BS);
762 v3s16 center = getNodeBlockPos(center_nodepos);
764 s16 d_max = g_settings.getS16("active_object_range");
766 // Number of blocks whose objects were written to bos
769 std::ostringstream bos(std::ios_base::binary);
771 for(s16 d = 0; d <= d_max; d++)
773 core::list<v3s16> list;
774 getFacePositions(list, d);
776 core::list<v3s16>::Iterator li;
777 for(li=list.begin(); li!=list.end(); li++)
779 v3s16 p = *li + center;
782 Ignore blocks that haven't been sent to the client
785 if(m_blocks_sent.find(p) == NULL)
789 // Try stepping block and add it to a send queue
794 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
797 Step block if not in stepped_blocks and add to stepped_blocks.
799 if(stepped_blocks.find(p) == NULL)
801 block->stepObjects(dtime, true, server->getDayNightRatio());
802 stepped_blocks.insert(p, true);
803 block->setChangedFlag();
806 // Skip block if there are no objects
807 if(block->getObjectCount() == 0)
816 bos.write((char*)buf, 6);
819 block->serializeObjects(bos, serialization_version);
824 Stop collecting objects if data is already too big
826 // Sum of player and object data sizes
827 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
828 // break out if data too big
829 if(sum > MAX_OBJECTDATA_SIZE)
831 goto skip_subsequent;
835 catch(InvalidPositionException &e)
838 // Add it to the emerge queue and trigger the thread.
839 // Fetch the block only if it is on disk.
841 // Grab and increment counter
842 /*SharedPtr<JMutexAutoLock> lock
843 (m_num_blocks_in_emerge_queue.getLock());
844 m_num_blocks_in_emerge_queue.m_value++;*/
846 // Add to queue as an anonymous fetch from disk
847 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
848 server->m_emerge_queue.addBlock(0, p, flags);
849 server->m_emergethread.trigger();
857 writeU16(buf, blockcount);
858 os.write((char*)buf, 2);
860 // Write block objects
867 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
870 std::string s = os.str();
871 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
872 // Send as unreliable
873 server->m_con.Send(peer_id, 0, data, false);
876 void RemoteClient::GotBlock(v3s16 p)
878 if(m_blocks_sending.find(p) != NULL)
879 m_blocks_sending.remove(p);
882 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
883 " m_blocks_sending"<<std::endl;*/
884 m_excess_gotblocks++;
886 m_blocks_sent.insert(p, true);
889 void RemoteClient::SentBlock(v3s16 p)
891 if(m_blocks_sending.find(p) == NULL)
892 m_blocks_sending.insert(p, 0.0);
894 dstream<<"RemoteClient::SentBlock(): Sent block"
895 " already in m_blocks_sending"<<std::endl;
898 void RemoteClient::SetBlockNotSent(v3s16 p)
900 m_nearest_unsent_d = 0;
902 if(m_blocks_sending.find(p) != NULL)
903 m_blocks_sending.remove(p);
904 if(m_blocks_sent.find(p) != NULL)
905 m_blocks_sent.remove(p);
908 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
910 m_nearest_unsent_d = 0;
912 for(core::map<v3s16, MapBlock*>::Iterator
913 i = blocks.getIterator();
914 i.atEnd()==false; i++)
916 v3s16 p = i.getNode()->getKey();
918 if(m_blocks_sending.find(p) != NULL)
919 m_blocks_sending.remove(p);
920 if(m_blocks_sent.find(p) != NULL)
921 m_blocks_sent.remove(p);
929 PlayerInfo::PlayerInfo()
935 void PlayerInfo::PrintLine(std::ostream *s)
938 (*s)<<"\""<<name<<"\" ("
939 <<(position.X/10)<<","<<(position.Y/10)
940 <<","<<(position.Z/10)<<") ";
942 (*s)<<" avg_rtt="<<avg_rtt;
946 u32 PIChecksum(core::list<PlayerInfo> &l)
948 core::list<PlayerInfo>::Iterator i;
951 for(i=l.begin(); i!=l.end(); i++)
953 checksum += a * (i->id+1);
954 checksum ^= 0x435aafcd;
965 std::string mapsavedir
967 m_env(new ServerMap(mapsavedir), this),
968 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
970 m_emergethread(this),
973 m_time_of_day_send_timer(0),
975 m_mapsavedir(mapsavedir),
976 m_shutdown_requested(false),
977 m_ignore_map_edit_events(false),
978 m_ignore_map_edit_events_peer_id(0)
980 m_liquid_transform_timer = 0.0;
981 m_print_info_timer = 0.0;
982 m_objectdata_timer = 0.0;
983 m_emergethread_trigger_timer = 0.0;
984 m_savemap_timer = 0.0;
988 m_step_dtime_mutex.Init();
991 m_env.getMap().addEventReceiver(this);
994 m_env.deSerializePlayers(m_mapsavedir);
999 dstream<<"Server::~Server()"<<std::endl;
1002 Send shutdown message
1005 JMutexAutoLock conlock(m_con_mutex);
1007 std::wstring line = L"*** Server shutting down";
1010 Send the message to clients
1012 for(core::map<u16, RemoteClient*>::Iterator
1013 i = m_clients.getIterator();
1014 i.atEnd() == false; i++)
1016 // Get client and check that it is valid
1017 RemoteClient *client = i.getNode()->getValue();
1018 assert(client->peer_id == i.getNode()->getKey());
1019 if(client->serialization_version == SER_FMT_VER_INVALID)
1023 SendChatMessage(client->peer_id, line);
1025 catch(con::PeerNotFoundException &e)
1033 dstream<<"Server: Saving players"<<std::endl;
1034 m_env.serializePlayers(m_mapsavedir);
1045 JMutexAutoLock clientslock(m_con_mutex);
1047 for(core::map<u16, RemoteClient*>::Iterator
1048 i = m_clients.getIterator();
1049 i.atEnd() == false; i++)
1052 // NOTE: These are removed by env destructor
1054 u16 peer_id = i.getNode()->getKey();
1055 JMutexAutoLock envlock(m_env_mutex);
1056 m_env.removePlayer(peer_id);
1060 delete i.getNode()->getValue();
1065 void Server::start(unsigned short port)
1067 DSTACK(__FUNCTION_NAME);
1068 // Stop thread if already running
1071 // Initialize connection
1072 m_con.setTimeoutMs(30);
1076 m_thread.setRun(true);
1079 dout_server<<"Server: Started on port "<<port<<std::endl;
1084 DSTACK(__FUNCTION_NAME);
1086 // Stop threads (set run=false first so both start stopping)
1087 m_thread.setRun(false);
1088 m_emergethread.setRun(false);
1090 m_emergethread.stop();
1092 dout_server<<"Server: Threads stopped"<<std::endl;
1095 void Server::step(float dtime)
1097 DSTACK(__FUNCTION_NAME);
1102 JMutexAutoLock lock(m_step_dtime_mutex);
1103 m_step_dtime += dtime;
1107 void Server::AsyncRunStep()
1109 DSTACK(__FUNCTION_NAME);
1113 JMutexAutoLock lock1(m_step_dtime_mutex);
1114 dtime = m_step_dtime;
1117 // Send blocks to clients
1123 //dstream<<"Server steps "<<dtime<<std::endl;
1124 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1127 JMutexAutoLock lock1(m_step_dtime_mutex);
1128 m_step_dtime -= dtime;
1135 m_uptime.set(m_uptime.get() + dtime);
1139 Update m_time_of_day
1142 m_time_counter += dtime;
1143 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1144 u32 units = (u32)(m_time_counter*speed);
1145 m_time_counter -= (f32)units / speed;
1146 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1148 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1151 Send to clients at constant intervals
1154 m_time_of_day_send_timer -= dtime;
1155 if(m_time_of_day_send_timer < 0.0)
1157 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1159 //JMutexAutoLock envlock(m_env_mutex);
1160 JMutexAutoLock conlock(m_con_mutex);
1162 for(core::map<u16, RemoteClient*>::Iterator
1163 i = m_clients.getIterator();
1164 i.atEnd() == false; i++)
1166 RemoteClient *client = i.getNode()->getValue();
1167 //Player *player = m_env.getPlayer(client->peer_id);
1169 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1170 m_time_of_day.get());
1172 m_con.Send(client->peer_id, 0, data, true);
1178 // Process connection's timeouts
1179 JMutexAutoLock lock2(m_con_mutex);
1180 m_con.RunTimeouts(dtime);
1184 // This has to be called so that the client list gets synced
1185 // with the peer list of the connection
1186 handlePeerChanges();
1191 // This also runs Map's timers
1192 JMutexAutoLock lock(m_env_mutex);
1203 m_liquid_transform_timer += dtime;
1204 if(m_liquid_transform_timer >= 1.00)
1206 m_liquid_transform_timer -= 1.00;
1208 JMutexAutoLock lock(m_env_mutex);
1210 core::map<v3s16, MapBlock*> modified_blocks;
1211 m_env.getMap().transformLiquids(modified_blocks);
1216 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1217 ServerMap &map = ((ServerMap&)m_env.getMap());
1218 map.updateLighting(modified_blocks, lighting_modified_blocks);
1220 // Add blocks modified by lighting to modified_blocks
1221 for(core::map<v3s16, MapBlock*>::Iterator
1222 i = lighting_modified_blocks.getIterator();
1223 i.atEnd() == false; i++)
1225 MapBlock *block = i.getNode()->getValue();
1226 modified_blocks.insert(block->getPos(), block);
1230 Set the modified blocks unsent for all the clients
1233 JMutexAutoLock lock2(m_con_mutex);
1235 for(core::map<u16, RemoteClient*>::Iterator
1236 i = m_clients.getIterator();
1237 i.atEnd() == false; i++)
1239 RemoteClient *client = i.getNode()->getValue();
1241 if(modified_blocks.size() > 0)
1243 // Remove block from sent history
1244 client->SetBlocksNotSent(modified_blocks);
1249 // Periodically print some info
1251 float &counter = m_print_info_timer;
1257 JMutexAutoLock lock2(m_con_mutex);
1259 for(core::map<u16, RemoteClient*>::Iterator
1260 i = m_clients.getIterator();
1261 i.atEnd() == false; i++)
1263 //u16 peer_id = i.getNode()->getKey();
1264 RemoteClient *client = i.getNode()->getValue();
1265 Player *player = m_env.getPlayer(client->peer_id);
1268 std::cout<<player->getName()<<"\t";
1269 client->PrintInfo(std::cout);
1274 //if(g_settings.getBool("enable_experimental"))
1278 Check added and deleted active objects
1281 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1283 JMutexAutoLock envlock(m_env_mutex);
1284 JMutexAutoLock conlock(m_con_mutex);
1286 // Radius inside which objects are active
1289 for(core::map<u16, RemoteClient*>::Iterator
1290 i = m_clients.getIterator();
1291 i.atEnd() == false; i++)
1293 RemoteClient *client = i.getNode()->getValue();
1294 Player *player = m_env.getPlayer(client->peer_id);
1297 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1298 <<" has no associated player"<<std::endl;
1301 v3s16 pos = floatToInt(player->getPosition(), BS);
1303 core::map<u16, bool> removed_objects;
1304 core::map<u16, bool> added_objects;
1305 m_env.getRemovedActiveObjects(pos, radius,
1306 client->m_known_objects, removed_objects);
1307 m_env.getAddedActiveObjects(pos, radius,
1308 client->m_known_objects, added_objects);
1310 // Ignore if nothing happened
1311 if(removed_objects.size() == 0 && added_objects.size() == 0)
1313 //dstream<<"INFO: active objects: none changed"<<std::endl;
1317 std::string data_buffer;
1321 // Handle removed objects
1322 writeU16((u8*)buf, removed_objects.size());
1323 data_buffer.append(buf, 2);
1324 for(core::map<u16, bool>::Iterator
1325 i = removed_objects.getIterator();
1326 i.atEnd()==false; i++)
1329 u16 id = i.getNode()->getKey();
1330 ServerActiveObject* obj = m_env.getActiveObject(id);
1332 // Add to data buffer for sending
1333 writeU16((u8*)buf, i.getNode()->getKey());
1334 data_buffer.append(buf, 2);
1336 // Remove from known objects
1337 client->m_known_objects.remove(i.getNode()->getKey());
1339 if(obj && obj->m_known_by_count > 0)
1340 obj->m_known_by_count--;
1343 // Handle added objects
1344 writeU16((u8*)buf, added_objects.size());
1345 data_buffer.append(buf, 2);
1346 for(core::map<u16, bool>::Iterator
1347 i = added_objects.getIterator();
1348 i.atEnd()==false; i++)
1351 u16 id = i.getNode()->getKey();
1352 ServerActiveObject* obj = m_env.getActiveObject(id);
1355 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1357 dstream<<"WARNING: "<<__FUNCTION_NAME
1358 <<": NULL object"<<std::endl;
1360 type = obj->getType();
1362 // Add to data buffer for sending
1363 writeU16((u8*)buf, id);
1364 data_buffer.append(buf, 2);
1365 writeU8((u8*)buf, type);
1366 data_buffer.append(buf, 1);
1369 data_buffer.append(serializeLongString(
1370 obj->getClientInitializationData()));
1372 data_buffer.append(serializeLongString(""));
1374 // Add to known objects
1375 client->m_known_objects.insert(i.getNode()->getKey(), false);
1378 obj->m_known_by_count++;
1382 SharedBuffer<u8> reply(2 + data_buffer.size());
1383 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1384 memcpy((char*)&reply[2], data_buffer.c_str(),
1385 data_buffer.size());
1387 m_con.Send(client->peer_id, 0, reply, true);
1389 dstream<<"INFO: Server: Sent object remove/add: "
1390 <<removed_objects.size()<<" removed, "
1391 <<added_objects.size()<<" added, "
1392 <<"packet size is "<<reply.getSize()<<std::endl;
1397 Collect a list of all the objects known by the clients
1398 and report it back to the environment.
1401 core::map<u16, bool> all_known_objects;
1403 for(core::map<u16, RemoteClient*>::Iterator
1404 i = m_clients.getIterator();
1405 i.atEnd() == false; i++)
1407 RemoteClient *client = i.getNode()->getValue();
1408 // Go through all known objects of client
1409 for(core::map<u16, bool>::Iterator
1410 i = client->m_known_objects.getIterator();
1411 i.atEnd()==false; i++)
1413 u16 id = i.getNode()->getKey();
1414 all_known_objects[id] = true;
1418 m_env.setKnownActiveObjects(whatever);
1424 Send object messages
1427 JMutexAutoLock envlock(m_env_mutex);
1428 JMutexAutoLock conlock(m_con_mutex);
1431 // Value = data sent by object
1432 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1434 // Get active object messages from environment
1437 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1441 core::list<ActiveObjectMessage>* message_list = NULL;
1442 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1443 n = buffered_messages.find(aom.id);
1446 message_list = new core::list<ActiveObjectMessage>;
1447 buffered_messages.insert(aom.id, message_list);
1451 message_list = n->getValue();
1453 message_list->push_back(aom);
1456 // Route data to every client
1457 for(core::map<u16, RemoteClient*>::Iterator
1458 i = m_clients.getIterator();
1459 i.atEnd()==false; i++)
1461 RemoteClient *client = i.getNode()->getValue();
1462 std::string reliable_data;
1463 std::string unreliable_data;
1464 // Go through all objects in message buffer
1465 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1466 j = buffered_messages.getIterator();
1467 j.atEnd()==false; j++)
1469 // If object is not known by client, skip it
1470 u16 id = j.getNode()->getKey();
1471 if(client->m_known_objects.find(id) == NULL)
1473 // Get message list of object
1474 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1475 // Go through every message
1476 for(core::list<ActiveObjectMessage>::Iterator
1477 k = list->begin(); k != list->end(); k++)
1479 // Compose the full new data with header
1480 ActiveObjectMessage aom = *k;
1481 std::string new_data;
1484 writeU16((u8*)&buf[0], aom.id);
1485 new_data.append(buf, 2);
1487 new_data += serializeString(aom.datastring);
1488 // Add data to buffer
1490 reliable_data += new_data;
1492 unreliable_data += new_data;
1496 reliable_data and unreliable_data are now ready.
1499 if(reliable_data.size() > 0)
1501 SharedBuffer<u8> reply(2 + reliable_data.size());
1502 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1503 memcpy((char*)&reply[2], reliable_data.c_str(),
1504 reliable_data.size());
1506 m_con.Send(client->peer_id, 0, reply, true);
1508 if(unreliable_data.size() > 0)
1510 SharedBuffer<u8> reply(2 + unreliable_data.size());
1511 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1512 memcpy((char*)&reply[2], unreliable_data.c_str(),
1513 unreliable_data.size());
1514 // Send as unreliable
1515 m_con.Send(client->peer_id, 0, reply, false);
1518 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1520 dstream<<"INFO: Server: Size of object message data: "
1521 <<"reliable: "<<reliable_data.size()
1522 <<", unreliable: "<<unreliable_data.size()
1527 // Clear buffered_messages
1528 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1529 i = buffered_messages.getIterator();
1530 i.atEnd()==false; i++)
1532 delete i.getNode()->getValue();
1536 } // enable_experimental
1539 Send queued-for-sending map edit events.
1542 while(m_unsent_map_edit_queue.size() != 0)
1544 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1546 if(event->type == MEET_ADDNODE)
1548 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1549 sendAddNode(event->p, event->n, event->already_known_by_peer);
1551 else if(event->type == MEET_REMOVENODE)
1553 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1554 sendRemoveNode(event->p, event->already_known_by_peer);
1556 else if(event->type == MEET_OTHER)
1558 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1563 dstream<<"WARNING: Server: Unknown MapEditEvent "
1564 <<((u32)event->type)<<std::endl;
1572 Send object positions
1573 TODO: Get rid of MapBlockObjects
1576 float &counter = m_objectdata_timer;
1578 if(counter >= g_settings.getFloat("objectdata_interval"))
1580 JMutexAutoLock lock1(m_env_mutex);
1581 JMutexAutoLock lock2(m_con_mutex);
1582 SendObjectData(counter);
1592 //TimeTaker timer("Step node metadata");
1594 JMutexAutoLock envlock(m_env_mutex);
1595 JMutexAutoLock conlock(m_con_mutex);
1597 core::map<v3s16, MapBlock*> changed_blocks;
1598 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1600 for(core::map<v3s16, MapBlock*>::Iterator
1601 i = changed_blocks.getIterator();
1602 i.atEnd() == false; i++)
1604 MapBlock *block = i.getNode()->getValue();
1606 for(core::map<u16, RemoteClient*>::Iterator
1607 i = m_clients.getIterator();
1608 i.atEnd()==false; i++)
1610 RemoteClient *client = i.getNode()->getValue();
1611 client->SetBlockNotSent(block->getPos());
1617 Trigger emergethread (it somehow gets to a non-triggered but
1618 bysy state sometimes)
1621 float &counter = m_emergethread_trigger_timer;
1627 m_emergethread.trigger();
1633 float &counter = m_savemap_timer;
1635 if(counter >= g_settings.getFloat("server_map_save_interval"))
1639 JMutexAutoLock lock(m_env_mutex);
1641 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1643 // Save only changed parts
1644 m_env.getMap().save(true);
1646 // Delete unused sectors
1647 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1648 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1649 if(deleted_count > 0)
1651 dout_server<<"Server: Unloaded "<<deleted_count
1652 <<" sectors from memory"<<std::endl;
1656 m_env.serializePlayers(m_mapsavedir);
1662 void Server::Receive()
1664 DSTACK(__FUNCTION_NAME);
1665 u32 data_maxsize = 10000;
1666 Buffer<u8> data(data_maxsize);
1671 JMutexAutoLock conlock(m_con_mutex);
1672 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1675 // This has to be called so that the client list gets synced
1676 // with the peer list of the connection
1677 handlePeerChanges();
1679 ProcessData(*data, datasize, peer_id);
1681 catch(con::InvalidIncomingDataException &e)
1683 derr_server<<"Server::Receive(): "
1684 "InvalidIncomingDataException: what()="
1685 <<e.what()<<std::endl;
1687 catch(con::PeerNotFoundException &e)
1689 //NOTE: This is not needed anymore
1691 // The peer has been disconnected.
1692 // Find the associated player and remove it.
1694 /*JMutexAutoLock envlock(m_env_mutex);
1696 dout_server<<"ServerThread: peer_id="<<peer_id
1697 <<" has apparently closed connection. "
1698 <<"Removing player."<<std::endl;
1700 m_env.removePlayer(peer_id);*/
1704 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1706 DSTACK(__FUNCTION_NAME);
1707 // Environment is locked first.
1708 JMutexAutoLock envlock(m_env_mutex);
1709 JMutexAutoLock conlock(m_con_mutex);
1713 peer = m_con.GetPeer(peer_id);
1715 catch(con::PeerNotFoundException &e)
1717 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1718 <<peer_id<<" not found"<<std::endl;
1722 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1730 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1732 if(command == TOSERVER_INIT)
1734 // [0] u16 TOSERVER_INIT
1735 // [2] u8 SER_FMT_VER_HIGHEST
1736 // [3] u8[20] player_name
1741 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1742 <<peer->id<<std::endl;
1744 // First byte after command is maximum supported
1745 // serialization version
1746 u8 client_max = data[2];
1747 u8 our_max = SER_FMT_VER_HIGHEST;
1748 // Use the highest version supported by both
1749 u8 deployed = core::min_(client_max, our_max);
1750 // If it's lower than the lowest supported, give up.
1751 if(deployed < SER_FMT_VER_LOWEST)
1752 deployed = SER_FMT_VER_INVALID;
1754 //peer->serialization_version = deployed;
1755 getClient(peer->id)->pending_serialization_version = deployed;
1757 if(deployed == SER_FMT_VER_INVALID)
1759 derr_server<<DTIME<<"Server: Cannot negotiate "
1760 "serialization version with peer "
1761 <<peer_id<<std::endl;
1770 const u32 playername_size = 20;
1771 char playername[playername_size];
1772 for(u32 i=0; i<playername_size-1; i++)
1774 playername[i] = data[3+i];
1776 playername[playername_size-1] = 0;
1779 Player *player = emergePlayer(playername, "", peer_id);
1780 //Player *player = m_env.getPlayer(peer_id);
1783 // DEBUG: Test serialization
1784 std::ostringstream test_os;
1785 player->serialize(test_os);
1786 dstream<<"Player serialization test: \""<<test_os.str()
1788 std::istringstream test_is(test_os.str());
1789 player->deSerialize(test_is);
1792 // If failed, cancel
1795 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1796 <<": failed to emerge player"<<std::endl;
1801 // If a client is already connected to the player, cancel
1802 if(player->peer_id != 0)
1804 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1805 <<" tried to connect to "
1806 "an already connected player (peer_id="
1807 <<player->peer_id<<")"<<std::endl;
1810 // Set client of player
1811 player->peer_id = peer_id;
1814 // Check if player doesn't exist
1816 throw con::InvalidIncomingDataException
1817 ("Server::ProcessData(): INIT: Player doesn't exist");
1819 /*// update name if it was supplied
1820 if(datasize >= 20+3)
1823 player->updateName((const char*)&data[3]);
1827 Answer with a TOCLIENT_INIT
1830 SharedBuffer<u8> reply(2+1+6+8);
1831 writeU16(&reply[0], TOCLIENT_INIT);
1832 writeU8(&reply[2], deployed);
1833 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1834 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1835 writeU64(&reply[2+1+6], 0); // no seed
1838 m_con.Send(peer_id, 0, reply, true);
1842 Send complete position information
1844 SendMovePlayer(player);
1849 if(command == TOSERVER_INIT2)
1851 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1852 <<peer->id<<std::endl;
1855 getClient(peer->id)->serialization_version
1856 = getClient(peer->id)->pending_serialization_version;
1859 Send some initialization data
1862 // Send player info to all players
1865 // Send inventory to player
1866 UpdateCrafting(peer->id);
1867 SendInventory(peer->id);
1871 Player *player = m_env.getPlayer(peer_id);
1872 SendPlayerHP(player);
1877 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1878 m_time_of_day.get());
1879 m_con.Send(peer->id, 0, data, true);
1882 // Send information about server to player in chat
1883 SendChatMessage(peer_id, getStatusString());
1885 // Send information about joining in chat
1887 std::wstring name = L"unknown";
1888 Player *player = m_env.getPlayer(peer_id);
1890 name = narrow_to_wide(player->getName());
1892 std::wstring message;
1895 message += L" joined game";
1896 BroadcastChatMessage(message);
1902 if(peer_ser_ver == SER_FMT_VER_INVALID)
1904 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1905 " serialization format invalid or not initialized."
1906 " Skipping incoming command="<<command<<std::endl;
1910 Player *player = m_env.getPlayer(peer_id);
1913 derr_server<<"Server::ProcessData(): Cancelling: "
1914 "No player for peer_id="<<peer_id
1918 if(command == TOSERVER_PLAYERPOS)
1920 if(datasize < 2+12+12+4+4)
1924 v3s32 ps = readV3S32(&data[start+2]);
1925 v3s32 ss = readV3S32(&data[start+2+12]);
1926 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1927 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1928 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1929 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1930 pitch = wrapDegrees(pitch);
1931 yaw = wrapDegrees(yaw);
1932 player->setPosition(position);
1933 player->setSpeed(speed);
1934 player->setPitch(pitch);
1935 player->setYaw(yaw);
1937 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1938 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1939 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1941 else if(command == TOSERVER_GOTBLOCKS)
1954 u16 count = data[2];
1955 for(u16 i=0; i<count; i++)
1957 if((s16)datasize < 2+1+(i+1)*6)
1958 throw con::InvalidIncomingDataException
1959 ("GOTBLOCKS length is too short");
1960 v3s16 p = readV3S16(&data[2+1+i*6]);
1961 /*dstream<<"Server: GOTBLOCKS ("
1962 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1963 RemoteClient *client = getClient(peer_id);
1964 client->GotBlock(p);
1967 else if(command == TOSERVER_DELETEDBLOCKS)
1980 u16 count = data[2];
1981 for(u16 i=0; i<count; i++)
1983 if((s16)datasize < 2+1+(i+1)*6)
1984 throw con::InvalidIncomingDataException
1985 ("DELETEDBLOCKS length is too short");
1986 v3s16 p = readV3S16(&data[2+1+i*6]);
1987 /*dstream<<"Server: DELETEDBLOCKS ("
1988 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1989 RemoteClient *client = getClient(peer_id);
1990 client->SetBlockNotSent(p);
1993 else if(command == TOSERVER_CLICK_OBJECT)
1998 if((player->privs & PRIV_BUILD) == 0)
2003 [2] u8 button (0=left, 1=right)
2008 u8 button = readU8(&data[2]);
2010 p.X = readS16(&data[3]);
2011 p.Y = readS16(&data[5]);
2012 p.Z = readS16(&data[7]);
2013 s16 id = readS16(&data[9]);
2014 //u16 item_i = readU16(&data[11]);
2016 MapBlock *block = NULL;
2019 block = m_env.getMap().getBlockNoCreate(p);
2021 catch(InvalidPositionException &e)
2023 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2027 MapBlockObject *obj = block->getObject(id);
2031 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2035 //TODO: Check that object is reasonably close
2040 InventoryList *ilist = player->inventory.getList("main");
2041 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2044 // Skip if inventory has no free space
2045 if(ilist->getUsedSlots() == ilist->getSize())
2047 dout_server<<"Player inventory has no free space"<<std::endl;
2052 Create the inventory item
2054 InventoryItem *item = NULL;
2055 // If it is an item-object, take the item from it
2056 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2058 item = ((ItemObject*)obj)->createInventoryItem();
2060 // Else create an item of the object
2063 item = new MapBlockObjectItem
2064 (obj->getInventoryString());
2067 // Add to inventory and send inventory
2068 ilist->addItem(item);
2069 UpdateCrafting(player->peer_id);
2070 SendInventory(player->peer_id);
2073 // Remove from block
2074 block->removeObject(id);
2077 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2082 if((player->privs & PRIV_BUILD) == 0)
2088 [2] u8 button (0=left, 1=right)
2092 u8 button = readU8(&data[2]);
2093 u16 id = readS16(&data[3]);
2094 u16 item_i = readU16(&data[11]);
2096 ServerActiveObject *obj = m_env.getActiveObject(id);
2100 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2105 //TODO: Check that object is reasonably close
2107 // Left click, pick object up (usually)
2110 InventoryList *ilist = player->inventory.getList("main");
2111 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2114 // Skip if inventory has no free space
2115 if(ilist->getUsedSlots() == ilist->getSize())
2117 dout_server<<"Player inventory has no free space"<<std::endl;
2121 // Skip if object has been removed
2126 Create the inventory item
2128 InventoryItem *item = obj->createPickedUpItem();
2132 // Add to inventory and send inventory
2133 ilist->addItem(item);
2134 UpdateCrafting(player->peer_id);
2135 SendInventory(player->peer_id);
2137 // Remove object from environment
2138 obj->m_removed = true;
2143 Item cannot be picked up. Punch it instead.
2146 ToolItem *titem = NULL;
2147 std::string toolname = "";
2149 InventoryList *mlist = player->inventory.getList("main");
2152 InventoryItem *item = mlist->getItem(item_i);
2153 if(item && (std::string)item->getName() == "ToolItem")
2155 titem = (ToolItem*)item;
2156 toolname = titem->getToolName();
2160 u16 wear = obj->punch(toolname);
2164 bool weared_out = titem->addWear(wear);
2166 mlist->deleteItem(item_i);
2167 SendInventory(player->peer_id);
2173 else if(command == TOSERVER_GROUND_ACTION)
2181 [3] v3s16 nodepos_undersurface
2182 [9] v3s16 nodepos_abovesurface
2187 2: stop digging (all parameters ignored)
2188 3: digging completed
2190 u8 action = readU8(&data[2]);
2192 p_under.X = readS16(&data[3]);
2193 p_under.Y = readS16(&data[5]);
2194 p_under.Z = readS16(&data[7]);
2196 p_over.X = readS16(&data[9]);
2197 p_over.Y = readS16(&data[11]);
2198 p_over.Z = readS16(&data[13]);
2199 u16 item_i = readU16(&data[15]);
2201 //TODO: Check that target is reasonably close
2209 NOTE: This can be used in the future to check if
2210 somebody is cheating, by checking the timing.
2217 else if(action == 2)
2220 RemoteClient *client = getClient(peer->id);
2221 JMutexAutoLock digmutex(client->m_dig_mutex);
2222 client->m_dig_tool_item = -1;
2227 3: Digging completed
2229 else if(action == 3)
2231 // Mandatory parameter; actually used for nothing
2232 core::map<v3s16, MapBlock*> modified_blocks;
2235 u8 mineral = MINERAL_NONE;
2237 bool cannot_remove_node = false;
2241 MapNode n = m_env.getMap().getNode(p_under);
2243 mineral = n.getMineral();
2244 // Get material at position
2246 // If not yet cancelled
2247 if(cannot_remove_node == false)
2249 // If it's not diggable, do nothing
2250 if(content_diggable(material) == false)
2252 derr_server<<"Server: Not finishing digging: "
2253 <<"Node not diggable"
2255 cannot_remove_node = true;
2258 // If not yet cancelled
2259 if(cannot_remove_node == false)
2261 // Get node metadata
2262 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2263 if(meta && meta->nodeRemovalDisabled() == true)
2265 derr_server<<"Server: Not finishing digging: "
2266 <<"Node metadata disables removal"
2268 cannot_remove_node = true;
2272 catch(InvalidPositionException &e)
2274 derr_server<<"Server: Not finishing digging: Node not found."
2275 <<" Adding block to emerge queue."
2277 m_emerge_queue.addBlock(peer_id,
2278 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2279 cannot_remove_node = true;
2282 // Make sure the player is allowed to do it
2283 if((player->privs & PRIV_BUILD) == 0)
2284 cannot_remove_node = true;
2287 If node can't be removed, set block to be re-sent to
2290 if(cannot_remove_node)
2292 derr_server<<"Server: Not finishing digging."<<std::endl;
2294 // Client probably has wrong data.
2295 // Set block not sent, so that client will get
2297 dstream<<"Client "<<peer_id<<" tried to dig "
2298 <<"node; but node cannot be removed."
2299 <<" setting MapBlock not sent."<<std::endl;
2300 RemoteClient *client = getClient(peer_id);
2301 v3s16 blockpos = getNodeBlockPos(p_under);
2302 client->SetBlockNotSent(blockpos);
2308 Send the removal to all other clients.
2309 - If other player is close, send REMOVENODE
2310 - Otherwise set blocks not sent
2312 core::list<u16> far_players;
2313 sendRemoveNode(p_under, peer_id, &far_players, 100);
2316 Update and send inventory
2319 if(g_settings.getBool("creative_mode") == false)
2324 InventoryList *mlist = player->inventory.getList("main");
2327 InventoryItem *item = mlist->getItem(item_i);
2328 if(item && (std::string)item->getName() == "ToolItem")
2330 ToolItem *titem = (ToolItem*)item;
2331 std::string toolname = titem->getToolName();
2333 // Get digging properties for material and tool
2334 DiggingProperties prop =
2335 getDiggingProperties(material, toolname);
2337 if(prop.diggable == false)
2339 derr_server<<"Server: WARNING: Player digged"
2340 <<" with impossible material + tool"
2341 <<" combination"<<std::endl;
2344 bool weared_out = titem->addWear(prop.wear);
2348 mlist->deleteItem(item_i);
2354 Add dug item to inventory
2357 InventoryItem *item = NULL;
2359 if(mineral != MINERAL_NONE)
2360 item = getDiggedMineralItem(mineral);
2365 std::string &dug_s = content_features(material).dug_item;
2368 std::istringstream is(dug_s, std::ios::binary);
2369 item = InventoryItem::deSerialize(is);
2375 // Add a item to inventory
2376 player->inventory.addItem("main", item);
2379 UpdateCrafting(player->peer_id);
2380 SendInventory(player->peer_id);
2386 (this takes some time so it is done after the quick stuff)
2388 m_ignore_map_edit_events = true;
2389 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2390 m_ignore_map_edit_events = false;
2393 Set blocks not sent to far players
2395 for(core::list<u16>::Iterator
2396 i = far_players.begin();
2397 i != far_players.end(); i++)
2400 RemoteClient *client = getClient(peer_id);
2403 client->SetBlocksNotSent(modified_blocks);
2410 else if(action == 1)
2413 InventoryList *ilist = player->inventory.getList("main");
2418 InventoryItem *item = ilist->getItem(item_i);
2420 // If there is no item, it is not possible to add it anywhere
2425 Handle material items
2427 if(std::string("MaterialItem") == item->getName())
2430 // Don't add a node if this is not a free space
2431 MapNode n2 = m_env.getMap().getNode(p_over);
2432 if(content_buildable_to(n2.d) == false
2433 || (player->privs & PRIV_BUILD) ==0)
2435 // Client probably has wrong data.
2436 // Set block not sent, so that client will get
2438 dstream<<"Client "<<peer_id<<" tried to place"
2439 <<" node in invalid position; setting"
2440 <<" MapBlock not sent."<<std::endl;
2441 RemoteClient *client = getClient(peer_id);
2442 v3s16 blockpos = getNodeBlockPos(p_over);
2443 client->SetBlockNotSent(blockpos);
2447 catch(InvalidPositionException &e)
2449 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2450 <<" Adding block to emerge queue."
2452 m_emerge_queue.addBlock(peer_id,
2453 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2457 // Reset build time counter
2458 getClient(peer->id)->m_time_from_building = 0.0;
2461 MaterialItem *mitem = (MaterialItem*)item;
2463 n.d = mitem->getMaterial();
2464 if(content_features(n.d).wall_mounted)
2465 n.dir = packDir(p_under - p_over);
2470 core::list<u16> far_players;
2471 sendAddNode(p_over, n, 0, &far_players, 100);
2476 InventoryList *ilist = player->inventory.getList("main");
2477 if(g_settings.getBool("creative_mode") == false && ilist)
2479 // Remove from inventory and send inventory
2480 if(mitem->getCount() == 1)
2481 ilist->deleteItem(item_i);
2485 UpdateCrafting(peer_id);
2486 SendInventory(peer_id);
2492 This takes some time so it is done after the quick stuff
2494 core::map<v3s16, MapBlock*> modified_blocks;
2495 m_ignore_map_edit_events = true;
2496 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2497 m_ignore_map_edit_events = false;
2500 Set blocks not sent to far players
2502 for(core::list<u16>::Iterator
2503 i = far_players.begin();
2504 i != far_players.end(); i++)
2507 RemoteClient *client = getClient(peer_id);
2510 client->SetBlocksNotSent(modified_blocks);
2514 Calculate special events
2517 /*if(n.d == CONTENT_MESE)
2520 for(s16 z=-1; z<=1; z++)
2521 for(s16 y=-1; y<=1; y++)
2522 for(s16 x=-1; x<=1; x++)
2529 Place other item (not a block)
2533 v3s16 blockpos = getNodeBlockPos(p_over);
2536 Check that the block is loaded so that the item
2537 can properly be added to the static list too
2539 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2542 derr_server<<"Error while placing object: "
2543 "block not found"<<std::endl;
2547 dout_server<<"Placing a miscellaneous item on map"
2550 // Calculate a position for it
2551 v3f pos = intToFloat(p_over, BS);
2553 pos.Y -= BS*0.25; // let it drop a bit
2555 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2556 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2561 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2565 derr_server<<"WARNING: item resulted in NULL object, "
2566 <<"not placing onto map"
2571 // Add the object to the environment
2572 m_env.addActiveObject(obj);
2574 dout_server<<"Placed object"<<std::endl;
2576 if(g_settings.getBool("creative_mode") == false)
2578 // Delete the right amount of items from the slot
2579 u16 dropcount = item->getDropCount();
2581 // Delete item if all gone
2582 if(item->getCount() <= dropcount)
2584 if(item->getCount() < dropcount)
2585 dstream<<"WARNING: Server: dropped more items"
2586 <<" than the slot contains"<<std::endl;
2588 InventoryList *ilist = player->inventory.getList("main");
2590 // Remove from inventory and send inventory
2591 ilist->deleteItem(item_i);
2593 // Else decrement it
2595 item->remove(dropcount);
2598 UpdateCrafting(peer_id);
2599 SendInventory(peer_id);
2607 Catch invalid actions
2611 derr_server<<"WARNING: Server: Invalid action "
2612 <<action<<std::endl;
2616 else if(command == TOSERVER_RELEASE)
2625 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2628 else if(command == TOSERVER_SIGNTEXT)
2630 if((player->privs & PRIV_BUILD) == 0)
2639 std::string datastring((char*)&data[2], datasize-2);
2640 std::istringstream is(datastring, std::ios_base::binary);
2643 is.read((char*)buf, 6);
2644 v3s16 blockpos = readV3S16(buf);
2645 is.read((char*)buf, 2);
2646 s16 id = readS16(buf);
2647 is.read((char*)buf, 2);
2648 u16 textlen = readU16(buf);
2650 for(u16 i=0; i<textlen; i++)
2652 is.read((char*)buf, 1);
2653 text += (char)buf[0];
2656 MapBlock *block = NULL;
2659 block = m_env.getMap().getBlockNoCreate(blockpos);
2661 catch(InvalidPositionException &e)
2663 derr_server<<"Error while setting sign text: "
2664 "block not found"<<std::endl;
2668 MapBlockObject *obj = block->getObject(id);
2671 derr_server<<"Error while setting sign text: "
2672 "object not found"<<std::endl;
2676 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2678 derr_server<<"Error while setting sign text: "
2679 "object is not a sign"<<std::endl;
2683 ((SignObject*)obj)->setText(text);
2685 obj->getBlock()->setChangedFlag();
2687 else if(command == TOSERVER_SIGNNODETEXT)
2689 if((player->privs & PRIV_BUILD) == 0)
2697 std::string datastring((char*)&data[2], datasize-2);
2698 std::istringstream is(datastring, std::ios_base::binary);
2701 is.read((char*)buf, 6);
2702 v3s16 p = readV3S16(buf);
2703 is.read((char*)buf, 2);
2704 u16 textlen = readU16(buf);
2706 for(u16 i=0; i<textlen; i++)
2708 is.read((char*)buf, 1);
2709 text += (char)buf[0];
2712 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2715 if(meta->typeId() != CONTENT_SIGN_WALL)
2717 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2718 signmeta->setText(text);
2720 v3s16 blockpos = getNodeBlockPos(p);
2721 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2724 block->setChangedFlag();
2727 for(core::map<u16, RemoteClient*>::Iterator
2728 i = m_clients.getIterator();
2729 i.atEnd()==false; i++)
2731 RemoteClient *client = i.getNode()->getValue();
2732 client->SetBlockNotSent(blockpos);
2735 else if(command == TOSERVER_INVENTORY_ACTION)
2737 /*// Ignore inventory changes if in creative mode
2738 if(g_settings.getBool("creative_mode") == true)
2740 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2744 // Strip command and create a stream
2745 std::string datastring((char*)&data[2], datasize-2);
2746 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2747 std::istringstream is(datastring, std::ios_base::binary);
2749 InventoryAction *a = InventoryAction::deSerialize(is);
2754 c.current_player = player;
2757 Handle craftresult specially if not in creative mode
2759 bool disable_action = false;
2760 if(a->getType() == IACTION_MOVE
2761 && g_settings.getBool("creative_mode") == false)
2763 IMoveAction *ma = (IMoveAction*)a;
2764 if(ma->to_inv == "current_player" &&
2765 ma->from_inv == "current_player")
2767 InventoryList *rlist = player->inventory.getList("craftresult");
2769 InventoryList *clist = player->inventory.getList("craft");
2771 InventoryList *mlist = player->inventory.getList("main");
2774 Craftresult is no longer preview if something
2777 if(ma->to_list == "craftresult"
2778 && ma->from_list != "craftresult")
2780 // If it currently is a preview, remove
2782 if(player->craftresult_is_preview)
2784 rlist->deleteItem(0);
2786 player->craftresult_is_preview = false;
2789 Crafting takes place if this condition is true.
2791 if(player->craftresult_is_preview &&
2792 ma->from_list == "craftresult")
2794 player->craftresult_is_preview = false;
2795 clist->decrementMaterials(1);
2798 If the craftresult is placed on itself, move it to
2799 main inventory instead of doing the action
2801 if(ma->to_list == "craftresult"
2802 && ma->from_list == "craftresult")
2804 disable_action = true;
2806 InventoryItem *item1 = rlist->changeItem(0, NULL);
2807 mlist->addItem(item1);
2812 if(disable_action == false)
2814 // Feed action to player inventory
2822 UpdateCrafting(player->peer_id);
2823 SendInventory(player->peer_id);
2828 dstream<<"TOSERVER_INVENTORY_ACTION: "
2829 <<"InventoryAction::deSerialize() returned NULL"
2833 else if(command == TOSERVER_CHAT_MESSAGE)
2841 std::string datastring((char*)&data[2], datasize-2);
2842 std::istringstream is(datastring, std::ios_base::binary);
2845 is.read((char*)buf, 2);
2846 u16 len = readU16(buf);
2848 std::wstring message;
2849 for(u16 i=0; i<len; i++)
2851 is.read((char*)buf, 2);
2852 message += (wchar_t)readU16(buf);
2855 // Get player name of this client
2856 std::wstring name = narrow_to_wide(player->getName());
2858 // Line to send to players
2860 // Whether to send to the player that sent the line
2861 bool send_to_sender = false;
2862 // Whether to send to other players
2863 bool send_to_others = false;
2866 std::wstring commandprefix = L"/#";
2867 if(message.substr(0, commandprefix.size()) == commandprefix)
2869 line += L"Server: ";
2871 message = message.substr(commandprefix.size());
2873 ServerCommandContext *ctx = new ServerCommandContext(
2874 str_split(message, L' '),
2880 line += processServerCommand(ctx);
2881 send_to_sender = ctx->flags & 1;
2882 send_to_others = ctx->flags & 2;
2894 send_to_others = true;
2899 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2902 Send the message to clients
2904 for(core::map<u16, RemoteClient*>::Iterator
2905 i = m_clients.getIterator();
2906 i.atEnd() == false; i++)
2908 // Get client and check that it is valid
2909 RemoteClient *client = i.getNode()->getValue();
2910 assert(client->peer_id == i.getNode()->getKey());
2911 if(client->serialization_version == SER_FMT_VER_INVALID)
2915 bool sender_selected = (peer_id == client->peer_id);
2916 if(sender_selected == true && send_to_sender == false)
2918 if(sender_selected == false && send_to_others == false)
2921 SendChatMessage(client->peer_id, line);
2925 else if(command == TOSERVER_DAMAGE)
2927 if(g_settings.getBool("enable_damage"))
2929 std::string datastring((char*)&data[2], datasize-2);
2930 std::istringstream is(datastring, std::ios_base::binary);
2931 u8 damage = readU8(is);
2932 if(player->hp > damage)
2934 player->hp -= damage;
2940 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
2943 v3f pos = findSpawnPos(m_env.getServerMap());
2944 player->setPosition(pos);
2946 SendMovePlayer(player);
2947 SendPlayerHP(player);
2949 //TODO: Throw items around
2953 SendPlayerHP(player);
2957 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2958 "unknown command "<<command<<std::endl;
2962 catch(SendFailedException &e)
2964 derr_server<<"Server::ProcessData(): SendFailedException: "
2970 void Server::onMapEditEvent(MapEditEvent *event)
2972 dstream<<"Server::onMapEditEvent()"<<std::endl;
2973 if(m_ignore_map_edit_events)
2975 MapEditEvent *e = event->clone();
2976 m_unsent_map_edit_queue.push_back(e);
2979 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2981 if(id == "current_player")
2983 assert(c->current_player);
2984 return &(c->current_player->inventory);
2988 std::string id0 = fn.next(":");
2990 if(id0 == "nodemeta")
2993 p.X = stoi(fn.next(","));
2994 p.Y = stoi(fn.next(","));
2995 p.Z = stoi(fn.next(","));
2996 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2998 return meta->getInventory();
2999 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3000 <<"no metadata found"<<std::endl;
3004 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3007 void Server::inventoryModified(InventoryContext *c, std::string id)
3009 if(id == "current_player")
3011 assert(c->current_player);
3013 UpdateCrafting(c->current_player->peer_id);
3014 SendInventory(c->current_player->peer_id);
3019 std::string id0 = fn.next(":");
3021 if(id0 == "nodemeta")
3024 p.X = stoi(fn.next(","));
3025 p.Y = stoi(fn.next(","));
3026 p.Z = stoi(fn.next(","));
3027 v3s16 blockpos = getNodeBlockPos(p);
3029 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3031 meta->inventoryModified();
3033 for(core::map<u16, RemoteClient*>::Iterator
3034 i = m_clients.getIterator();
3035 i.atEnd()==false; i++)
3037 RemoteClient *client = i.getNode()->getValue();
3038 client->SetBlockNotSent(blockpos);
3044 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3047 core::list<PlayerInfo> Server::getPlayerInfo()
3049 DSTACK(__FUNCTION_NAME);
3050 JMutexAutoLock envlock(m_env_mutex);
3051 JMutexAutoLock conlock(m_con_mutex);
3053 core::list<PlayerInfo> list;
3055 core::list<Player*> players = m_env.getPlayers();
3057 core::list<Player*>::Iterator i;
3058 for(i = players.begin();
3059 i != players.end(); i++)
3063 Player *player = *i;
3066 con::Peer *peer = m_con.GetPeer(player->peer_id);
3067 // Copy info from peer to info struct
3069 info.address = peer->address;
3070 info.avg_rtt = peer->avg_rtt;
3072 catch(con::PeerNotFoundException &e)
3074 // Set dummy peer info
3076 info.address = Address(0,0,0,0,0);
3080 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3081 info.position = player->getPosition();
3083 list.push_back(info);
3090 void Server::peerAdded(con::Peer *peer)
3092 DSTACK(__FUNCTION_NAME);
3093 dout_server<<"Server::peerAdded(): peer->id="
3094 <<peer->id<<std::endl;
3097 c.type = PEER_ADDED;
3098 c.peer_id = peer->id;
3100 m_peer_change_queue.push_back(c);
3103 void Server::deletingPeer(con::Peer *peer, bool timeout)
3105 DSTACK(__FUNCTION_NAME);
3106 dout_server<<"Server::deletingPeer(): peer->id="
3107 <<peer->id<<", timeout="<<timeout<<std::endl;
3110 c.type = PEER_REMOVED;
3111 c.peer_id = peer->id;
3112 c.timeout = timeout;
3113 m_peer_change_queue.push_back(c);
3120 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3122 DSTACK(__FUNCTION_NAME);
3123 std::ostringstream os(std::ios_base::binary);
3125 writeU16(os, TOCLIENT_HP);
3129 std::string s = os.str();
3130 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3132 con.Send(peer_id, 0, data, true);
3136 Non-static send methods
3139 void Server::SendObjectData(float dtime)
3141 DSTACK(__FUNCTION_NAME);
3143 core::map<v3s16, bool> stepped_blocks;
3145 for(core::map<u16, RemoteClient*>::Iterator
3146 i = m_clients.getIterator();
3147 i.atEnd() == false; i++)
3149 u16 peer_id = i.getNode()->getKey();
3150 RemoteClient *client = i.getNode()->getValue();
3151 assert(client->peer_id == peer_id);
3153 if(client->serialization_version == SER_FMT_VER_INVALID)
3156 client->SendObjectData(this, dtime, stepped_blocks);
3160 void Server::SendPlayerInfos()
3162 DSTACK(__FUNCTION_NAME);
3164 //JMutexAutoLock envlock(m_env_mutex);
3166 // Get connected players
3167 core::list<Player*> players = m_env.getPlayers(true);
3169 u32 player_count = players.getSize();
3170 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3172 SharedBuffer<u8> data(datasize);
3173 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3176 core::list<Player*>::Iterator i;
3177 for(i = players.begin();
3178 i != players.end(); i++)
3180 Player *player = *i;
3182 /*dstream<<"Server sending player info for player with "
3183 "peer_id="<<player->peer_id<<std::endl;*/
3185 writeU16(&data[start], player->peer_id);
3186 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3187 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3188 start += 2+PLAYERNAME_SIZE;
3191 //JMutexAutoLock conlock(m_con_mutex);
3194 m_con.SendToAll(0, data, true);
3197 void Server::SendInventory(u16 peer_id)
3199 DSTACK(__FUNCTION_NAME);
3201 Player* player = m_env.getPlayer(peer_id);
3208 std::ostringstream os;
3209 //os.imbue(std::locale("C"));
3211 player->inventory.serialize(os);
3213 std::string s = os.str();
3215 SharedBuffer<u8> data(s.size()+2);
3216 writeU16(&data[0], TOCLIENT_INVENTORY);
3217 memcpy(&data[2], s.c_str(), s.size());
3220 m_con.Send(peer_id, 0, data, true);
3223 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3225 DSTACK(__FUNCTION_NAME);
3227 std::ostringstream os(std::ios_base::binary);
3231 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3232 os.write((char*)buf, 2);
3235 writeU16(buf, message.size());
3236 os.write((char*)buf, 2);
3239 for(u32 i=0; i<message.size(); i++)
3243 os.write((char*)buf, 2);
3247 std::string s = os.str();
3248 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3250 m_con.Send(peer_id, 0, data, true);
3253 void Server::BroadcastChatMessage(const std::wstring &message)
3255 for(core::map<u16, RemoteClient*>::Iterator
3256 i = m_clients.getIterator();
3257 i.atEnd() == false; i++)
3259 // Get client and check that it is valid
3260 RemoteClient *client = i.getNode()->getValue();
3261 assert(client->peer_id == i.getNode()->getKey());
3262 if(client->serialization_version == SER_FMT_VER_INVALID)
3265 SendChatMessage(client->peer_id, message);
3269 void Server::SendPlayerHP(Player *player)
3271 SendHP(m_con, player->peer_id, player->hp);
3274 void Server::SendMovePlayer(Player *player)
3276 DSTACK(__FUNCTION_NAME);
3277 std::ostringstream os(std::ios_base::binary);
3279 writeU16(os, TOCLIENT_MOVE_PLAYER);
3280 writeV3F1000(os, player->getPosition());
3281 writeF1000(os, player->getPitch());
3282 writeF1000(os, player->getYaw());
3285 v3f pos = player->getPosition();
3286 f32 pitch = player->getPitch();
3287 f32 yaw = player->getYaw();
3288 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3289 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3296 std::string s = os.str();
3297 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3299 m_con.Send(player->peer_id, 0, data, true);
3302 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3303 core::list<u16> *far_players, float far_d_nodes)
3305 float maxd = far_d_nodes*BS;
3306 v3f p_f = intToFloat(p, BS);
3310 SharedBuffer<u8> reply(replysize);
3311 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3312 writeS16(&reply[2], p.X);
3313 writeS16(&reply[4], p.Y);
3314 writeS16(&reply[6], p.Z);
3316 for(core::map<u16, RemoteClient*>::Iterator
3317 i = m_clients.getIterator();
3318 i.atEnd() == false; i++)
3320 // Get client and check that it is valid
3321 RemoteClient *client = i.getNode()->getValue();
3322 assert(client->peer_id == i.getNode()->getKey());
3323 if(client->serialization_version == SER_FMT_VER_INVALID)
3326 // Don't send if it's the same one
3327 if(client->peer_id == ignore_id)
3333 Player *player = m_env.getPlayer(client->peer_id);
3336 // If player is far away, only set modified blocks not sent
3337 v3f player_pos = player->getPosition();
3338 if(player_pos.getDistanceFrom(p_f) > maxd)
3340 far_players->push_back(client->peer_id);
3347 m_con.Send(client->peer_id, 0, reply, true);
3351 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3352 core::list<u16> *far_players, float far_d_nodes)
3354 float maxd = far_d_nodes*BS;
3355 v3f p_f = intToFloat(p, BS);
3357 for(core::map<u16, RemoteClient*>::Iterator
3358 i = m_clients.getIterator();
3359 i.atEnd() == false; i++)
3361 // Get client and check that it is valid
3362 RemoteClient *client = i.getNode()->getValue();
3363 assert(client->peer_id == i.getNode()->getKey());
3364 if(client->serialization_version == SER_FMT_VER_INVALID)
3367 // Don't send if it's the same one
3368 if(client->peer_id == ignore_id)
3374 Player *player = m_env.getPlayer(client->peer_id);
3377 // If player is far away, only set modified blocks not sent
3378 v3f player_pos = player->getPosition();
3379 if(player_pos.getDistanceFrom(p_f) > maxd)
3381 far_players->push_back(client->peer_id);
3388 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3389 SharedBuffer<u8> reply(replysize);
3390 writeU16(&reply[0], TOCLIENT_ADDNODE);
3391 writeS16(&reply[2], p.X);
3392 writeS16(&reply[4], p.Y);
3393 writeS16(&reply[6], p.Z);
3394 n.serialize(&reply[8], client->serialization_version);
3397 m_con.Send(client->peer_id, 0, reply, true);
3401 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3403 DSTACK(__FUNCTION_NAME);
3405 v3s16 p = block->getPos();
3409 bool completely_air = true;
3410 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3411 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3412 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3414 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3416 completely_air = false;
3417 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3422 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3424 dstream<<"[completely air] ";
3429 Create a packet with the block in the right format
3432 std::ostringstream os(std::ios_base::binary);
3433 block->serialize(os, ver);
3434 std::string s = os.str();
3435 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3437 u32 replysize = 8 + blockdata.getSize();
3438 SharedBuffer<u8> reply(replysize);
3439 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3440 writeS16(&reply[2], p.X);
3441 writeS16(&reply[4], p.Y);
3442 writeS16(&reply[6], p.Z);
3443 memcpy(&reply[8], *blockdata, blockdata.getSize());
3445 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3446 <<": \tpacket size: "<<replysize<<std::endl;*/
3451 m_con.Send(peer_id, 1, reply, true);
3454 void Server::SendBlocks(float dtime)
3456 DSTACK(__FUNCTION_NAME);
3458 JMutexAutoLock envlock(m_env_mutex);
3459 JMutexAutoLock conlock(m_con_mutex);
3461 //TimeTaker timer("Server::SendBlocks");
3463 core::array<PrioritySortedBlockTransfer> queue;
3465 s32 total_sending = 0;
3467 for(core::map<u16, RemoteClient*>::Iterator
3468 i = m_clients.getIterator();
3469 i.atEnd() == false; i++)
3471 RemoteClient *client = i.getNode()->getValue();
3472 assert(client->peer_id == i.getNode()->getKey());
3474 total_sending += client->SendingCount();
3476 if(client->serialization_version == SER_FMT_VER_INVALID)
3479 client->GetNextBlocks(this, dtime, queue);
3483 // Lowest priority number comes first.
3484 // Lowest is most important.
3487 for(u32 i=0; i<queue.size(); i++)
3489 //TODO: Calculate limit dynamically
3490 if(total_sending >= g_settings.getS32
3491 ("max_simultaneous_block_sends_server_total"))
3494 PrioritySortedBlockTransfer q = queue[i];
3496 MapBlock *block = NULL;
3499 block = m_env.getMap().getBlockNoCreate(q.pos);
3501 catch(InvalidPositionException &e)
3506 RemoteClient *client = getClient(q.peer_id);
3508 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3510 client->SentBlock(q.pos);
3520 void Server::UpdateCrafting(u16 peer_id)
3522 DSTACK(__FUNCTION_NAME);
3524 Player* player = m_env.getPlayer(peer_id);
3528 Calculate crafting stuff
3530 if(g_settings.getBool("creative_mode") == false)
3532 InventoryList *clist = player->inventory.getList("craft");
3533 InventoryList *rlist = player->inventory.getList("craftresult");
3535 if(rlist->getUsedSlots() == 0)
3536 player->craftresult_is_preview = true;
3538 if(rlist && player->craftresult_is_preview)
3540 rlist->clearItems();
3542 if(clist && rlist && player->craftresult_is_preview)
3544 InventoryItem *items[9];
3545 for(u16 i=0; i<9; i++)
3547 items[i] = clist->getItem(i);
3556 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3557 if(checkItemCombination(items, specs))
3559 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3568 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3569 if(checkItemCombination(items, specs))
3571 rlist->addItem(new CraftItem("Stick", 4));
3580 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3581 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3582 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3583 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3584 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3585 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3586 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3587 if(checkItemCombination(items, specs))
3589 //rlist->addItem(new MapBlockObjectItem("Sign"));
3590 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3599 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3600 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3601 if(checkItemCombination(items, specs))
3603 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3612 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3613 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3614 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3615 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3616 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3617 if(checkItemCombination(items, specs))
3619 rlist->addItem(new ToolItem("WPick", 0));
3628 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3629 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3630 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3631 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3632 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3633 if(checkItemCombination(items, specs))
3635 rlist->addItem(new ToolItem("STPick", 0));
3644 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3645 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3646 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3647 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3648 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3649 if(checkItemCombination(items, specs))
3651 rlist->addItem(new ToolItem("SteelPick", 0));
3660 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3661 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3662 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3663 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3664 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3665 if(checkItemCombination(items, specs))
3667 rlist->addItem(new ToolItem("MesePick", 0));
3676 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3677 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3678 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3679 if(checkItemCombination(items, specs))
3681 rlist->addItem(new ToolItem("WShovel", 0));
3690 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3691 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3692 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3693 if(checkItemCombination(items, specs))
3695 rlist->addItem(new ToolItem("STShovel", 0));
3704 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3705 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3706 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3707 if(checkItemCombination(items, specs))
3709 rlist->addItem(new ToolItem("SteelShovel", 0));
3718 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3719 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3720 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3721 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3722 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3723 if(checkItemCombination(items, specs))
3725 rlist->addItem(new ToolItem("WAxe", 0));
3734 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3735 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3736 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3737 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3738 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3739 if(checkItemCombination(items, specs))
3741 rlist->addItem(new ToolItem("STAxe", 0));
3750 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3751 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3752 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3753 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3754 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3755 if(checkItemCombination(items, specs))
3757 rlist->addItem(new ToolItem("SteelAxe", 0));
3766 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3767 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3768 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3769 if(checkItemCombination(items, specs))
3771 rlist->addItem(new ToolItem("WSword", 0));
3780 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3781 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3782 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3783 if(checkItemCombination(items, specs))
3785 rlist->addItem(new ToolItem("STSword", 0));
3794 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3795 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3796 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3797 if(checkItemCombination(items, specs))
3799 rlist->addItem(new ToolItem("SteelSword", 0));
3808 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3809 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3810 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3811 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3812 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3813 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3814 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3815 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3816 if(checkItemCombination(items, specs))
3818 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3827 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3828 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3829 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3830 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3831 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3832 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3833 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3834 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3835 if(checkItemCombination(items, specs))
3837 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3846 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3847 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3848 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3849 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3850 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3851 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3852 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3853 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3854 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3855 if(checkItemCombination(items, specs))
3857 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3863 } // if creative_mode == false
3866 RemoteClient* Server::getClient(u16 peer_id)
3868 DSTACK(__FUNCTION_NAME);
3869 //JMutexAutoLock lock(m_con_mutex);
3870 core::map<u16, RemoteClient*>::Node *n;
3871 n = m_clients.find(peer_id);
3872 // A client should exist for all peers
3874 return n->getValue();
3877 std::wstring Server::getStatusString()
3879 std::wostringstream os(std::ios_base::binary);
3882 os<<L"version="<<narrow_to_wide(VERSION_STRING);
3884 os<<L", uptime="<<m_uptime.get();
3885 // Information about clients
3887 for(core::map<u16, RemoteClient*>::Iterator
3888 i = m_clients.getIterator();
3889 i.atEnd() == false; i++)
3891 // Get client and check that it is valid
3892 RemoteClient *client = i.getNode()->getValue();
3893 assert(client->peer_id == i.getNode()->getKey());
3894 if(client->serialization_version == SER_FMT_VER_INVALID)
3897 Player *player = m_env.getPlayer(client->peer_id);
3898 // Get name of player
3899 std::wstring name = L"unknown";
3901 name = narrow_to_wide(player->getName());
3902 // Add name to information string
3906 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3907 os<<" WARNING: Map saving is disabled."<<std::endl;
3912 void setCreativeInventory(Player *player)
3914 player->resetInventory();
3916 // Give some good tools
3918 InventoryItem *item = new ToolItem("MesePick", 0);
3919 void* r = player->inventory.addItem("main", item);
3923 InventoryItem *item = new ToolItem("SteelPick", 0);
3924 void* r = player->inventory.addItem("main", item);
3928 InventoryItem *item = new ToolItem("SteelAxe", 0);
3929 void* r = player->inventory.addItem("main", item);
3933 InventoryItem *item = new ToolItem("SteelShovel", 0);
3934 void* r = player->inventory.addItem("main", item);
3942 // CONTENT_IGNORE-terminated list
3943 u8 material_items[] = {
3953 CONTENT_WATERSOURCE,
3961 u8 *mip = material_items;
3962 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3964 if(*mip == CONTENT_IGNORE)
3967 InventoryItem *item = new MaterialItem(*mip, 1);
3968 player->inventory.addItem("main", item);
3974 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3977 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3978 player->inventory.addItem("main", item);
3981 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3983 // Skip some materials
3984 if(i == CONTENT_WATER || i == CONTENT_TORCH
3985 || i == CONTENT_COALSTONE)
3988 InventoryItem *item = new MaterialItem(i, 1);
3989 player->inventory.addItem("main", item);
3995 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3996 void* r = player->inventory.addItem("main", item);
4001 v3f findSpawnPos(ServerMap &map)
4003 //return v3f(50,50,50)*BS;
4006 s16 groundheight = 0;
4008 // Try to find a good place a few times
4009 for(s32 i=0; i<1000; i++)
4012 // We're going to try to throw the player to this position
4013 nodepos = v2s16(-range + (myrand()%(range*2)),
4014 -range + (myrand()%(range*2)));
4015 v2s16 sectorpos = getNodeSectorPos(nodepos);
4016 // Get sector (NOTE: Don't get because it's slow)
4017 //m_env.getMap().emergeSector(sectorpos);
4018 // Get ground height at point (fallbacks to heightmap function)
4019 groundheight = map.findGroundLevel(nodepos);
4020 // Don't go underwater
4021 if(groundheight < WATER_LEVEL)
4023 //dstream<<"-> Underwater"<<std::endl;
4026 // Don't go to high places
4027 if(groundheight > WATER_LEVEL + 4)
4029 //dstream<<"-> Underwater"<<std::endl;
4033 // Found a good place
4034 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4038 // If no suitable place was not found, go above water at least.
4039 if(groundheight < WATER_LEVEL)
4040 groundheight = WATER_LEVEL;
4042 return intToFloat(v3s16(
4049 Player *Server::emergePlayer(const char *name, const char *password,
4053 Try to get an existing player
4055 Player *player = m_env.getPlayer(name);
4058 // If player is already connected, cancel
4059 if(player->peer_id != 0)
4061 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4066 player->peer_id = peer_id;
4068 // Reset inventory to creative if in creative mode
4069 if(g_settings.getBool("creative_mode"))
4071 setCreativeInventory(player);
4078 If player with the wanted peer_id already exists, cancel.
4080 if(m_env.getPlayer(peer_id) != NULL)
4082 dstream<<"emergePlayer(): Player with wrong name but same"
4083 " peer_id already exists"<<std::endl;
4091 player = new ServerRemotePlayer();
4092 //player->peer_id = c.peer_id;
4093 //player->peer_id = PEER_ID_INEXISTENT;
4094 player->peer_id = peer_id;
4095 player->updateName(name);
4101 dstream<<"Server: Finding spawn place for player \""
4102 <<player->getName()<<"\""<<std::endl;
4104 v3f pos = findSpawnPos(m_env.getServerMap());
4106 player->setPosition(pos);
4109 Add player to environment
4112 m_env.addPlayer(player);
4115 Add stuff to inventory
4118 if(g_settings.getBool("creative_mode"))
4120 setCreativeInventory(player);
4122 else if(g_settings.getBool("give_initial_stuff"))
4125 InventoryItem *item = new ToolItem("SteelPick", 0);
4126 void* r = player->inventory.addItem("main", item);
4130 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4131 void* r = player->inventory.addItem("main", item);
4135 InventoryItem *item = new ToolItem("SteelAxe", 0);
4136 void* r = player->inventory.addItem("main", item);
4140 InventoryItem *item = new ToolItem("SteelShovel", 0);
4141 void* r = player->inventory.addItem("main", item);
4145 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4146 void* r = player->inventory.addItem("main", item);
4150 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4151 void* r = player->inventory.addItem("main", item);
4155 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4156 void* r = player->inventory.addItem("main", item);
4160 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4161 void* r = player->inventory.addItem("main", item);
4165 InventoryItem *item = new CraftItem("Stick", 4);
4166 void* r = player->inventory.addItem("main", item);
4170 InventoryItem *item = new ToolItem("WPick", 32000);
4171 void* r = player->inventory.addItem("main", item);
4175 InventoryItem *item = new ToolItem("STPick", 32000);
4176 void* r = player->inventory.addItem("main", item);
4180 for(u16 i=0; i<4; i++)
4182 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4183 bool r = player->inventory.addItem("main", item);
4186 /*// Give some other stuff
4188 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4189 bool r = player->inventory.addItem("main", item);
4196 } // create new player
4199 void Server::handlePeerChange(PeerChange &c)
4201 JMutexAutoLock envlock(m_env_mutex);
4202 JMutexAutoLock conlock(m_con_mutex);
4204 if(c.type == PEER_ADDED)
4211 core::map<u16, RemoteClient*>::Node *n;
4212 n = m_clients.find(c.peer_id);
4213 // The client shouldn't already exist
4217 RemoteClient *client = new RemoteClient();
4218 client->peer_id = c.peer_id;
4219 m_clients.insert(client->peer_id, client);
4222 else if(c.type == PEER_REMOVED)
4229 core::map<u16, RemoteClient*>::Node *n;
4230 n = m_clients.find(c.peer_id);
4231 // The client should exist
4235 Mark objects to be not known by the client
4237 RemoteClient *client = n->getValue();
4239 for(core::map<u16, bool>::Iterator
4240 i = client->m_known_objects.getIterator();
4241 i.atEnd()==false; i++)
4244 u16 id = i.getNode()->getKey();
4245 ServerActiveObject* obj = m_env.getActiveObject(id);
4247 if(obj && obj->m_known_by_count > 0)
4248 obj->m_known_by_count--;
4251 // Collect information about leaving in chat
4252 std::wstring message;
4254 std::wstring name = L"unknown";
4255 Player *player = m_env.getPlayer(c.peer_id);
4257 name = narrow_to_wide(player->getName());
4261 message += L" left game";
4263 message += L" (timed out)";
4268 m_env.removePlayer(c.peer_id);
4271 // Set player client disconnected
4273 Player *player = m_env.getPlayer(c.peer_id);
4275 player->peer_id = 0;
4279 delete m_clients[c.peer_id];
4280 m_clients.remove(c.peer_id);
4282 // Send player info to all remaining clients
4285 // Send leave chat message to all remaining clients
4286 BroadcastChatMessage(message);
4295 void Server::handlePeerChanges()
4297 while(m_peer_change_queue.size() > 0)
4299 PeerChange c = m_peer_change_queue.pop_front();
4301 dout_server<<"Server: Handling peer change: "
4302 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4305 handlePeerChange(c);
4309 void dedicated_server_loop(Server &server, bool &kill)
4311 DSTACK(__FUNCTION_NAME);
4313 std::cout<<DTIME<<std::endl;
4314 std::cout<<"========================"<<std::endl;
4315 std::cout<<"Running dedicated server"<<std::endl;
4316 std::cout<<"========================"<<std::endl;
4317 std::cout<<std::endl;
4321 // This is kind of a hack but can be done like this
4322 // because server.step() is very light
4326 if(server.getShutdownRequested() || kill)
4328 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4332 static int counter = 0;
4338 core::list<PlayerInfo> list = server.getPlayerInfo();
4339 core::list<PlayerInfo>::Iterator i;
4340 static u32 sum_old = 0;
4341 u32 sum = PIChecksum(list);
4344 std::cout<<DTIME<<"Player info:"<<std::endl;
4345 for(i=list.begin(); i!=list.end(); i++)
4347 i->PrintLine(&std::cout);