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;
2234 u8 material = CONTENT_IGNORE;
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 // Local player gets all privileges regardless of
2874 // what's set on their account.
2875 u64 privs = player->privs;
2876 if(g_settings.get("name") == player->getName())
2879 ServerCommandContext *ctx = new ServerCommandContext(
2880 str_split(message, L' '),
2886 line += processServerCommand(ctx);
2887 send_to_sender = ctx->flags & 1;
2888 send_to_others = ctx->flags & 2;
2900 send_to_others = true;
2905 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2908 Send the message to clients
2910 for(core::map<u16, RemoteClient*>::Iterator
2911 i = m_clients.getIterator();
2912 i.atEnd() == false; i++)
2914 // Get client and check that it is valid
2915 RemoteClient *client = i.getNode()->getValue();
2916 assert(client->peer_id == i.getNode()->getKey());
2917 if(client->serialization_version == SER_FMT_VER_INVALID)
2921 bool sender_selected = (peer_id == client->peer_id);
2922 if(sender_selected == true && send_to_sender == false)
2924 if(sender_selected == false && send_to_others == false)
2927 SendChatMessage(client->peer_id, line);
2931 else if(command == TOSERVER_DAMAGE)
2933 if(g_settings.getBool("enable_damage"))
2935 std::string datastring((char*)&data[2], datasize-2);
2936 std::istringstream is(datastring, std::ios_base::binary);
2937 u8 damage = readU8(is);
2938 if(player->hp > damage)
2940 player->hp -= damage;
2946 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
2949 v3f pos = findSpawnPos(m_env.getServerMap());
2950 player->setPosition(pos);
2952 SendMovePlayer(player);
2953 SendPlayerHP(player);
2955 //TODO: Throw items around
2959 SendPlayerHP(player);
2963 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2964 "unknown command "<<command<<std::endl;
2968 catch(SendFailedException &e)
2970 derr_server<<"Server::ProcessData(): SendFailedException: "
2976 void Server::onMapEditEvent(MapEditEvent *event)
2978 dstream<<"Server::onMapEditEvent()"<<std::endl;
2979 if(m_ignore_map_edit_events)
2981 MapEditEvent *e = event->clone();
2982 m_unsent_map_edit_queue.push_back(e);
2985 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2987 if(id == "current_player")
2989 assert(c->current_player);
2990 return &(c->current_player->inventory);
2994 std::string id0 = fn.next(":");
2996 if(id0 == "nodemeta")
2999 p.X = stoi(fn.next(","));
3000 p.Y = stoi(fn.next(","));
3001 p.Z = stoi(fn.next(","));
3002 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3004 return meta->getInventory();
3005 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3006 <<"no metadata found"<<std::endl;
3010 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3013 void Server::inventoryModified(InventoryContext *c, std::string id)
3015 if(id == "current_player")
3017 assert(c->current_player);
3019 UpdateCrafting(c->current_player->peer_id);
3020 SendInventory(c->current_player->peer_id);
3025 std::string id0 = fn.next(":");
3027 if(id0 == "nodemeta")
3030 p.X = stoi(fn.next(","));
3031 p.Y = stoi(fn.next(","));
3032 p.Z = stoi(fn.next(","));
3033 v3s16 blockpos = getNodeBlockPos(p);
3035 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3037 meta->inventoryModified();
3039 for(core::map<u16, RemoteClient*>::Iterator
3040 i = m_clients.getIterator();
3041 i.atEnd()==false; i++)
3043 RemoteClient *client = i.getNode()->getValue();
3044 client->SetBlockNotSent(blockpos);
3050 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3053 core::list<PlayerInfo> Server::getPlayerInfo()
3055 DSTACK(__FUNCTION_NAME);
3056 JMutexAutoLock envlock(m_env_mutex);
3057 JMutexAutoLock conlock(m_con_mutex);
3059 core::list<PlayerInfo> list;
3061 core::list<Player*> players = m_env.getPlayers();
3063 core::list<Player*>::Iterator i;
3064 for(i = players.begin();
3065 i != players.end(); i++)
3069 Player *player = *i;
3072 con::Peer *peer = m_con.GetPeer(player->peer_id);
3073 // Copy info from peer to info struct
3075 info.address = peer->address;
3076 info.avg_rtt = peer->avg_rtt;
3078 catch(con::PeerNotFoundException &e)
3080 // Set dummy peer info
3082 info.address = Address(0,0,0,0,0);
3086 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3087 info.position = player->getPosition();
3089 list.push_back(info);
3096 void Server::peerAdded(con::Peer *peer)
3098 DSTACK(__FUNCTION_NAME);
3099 dout_server<<"Server::peerAdded(): peer->id="
3100 <<peer->id<<std::endl;
3103 c.type = PEER_ADDED;
3104 c.peer_id = peer->id;
3106 m_peer_change_queue.push_back(c);
3109 void Server::deletingPeer(con::Peer *peer, bool timeout)
3111 DSTACK(__FUNCTION_NAME);
3112 dout_server<<"Server::deletingPeer(): peer->id="
3113 <<peer->id<<", timeout="<<timeout<<std::endl;
3116 c.type = PEER_REMOVED;
3117 c.peer_id = peer->id;
3118 c.timeout = timeout;
3119 m_peer_change_queue.push_back(c);
3126 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3128 DSTACK(__FUNCTION_NAME);
3129 std::ostringstream os(std::ios_base::binary);
3131 writeU16(os, TOCLIENT_HP);
3135 std::string s = os.str();
3136 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3138 con.Send(peer_id, 0, data, true);
3142 Non-static send methods
3145 void Server::SendObjectData(float dtime)
3147 DSTACK(__FUNCTION_NAME);
3149 core::map<v3s16, bool> stepped_blocks;
3151 for(core::map<u16, RemoteClient*>::Iterator
3152 i = m_clients.getIterator();
3153 i.atEnd() == false; i++)
3155 u16 peer_id = i.getNode()->getKey();
3156 RemoteClient *client = i.getNode()->getValue();
3157 assert(client->peer_id == peer_id);
3159 if(client->serialization_version == SER_FMT_VER_INVALID)
3162 client->SendObjectData(this, dtime, stepped_blocks);
3166 void Server::SendPlayerInfos()
3168 DSTACK(__FUNCTION_NAME);
3170 //JMutexAutoLock envlock(m_env_mutex);
3172 // Get connected players
3173 core::list<Player*> players = m_env.getPlayers(true);
3175 u32 player_count = players.getSize();
3176 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3178 SharedBuffer<u8> data(datasize);
3179 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3182 core::list<Player*>::Iterator i;
3183 for(i = players.begin();
3184 i != players.end(); i++)
3186 Player *player = *i;
3188 /*dstream<<"Server sending player info for player with "
3189 "peer_id="<<player->peer_id<<std::endl;*/
3191 writeU16(&data[start], player->peer_id);
3192 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3193 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3194 start += 2+PLAYERNAME_SIZE;
3197 //JMutexAutoLock conlock(m_con_mutex);
3200 m_con.SendToAll(0, data, true);
3203 void Server::SendInventory(u16 peer_id)
3205 DSTACK(__FUNCTION_NAME);
3207 Player* player = m_env.getPlayer(peer_id);
3214 std::ostringstream os;
3215 //os.imbue(std::locale("C"));
3217 player->inventory.serialize(os);
3219 std::string s = os.str();
3221 SharedBuffer<u8> data(s.size()+2);
3222 writeU16(&data[0], TOCLIENT_INVENTORY);
3223 memcpy(&data[2], s.c_str(), s.size());
3226 m_con.Send(peer_id, 0, data, true);
3229 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3231 DSTACK(__FUNCTION_NAME);
3233 std::ostringstream os(std::ios_base::binary);
3237 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3238 os.write((char*)buf, 2);
3241 writeU16(buf, message.size());
3242 os.write((char*)buf, 2);
3245 for(u32 i=0; i<message.size(); i++)
3249 os.write((char*)buf, 2);
3253 std::string s = os.str();
3254 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3256 m_con.Send(peer_id, 0, data, true);
3259 void Server::BroadcastChatMessage(const std::wstring &message)
3261 for(core::map<u16, RemoteClient*>::Iterator
3262 i = m_clients.getIterator();
3263 i.atEnd() == false; i++)
3265 // Get client and check that it is valid
3266 RemoteClient *client = i.getNode()->getValue();
3267 assert(client->peer_id == i.getNode()->getKey());
3268 if(client->serialization_version == SER_FMT_VER_INVALID)
3271 SendChatMessage(client->peer_id, message);
3275 void Server::SendPlayerHP(Player *player)
3277 SendHP(m_con, player->peer_id, player->hp);
3280 void Server::SendMovePlayer(Player *player)
3282 DSTACK(__FUNCTION_NAME);
3283 std::ostringstream os(std::ios_base::binary);
3285 writeU16(os, TOCLIENT_MOVE_PLAYER);
3286 writeV3F1000(os, player->getPosition());
3287 writeF1000(os, player->getPitch());
3288 writeF1000(os, player->getYaw());
3291 v3f pos = player->getPosition();
3292 f32 pitch = player->getPitch();
3293 f32 yaw = player->getYaw();
3294 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3295 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3302 std::string s = os.str();
3303 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3305 m_con.Send(player->peer_id, 0, data, true);
3308 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3309 core::list<u16> *far_players, float far_d_nodes)
3311 float maxd = far_d_nodes*BS;
3312 v3f p_f = intToFloat(p, BS);
3316 SharedBuffer<u8> reply(replysize);
3317 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3318 writeS16(&reply[2], p.X);
3319 writeS16(&reply[4], p.Y);
3320 writeS16(&reply[6], p.Z);
3322 for(core::map<u16, RemoteClient*>::Iterator
3323 i = m_clients.getIterator();
3324 i.atEnd() == false; i++)
3326 // Get client and check that it is valid
3327 RemoteClient *client = i.getNode()->getValue();
3328 assert(client->peer_id == i.getNode()->getKey());
3329 if(client->serialization_version == SER_FMT_VER_INVALID)
3332 // Don't send if it's the same one
3333 if(client->peer_id == ignore_id)
3339 Player *player = m_env.getPlayer(client->peer_id);
3342 // If player is far away, only set modified blocks not sent
3343 v3f player_pos = player->getPosition();
3344 if(player_pos.getDistanceFrom(p_f) > maxd)
3346 far_players->push_back(client->peer_id);
3353 m_con.Send(client->peer_id, 0, reply, true);
3357 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3358 core::list<u16> *far_players, float far_d_nodes)
3360 float maxd = far_d_nodes*BS;
3361 v3f p_f = intToFloat(p, BS);
3363 for(core::map<u16, RemoteClient*>::Iterator
3364 i = m_clients.getIterator();
3365 i.atEnd() == false; i++)
3367 // Get client and check that it is valid
3368 RemoteClient *client = i.getNode()->getValue();
3369 assert(client->peer_id == i.getNode()->getKey());
3370 if(client->serialization_version == SER_FMT_VER_INVALID)
3373 // Don't send if it's the same one
3374 if(client->peer_id == ignore_id)
3380 Player *player = m_env.getPlayer(client->peer_id);
3383 // If player is far away, only set modified blocks not sent
3384 v3f player_pos = player->getPosition();
3385 if(player_pos.getDistanceFrom(p_f) > maxd)
3387 far_players->push_back(client->peer_id);
3394 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3395 SharedBuffer<u8> reply(replysize);
3396 writeU16(&reply[0], TOCLIENT_ADDNODE);
3397 writeS16(&reply[2], p.X);
3398 writeS16(&reply[4], p.Y);
3399 writeS16(&reply[6], p.Z);
3400 n.serialize(&reply[8], client->serialization_version);
3403 m_con.Send(client->peer_id, 0, reply, true);
3407 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3409 DSTACK(__FUNCTION_NAME);
3411 v3s16 p = block->getPos();
3415 bool completely_air = true;
3416 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3417 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3418 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3420 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3422 completely_air = false;
3423 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3428 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3430 dstream<<"[completely air] ";
3435 Create a packet with the block in the right format
3438 std::ostringstream os(std::ios_base::binary);
3439 block->serialize(os, ver);
3440 std::string s = os.str();
3441 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3443 u32 replysize = 8 + blockdata.getSize();
3444 SharedBuffer<u8> reply(replysize);
3445 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3446 writeS16(&reply[2], p.X);
3447 writeS16(&reply[4], p.Y);
3448 writeS16(&reply[6], p.Z);
3449 memcpy(&reply[8], *blockdata, blockdata.getSize());
3451 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3452 <<": \tpacket size: "<<replysize<<std::endl;*/
3457 m_con.Send(peer_id, 1, reply, true);
3460 void Server::SendBlocks(float dtime)
3462 DSTACK(__FUNCTION_NAME);
3464 JMutexAutoLock envlock(m_env_mutex);
3465 JMutexAutoLock conlock(m_con_mutex);
3467 //TimeTaker timer("Server::SendBlocks");
3469 core::array<PrioritySortedBlockTransfer> queue;
3471 s32 total_sending = 0;
3473 for(core::map<u16, RemoteClient*>::Iterator
3474 i = m_clients.getIterator();
3475 i.atEnd() == false; i++)
3477 RemoteClient *client = i.getNode()->getValue();
3478 assert(client->peer_id == i.getNode()->getKey());
3480 total_sending += client->SendingCount();
3482 if(client->serialization_version == SER_FMT_VER_INVALID)
3485 client->GetNextBlocks(this, dtime, queue);
3489 // Lowest priority number comes first.
3490 // Lowest is most important.
3493 for(u32 i=0; i<queue.size(); i++)
3495 //TODO: Calculate limit dynamically
3496 if(total_sending >= g_settings.getS32
3497 ("max_simultaneous_block_sends_server_total"))
3500 PrioritySortedBlockTransfer q = queue[i];
3502 MapBlock *block = NULL;
3505 block = m_env.getMap().getBlockNoCreate(q.pos);
3507 catch(InvalidPositionException &e)
3512 RemoteClient *client = getClient(q.peer_id);
3514 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3516 client->SentBlock(q.pos);
3526 void Server::UpdateCrafting(u16 peer_id)
3528 DSTACK(__FUNCTION_NAME);
3530 Player* player = m_env.getPlayer(peer_id);
3534 Calculate crafting stuff
3536 if(g_settings.getBool("creative_mode") == false)
3538 InventoryList *clist = player->inventory.getList("craft");
3539 InventoryList *rlist = player->inventory.getList("craftresult");
3541 if(rlist->getUsedSlots() == 0)
3542 player->craftresult_is_preview = true;
3544 if(rlist && player->craftresult_is_preview)
3546 rlist->clearItems();
3548 if(clist && rlist && player->craftresult_is_preview)
3550 InventoryItem *items[9];
3551 for(u16 i=0; i<9; i++)
3553 items[i] = clist->getItem(i);
3562 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3563 if(checkItemCombination(items, specs))
3565 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3574 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3575 if(checkItemCombination(items, specs))
3577 rlist->addItem(new CraftItem("Stick", 4));
3586 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3587 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3588 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3589 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3590 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3591 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3592 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3593 if(checkItemCombination(items, specs))
3595 //rlist->addItem(new MapBlockObjectItem("Sign"));
3596 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3605 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3606 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3607 if(checkItemCombination(items, specs))
3609 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3618 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3619 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3620 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3621 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3622 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3623 if(checkItemCombination(items, specs))
3625 rlist->addItem(new ToolItem("WPick", 0));
3634 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3635 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3636 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3637 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3638 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3639 if(checkItemCombination(items, specs))
3641 rlist->addItem(new ToolItem("STPick", 0));
3650 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3651 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3652 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3653 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3654 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3655 if(checkItemCombination(items, specs))
3657 rlist->addItem(new ToolItem("SteelPick", 0));
3666 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3667 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3668 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3669 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3670 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3671 if(checkItemCombination(items, specs))
3673 rlist->addItem(new ToolItem("MesePick", 0));
3682 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3683 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3684 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3685 if(checkItemCombination(items, specs))
3687 rlist->addItem(new ToolItem("WShovel", 0));
3696 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3697 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3698 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3699 if(checkItemCombination(items, specs))
3701 rlist->addItem(new ToolItem("STShovel", 0));
3710 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3711 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3712 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3713 if(checkItemCombination(items, specs))
3715 rlist->addItem(new ToolItem("SteelShovel", 0));
3724 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3725 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3726 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3727 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3728 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3729 if(checkItemCombination(items, specs))
3731 rlist->addItem(new ToolItem("WAxe", 0));
3740 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3741 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3742 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3743 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3744 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3745 if(checkItemCombination(items, specs))
3747 rlist->addItem(new ToolItem("STAxe", 0));
3756 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3757 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3758 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3759 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3760 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3761 if(checkItemCombination(items, specs))
3763 rlist->addItem(new ToolItem("SteelAxe", 0));
3772 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3773 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3774 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3775 if(checkItemCombination(items, specs))
3777 rlist->addItem(new ToolItem("WSword", 0));
3786 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3787 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3788 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3789 if(checkItemCombination(items, specs))
3791 rlist->addItem(new ToolItem("STSword", 0));
3800 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3801 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3802 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3803 if(checkItemCombination(items, specs))
3805 rlist->addItem(new ToolItem("SteelSword", 0));
3814 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3815 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3816 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3817 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3818 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3819 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3820 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3821 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3822 if(checkItemCombination(items, specs))
3824 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3833 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3834 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3835 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3836 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3837 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3838 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3839 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3840 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3841 if(checkItemCombination(items, specs))
3843 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3852 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3853 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3854 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3855 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3856 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3857 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3858 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3859 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3860 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3861 if(checkItemCombination(items, specs))
3863 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3869 } // if creative_mode == false
3872 RemoteClient* Server::getClient(u16 peer_id)
3874 DSTACK(__FUNCTION_NAME);
3875 //JMutexAutoLock lock(m_con_mutex);
3876 core::map<u16, RemoteClient*>::Node *n;
3877 n = m_clients.find(peer_id);
3878 // A client should exist for all peers
3880 return n->getValue();
3883 std::wstring Server::getStatusString()
3885 std::wostringstream os(std::ios_base::binary);
3888 os<<L"version="<<narrow_to_wide(VERSION_STRING);
3890 os<<L", uptime="<<m_uptime.get();
3891 // Information about clients
3893 for(core::map<u16, RemoteClient*>::Iterator
3894 i = m_clients.getIterator();
3895 i.atEnd() == false; i++)
3897 // Get client and check that it is valid
3898 RemoteClient *client = i.getNode()->getValue();
3899 assert(client->peer_id == i.getNode()->getKey());
3900 if(client->serialization_version == SER_FMT_VER_INVALID)
3903 Player *player = m_env.getPlayer(client->peer_id);
3904 // Get name of player
3905 std::wstring name = L"unknown";
3907 name = narrow_to_wide(player->getName());
3908 // Add name to information string
3912 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3913 os<<" WARNING: Map saving is disabled."<<std::endl;
3918 void setCreativeInventory(Player *player)
3920 player->resetInventory();
3922 // Give some good tools
3924 InventoryItem *item = new ToolItem("MesePick", 0);
3925 void* r = player->inventory.addItem("main", item);
3929 InventoryItem *item = new ToolItem("SteelPick", 0);
3930 void* r = player->inventory.addItem("main", item);
3934 InventoryItem *item = new ToolItem("SteelAxe", 0);
3935 void* r = player->inventory.addItem("main", item);
3939 InventoryItem *item = new ToolItem("SteelShovel", 0);
3940 void* r = player->inventory.addItem("main", item);
3948 // CONTENT_IGNORE-terminated list
3949 u8 material_items[] = {
3959 CONTENT_WATERSOURCE,
3967 u8 *mip = material_items;
3968 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3970 if(*mip == CONTENT_IGNORE)
3973 InventoryItem *item = new MaterialItem(*mip, 1);
3974 player->inventory.addItem("main", item);
3980 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3983 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3984 player->inventory.addItem("main", item);
3987 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3989 // Skip some materials
3990 if(i == CONTENT_WATER || i == CONTENT_TORCH
3991 || i == CONTENT_COALSTONE)
3994 InventoryItem *item = new MaterialItem(i, 1);
3995 player->inventory.addItem("main", item);
4001 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4002 void* r = player->inventory.addItem("main", item);
4007 v3f findSpawnPos(ServerMap &map)
4009 //return v3f(50,50,50)*BS;
4012 s16 groundheight = 0;
4014 // Try to find a good place a few times
4015 for(s32 i=0; i<1000; i++)
4018 // We're going to try to throw the player to this position
4019 nodepos = v2s16(-range + (myrand()%(range*2)),
4020 -range + (myrand()%(range*2)));
4021 v2s16 sectorpos = getNodeSectorPos(nodepos);
4022 // Get sector (NOTE: Don't get because it's slow)
4023 //m_env.getMap().emergeSector(sectorpos);
4024 // Get ground height at point (fallbacks to heightmap function)
4025 groundheight = map.findGroundLevel(nodepos);
4026 // Don't go underwater
4027 if(groundheight < WATER_LEVEL)
4029 //dstream<<"-> Underwater"<<std::endl;
4032 // Don't go to high places
4033 if(groundheight > WATER_LEVEL + 4)
4035 //dstream<<"-> Underwater"<<std::endl;
4039 // Found a good place
4040 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4044 // If no suitable place was not found, go above water at least.
4045 if(groundheight < WATER_LEVEL)
4046 groundheight = WATER_LEVEL;
4048 return intToFloat(v3s16(
4055 Player *Server::emergePlayer(const char *name, const char *password,
4059 Try to get an existing player
4061 Player *player = m_env.getPlayer(name);
4064 // If player is already connected, cancel
4065 if(player->peer_id != 0)
4067 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4072 player->peer_id = peer_id;
4074 // Reset inventory to creative if in creative mode
4075 if(g_settings.getBool("creative_mode"))
4077 setCreativeInventory(player);
4084 If player with the wanted peer_id already exists, cancel.
4086 if(m_env.getPlayer(peer_id) != NULL)
4088 dstream<<"emergePlayer(): Player with wrong name but same"
4089 " peer_id already exists"<<std::endl;
4097 player = new ServerRemotePlayer();
4098 //player->peer_id = c.peer_id;
4099 //player->peer_id = PEER_ID_INEXISTENT;
4100 player->peer_id = peer_id;
4101 player->updateName(name);
4107 dstream<<"Server: Finding spawn place for player \""
4108 <<player->getName()<<"\""<<std::endl;
4110 v3f pos = findSpawnPos(m_env.getServerMap());
4112 player->setPosition(pos);
4115 Add player to environment
4118 m_env.addPlayer(player);
4121 Add stuff to inventory
4124 if(g_settings.getBool("creative_mode"))
4126 setCreativeInventory(player);
4128 else if(g_settings.getBool("give_initial_stuff"))
4131 InventoryItem *item = new ToolItem("SteelPick", 0);
4132 void* r = player->inventory.addItem("main", item);
4136 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4137 void* r = player->inventory.addItem("main", item);
4141 InventoryItem *item = new ToolItem("SteelAxe", 0);
4142 void* r = player->inventory.addItem("main", item);
4146 InventoryItem *item = new ToolItem("SteelShovel", 0);
4147 void* r = player->inventory.addItem("main", item);
4151 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4152 void* r = player->inventory.addItem("main", item);
4156 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4157 void* r = player->inventory.addItem("main", item);
4161 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4162 void* r = player->inventory.addItem("main", item);
4166 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4167 void* r = player->inventory.addItem("main", item);
4171 InventoryItem *item = new CraftItem("Stick", 4);
4172 void* r = player->inventory.addItem("main", item);
4176 InventoryItem *item = new ToolItem("WPick", 32000);
4177 void* r = player->inventory.addItem("main", item);
4181 InventoryItem *item = new ToolItem("STPick", 32000);
4182 void* r = player->inventory.addItem("main", item);
4186 for(u16 i=0; i<4; i++)
4188 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4189 bool r = player->inventory.addItem("main", item);
4192 /*// Give some other stuff
4194 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4195 bool r = player->inventory.addItem("main", item);
4202 } // create new player
4205 void Server::handlePeerChange(PeerChange &c)
4207 JMutexAutoLock envlock(m_env_mutex);
4208 JMutexAutoLock conlock(m_con_mutex);
4210 if(c.type == PEER_ADDED)
4217 core::map<u16, RemoteClient*>::Node *n;
4218 n = m_clients.find(c.peer_id);
4219 // The client shouldn't already exist
4223 RemoteClient *client = new RemoteClient();
4224 client->peer_id = c.peer_id;
4225 m_clients.insert(client->peer_id, client);
4228 else if(c.type == PEER_REMOVED)
4235 core::map<u16, RemoteClient*>::Node *n;
4236 n = m_clients.find(c.peer_id);
4237 // The client should exist
4241 Mark objects to be not known by the client
4243 RemoteClient *client = n->getValue();
4245 for(core::map<u16, bool>::Iterator
4246 i = client->m_known_objects.getIterator();
4247 i.atEnd()==false; i++)
4250 u16 id = i.getNode()->getKey();
4251 ServerActiveObject* obj = m_env.getActiveObject(id);
4253 if(obj && obj->m_known_by_count > 0)
4254 obj->m_known_by_count--;
4257 // Collect information about leaving in chat
4258 std::wstring message;
4260 std::wstring name = L"unknown";
4261 Player *player = m_env.getPlayer(c.peer_id);
4263 name = narrow_to_wide(player->getName());
4267 message += L" left game";
4269 message += L" (timed out)";
4274 m_env.removePlayer(c.peer_id);
4277 // Set player client disconnected
4279 Player *player = m_env.getPlayer(c.peer_id);
4281 player->peer_id = 0;
4285 delete m_clients[c.peer_id];
4286 m_clients.remove(c.peer_id);
4288 // Send player info to all remaining clients
4291 // Send leave chat message to all remaining clients
4292 BroadcastChatMessage(message);
4301 void Server::handlePeerChanges()
4303 while(m_peer_change_queue.size() > 0)
4305 PeerChange c = m_peer_change_queue.pop_front();
4307 dout_server<<"Server: Handling peer change: "
4308 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4311 handlePeerChange(c);
4315 void dedicated_server_loop(Server &server, bool &kill)
4317 DSTACK(__FUNCTION_NAME);
4319 std::cout<<DTIME<<std::endl;
4320 std::cout<<"========================"<<std::endl;
4321 std::cout<<"Running dedicated server"<<std::endl;
4322 std::cout<<"========================"<<std::endl;
4323 std::cout<<std::endl;
4327 // This is kind of a hack but can be done like this
4328 // because server.step() is very light
4332 if(server.getShutdownRequested() || kill)
4334 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4338 static int counter = 0;
4344 core::list<PlayerInfo> list = server.getPlayerInfo();
4345 core::list<PlayerInfo>::Iterator i;
4346 static u32 sum_old = 0;
4347 u32 sum = PIChecksum(list);
4350 std::cout<<DTIME<<"Player info:"<<std::endl;
4351 for(i=list.begin(); i!=list.end(); i++)
4353 i->PrintLine(&std::cout);