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)
2177 if((player->privs & PRIV_BUILD) == 0)
2183 [3] v3s16 nodepos_undersurface
2184 [9] v3s16 nodepos_abovesurface
2189 2: stop digging (all parameters ignored)
2190 3: digging completed
2192 u8 action = readU8(&data[2]);
2194 p_under.X = readS16(&data[3]);
2195 p_under.Y = readS16(&data[5]);
2196 p_under.Z = readS16(&data[7]);
2198 p_over.X = readS16(&data[9]);
2199 p_over.Y = readS16(&data[11]);
2200 p_over.Z = readS16(&data[13]);
2201 u16 item_i = readU16(&data[15]);
2203 //TODO: Check that target is reasonably close
2211 NOTE: This can be used in the future to check if
2212 somebody is cheating, by checking the timing.
2219 else if(action == 2)
2222 RemoteClient *client = getClient(peer->id);
2223 JMutexAutoLock digmutex(client->m_dig_mutex);
2224 client->m_dig_tool_item = -1;
2229 3: Digging completed
2231 else if(action == 3)
2233 // Mandatory parameter; actually used for nothing
2234 core::map<v3s16, MapBlock*> modified_blocks;
2237 u8 mineral = MINERAL_NONE;
2239 bool cannot_remove_node = false;
2243 MapNode n = m_env.getMap().getNode(p_under);
2245 mineral = n.getMineral();
2246 // Get material at position
2248 // If not yet cancelled
2249 if(cannot_remove_node == false)
2251 // If it's not diggable, do nothing
2252 if(content_diggable(material) == false)
2254 derr_server<<"Server: Not finishing digging: "
2255 <<"Node not diggable"
2257 cannot_remove_node = true;
2260 // If not yet cancelled
2261 if(cannot_remove_node == false)
2263 // Get node metadata
2264 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2265 if(meta && meta->nodeRemovalDisabled() == true)
2267 derr_server<<"Server: Not finishing digging: "
2268 <<"Node metadata disables removal"
2270 cannot_remove_node = true;
2274 catch(InvalidPositionException &e)
2276 derr_server<<"Server: Not finishing digging: Node not found."
2277 <<" Adding block to emerge queue."
2279 m_emerge_queue.addBlock(peer_id,
2280 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2281 cannot_remove_node = true;
2285 If node can't be removed, set block to be re-sent to
2288 if(cannot_remove_node)
2290 derr_server<<"Server: Not finishing digging."<<std::endl;
2292 // Client probably has wrong data.
2293 // Set block not sent, so that client will get
2295 dstream<<"Client "<<peer_id<<" tried to dig "
2296 <<"node; but node cannot be removed."
2297 <<" setting MapBlock not sent."<<std::endl;
2298 RemoteClient *client = getClient(peer_id);
2299 v3s16 blockpos = getNodeBlockPos(p_under);
2300 client->SetBlockNotSent(blockpos);
2306 Send the removal to all other clients.
2307 - If other player is close, send REMOVENODE
2308 - Otherwise set blocks not sent
2310 core::list<u16> far_players;
2311 sendRemoveNode(p_under, peer_id, &far_players, 100);
2314 Update and send inventory
2317 if(g_settings.getBool("creative_mode") == false)
2322 InventoryList *mlist = player->inventory.getList("main");
2325 InventoryItem *item = mlist->getItem(item_i);
2326 if(item && (std::string)item->getName() == "ToolItem")
2328 ToolItem *titem = (ToolItem*)item;
2329 std::string toolname = titem->getToolName();
2331 // Get digging properties for material and tool
2332 DiggingProperties prop =
2333 getDiggingProperties(material, toolname);
2335 if(prop.diggable == false)
2337 derr_server<<"Server: WARNING: Player digged"
2338 <<" with impossible material + tool"
2339 <<" combination"<<std::endl;
2342 bool weared_out = titem->addWear(prop.wear);
2346 mlist->deleteItem(item_i);
2352 Add dug item to inventory
2355 InventoryItem *item = NULL;
2357 if(mineral != MINERAL_NONE)
2358 item = getDiggedMineralItem(mineral);
2363 std::string &dug_s = content_features(material).dug_item;
2366 std::istringstream is(dug_s, std::ios::binary);
2367 item = InventoryItem::deSerialize(is);
2373 // Add a item to inventory
2374 player->inventory.addItem("main", item);
2377 UpdateCrafting(player->peer_id);
2378 SendInventory(player->peer_id);
2384 (this takes some time so it is done after the quick stuff)
2386 m_ignore_map_edit_events = true;
2387 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2388 m_ignore_map_edit_events = false;
2391 Set blocks not sent to far players
2393 for(core::list<u16>::Iterator
2394 i = far_players.begin();
2395 i != far_players.end(); i++)
2398 RemoteClient *client = getClient(peer_id);
2401 client->SetBlocksNotSent(modified_blocks);
2408 else if(action == 1)
2411 InventoryList *ilist = player->inventory.getList("main");
2416 InventoryItem *item = ilist->getItem(item_i);
2418 // If there is no item, it is not possible to add it anywhere
2423 Handle material items
2425 if(std::string("MaterialItem") == item->getName())
2428 // Don't add a node if this is not a free space
2429 MapNode n2 = m_env.getMap().getNode(p_over);
2430 if(content_buildable_to(n2.d) == false)
2432 // Client probably has wrong data.
2433 // Set block not sent, so that client will get
2435 dstream<<"Client "<<peer_id<<" tried to place"
2436 <<" node in invalid position; setting"
2437 <<" MapBlock not sent."<<std::endl;
2438 RemoteClient *client = getClient(peer_id);
2439 v3s16 blockpos = getNodeBlockPos(p_over);
2440 client->SetBlockNotSent(blockpos);
2444 catch(InvalidPositionException &e)
2446 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2447 <<" Adding block to emerge queue."
2449 m_emerge_queue.addBlock(peer_id,
2450 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2454 // Reset build time counter
2455 getClient(peer->id)->m_time_from_building = 0.0;
2458 MaterialItem *mitem = (MaterialItem*)item;
2460 n.d = mitem->getMaterial();
2461 if(content_features(n.d).wall_mounted)
2462 n.dir = packDir(p_under - p_over);
2467 core::list<u16> far_players;
2468 sendAddNode(p_over, n, 0, &far_players, 100);
2473 InventoryList *ilist = player->inventory.getList("main");
2474 if(g_settings.getBool("creative_mode") == false && ilist)
2476 // Remove from inventory and send inventory
2477 if(mitem->getCount() == 1)
2478 ilist->deleteItem(item_i);
2482 UpdateCrafting(peer_id);
2483 SendInventory(peer_id);
2489 This takes some time so it is done after the quick stuff
2491 core::map<v3s16, MapBlock*> modified_blocks;
2492 m_ignore_map_edit_events = true;
2493 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2494 m_ignore_map_edit_events = false;
2497 Set blocks not sent to far players
2499 for(core::list<u16>::Iterator
2500 i = far_players.begin();
2501 i != far_players.end(); i++)
2504 RemoteClient *client = getClient(peer_id);
2507 client->SetBlocksNotSent(modified_blocks);
2511 Calculate special events
2514 /*if(n.d == CONTENT_MESE)
2517 for(s16 z=-1; z<=1; z++)
2518 for(s16 y=-1; y<=1; y++)
2519 for(s16 x=-1; x<=1; x++)
2526 Place other item (not a block)
2530 v3s16 blockpos = getNodeBlockPos(p_over);
2533 Check that the block is loaded so that the item
2534 can properly be added to the static list too
2536 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2539 derr_server<<"Error while placing object: "
2540 "block not found"<<std::endl;
2544 dout_server<<"Placing a miscellaneous item on map"
2547 // Calculate a position for it
2548 v3f pos = intToFloat(p_over, BS);
2550 pos.Y -= BS*0.25; // let it drop a bit
2552 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2553 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2558 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2562 derr_server<<"WARNING: item resulted in NULL object, "
2563 <<"not placing onto map"
2568 // Add the object to the environment
2569 m_env.addActiveObject(obj);
2571 dout_server<<"Placed object"<<std::endl;
2573 if(g_settings.getBool("creative_mode") == false)
2575 // Delete the right amount of items from the slot
2576 u16 dropcount = item->getDropCount();
2578 // Delete item if all gone
2579 if(item->getCount() <= dropcount)
2581 if(item->getCount() < dropcount)
2582 dstream<<"WARNING: Server: dropped more items"
2583 <<" than the slot contains"<<std::endl;
2585 InventoryList *ilist = player->inventory.getList("main");
2587 // Remove from inventory and send inventory
2588 ilist->deleteItem(item_i);
2590 // Else decrement it
2592 item->remove(dropcount);
2595 UpdateCrafting(peer_id);
2596 SendInventory(peer_id);
2604 Catch invalid actions
2608 derr_server<<"WARNING: Server: Invalid action "
2609 <<action<<std::endl;
2613 else if(command == TOSERVER_RELEASE)
2622 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2625 else if(command == TOSERVER_SIGNTEXT)
2627 if((player->privs & PRIV_BUILD) == 0)
2636 std::string datastring((char*)&data[2], datasize-2);
2637 std::istringstream is(datastring, std::ios_base::binary);
2640 is.read((char*)buf, 6);
2641 v3s16 blockpos = readV3S16(buf);
2642 is.read((char*)buf, 2);
2643 s16 id = readS16(buf);
2644 is.read((char*)buf, 2);
2645 u16 textlen = readU16(buf);
2647 for(u16 i=0; i<textlen; i++)
2649 is.read((char*)buf, 1);
2650 text += (char)buf[0];
2653 MapBlock *block = NULL;
2656 block = m_env.getMap().getBlockNoCreate(blockpos);
2658 catch(InvalidPositionException &e)
2660 derr_server<<"Error while setting sign text: "
2661 "block not found"<<std::endl;
2665 MapBlockObject *obj = block->getObject(id);
2668 derr_server<<"Error while setting sign text: "
2669 "object not found"<<std::endl;
2673 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2675 derr_server<<"Error while setting sign text: "
2676 "object is not a sign"<<std::endl;
2680 ((SignObject*)obj)->setText(text);
2682 obj->getBlock()->setChangedFlag();
2684 else if(command == TOSERVER_SIGNNODETEXT)
2686 if((player->privs & PRIV_BUILD) == 0)
2694 std::string datastring((char*)&data[2], datasize-2);
2695 std::istringstream is(datastring, std::ios_base::binary);
2698 is.read((char*)buf, 6);
2699 v3s16 p = readV3S16(buf);
2700 is.read((char*)buf, 2);
2701 u16 textlen = readU16(buf);
2703 for(u16 i=0; i<textlen; i++)
2705 is.read((char*)buf, 1);
2706 text += (char)buf[0];
2709 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2712 if(meta->typeId() != CONTENT_SIGN_WALL)
2714 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2715 signmeta->setText(text);
2717 v3s16 blockpos = getNodeBlockPos(p);
2718 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2721 block->setChangedFlag();
2724 for(core::map<u16, RemoteClient*>::Iterator
2725 i = m_clients.getIterator();
2726 i.atEnd()==false; i++)
2728 RemoteClient *client = i.getNode()->getValue();
2729 client->SetBlockNotSent(blockpos);
2732 else if(command == TOSERVER_INVENTORY_ACTION)
2734 /*// Ignore inventory changes if in creative mode
2735 if(g_settings.getBool("creative_mode") == true)
2737 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2741 // Strip command and create a stream
2742 std::string datastring((char*)&data[2], datasize-2);
2743 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2744 std::istringstream is(datastring, std::ios_base::binary);
2746 InventoryAction *a = InventoryAction::deSerialize(is);
2751 c.current_player = player;
2754 Handle craftresult specially if not in creative mode
2756 bool disable_action = false;
2757 if(a->getType() == IACTION_MOVE
2758 && g_settings.getBool("creative_mode") == false)
2760 IMoveAction *ma = (IMoveAction*)a;
2761 if(ma->to_inv == "current_player" &&
2762 ma->from_inv == "current_player")
2764 InventoryList *rlist = player->inventory.getList("craftresult");
2766 InventoryList *clist = player->inventory.getList("craft");
2768 InventoryList *mlist = player->inventory.getList("main");
2771 Craftresult is no longer preview if something
2774 if(ma->to_list == "craftresult"
2775 && ma->from_list != "craftresult")
2777 // If it currently is a preview, remove
2779 if(player->craftresult_is_preview)
2781 rlist->deleteItem(0);
2783 player->craftresult_is_preview = false;
2786 Crafting takes place if this condition is true.
2788 if(player->craftresult_is_preview &&
2789 ma->from_list == "craftresult")
2791 player->craftresult_is_preview = false;
2792 clist->decrementMaterials(1);
2795 If the craftresult is placed on itself, move it to
2796 main inventory instead of doing the action
2798 if(ma->to_list == "craftresult"
2799 && ma->from_list == "craftresult")
2801 disable_action = true;
2803 InventoryItem *item1 = rlist->changeItem(0, NULL);
2804 mlist->addItem(item1);
2809 if(disable_action == false)
2811 // Feed action to player inventory
2819 UpdateCrafting(player->peer_id);
2820 SendInventory(player->peer_id);
2825 dstream<<"TOSERVER_INVENTORY_ACTION: "
2826 <<"InventoryAction::deSerialize() returned NULL"
2830 else if(command == TOSERVER_CHAT_MESSAGE)
2838 std::string datastring((char*)&data[2], datasize-2);
2839 std::istringstream is(datastring, std::ios_base::binary);
2842 is.read((char*)buf, 2);
2843 u16 len = readU16(buf);
2845 std::wstring message;
2846 for(u16 i=0; i<len; i++)
2848 is.read((char*)buf, 2);
2849 message += (wchar_t)readU16(buf);
2852 // Get player name of this client
2853 std::wstring name = narrow_to_wide(player->getName());
2855 // Line to send to players
2857 // Whether to send to the player that sent the line
2858 bool send_to_sender = false;
2859 // Whether to send to other players
2860 bool send_to_others = false;
2863 std::wstring commandprefix = L"/#";
2864 if(message.substr(0, commandprefix.size()) == commandprefix)
2866 line += L"Server: ";
2868 message = message.substr(commandprefix.size());
2870 ServerCommandContext *ctx = new ServerCommandContext(
2871 str_split(message, L' '),
2877 line += ServerCommand::processCommand(ctx);
2878 send_to_sender = ctx->flags & 1;
2879 send_to_others = ctx->flags & 2;
2891 send_to_others = true;
2896 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2899 Send the message to clients
2901 for(core::map<u16, RemoteClient*>::Iterator
2902 i = m_clients.getIterator();
2903 i.atEnd() == false; i++)
2905 // Get client and check that it is valid
2906 RemoteClient *client = i.getNode()->getValue();
2907 assert(client->peer_id == i.getNode()->getKey());
2908 if(client->serialization_version == SER_FMT_VER_INVALID)
2912 bool sender_selected = (peer_id == client->peer_id);
2913 if(sender_selected == true && send_to_sender == false)
2915 if(sender_selected == false && send_to_others == false)
2918 SendChatMessage(client->peer_id, line);
2922 else if(command == TOSERVER_DAMAGE)
2924 if(g_settings.getBool("enable_damage"))
2926 std::string datastring((char*)&data[2], datasize-2);
2927 std::istringstream is(datastring, std::ios_base::binary);
2928 u8 damage = readU8(is);
2929 if(player->hp > damage)
2931 player->hp -= damage;
2937 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
2940 v3f pos = findSpawnPos(m_env.getServerMap());
2941 player->setPosition(pos);
2943 SendMovePlayer(player);
2944 SendPlayerHP(player);
2946 //TODO: Throw items around
2950 SendPlayerHP(player);
2954 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2955 "unknown command "<<command<<std::endl;
2959 catch(SendFailedException &e)
2961 derr_server<<"Server::ProcessData(): SendFailedException: "
2967 void Server::onMapEditEvent(MapEditEvent *event)
2969 dstream<<"Server::onMapEditEvent()"<<std::endl;
2970 if(m_ignore_map_edit_events)
2972 MapEditEvent *e = event->clone();
2973 m_unsent_map_edit_queue.push_back(e);
2976 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2978 if(id == "current_player")
2980 assert(c->current_player);
2981 return &(c->current_player->inventory);
2985 std::string id0 = fn.next(":");
2987 if(id0 == "nodemeta")
2990 p.X = stoi(fn.next(","));
2991 p.Y = stoi(fn.next(","));
2992 p.Z = stoi(fn.next(","));
2993 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2995 return meta->getInventory();
2996 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2997 <<"no metadata found"<<std::endl;
3001 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3004 void Server::inventoryModified(InventoryContext *c, std::string id)
3006 if(id == "current_player")
3008 assert(c->current_player);
3010 UpdateCrafting(c->current_player->peer_id);
3011 SendInventory(c->current_player->peer_id);
3016 std::string id0 = fn.next(":");
3018 if(id0 == "nodemeta")
3021 p.X = stoi(fn.next(","));
3022 p.Y = stoi(fn.next(","));
3023 p.Z = stoi(fn.next(","));
3024 v3s16 blockpos = getNodeBlockPos(p);
3026 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3028 meta->inventoryModified();
3030 for(core::map<u16, RemoteClient*>::Iterator
3031 i = m_clients.getIterator();
3032 i.atEnd()==false; i++)
3034 RemoteClient *client = i.getNode()->getValue();
3035 client->SetBlockNotSent(blockpos);
3041 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3044 core::list<PlayerInfo> Server::getPlayerInfo()
3046 DSTACK(__FUNCTION_NAME);
3047 JMutexAutoLock envlock(m_env_mutex);
3048 JMutexAutoLock conlock(m_con_mutex);
3050 core::list<PlayerInfo> list;
3052 core::list<Player*> players = m_env.getPlayers();
3054 core::list<Player*>::Iterator i;
3055 for(i = players.begin();
3056 i != players.end(); i++)
3060 Player *player = *i;
3063 con::Peer *peer = m_con.GetPeer(player->peer_id);
3064 // Copy info from peer to info struct
3066 info.address = peer->address;
3067 info.avg_rtt = peer->avg_rtt;
3069 catch(con::PeerNotFoundException &e)
3071 // Set dummy peer info
3073 info.address = Address(0,0,0,0,0);
3077 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3078 info.position = player->getPosition();
3080 list.push_back(info);
3087 void Server::peerAdded(con::Peer *peer)
3089 DSTACK(__FUNCTION_NAME);
3090 dout_server<<"Server::peerAdded(): peer->id="
3091 <<peer->id<<std::endl;
3094 c.type = PEER_ADDED;
3095 c.peer_id = peer->id;
3097 m_peer_change_queue.push_back(c);
3100 void Server::deletingPeer(con::Peer *peer, bool timeout)
3102 DSTACK(__FUNCTION_NAME);
3103 dout_server<<"Server::deletingPeer(): peer->id="
3104 <<peer->id<<", timeout="<<timeout<<std::endl;
3107 c.type = PEER_REMOVED;
3108 c.peer_id = peer->id;
3109 c.timeout = timeout;
3110 m_peer_change_queue.push_back(c);
3117 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3119 DSTACK(__FUNCTION_NAME);
3120 std::ostringstream os(std::ios_base::binary);
3122 writeU16(os, TOCLIENT_HP);
3126 std::string s = os.str();
3127 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3129 con.Send(peer_id, 0, data, true);
3133 Non-static send methods
3136 void Server::SendObjectData(float dtime)
3138 DSTACK(__FUNCTION_NAME);
3140 core::map<v3s16, bool> stepped_blocks;
3142 for(core::map<u16, RemoteClient*>::Iterator
3143 i = m_clients.getIterator();
3144 i.atEnd() == false; i++)
3146 u16 peer_id = i.getNode()->getKey();
3147 RemoteClient *client = i.getNode()->getValue();
3148 assert(client->peer_id == peer_id);
3150 if(client->serialization_version == SER_FMT_VER_INVALID)
3153 client->SendObjectData(this, dtime, stepped_blocks);
3157 void Server::SendPlayerInfos()
3159 DSTACK(__FUNCTION_NAME);
3161 //JMutexAutoLock envlock(m_env_mutex);
3163 // Get connected players
3164 core::list<Player*> players = m_env.getPlayers(true);
3166 u32 player_count = players.getSize();
3167 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3169 SharedBuffer<u8> data(datasize);
3170 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3173 core::list<Player*>::Iterator i;
3174 for(i = players.begin();
3175 i != players.end(); i++)
3177 Player *player = *i;
3179 /*dstream<<"Server sending player info for player with "
3180 "peer_id="<<player->peer_id<<std::endl;*/
3182 writeU16(&data[start], player->peer_id);
3183 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3184 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3185 start += 2+PLAYERNAME_SIZE;
3188 //JMutexAutoLock conlock(m_con_mutex);
3191 m_con.SendToAll(0, data, true);
3194 void Server::SendInventory(u16 peer_id)
3196 DSTACK(__FUNCTION_NAME);
3198 Player* player = m_env.getPlayer(peer_id);
3205 std::ostringstream os;
3206 //os.imbue(std::locale("C"));
3208 player->inventory.serialize(os);
3210 std::string s = os.str();
3212 SharedBuffer<u8> data(s.size()+2);
3213 writeU16(&data[0], TOCLIENT_INVENTORY);
3214 memcpy(&data[2], s.c_str(), s.size());
3217 m_con.Send(peer_id, 0, data, true);
3220 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3222 DSTACK(__FUNCTION_NAME);
3224 std::ostringstream os(std::ios_base::binary);
3228 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3229 os.write((char*)buf, 2);
3232 writeU16(buf, message.size());
3233 os.write((char*)buf, 2);
3236 for(u32 i=0; i<message.size(); i++)
3240 os.write((char*)buf, 2);
3244 std::string s = os.str();
3245 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3247 m_con.Send(peer_id, 0, data, true);
3250 void Server::BroadcastChatMessage(const std::wstring &message)
3252 for(core::map<u16, RemoteClient*>::Iterator
3253 i = m_clients.getIterator();
3254 i.atEnd() == false; i++)
3256 // Get client and check that it is valid
3257 RemoteClient *client = i.getNode()->getValue();
3258 assert(client->peer_id == i.getNode()->getKey());
3259 if(client->serialization_version == SER_FMT_VER_INVALID)
3262 SendChatMessage(client->peer_id, message);
3266 void Server::SendPlayerHP(Player *player)
3268 SendHP(m_con, player->peer_id, player->hp);
3271 void Server::SendMovePlayer(Player *player)
3273 DSTACK(__FUNCTION_NAME);
3274 std::ostringstream os(std::ios_base::binary);
3276 writeU16(os, TOCLIENT_MOVE_PLAYER);
3277 writeV3F1000(os, player->getPosition());
3278 writeF1000(os, player->getPitch());
3279 writeF1000(os, player->getYaw());
3282 v3f pos = player->getPosition();
3283 f32 pitch = player->getPitch();
3284 f32 yaw = player->getYaw();
3285 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3286 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3293 std::string s = os.str();
3294 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3296 m_con.Send(player->peer_id, 0, data, true);
3299 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3300 core::list<u16> *far_players, float far_d_nodes)
3302 float maxd = far_d_nodes*BS;
3303 v3f p_f = intToFloat(p, BS);
3307 SharedBuffer<u8> reply(replysize);
3308 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3309 writeS16(&reply[2], p.X);
3310 writeS16(&reply[4], p.Y);
3311 writeS16(&reply[6], p.Z);
3313 for(core::map<u16, RemoteClient*>::Iterator
3314 i = m_clients.getIterator();
3315 i.atEnd() == false; i++)
3317 // Get client and check that it is valid
3318 RemoteClient *client = i.getNode()->getValue();
3319 assert(client->peer_id == i.getNode()->getKey());
3320 if(client->serialization_version == SER_FMT_VER_INVALID)
3323 // Don't send if it's the same one
3324 if(client->peer_id == ignore_id)
3330 Player *player = m_env.getPlayer(client->peer_id);
3333 // If player is far away, only set modified blocks not sent
3334 v3f player_pos = player->getPosition();
3335 if(player_pos.getDistanceFrom(p_f) > maxd)
3337 far_players->push_back(client->peer_id);
3344 m_con.Send(client->peer_id, 0, reply, true);
3348 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3349 core::list<u16> *far_players, float far_d_nodes)
3351 float maxd = far_d_nodes*BS;
3352 v3f p_f = intToFloat(p, BS);
3354 for(core::map<u16, RemoteClient*>::Iterator
3355 i = m_clients.getIterator();
3356 i.atEnd() == false; i++)
3358 // Get client and check that it is valid
3359 RemoteClient *client = i.getNode()->getValue();
3360 assert(client->peer_id == i.getNode()->getKey());
3361 if(client->serialization_version == SER_FMT_VER_INVALID)
3364 // Don't send if it's the same one
3365 if(client->peer_id == ignore_id)
3371 Player *player = m_env.getPlayer(client->peer_id);
3374 // If player is far away, only set modified blocks not sent
3375 v3f player_pos = player->getPosition();
3376 if(player_pos.getDistanceFrom(p_f) > maxd)
3378 far_players->push_back(client->peer_id);
3385 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3386 SharedBuffer<u8> reply(replysize);
3387 writeU16(&reply[0], TOCLIENT_ADDNODE);
3388 writeS16(&reply[2], p.X);
3389 writeS16(&reply[4], p.Y);
3390 writeS16(&reply[6], p.Z);
3391 n.serialize(&reply[8], client->serialization_version);
3394 m_con.Send(client->peer_id, 0, reply, true);
3398 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3400 DSTACK(__FUNCTION_NAME);
3402 v3s16 p = block->getPos();
3406 bool completely_air = true;
3407 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3408 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3409 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3411 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3413 completely_air = false;
3414 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3419 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3421 dstream<<"[completely air] ";
3426 Create a packet with the block in the right format
3429 std::ostringstream os(std::ios_base::binary);
3430 block->serialize(os, ver);
3431 std::string s = os.str();
3432 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3434 u32 replysize = 8 + blockdata.getSize();
3435 SharedBuffer<u8> reply(replysize);
3436 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3437 writeS16(&reply[2], p.X);
3438 writeS16(&reply[4], p.Y);
3439 writeS16(&reply[6], p.Z);
3440 memcpy(&reply[8], *blockdata, blockdata.getSize());
3442 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3443 <<": \tpacket size: "<<replysize<<std::endl;*/
3448 m_con.Send(peer_id, 1, reply, true);
3451 void Server::SendBlocks(float dtime)
3453 DSTACK(__FUNCTION_NAME);
3455 JMutexAutoLock envlock(m_env_mutex);
3456 JMutexAutoLock conlock(m_con_mutex);
3458 //TimeTaker timer("Server::SendBlocks");
3460 core::array<PrioritySortedBlockTransfer> queue;
3462 s32 total_sending = 0;
3464 for(core::map<u16, RemoteClient*>::Iterator
3465 i = m_clients.getIterator();
3466 i.atEnd() == false; i++)
3468 RemoteClient *client = i.getNode()->getValue();
3469 assert(client->peer_id == i.getNode()->getKey());
3471 total_sending += client->SendingCount();
3473 if(client->serialization_version == SER_FMT_VER_INVALID)
3476 client->GetNextBlocks(this, dtime, queue);
3480 // Lowest priority number comes first.
3481 // Lowest is most important.
3484 for(u32 i=0; i<queue.size(); i++)
3486 //TODO: Calculate limit dynamically
3487 if(total_sending >= g_settings.getS32
3488 ("max_simultaneous_block_sends_server_total"))
3491 PrioritySortedBlockTransfer q = queue[i];
3493 MapBlock *block = NULL;
3496 block = m_env.getMap().getBlockNoCreate(q.pos);
3498 catch(InvalidPositionException &e)
3503 RemoteClient *client = getClient(q.peer_id);
3505 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3507 client->SentBlock(q.pos);
3517 void Server::UpdateCrafting(u16 peer_id)
3519 DSTACK(__FUNCTION_NAME);
3521 Player* player = m_env.getPlayer(peer_id);
3525 Calculate crafting stuff
3527 if(g_settings.getBool("creative_mode") == false)
3529 InventoryList *clist = player->inventory.getList("craft");
3530 InventoryList *rlist = player->inventory.getList("craftresult");
3532 if(rlist->getUsedSlots() == 0)
3533 player->craftresult_is_preview = true;
3535 if(rlist && player->craftresult_is_preview)
3537 rlist->clearItems();
3539 if(clist && rlist && player->craftresult_is_preview)
3541 InventoryItem *items[9];
3542 for(u16 i=0; i<9; i++)
3544 items[i] = clist->getItem(i);
3553 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3554 if(checkItemCombination(items, specs))
3556 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3565 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3566 if(checkItemCombination(items, specs))
3568 rlist->addItem(new CraftItem("Stick", 4));
3577 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3578 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3579 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3580 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3581 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3582 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3583 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3584 if(checkItemCombination(items, specs))
3586 //rlist->addItem(new MapBlockObjectItem("Sign"));
3587 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3596 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3597 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3598 if(checkItemCombination(items, specs))
3600 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3609 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3610 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3611 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3612 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3613 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3614 if(checkItemCombination(items, specs))
3616 rlist->addItem(new ToolItem("WPick", 0));
3625 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3626 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3627 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3628 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3629 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3630 if(checkItemCombination(items, specs))
3632 rlist->addItem(new ToolItem("STPick", 0));
3641 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3642 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3643 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3644 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3645 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3646 if(checkItemCombination(items, specs))
3648 rlist->addItem(new ToolItem("SteelPick", 0));
3657 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3658 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3659 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3660 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3661 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3662 if(checkItemCombination(items, specs))
3664 rlist->addItem(new ToolItem("MesePick", 0));
3673 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3674 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3675 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3676 if(checkItemCombination(items, specs))
3678 rlist->addItem(new ToolItem("WShovel", 0));
3687 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3688 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3689 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3690 if(checkItemCombination(items, specs))
3692 rlist->addItem(new ToolItem("STShovel", 0));
3701 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3702 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3703 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3704 if(checkItemCombination(items, specs))
3706 rlist->addItem(new ToolItem("SteelShovel", 0));
3715 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3716 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3717 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3718 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3719 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3720 if(checkItemCombination(items, specs))
3722 rlist->addItem(new ToolItem("WAxe", 0));
3731 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3732 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3733 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3734 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3735 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3736 if(checkItemCombination(items, specs))
3738 rlist->addItem(new ToolItem("STAxe", 0));
3747 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3748 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3749 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3750 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3751 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3752 if(checkItemCombination(items, specs))
3754 rlist->addItem(new ToolItem("SteelAxe", 0));
3763 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3764 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3765 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3766 if(checkItemCombination(items, specs))
3768 rlist->addItem(new ToolItem("WSword", 0));
3777 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3778 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3779 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3780 if(checkItemCombination(items, specs))
3782 rlist->addItem(new ToolItem("STSword", 0));
3791 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3792 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3793 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3794 if(checkItemCombination(items, specs))
3796 rlist->addItem(new ToolItem("SteelSword", 0));
3805 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3806 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3807 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3808 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3809 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3810 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3811 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3812 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3813 if(checkItemCombination(items, specs))
3815 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3824 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3825 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3826 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3827 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3828 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3829 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3830 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3831 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3832 if(checkItemCombination(items, specs))
3834 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3843 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3844 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3845 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3846 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3847 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3848 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3849 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3850 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3851 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3852 if(checkItemCombination(items, specs))
3854 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3860 } // if creative_mode == false
3863 RemoteClient* Server::getClient(u16 peer_id)
3865 DSTACK(__FUNCTION_NAME);
3866 //JMutexAutoLock lock(m_con_mutex);
3867 core::map<u16, RemoteClient*>::Node *n;
3868 n = m_clients.find(peer_id);
3869 // A client should exist for all peers
3871 return n->getValue();
3874 std::wstring Server::getStatusString()
3876 std::wostringstream os(std::ios_base::binary);
3879 os<<L"version="<<narrow_to_wide(VERSION_STRING);
3881 os<<L", uptime="<<m_uptime.get();
3882 // Information about clients
3884 for(core::map<u16, RemoteClient*>::Iterator
3885 i = m_clients.getIterator();
3886 i.atEnd() == false; i++)
3888 // Get client and check that it is valid
3889 RemoteClient *client = i.getNode()->getValue();
3890 assert(client->peer_id == i.getNode()->getKey());
3891 if(client->serialization_version == SER_FMT_VER_INVALID)
3894 Player *player = m_env.getPlayer(client->peer_id);
3895 // Get name of player
3896 std::wstring name = L"unknown";
3898 name = narrow_to_wide(player->getName());
3899 // Add name to information string
3903 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3904 os<<" WARNING: Map saving is disabled."<<std::endl;
3909 void setCreativeInventory(Player *player)
3911 player->resetInventory();
3913 // Give some good tools
3915 InventoryItem *item = new ToolItem("MesePick", 0);
3916 void* r = player->inventory.addItem("main", item);
3920 InventoryItem *item = new ToolItem("SteelPick", 0);
3921 void* r = player->inventory.addItem("main", item);
3925 InventoryItem *item = new ToolItem("SteelAxe", 0);
3926 void* r = player->inventory.addItem("main", item);
3930 InventoryItem *item = new ToolItem("SteelShovel", 0);
3931 void* r = player->inventory.addItem("main", item);
3939 // CONTENT_IGNORE-terminated list
3940 u8 material_items[] = {
3950 CONTENT_WATERSOURCE,
3958 u8 *mip = material_items;
3959 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3961 if(*mip == CONTENT_IGNORE)
3964 InventoryItem *item = new MaterialItem(*mip, 1);
3965 player->inventory.addItem("main", item);
3971 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3974 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3975 player->inventory.addItem("main", item);
3978 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3980 // Skip some materials
3981 if(i == CONTENT_WATER || i == CONTENT_TORCH
3982 || i == CONTENT_COALSTONE)
3985 InventoryItem *item = new MaterialItem(i, 1);
3986 player->inventory.addItem("main", item);
3992 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3993 void* r = player->inventory.addItem("main", item);
3998 v3f findSpawnPos(ServerMap &map)
4000 //return v3f(50,50,50)*BS;
4003 s16 groundheight = 0;
4005 // Try to find a good place a few times
4006 for(s32 i=0; i<1000; i++)
4009 // We're going to try to throw the player to this position
4010 nodepos = v2s16(-range + (myrand()%(range*2)),
4011 -range + (myrand()%(range*2)));
4012 v2s16 sectorpos = getNodeSectorPos(nodepos);
4013 // Get sector (NOTE: Don't get because it's slow)
4014 //m_env.getMap().emergeSector(sectorpos);
4015 // Get ground height at point (fallbacks to heightmap function)
4016 groundheight = map.findGroundLevel(nodepos);
4017 // Don't go underwater
4018 if(groundheight < WATER_LEVEL)
4020 //dstream<<"-> Underwater"<<std::endl;
4023 // Don't go to high places
4024 if(groundheight > WATER_LEVEL + 4)
4026 //dstream<<"-> Underwater"<<std::endl;
4030 // Found a good place
4031 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4035 // If no suitable place was not found, go above water at least.
4036 if(groundheight < WATER_LEVEL)
4037 groundheight = WATER_LEVEL;
4039 return intToFloat(v3s16(
4046 Player *Server::emergePlayer(const char *name, const char *password,
4050 Try to get an existing player
4052 Player *player = m_env.getPlayer(name);
4055 // If player is already connected, cancel
4056 if(player->peer_id != 0)
4058 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4063 player->peer_id = peer_id;
4065 // Reset inventory to creative if in creative mode
4066 if(g_settings.getBool("creative_mode"))
4068 setCreativeInventory(player);
4075 If player with the wanted peer_id already exists, cancel.
4077 if(m_env.getPlayer(peer_id) != NULL)
4079 dstream<<"emergePlayer(): Player with wrong name but same"
4080 " peer_id already exists"<<std::endl;
4088 player = new ServerRemotePlayer();
4089 //player->peer_id = c.peer_id;
4090 //player->peer_id = PEER_ID_INEXISTENT;
4091 player->peer_id = peer_id;
4092 player->updateName(name);
4098 dstream<<"Server: Finding spawn place for player \""
4099 <<player->getName()<<"\""<<std::endl;
4101 v3f pos = findSpawnPos(m_env.getServerMap());
4103 player->setPosition(pos);
4106 Add player to environment
4109 m_env.addPlayer(player);
4112 Add stuff to inventory
4115 if(g_settings.getBool("creative_mode"))
4117 setCreativeInventory(player);
4119 else if(g_settings.getBool("give_initial_stuff"))
4122 InventoryItem *item = new ToolItem("SteelPick", 0);
4123 void* r = player->inventory.addItem("main", item);
4127 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4128 void* r = player->inventory.addItem("main", item);
4132 InventoryItem *item = new ToolItem("SteelAxe", 0);
4133 void* r = player->inventory.addItem("main", item);
4137 InventoryItem *item = new ToolItem("SteelShovel", 0);
4138 void* r = player->inventory.addItem("main", item);
4142 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4143 void* r = player->inventory.addItem("main", item);
4147 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4148 void* r = player->inventory.addItem("main", item);
4152 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4153 void* r = player->inventory.addItem("main", item);
4157 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4158 void* r = player->inventory.addItem("main", item);
4162 InventoryItem *item = new CraftItem("Stick", 4);
4163 void* r = player->inventory.addItem("main", item);
4167 InventoryItem *item = new ToolItem("WPick", 32000);
4168 void* r = player->inventory.addItem("main", item);
4172 InventoryItem *item = new ToolItem("STPick", 32000);
4173 void* r = player->inventory.addItem("main", item);
4177 for(u16 i=0; i<4; i++)
4179 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4180 bool r = player->inventory.addItem("main", item);
4183 /*// Give some other stuff
4185 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4186 bool r = player->inventory.addItem("main", item);
4193 } // create new player
4196 void Server::handlePeerChange(PeerChange &c)
4198 JMutexAutoLock envlock(m_env_mutex);
4199 JMutexAutoLock conlock(m_con_mutex);
4201 if(c.type == PEER_ADDED)
4208 core::map<u16, RemoteClient*>::Node *n;
4209 n = m_clients.find(c.peer_id);
4210 // The client shouldn't already exist
4214 RemoteClient *client = new RemoteClient();
4215 client->peer_id = c.peer_id;
4216 m_clients.insert(client->peer_id, client);
4219 else if(c.type == PEER_REMOVED)
4226 core::map<u16, RemoteClient*>::Node *n;
4227 n = m_clients.find(c.peer_id);
4228 // The client should exist
4232 Mark objects to be not known by the client
4234 RemoteClient *client = n->getValue();
4236 for(core::map<u16, bool>::Iterator
4237 i = client->m_known_objects.getIterator();
4238 i.atEnd()==false; i++)
4241 u16 id = i.getNode()->getKey();
4242 ServerActiveObject* obj = m_env.getActiveObject(id);
4244 if(obj && obj->m_known_by_count > 0)
4245 obj->m_known_by_count--;
4248 // Collect information about leaving in chat
4249 std::wstring message;
4251 std::wstring name = L"unknown";
4252 Player *player = m_env.getPlayer(c.peer_id);
4254 name = narrow_to_wide(player->getName());
4258 message += L" left game";
4260 message += L" (timed out)";
4265 m_env.removePlayer(c.peer_id);
4268 // Set player client disconnected
4270 Player *player = m_env.getPlayer(c.peer_id);
4272 player->peer_id = 0;
4276 delete m_clients[c.peer_id];
4277 m_clients.remove(c.peer_id);
4279 // Send player info to all remaining clients
4282 // Send leave chat message to all remaining clients
4283 BroadcastChatMessage(message);
4292 void Server::handlePeerChanges()
4294 while(m_peer_change_queue.size() > 0)
4296 PeerChange c = m_peer_change_queue.pop_front();
4298 dout_server<<"Server: Handling peer change: "
4299 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4302 handlePeerChange(c);
4306 void dedicated_server_loop(Server &server, bool &kill)
4308 DSTACK(__FUNCTION_NAME);
4310 std::cout<<DTIME<<std::endl;
4311 std::cout<<"========================"<<std::endl;
4312 std::cout<<"Running dedicated server"<<std::endl;
4313 std::cout<<"========================"<<std::endl;
4314 std::cout<<std::endl;
4318 // This is kind of a hack but can be done like this
4319 // because server.step() is very light
4323 if(server.getShutdownRequested() || kill)
4325 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4329 static int counter = 0;
4335 core::list<PlayerInfo> list = server.getPlayerInfo();
4336 core::list<PlayerInfo>::Iterator i;
4337 static u32 sum_old = 0;
4338 u32 sum = PIChecksum(list);
4341 std::cout<<DTIME<<"Player info:"<<std::endl;
4342 for(i=list.begin(); i!=list.end(); i++)
4344 i->PrintLine(&std::cout);