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"
35 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
37 void * ServerThread::Thread()
41 DSTACK(__FUNCTION_NAME);
43 BEGIN_DEBUG_EXCEPTION_HANDLER
48 //TimeTaker timer("AsyncRunStep() + Receive()");
51 //TimeTaker timer("AsyncRunStep()");
52 m_server->AsyncRunStep();
55 //dout_server<<"Running m_server->Receive()"<<std::endl;
58 catch(con::NoIncomingDataException &e)
61 catch(con::PeerNotFoundException &e)
63 dout_server<<"Server: PeerNotFoundException"<<std::endl;
67 END_DEBUG_EXCEPTION_HANDLER
72 void * EmergeThread::Thread()
76 DSTACK(__FUNCTION_NAME);
80 BEGIN_DEBUG_EXCEPTION_HANDLER
83 Get block info from queue, emerge them and send them
86 After queue is empty, exit.
90 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
94 SharedPtr<QueuedBlockEmerge> q(qptr);
100 Do not generate over-limit
102 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
103 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
104 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
105 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
106 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
107 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
110 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
112 //TimeTaker timer("block emerge");
115 Try to emerge it from somewhere.
117 If it is only wanted as optional, only loading from disk
122 Check if any peer wants it as non-optional. In that case it
125 Also decrement the emerge queue count in clients.
128 bool optional = true;
131 core::map<u16, u8>::Iterator i;
132 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
134 //u16 peer_id = i.getNode()->getKey();
137 u8 flags = i.getNode()->getValue();
138 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
144 /*dstream<<"EmergeThread: p="
145 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
146 <<"optional="<<optional<<std::endl;*/
148 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
150 core::map<v3s16, MapBlock*> changed_blocks;
151 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
153 MapBlock *block = NULL;
154 bool got_block = true;
155 core::map<v3s16, MapBlock*> modified_blocks;
157 bool only_from_disk = false;
160 only_from_disk = true;
162 v2s16 chunkpos = map.sector_to_chunk(p2d);
164 bool generate_chunk = false;
165 if(only_from_disk == false)
167 JMutexAutoLock envlock(m_server->m_env_mutex);
168 if(map.chunkNonVolatile(chunkpos) == false)
169 generate_chunk = true;
176 JMutexAutoLock envlock(m_server->m_env_mutex);
177 map.initChunkMake(data, chunkpos);
183 JMutexAutoLock envlock(m_server->m_env_mutex);
184 map.finishChunkMake(data, changed_blocks);
189 Fetch block from map or generate a single block
192 JMutexAutoLock envlock(m_server->m_env_mutex);
194 // Load sector if it isn't loaded
195 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
196 map.loadSectorFull(p2d);
198 block = map.getBlockNoCreateNoEx(p);
199 if(!block || block->isDummy())
207 // Get, load or create sector
208 ServerMapSector *sector =
209 (ServerMapSector*)map.createSector(p2d);
211 block = map.generateBlock(p, block, sector, changed_blocks,
212 lighting_invalidated_blocks);
219 if(block->getLightingExpired()){
220 lighting_invalidated_blocks[block->getPos()] = block;
224 // TODO: Some additional checking and lighting updating,
229 JMutexAutoLock envlock(m_server->m_env_mutex);
234 Collect a list of blocks that have been modified in
235 addition to the fetched one.
238 if(lighting_invalidated_blocks.size() > 0)
240 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
241 <<" blocks"<<std::endl;*/
243 // 50-100ms for single block generation
244 //TimeTaker timer("** EmergeThread updateLighting");
246 // Update lighting without locking the environment mutex,
247 // add modified blocks to changed blocks
248 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
251 // Add all from changed_blocks to modified_blocks
252 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
253 i.atEnd() == false; i++)
255 MapBlock *block = i.getNode()->getValue();
256 modified_blocks.insert(block->getPos(), block);
259 // If we got no block, there should be no invalidated blocks
262 assert(lighting_invalidated_blocks.size() == 0);
268 Set sent status of modified blocks on clients
271 // NOTE: Server's clients are also behind the connection mutex
272 JMutexAutoLock lock(m_server->m_con_mutex);
275 Add the originally fetched block to the modified list
279 modified_blocks.insert(p, block);
283 Set the modified blocks unsent for all the clients
286 for(core::map<u16, RemoteClient*>::Iterator
287 i = m_server->m_clients.getIterator();
288 i.atEnd() == false; i++)
290 RemoteClient *client = i.getNode()->getValue();
292 if(modified_blocks.size() > 0)
294 // Remove block from sent history
295 client->SetBlocksNotSent(modified_blocks);
301 END_DEBUG_EXCEPTION_HANDLER
306 void RemoteClient::GetNextBlocks(Server *server, float dtime,
307 core::array<PrioritySortedBlockTransfer> &dest)
309 DSTACK(__FUNCTION_NAME);
312 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
315 m_nearest_unsent_reset_timer += dtime;
316 m_nothing_to_send_pause_timer -= dtime;
318 if(m_nothing_to_send_pause_timer >= 0)
321 // Won't send anything if already sending
322 if(m_blocks_sending.size() >= g_settings.getU16
323 ("max_simultaneous_block_sends_per_client"))
325 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
329 Player *player = server->m_env.getPlayer(peer_id);
331 assert(player != NULL);
333 v3f playerpos = player->getPosition();
334 v3f playerspeed = player->getSpeed();
336 v3s16 center_nodepos = floatToInt(playerpos, BS);
338 v3s16 center = getNodeBlockPos(center_nodepos);
340 // Camera position and direction
342 playerpos + v3f(0, BS+BS/2, 0);
343 v3f camera_dir = v3f(0,0,1);
344 camera_dir.rotateYZBy(player->getPitch());
345 camera_dir.rotateXZBy(player->getYaw());
348 Get the starting value of the block finder radius.
351 if(m_last_center != center)
353 m_nearest_unsent_d = 0;
354 m_last_center = center;
357 /*dstream<<"m_nearest_unsent_reset_timer="
358 <<m_nearest_unsent_reset_timer<<std::endl;*/
359 if(m_nearest_unsent_reset_timer > 5.0)
361 m_nearest_unsent_reset_timer = 0;
362 m_nearest_unsent_d = 0;
363 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
366 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
367 s16 d_start = m_nearest_unsent_d;
369 //dstream<<"d_start="<<d_start<<std::endl;
371 u16 max_simul_sends_setting = g_settings.getU16
372 ("max_simultaneous_block_sends_per_client");
373 u16 max_simul_sends_usually = max_simul_sends_setting;
376 Check the time from last addNode/removeNode.
378 Decrease send rate if player is building stuff.
380 m_time_from_building += dtime;
381 if(m_time_from_building < g_settings.getFloat(
382 "full_block_send_enable_min_time_from_building"))
384 max_simul_sends_usually
385 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
389 Number of blocks sending + number of blocks selected for sending
391 u32 num_blocks_selected = m_blocks_sending.size();
394 next time d will be continued from the d from which the nearest
395 unsent block was found this time.
397 This is because not necessarily any of the blocks found this
398 time are actually sent.
400 s32 new_nearest_unsent_d = -1;
402 s16 d_max = g_settings.getS16("max_block_send_distance");
403 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
405 //dstream<<"Starting from "<<d_start<<std::endl;
407 bool sending_something = false;
409 for(s16 d = d_start; d <= d_max; d++)
411 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
414 If m_nearest_unsent_d was changed by the EmergeThread
415 (it can change it to 0 through SetBlockNotSent),
417 Else update m_nearest_unsent_d
419 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
421 d = m_nearest_unsent_d;
422 last_nearest_unsent_d = m_nearest_unsent_d;
426 Get the border/face dot coordinates of a "d-radiused"
429 core::list<v3s16> list;
430 getFacePositions(list, d);
432 core::list<v3s16>::Iterator li;
433 for(li=list.begin(); li!=list.end(); li++)
435 v3s16 p = *li + center;
439 - Don't allow too many simultaneous transfers
440 - EXCEPT when the blocks are very close
442 Also, don't send blocks that are already flying.
445 // Start with the usual maximum
446 u16 max_simul_dynamic = max_simul_sends_usually;
448 // If block is very close, allow full maximum
449 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
450 max_simul_dynamic = max_simul_sends_setting;
452 // Don't select too many blocks for sending
453 if(num_blocks_selected >= max_simul_dynamic)
456 // Don't send blocks that are currently being transferred
457 if(m_blocks_sending.find(p) != NULL)
463 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
464 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
465 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
466 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
467 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
468 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
471 // If this is true, inexistent block will be made from scratch
472 bool generate = d <= d_max_gen;
475 /*// Limit the generating area vertically to 2/3
476 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
479 // Limit the send area vertically to 2/3
480 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
486 If block is far away, don't generate it unless it is
492 // Block center y in nodes
493 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
494 // Don't generate if it's very high or very low
495 if(y < -64 || y > 64)
499 v2s16 p2d_nodes_center(
503 // Get ground height in nodes
504 s16 gh = server->m_env.getServerMap().findGroundLevel(
507 // If differs a lot, don't generate
508 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
510 // Actually, don't even send it
517 Don't generate or send if not in sight
520 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
526 Don't send already sent blocks
529 if(m_blocks_sent.find(p) != NULL)
534 Check if map has this block
536 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
538 bool surely_not_found_on_disk = false;
539 bool block_is_invalid = false;
544 surely_not_found_on_disk = true;
547 if(block->isValid() == false)
549 block_is_invalid = true;
552 /*if(block->isFullyGenerated() == false)
554 block_is_invalid = true;
558 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
559 v2s16 chunkpos = map->sector_to_chunk(p2d);
560 if(map->chunkNonVolatile(chunkpos) == false)
561 block_is_invalid = true;
564 If block is not close, don't send it unless it is near
567 Block is not near ground level if night-time mesh
568 doesn't differ from day-time mesh.
572 if(block->dayNightDiffed() == false)
579 If block has been marked to not exist on disk (dummy)
580 and generating new ones is not wanted, skip block.
582 if(generate == false && surely_not_found_on_disk == true)
589 Record the lowest d from which a a block has been
590 found being not sent and possibly to exist
592 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
595 new_nearest_unsent_d = d;
599 Add inexistent block to emerge queue.
601 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
603 //TODO: Get value from somewhere
604 // Allow only one block in emerge queue
605 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
606 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
608 //dstream<<"Adding block to emerge queue"<<std::endl;
610 // Add it to the emerge queue and trigger the thread
613 if(generate == false)
614 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
616 server->m_emerge_queue.addBlock(peer_id, p, flags);
617 server->m_emergethread.trigger();
625 Add block to send queue
628 PrioritySortedBlockTransfer q((float)d, p, peer_id);
632 num_blocks_selected += 1;
633 sending_something = true;
638 if(new_nearest_unsent_d != -1)
640 m_nearest_unsent_d = new_nearest_unsent_d;
643 if(sending_something == false)
645 m_nothing_to_send_counter++;
646 if(m_nothing_to_send_counter >= 3)
648 // Pause time in seconds
649 m_nothing_to_send_pause_timer = 2.0;
654 m_nothing_to_send_counter = 0;
657 /*timer_result = timer.stop(true);
658 if(timer_result != 0)
659 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
662 void RemoteClient::SendObjectData(
665 core::map<v3s16, bool> &stepped_blocks
668 DSTACK(__FUNCTION_NAME);
670 // Can't send anything without knowing version
671 if(serialization_version == SER_FMT_VER_INVALID)
673 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
679 Send a TOCLIENT_OBJECTDATA packet.
683 u16 number of player positions
694 std::ostringstream os(std::ios_base::binary);
698 writeU16(buf, TOCLIENT_OBJECTDATA);
699 os.write((char*)buf, 2);
702 Get and write player data
705 // Get connected players
706 core::list<Player*> players = server->m_env.getPlayers(true);
708 // Write player count
709 u16 playercount = players.size();
710 writeU16(buf, playercount);
711 os.write((char*)buf, 2);
713 core::list<Player*>::Iterator i;
714 for(i = players.begin();
715 i != players.end(); i++)
719 v3f pf = player->getPosition();
720 v3f sf = player->getSpeed();
722 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
723 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
724 s32 pitch_i (player->getPitch() * 100);
725 s32 yaw_i (player->getYaw() * 100);
727 writeU16(buf, player->peer_id);
728 os.write((char*)buf, 2);
729 writeV3S32(buf, position_i);
730 os.write((char*)buf, 12);
731 writeV3S32(buf, speed_i);
732 os.write((char*)buf, 12);
733 writeS32(buf, pitch_i);
734 os.write((char*)buf, 4);
735 writeS32(buf, yaw_i);
736 os.write((char*)buf, 4);
740 Get and write object data
746 For making players to be able to build to their nearby
747 environment (building is not possible on blocks that are not
750 - Add blocks to emerge queue if they are not found
752 SUGGESTION: These could be ignored from the backside of the player
755 Player *player = server->m_env.getPlayer(peer_id);
759 v3f playerpos = player->getPosition();
760 v3f playerspeed = player->getSpeed();
762 v3s16 center_nodepos = floatToInt(playerpos, BS);
763 v3s16 center = getNodeBlockPos(center_nodepos);
765 s16 d_max = g_settings.getS16("active_object_range");
767 // Number of blocks whose objects were written to bos
770 std::ostringstream bos(std::ios_base::binary);
772 for(s16 d = 0; d <= d_max; d++)
774 core::list<v3s16> list;
775 getFacePositions(list, d);
777 core::list<v3s16>::Iterator li;
778 for(li=list.begin(); li!=list.end(); li++)
780 v3s16 p = *li + center;
783 Ignore blocks that haven't been sent to the client
786 if(m_blocks_sent.find(p) == NULL)
790 // Try stepping block and add it to a send queue
795 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
798 Step block if not in stepped_blocks and add to stepped_blocks.
800 if(stepped_blocks.find(p) == NULL)
802 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
803 stepped_blocks.insert(p, true);
804 block->setChangedFlag();
807 // Skip block if there are no objects
808 if(block->getObjectCount() == 0)
817 bos.write((char*)buf, 6);
820 block->serializeObjects(bos, serialization_version);
825 Stop collecting objects if data is already too big
827 // Sum of player and object data sizes
828 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
829 // break out if data too big
830 if(sum > MAX_OBJECTDATA_SIZE)
832 goto skip_subsequent;
836 catch(InvalidPositionException &e)
839 // Add it to the emerge queue and trigger the thread.
840 // Fetch the block only if it is on disk.
842 // Grab and increment counter
843 /*SharedPtr<JMutexAutoLock> lock
844 (m_num_blocks_in_emerge_queue.getLock());
845 m_num_blocks_in_emerge_queue.m_value++;*/
847 // Add to queue as an anonymous fetch from disk
848 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
849 server->m_emerge_queue.addBlock(0, p, flags);
850 server->m_emergethread.trigger();
858 writeU16(buf, blockcount);
859 os.write((char*)buf, 2);
861 // Write block objects
868 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
871 std::string s = os.str();
872 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
873 // Send as unreliable
874 server->m_con.Send(peer_id, 0, data, false);
877 void RemoteClient::GotBlock(v3s16 p)
879 if(m_blocks_sending.find(p) != NULL)
880 m_blocks_sending.remove(p);
883 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
884 " m_blocks_sending"<<std::endl;*/
885 m_excess_gotblocks++;
887 m_blocks_sent.insert(p, true);
890 void RemoteClient::SentBlock(v3s16 p)
892 if(m_blocks_sending.find(p) == NULL)
893 m_blocks_sending.insert(p, 0.0);
895 dstream<<"RemoteClient::SentBlock(): Sent block"
896 " already in m_blocks_sending"<<std::endl;
899 void RemoteClient::SetBlockNotSent(v3s16 p)
901 m_nearest_unsent_d = 0;
903 if(m_blocks_sending.find(p) != NULL)
904 m_blocks_sending.remove(p);
905 if(m_blocks_sent.find(p) != NULL)
906 m_blocks_sent.remove(p);
909 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
911 m_nearest_unsent_d = 0;
913 for(core::map<v3s16, MapBlock*>::Iterator
914 i = blocks.getIterator();
915 i.atEnd()==false; i++)
917 v3s16 p = i.getNode()->getKey();
919 if(m_blocks_sending.find(p) != NULL)
920 m_blocks_sending.remove(p);
921 if(m_blocks_sent.find(p) != NULL)
922 m_blocks_sent.remove(p);
930 PlayerInfo::PlayerInfo()
936 void PlayerInfo::PrintLine(std::ostream *s)
939 (*s)<<"\""<<name<<"\" ("
940 <<(position.X/10)<<","<<(position.Y/10)
941 <<","<<(position.Z/10)<<") ";
943 (*s)<<" avg_rtt="<<avg_rtt;
947 u32 PIChecksum(core::list<PlayerInfo> &l)
949 core::list<PlayerInfo>::Iterator i;
952 for(i=l.begin(); i!=l.end(); i++)
954 checksum += a * (i->id+1);
955 checksum ^= 0x435aafcd;
966 std::string mapsavedir
968 m_env(new ServerMap(mapsavedir), this),
969 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
971 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 // Register us to receive map edit events
992 m_env.getMap().addEventReceiver(this);
994 // If file exists, load environment metadata
995 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
997 dstream<<"Server: Loading environment metadata"<<std::endl;
998 m_env.loadMeta(m_mapsavedir);
1002 dstream<<"Server: Loading players"<<std::endl;
1003 m_env.deSerializePlayers(m_mapsavedir);
1008 dstream<<"Server::~Server()"<<std::endl;
1011 Send shutdown message
1014 JMutexAutoLock conlock(m_con_mutex);
1016 std::wstring line = L"*** Server shutting down";
1019 Send the message to clients
1021 for(core::map<u16, RemoteClient*>::Iterator
1022 i = m_clients.getIterator();
1023 i.atEnd() == false; i++)
1025 // Get client and check that it is valid
1026 RemoteClient *client = i.getNode()->getValue();
1027 assert(client->peer_id == i.getNode()->getKey());
1028 if(client->serialization_version == SER_FMT_VER_INVALID)
1032 SendChatMessage(client->peer_id, line);
1034 catch(con::PeerNotFoundException &e)
1042 dstream<<"Server: Saving players"<<std::endl;
1043 m_env.serializePlayers(m_mapsavedir);
1046 Save environment metadata
1048 dstream<<"Server: Saving environment metadata"<<std::endl;
1049 m_env.saveMeta(m_mapsavedir);
1060 JMutexAutoLock clientslock(m_con_mutex);
1062 for(core::map<u16, RemoteClient*>::Iterator
1063 i = m_clients.getIterator();
1064 i.atEnd() == false; i++)
1067 // NOTE: These are removed by env destructor
1069 u16 peer_id = i.getNode()->getKey();
1070 JMutexAutoLock envlock(m_env_mutex);
1071 m_env.removePlayer(peer_id);
1075 delete i.getNode()->getValue();
1080 void Server::start(unsigned short port)
1082 DSTACK(__FUNCTION_NAME);
1083 // Stop thread if already running
1086 // Initialize connection
1087 m_con.setTimeoutMs(30);
1091 m_thread.setRun(true);
1094 dout_server<<"Server: Started on port "<<port<<std::endl;
1099 DSTACK(__FUNCTION_NAME);
1101 // Stop threads (set run=false first so both start stopping)
1102 m_thread.setRun(false);
1103 m_emergethread.setRun(false);
1105 m_emergethread.stop();
1107 dout_server<<"Server: Threads stopped"<<std::endl;
1110 void Server::step(float dtime)
1112 DSTACK(__FUNCTION_NAME);
1117 JMutexAutoLock lock(m_step_dtime_mutex);
1118 m_step_dtime += dtime;
1122 void Server::AsyncRunStep()
1124 DSTACK(__FUNCTION_NAME);
1128 JMutexAutoLock lock1(m_step_dtime_mutex);
1129 dtime = m_step_dtime;
1132 // Send blocks to clients
1138 //dstream<<"Server steps "<<dtime<<std::endl;
1139 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1142 JMutexAutoLock lock1(m_step_dtime_mutex);
1143 m_step_dtime -= dtime;
1150 m_uptime.set(m_uptime.get() + dtime);
1154 Update m_time_of_day and overall game time
1157 JMutexAutoLock envlock(m_env_mutex);
1159 m_time_counter += dtime;
1160 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1161 u32 units = (u32)(m_time_counter*speed);
1162 m_time_counter -= (f32)units / speed;
1164 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1166 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1169 Send to clients at constant intervals
1172 m_time_of_day_send_timer -= dtime;
1173 if(m_time_of_day_send_timer < 0.0)
1175 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1177 //JMutexAutoLock envlock(m_env_mutex);
1178 JMutexAutoLock conlock(m_con_mutex);
1180 for(core::map<u16, RemoteClient*>::Iterator
1181 i = m_clients.getIterator();
1182 i.atEnd() == false; i++)
1184 RemoteClient *client = i.getNode()->getValue();
1185 //Player *player = m_env.getPlayer(client->peer_id);
1187 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1188 m_env.getTimeOfDay());
1190 m_con.Send(client->peer_id, 0, data, true);
1196 // Process connection's timeouts
1197 JMutexAutoLock lock2(m_con_mutex);
1198 m_con.RunTimeouts(dtime);
1202 // This has to be called so that the client list gets synced
1203 // with the peer list of the connection
1204 handlePeerChanges();
1209 // This also runs Map's timers
1210 JMutexAutoLock lock(m_env_mutex);
1221 m_liquid_transform_timer += dtime;
1222 if(m_liquid_transform_timer >= 1.00)
1224 m_liquid_transform_timer -= 1.00;
1226 JMutexAutoLock lock(m_env_mutex);
1228 core::map<v3s16, MapBlock*> modified_blocks;
1229 m_env.getMap().transformLiquids(modified_blocks);
1234 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1235 ServerMap &map = ((ServerMap&)m_env.getMap());
1236 map.updateLighting(modified_blocks, lighting_modified_blocks);
1238 // Add blocks modified by lighting to modified_blocks
1239 for(core::map<v3s16, MapBlock*>::Iterator
1240 i = lighting_modified_blocks.getIterator();
1241 i.atEnd() == false; i++)
1243 MapBlock *block = i.getNode()->getValue();
1244 modified_blocks.insert(block->getPos(), block);
1248 Set the modified blocks unsent for all the clients
1251 JMutexAutoLock lock2(m_con_mutex);
1253 for(core::map<u16, RemoteClient*>::Iterator
1254 i = m_clients.getIterator();
1255 i.atEnd() == false; i++)
1257 RemoteClient *client = i.getNode()->getValue();
1259 if(modified_blocks.size() > 0)
1261 // Remove block from sent history
1262 client->SetBlocksNotSent(modified_blocks);
1267 // Periodically print some info
1269 float &counter = m_print_info_timer;
1275 JMutexAutoLock lock2(m_con_mutex);
1277 for(core::map<u16, RemoteClient*>::Iterator
1278 i = m_clients.getIterator();
1279 i.atEnd() == false; i++)
1281 //u16 peer_id = i.getNode()->getKey();
1282 RemoteClient *client = i.getNode()->getValue();
1283 Player *player = m_env.getPlayer(client->peer_id);
1286 std::cout<<player->getName()<<"\t";
1287 client->PrintInfo(std::cout);
1292 //if(g_settings.getBool("enable_experimental"))
1296 Check added and deleted active objects
1299 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1301 JMutexAutoLock envlock(m_env_mutex);
1302 JMutexAutoLock conlock(m_con_mutex);
1304 // Radius inside which objects are active
1307 for(core::map<u16, RemoteClient*>::Iterator
1308 i = m_clients.getIterator();
1309 i.atEnd() == false; i++)
1311 RemoteClient *client = i.getNode()->getValue();
1312 Player *player = m_env.getPlayer(client->peer_id);
1315 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1316 <<" has no associated player"<<std::endl;
1319 v3s16 pos = floatToInt(player->getPosition(), BS);
1321 core::map<u16, bool> removed_objects;
1322 core::map<u16, bool> added_objects;
1323 m_env.getRemovedActiveObjects(pos, radius,
1324 client->m_known_objects, removed_objects);
1325 m_env.getAddedActiveObjects(pos, radius,
1326 client->m_known_objects, added_objects);
1328 // Ignore if nothing happened
1329 if(removed_objects.size() == 0 && added_objects.size() == 0)
1331 //dstream<<"INFO: active objects: none changed"<<std::endl;
1335 std::string data_buffer;
1339 // Handle removed objects
1340 writeU16((u8*)buf, removed_objects.size());
1341 data_buffer.append(buf, 2);
1342 for(core::map<u16, bool>::Iterator
1343 i = removed_objects.getIterator();
1344 i.atEnd()==false; i++)
1347 u16 id = i.getNode()->getKey();
1348 ServerActiveObject* obj = m_env.getActiveObject(id);
1350 // Add to data buffer for sending
1351 writeU16((u8*)buf, i.getNode()->getKey());
1352 data_buffer.append(buf, 2);
1354 // Remove from known objects
1355 client->m_known_objects.remove(i.getNode()->getKey());
1357 if(obj && obj->m_known_by_count > 0)
1358 obj->m_known_by_count--;
1361 // Handle added objects
1362 writeU16((u8*)buf, added_objects.size());
1363 data_buffer.append(buf, 2);
1364 for(core::map<u16, bool>::Iterator
1365 i = added_objects.getIterator();
1366 i.atEnd()==false; i++)
1369 u16 id = i.getNode()->getKey();
1370 ServerActiveObject* obj = m_env.getActiveObject(id);
1373 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1375 dstream<<"WARNING: "<<__FUNCTION_NAME
1376 <<": NULL object"<<std::endl;
1378 type = obj->getType();
1380 // Add to data buffer for sending
1381 writeU16((u8*)buf, id);
1382 data_buffer.append(buf, 2);
1383 writeU8((u8*)buf, type);
1384 data_buffer.append(buf, 1);
1387 data_buffer.append(serializeLongString(
1388 obj->getClientInitializationData()));
1390 data_buffer.append(serializeLongString(""));
1392 // Add to known objects
1393 client->m_known_objects.insert(i.getNode()->getKey(), false);
1396 obj->m_known_by_count++;
1400 SharedBuffer<u8> reply(2 + data_buffer.size());
1401 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1402 memcpy((char*)&reply[2], data_buffer.c_str(),
1403 data_buffer.size());
1405 m_con.Send(client->peer_id, 0, reply, true);
1407 dstream<<"INFO: Server: Sent object remove/add: "
1408 <<removed_objects.size()<<" removed, "
1409 <<added_objects.size()<<" added, "
1410 <<"packet size is "<<reply.getSize()<<std::endl;
1415 Collect a list of all the objects known by the clients
1416 and report it back to the environment.
1419 core::map<u16, bool> all_known_objects;
1421 for(core::map<u16, RemoteClient*>::Iterator
1422 i = m_clients.getIterator();
1423 i.atEnd() == false; i++)
1425 RemoteClient *client = i.getNode()->getValue();
1426 // Go through all known objects of client
1427 for(core::map<u16, bool>::Iterator
1428 i = client->m_known_objects.getIterator();
1429 i.atEnd()==false; i++)
1431 u16 id = i.getNode()->getKey();
1432 all_known_objects[id] = true;
1436 m_env.setKnownActiveObjects(whatever);
1442 Send object messages
1445 JMutexAutoLock envlock(m_env_mutex);
1446 JMutexAutoLock conlock(m_con_mutex);
1449 // Value = data sent by object
1450 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1452 // Get active object messages from environment
1455 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1459 core::list<ActiveObjectMessage>* message_list = NULL;
1460 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1461 n = buffered_messages.find(aom.id);
1464 message_list = new core::list<ActiveObjectMessage>;
1465 buffered_messages.insert(aom.id, message_list);
1469 message_list = n->getValue();
1471 message_list->push_back(aom);
1474 // Route data to every client
1475 for(core::map<u16, RemoteClient*>::Iterator
1476 i = m_clients.getIterator();
1477 i.atEnd()==false; i++)
1479 RemoteClient *client = i.getNode()->getValue();
1480 std::string reliable_data;
1481 std::string unreliable_data;
1482 // Go through all objects in message buffer
1483 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1484 j = buffered_messages.getIterator();
1485 j.atEnd()==false; j++)
1487 // If object is not known by client, skip it
1488 u16 id = j.getNode()->getKey();
1489 if(client->m_known_objects.find(id) == NULL)
1491 // Get message list of object
1492 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1493 // Go through every message
1494 for(core::list<ActiveObjectMessage>::Iterator
1495 k = list->begin(); k != list->end(); k++)
1497 // Compose the full new data with header
1498 ActiveObjectMessage aom = *k;
1499 std::string new_data;
1502 writeU16((u8*)&buf[0], aom.id);
1503 new_data.append(buf, 2);
1505 new_data += serializeString(aom.datastring);
1506 // Add data to buffer
1508 reliable_data += new_data;
1510 unreliable_data += new_data;
1514 reliable_data and unreliable_data are now ready.
1517 if(reliable_data.size() > 0)
1519 SharedBuffer<u8> reply(2 + reliable_data.size());
1520 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1521 memcpy((char*)&reply[2], reliable_data.c_str(),
1522 reliable_data.size());
1524 m_con.Send(client->peer_id, 0, reply, true);
1526 if(unreliable_data.size() > 0)
1528 SharedBuffer<u8> reply(2 + unreliable_data.size());
1529 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1530 memcpy((char*)&reply[2], unreliable_data.c_str(),
1531 unreliable_data.size());
1532 // Send as unreliable
1533 m_con.Send(client->peer_id, 0, reply, false);
1536 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1538 dstream<<"INFO: Server: Size of object message data: "
1539 <<"reliable: "<<reliable_data.size()
1540 <<", unreliable: "<<unreliable_data.size()
1545 // Clear buffered_messages
1546 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1547 i = buffered_messages.getIterator();
1548 i.atEnd()==false; i++)
1550 delete i.getNode()->getValue();
1554 } // enable_experimental
1557 Send queued-for-sending map edit events.
1560 while(m_unsent_map_edit_queue.size() != 0)
1562 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1564 if(event->type == MEET_ADDNODE)
1566 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1567 sendAddNode(event->p, event->n, event->already_known_by_peer);
1569 else if(event->type == MEET_REMOVENODE)
1571 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1572 sendRemoveNode(event->p, event->already_known_by_peer);
1574 else if(event->type == MEET_OTHER)
1576 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1581 dstream<<"WARNING: Server: Unknown MapEditEvent "
1582 <<((u32)event->type)<<std::endl;
1590 Send object positions
1591 TODO: Get rid of MapBlockObjects
1594 float &counter = m_objectdata_timer;
1596 if(counter >= g_settings.getFloat("objectdata_interval"))
1598 JMutexAutoLock lock1(m_env_mutex);
1599 JMutexAutoLock lock2(m_con_mutex);
1600 SendObjectData(counter);
1610 //TimeTaker timer("Step node metadata");
1612 JMutexAutoLock envlock(m_env_mutex);
1613 JMutexAutoLock conlock(m_con_mutex);
1615 core::map<v3s16, MapBlock*> changed_blocks;
1616 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1618 for(core::map<v3s16, MapBlock*>::Iterator
1619 i = changed_blocks.getIterator();
1620 i.atEnd() == false; i++)
1622 MapBlock *block = i.getNode()->getValue();
1624 for(core::map<u16, RemoteClient*>::Iterator
1625 i = m_clients.getIterator();
1626 i.atEnd()==false; i++)
1628 RemoteClient *client = i.getNode()->getValue();
1629 client->SetBlockNotSent(block->getPos());
1635 Trigger emergethread (it somehow gets to a non-triggered but
1636 bysy state sometimes)
1639 float &counter = m_emergethread_trigger_timer;
1645 m_emergethread.trigger();
1651 float &counter = m_savemap_timer;
1653 if(counter >= g_settings.getFloat("server_map_save_interval"))
1657 JMutexAutoLock lock(m_env_mutex);
1659 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1661 // Save only changed parts
1662 m_env.getMap().save(true);
1664 // Delete unused sectors
1665 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1666 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1667 if(deleted_count > 0)
1669 dout_server<<"Server: Unloaded "<<deleted_count
1670 <<" sectors from memory"<<std::endl;
1674 m_env.serializePlayers(m_mapsavedir);
1676 // Save environment metadata
1677 m_env.saveMeta(m_mapsavedir);
1683 void Server::Receive()
1685 DSTACK(__FUNCTION_NAME);
1686 u32 data_maxsize = 10000;
1687 Buffer<u8> data(data_maxsize);
1692 JMutexAutoLock conlock(m_con_mutex);
1693 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1696 // This has to be called so that the client list gets synced
1697 // with the peer list of the connection
1698 handlePeerChanges();
1700 ProcessData(*data, datasize, peer_id);
1702 catch(con::InvalidIncomingDataException &e)
1704 derr_server<<"Server::Receive(): "
1705 "InvalidIncomingDataException: what()="
1706 <<e.what()<<std::endl;
1708 catch(con::PeerNotFoundException &e)
1710 //NOTE: This is not needed anymore
1712 // The peer has been disconnected.
1713 // Find the associated player and remove it.
1715 /*JMutexAutoLock envlock(m_env_mutex);
1717 dout_server<<"ServerThread: peer_id="<<peer_id
1718 <<" has apparently closed connection. "
1719 <<"Removing player."<<std::endl;
1721 m_env.removePlayer(peer_id);*/
1725 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1727 DSTACK(__FUNCTION_NAME);
1728 // Environment is locked first.
1729 JMutexAutoLock envlock(m_env_mutex);
1730 JMutexAutoLock conlock(m_con_mutex);
1734 peer = m_con.GetPeer(peer_id);
1736 catch(con::PeerNotFoundException &e)
1738 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1739 <<peer_id<<" not found"<<std::endl;
1743 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1751 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1753 if(command == TOSERVER_INIT)
1755 // [0] u16 TOSERVER_INIT
1756 // [2] u8 SER_FMT_VER_HIGHEST
1757 // [3] u8[20] player_name
1758 // [23] u8[28] password <--- can be sent without this, from old versions
1760 if(datasize < 2+1+PLAYERNAME_SIZE)
1763 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1764 <<peer->id<<std::endl;
1766 // First byte after command is maximum supported
1767 // serialization version
1768 u8 client_max = data[2];
1769 u8 our_max = SER_FMT_VER_HIGHEST;
1770 // Use the highest version supported by both
1771 u8 deployed = core::min_(client_max, our_max);
1772 // If it's lower than the lowest supported, give up.
1773 if(deployed < SER_FMT_VER_LOWEST)
1774 deployed = SER_FMT_VER_INVALID;
1776 //peer->serialization_version = deployed;
1777 getClient(peer->id)->pending_serialization_version = deployed;
1779 if(deployed == SER_FMT_VER_INVALID)
1781 derr_server<<DTIME<<"Server: Cannot negotiate "
1782 "serialization version with peer "
1783 <<peer_id<<std::endl;
1792 char playername[PLAYERNAME_SIZE];
1793 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1795 playername[i] = data[3+i];
1797 playername[PLAYERNAME_SIZE-1] = 0;
1800 char password[PASSWORD_SIZE];
1801 if(datasize == 2+1+PLAYERNAME_SIZE)
1803 // old version - assume blank password
1808 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1810 password[i] = data[23+i];
1812 password[PASSWORD_SIZE-1] = 0;
1814 Player *checkplayer = m_env.getPlayer(playername);
1815 if(checkplayer != NULL && strcmp(checkplayer->getPassword(),password))
1817 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1818 <<": supplied invalid password for "
1819 <<playername<<std::endl;
1820 SendAccessDenied(m_con, peer_id);
1825 Player *player = emergePlayer(playername, password, peer_id);
1829 // DEBUG: Test serialization
1830 std::ostringstream test_os;
1831 player->serialize(test_os);
1832 dstream<<"Player serialization test: \""<<test_os.str()
1834 std::istringstream test_is(test_os.str());
1835 player->deSerialize(test_is);
1838 // If failed, cancel
1841 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1842 <<": failed to emerge player"<<std::endl;
1847 // If a client is already connected to the player, cancel
1848 if(player->peer_id != 0)
1850 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1851 <<" tried to connect to "
1852 "an already connected player (peer_id="
1853 <<player->peer_id<<")"<<std::endl;
1856 // Set client of player
1857 player->peer_id = peer_id;
1860 // Check if player doesn't exist
1862 throw con::InvalidIncomingDataException
1863 ("Server::ProcessData(): INIT: Player doesn't exist");
1865 /*// update name if it was supplied
1866 if(datasize >= 20+3)
1869 player->updateName((const char*)&data[3]);
1873 Answer with a TOCLIENT_INIT
1876 SharedBuffer<u8> reply(2+1+6+8);
1877 writeU16(&reply[0], TOCLIENT_INIT);
1878 writeU8(&reply[2], deployed);
1879 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1880 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1881 writeU64(&reply[2+1+6], 0); // no seed
1884 m_con.Send(peer_id, 0, reply, true);
1888 Send complete position information
1890 SendMovePlayer(player);
1895 if(command == TOSERVER_INIT2)
1897 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1898 <<peer->id<<std::endl;
1901 getClient(peer->id)->serialization_version
1902 = getClient(peer->id)->pending_serialization_version;
1905 Send some initialization data
1908 // Send player info to all players
1911 // Send inventory to player
1912 UpdateCrafting(peer->id);
1913 SendInventory(peer->id);
1917 Player *player = m_env.getPlayer(peer_id);
1918 SendPlayerHP(player);
1923 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1924 m_env.getTimeOfDay());
1925 m_con.Send(peer->id, 0, data, true);
1928 // Send information about server to player in chat
1929 SendChatMessage(peer_id, getStatusString());
1931 // Send information about joining in chat
1933 std::wstring name = L"unknown";
1934 Player *player = m_env.getPlayer(peer_id);
1936 name = narrow_to_wide(player->getName());
1938 std::wstring message;
1941 message += L" joined game";
1942 BroadcastChatMessage(message);
1948 if(peer_ser_ver == SER_FMT_VER_INVALID)
1950 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1951 " serialization format invalid or not initialized."
1952 " Skipping incoming command="<<command<<std::endl;
1956 Player *player = m_env.getPlayer(peer_id);
1959 derr_server<<"Server::ProcessData(): Cancelling: "
1960 "No player for peer_id="<<peer_id
1964 if(command == TOSERVER_PLAYERPOS)
1966 if(datasize < 2+12+12+4+4)
1970 v3s32 ps = readV3S32(&data[start+2]);
1971 v3s32 ss = readV3S32(&data[start+2+12]);
1972 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1973 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1974 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1975 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1976 pitch = wrapDegrees(pitch);
1977 yaw = wrapDegrees(yaw);
1978 player->setPosition(position);
1979 player->setSpeed(speed);
1980 player->setPitch(pitch);
1981 player->setYaw(yaw);
1983 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1984 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1985 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1987 else if(command == TOSERVER_GOTBLOCKS)
2000 u16 count = data[2];
2001 for(u16 i=0; i<count; i++)
2003 if((s16)datasize < 2+1+(i+1)*6)
2004 throw con::InvalidIncomingDataException
2005 ("GOTBLOCKS length is too short");
2006 v3s16 p = readV3S16(&data[2+1+i*6]);
2007 /*dstream<<"Server: GOTBLOCKS ("
2008 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2009 RemoteClient *client = getClient(peer_id);
2010 client->GotBlock(p);
2013 else if(command == TOSERVER_DELETEDBLOCKS)
2026 u16 count = data[2];
2027 for(u16 i=0; i<count; i++)
2029 if((s16)datasize < 2+1+(i+1)*6)
2030 throw con::InvalidIncomingDataException
2031 ("DELETEDBLOCKS length is too short");
2032 v3s16 p = readV3S16(&data[2+1+i*6]);
2033 /*dstream<<"Server: DELETEDBLOCKS ("
2034 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2035 RemoteClient *client = getClient(peer_id);
2036 client->SetBlockNotSent(p);
2039 else if(command == TOSERVER_CLICK_OBJECT)
2044 if((player->privs & PRIV_BUILD) == 0)
2049 [2] u8 button (0=left, 1=right)
2054 u8 button = readU8(&data[2]);
2056 p.X = readS16(&data[3]);
2057 p.Y = readS16(&data[5]);
2058 p.Z = readS16(&data[7]);
2059 s16 id = readS16(&data[9]);
2060 //u16 item_i = readU16(&data[11]);
2062 MapBlock *block = NULL;
2065 block = m_env.getMap().getBlockNoCreate(p);
2067 catch(InvalidPositionException &e)
2069 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2073 MapBlockObject *obj = block->getObject(id);
2077 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2081 //TODO: Check that object is reasonably close
2086 InventoryList *ilist = player->inventory.getList("main");
2087 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2090 // Skip if inventory has no free space
2091 if(ilist->getUsedSlots() == ilist->getSize())
2093 dout_server<<"Player inventory has no free space"<<std::endl;
2098 Create the inventory item
2100 InventoryItem *item = NULL;
2101 // If it is an item-object, take the item from it
2102 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2104 item = ((ItemObject*)obj)->createInventoryItem();
2106 // Else create an item of the object
2109 item = new MapBlockObjectItem
2110 (obj->getInventoryString());
2113 // Add to inventory and send inventory
2114 ilist->addItem(item);
2115 UpdateCrafting(player->peer_id);
2116 SendInventory(player->peer_id);
2119 // Remove from block
2120 block->removeObject(id);
2123 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2128 if((player->privs & PRIV_BUILD) == 0)
2134 [2] u8 button (0=left, 1=right)
2138 u8 button = readU8(&data[2]);
2139 u16 id = readS16(&data[3]);
2140 u16 item_i = readU16(&data[11]);
2142 ServerActiveObject *obj = m_env.getActiveObject(id);
2146 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2151 //TODO: Check that object is reasonably close
2153 // Left click, pick object up (usually)
2156 InventoryList *ilist = player->inventory.getList("main");
2157 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2160 // Skip if inventory has no free space
2161 if(ilist->getUsedSlots() == ilist->getSize())
2163 dout_server<<"Player inventory has no free space"<<std::endl;
2167 // Skip if object has been removed
2172 Create the inventory item
2174 InventoryItem *item = obj->createPickedUpItem();
2178 // Add to inventory and send inventory
2179 ilist->addItem(item);
2180 UpdateCrafting(player->peer_id);
2181 SendInventory(player->peer_id);
2183 // Remove object from environment
2184 obj->m_removed = true;
2189 Item cannot be picked up. Punch it instead.
2192 ToolItem *titem = NULL;
2193 std::string toolname = "";
2195 InventoryList *mlist = player->inventory.getList("main");
2198 InventoryItem *item = mlist->getItem(item_i);
2199 if(item && (std::string)item->getName() == "ToolItem")
2201 titem = (ToolItem*)item;
2202 toolname = titem->getToolName();
2206 u16 wear = obj->punch(toolname);
2210 bool weared_out = titem->addWear(wear);
2212 mlist->deleteItem(item_i);
2213 SendInventory(player->peer_id);
2219 else if(command == TOSERVER_GROUND_ACTION)
2227 [3] v3s16 nodepos_undersurface
2228 [9] v3s16 nodepos_abovesurface
2233 2: stop digging (all parameters ignored)
2234 3: digging completed
2236 u8 action = readU8(&data[2]);
2238 p_under.X = readS16(&data[3]);
2239 p_under.Y = readS16(&data[5]);
2240 p_under.Z = readS16(&data[7]);
2242 p_over.X = readS16(&data[9]);
2243 p_over.Y = readS16(&data[11]);
2244 p_over.Z = readS16(&data[13]);
2245 u16 item_i = readU16(&data[15]);
2247 //TODO: Check that target is reasonably close
2255 NOTE: This can be used in the future to check if
2256 somebody is cheating, by checking the timing.
2263 else if(action == 2)
2266 RemoteClient *client = getClient(peer->id);
2267 JMutexAutoLock digmutex(client->m_dig_mutex);
2268 client->m_dig_tool_item = -1;
2273 3: Digging completed
2275 else if(action == 3)
2277 // Mandatory parameter; actually used for nothing
2278 core::map<v3s16, MapBlock*> modified_blocks;
2280 u8 material = CONTENT_IGNORE;
2281 u8 mineral = MINERAL_NONE;
2283 bool cannot_remove_node = false;
2287 MapNode n = m_env.getMap().getNode(p_under);
2289 mineral = n.getMineral();
2290 // Get material at position
2292 // If not yet cancelled
2293 if(cannot_remove_node == false)
2295 // If it's not diggable, do nothing
2296 if(content_diggable(material) == false)
2298 derr_server<<"Server: Not finishing digging: "
2299 <<"Node not diggable"
2301 cannot_remove_node = true;
2304 // If not yet cancelled
2305 if(cannot_remove_node == false)
2307 // Get node metadata
2308 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2309 if(meta && meta->nodeRemovalDisabled() == true)
2311 derr_server<<"Server: Not finishing digging: "
2312 <<"Node metadata disables removal"
2314 cannot_remove_node = true;
2318 catch(InvalidPositionException &e)
2320 derr_server<<"Server: Not finishing digging: Node not found."
2321 <<" Adding block to emerge queue."
2323 m_emerge_queue.addBlock(peer_id,
2324 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2325 cannot_remove_node = true;
2328 // Make sure the player is allowed to do it
2329 if((player->privs & PRIV_BUILD) == 0)
2330 cannot_remove_node = true;
2333 If node can't be removed, set block to be re-sent to
2336 if(cannot_remove_node)
2338 derr_server<<"Server: Not finishing digging."<<std::endl;
2340 // Client probably has wrong data.
2341 // Set block not sent, so that client will get
2343 dstream<<"Client "<<peer_id<<" tried to dig "
2344 <<"node; but node cannot be removed."
2345 <<" setting MapBlock not sent."<<std::endl;
2346 RemoteClient *client = getClient(peer_id);
2347 v3s16 blockpos = getNodeBlockPos(p_under);
2348 client->SetBlockNotSent(blockpos);
2354 Send the removal to all other clients.
2355 - If other player is close, send REMOVENODE
2356 - Otherwise set blocks not sent
2358 core::list<u16> far_players;
2359 sendRemoveNode(p_under, peer_id, &far_players, 100);
2362 Update and send inventory
2365 if(g_settings.getBool("creative_mode") == false)
2370 InventoryList *mlist = player->inventory.getList("main");
2373 InventoryItem *item = mlist->getItem(item_i);
2374 if(item && (std::string)item->getName() == "ToolItem")
2376 ToolItem *titem = (ToolItem*)item;
2377 std::string toolname = titem->getToolName();
2379 // Get digging properties for material and tool
2380 DiggingProperties prop =
2381 getDiggingProperties(material, toolname);
2383 if(prop.diggable == false)
2385 derr_server<<"Server: WARNING: Player digged"
2386 <<" with impossible material + tool"
2387 <<" combination"<<std::endl;
2390 bool weared_out = titem->addWear(prop.wear);
2394 mlist->deleteItem(item_i);
2400 Add dug item to inventory
2403 InventoryItem *item = NULL;
2405 if(mineral != MINERAL_NONE)
2406 item = getDiggedMineralItem(mineral);
2411 std::string &dug_s = content_features(material).dug_item;
2414 std::istringstream is(dug_s, std::ios::binary);
2415 item = InventoryItem::deSerialize(is);
2421 // Add a item to inventory
2422 player->inventory.addItem("main", item);
2425 UpdateCrafting(player->peer_id);
2426 SendInventory(player->peer_id);
2432 (this takes some time so it is done after the quick stuff)
2434 m_ignore_map_edit_events = true;
2435 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2436 m_ignore_map_edit_events = false;
2439 Set blocks not sent to far players
2441 for(core::list<u16>::Iterator
2442 i = far_players.begin();
2443 i != far_players.end(); i++)
2446 RemoteClient *client = getClient(peer_id);
2449 client->SetBlocksNotSent(modified_blocks);
2456 else if(action == 1)
2459 InventoryList *ilist = player->inventory.getList("main");
2464 InventoryItem *item = ilist->getItem(item_i);
2466 // If there is no item, it is not possible to add it anywhere
2471 Handle material items
2473 if(std::string("MaterialItem") == item->getName())
2476 // Don't add a node if this is not a free space
2477 MapNode n2 = m_env.getMap().getNode(p_over);
2478 if(content_buildable_to(n2.d) == false
2479 || (player->privs & PRIV_BUILD) ==0)
2481 // Client probably has wrong data.
2482 // Set block not sent, so that client will get
2484 dstream<<"Client "<<peer_id<<" tried to place"
2485 <<" node in invalid position; setting"
2486 <<" MapBlock not sent."<<std::endl;
2487 RemoteClient *client = getClient(peer_id);
2488 v3s16 blockpos = getNodeBlockPos(p_over);
2489 client->SetBlockNotSent(blockpos);
2493 catch(InvalidPositionException &e)
2495 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2496 <<" Adding block to emerge queue."
2498 m_emerge_queue.addBlock(peer_id,
2499 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2503 // Reset build time counter
2504 getClient(peer->id)->m_time_from_building = 0.0;
2507 MaterialItem *mitem = (MaterialItem*)item;
2509 n.d = mitem->getMaterial();
2510 if(content_features(n.d).wall_mounted)
2511 n.dir = packDir(p_under - p_over);
2516 core::list<u16> far_players;
2517 sendAddNode(p_over, n, 0, &far_players, 100);
2522 InventoryList *ilist = player->inventory.getList("main");
2523 if(g_settings.getBool("creative_mode") == false && ilist)
2525 // Remove from inventory and send inventory
2526 if(mitem->getCount() == 1)
2527 ilist->deleteItem(item_i);
2531 UpdateCrafting(peer_id);
2532 SendInventory(peer_id);
2538 This takes some time so it is done after the quick stuff
2540 core::map<v3s16, MapBlock*> modified_blocks;
2541 m_ignore_map_edit_events = true;
2542 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2543 m_ignore_map_edit_events = false;
2546 Set blocks not sent to far players
2548 for(core::list<u16>::Iterator
2549 i = far_players.begin();
2550 i != far_players.end(); i++)
2553 RemoteClient *client = getClient(peer_id);
2556 client->SetBlocksNotSent(modified_blocks);
2560 Calculate special events
2563 /*if(n.d == CONTENT_MESE)
2566 for(s16 z=-1; z<=1; z++)
2567 for(s16 y=-1; y<=1; y++)
2568 for(s16 x=-1; x<=1; x++)
2575 Place other item (not a block)
2579 v3s16 blockpos = getNodeBlockPos(p_over);
2582 Check that the block is loaded so that the item
2583 can properly be added to the static list too
2585 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2588 derr_server<<"Error while placing object: "
2589 "block not found"<<std::endl;
2593 dout_server<<"Placing a miscellaneous item on map"
2596 // Calculate a position for it
2597 v3f pos = intToFloat(p_over, BS);
2599 pos.Y -= BS*0.25; // let it drop a bit
2601 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2602 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2607 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2611 derr_server<<"WARNING: item resulted in NULL object, "
2612 <<"not placing onto map"
2617 // Add the object to the environment
2618 m_env.addActiveObject(obj);
2620 dout_server<<"Placed object"<<std::endl;
2622 if(g_settings.getBool("creative_mode") == false)
2624 // Delete the right amount of items from the slot
2625 u16 dropcount = item->getDropCount();
2627 // Delete item if all gone
2628 if(item->getCount() <= dropcount)
2630 if(item->getCount() < dropcount)
2631 dstream<<"WARNING: Server: dropped more items"
2632 <<" than the slot contains"<<std::endl;
2634 InventoryList *ilist = player->inventory.getList("main");
2636 // Remove from inventory and send inventory
2637 ilist->deleteItem(item_i);
2639 // Else decrement it
2641 item->remove(dropcount);
2644 UpdateCrafting(peer_id);
2645 SendInventory(peer_id);
2653 Catch invalid actions
2657 derr_server<<"WARNING: Server: Invalid action "
2658 <<action<<std::endl;
2662 else if(command == TOSERVER_RELEASE)
2671 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2674 else if(command == TOSERVER_SIGNTEXT)
2676 if((player->privs & PRIV_BUILD) == 0)
2685 std::string datastring((char*)&data[2], datasize-2);
2686 std::istringstream is(datastring, std::ios_base::binary);
2689 is.read((char*)buf, 6);
2690 v3s16 blockpos = readV3S16(buf);
2691 is.read((char*)buf, 2);
2692 s16 id = readS16(buf);
2693 is.read((char*)buf, 2);
2694 u16 textlen = readU16(buf);
2696 for(u16 i=0; i<textlen; i++)
2698 is.read((char*)buf, 1);
2699 text += (char)buf[0];
2702 MapBlock *block = NULL;
2705 block = m_env.getMap().getBlockNoCreate(blockpos);
2707 catch(InvalidPositionException &e)
2709 derr_server<<"Error while setting sign text: "
2710 "block not found"<<std::endl;
2714 MapBlockObject *obj = block->getObject(id);
2717 derr_server<<"Error while setting sign text: "
2718 "object not found"<<std::endl;
2722 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2724 derr_server<<"Error while setting sign text: "
2725 "object is not a sign"<<std::endl;
2729 ((SignObject*)obj)->setText(text);
2731 obj->getBlock()->setChangedFlag();
2733 else if(command == TOSERVER_SIGNNODETEXT)
2735 if((player->privs & PRIV_BUILD) == 0)
2743 std::string datastring((char*)&data[2], datasize-2);
2744 std::istringstream is(datastring, std::ios_base::binary);
2747 is.read((char*)buf, 6);
2748 v3s16 p = readV3S16(buf);
2749 is.read((char*)buf, 2);
2750 u16 textlen = readU16(buf);
2752 for(u16 i=0; i<textlen; i++)
2754 is.read((char*)buf, 1);
2755 text += (char)buf[0];
2758 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2761 if(meta->typeId() != CONTENT_SIGN_WALL)
2763 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2764 signmeta->setText(text);
2766 v3s16 blockpos = getNodeBlockPos(p);
2767 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2770 block->setChangedFlag();
2773 for(core::map<u16, RemoteClient*>::Iterator
2774 i = m_clients.getIterator();
2775 i.atEnd()==false; i++)
2777 RemoteClient *client = i.getNode()->getValue();
2778 client->SetBlockNotSent(blockpos);
2781 else if(command == TOSERVER_INVENTORY_ACTION)
2783 /*// Ignore inventory changes if in creative mode
2784 if(g_settings.getBool("creative_mode") == true)
2786 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2790 // Strip command and create a stream
2791 std::string datastring((char*)&data[2], datasize-2);
2792 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2793 std::istringstream is(datastring, std::ios_base::binary);
2795 InventoryAction *a = InventoryAction::deSerialize(is);
2800 c.current_player = player;
2803 Handle craftresult specially if not in creative mode
2805 bool disable_action = false;
2806 if(a->getType() == IACTION_MOVE
2807 && g_settings.getBool("creative_mode") == false)
2809 IMoveAction *ma = (IMoveAction*)a;
2810 if(ma->to_inv == "current_player" &&
2811 ma->from_inv == "current_player")
2813 InventoryList *rlist = player->inventory.getList("craftresult");
2815 InventoryList *clist = player->inventory.getList("craft");
2817 InventoryList *mlist = player->inventory.getList("main");
2820 Craftresult is no longer preview if something
2823 if(ma->to_list == "craftresult"
2824 && ma->from_list != "craftresult")
2826 // If it currently is a preview, remove
2828 if(player->craftresult_is_preview)
2830 rlist->deleteItem(0);
2832 player->craftresult_is_preview = false;
2835 Crafting takes place if this condition is true.
2837 if(player->craftresult_is_preview &&
2838 ma->from_list == "craftresult")
2840 player->craftresult_is_preview = false;
2841 clist->decrementMaterials(1);
2844 If the craftresult is placed on itself, move it to
2845 main inventory instead of doing the action
2847 if(ma->to_list == "craftresult"
2848 && ma->from_list == "craftresult")
2850 disable_action = true;
2852 InventoryItem *item1 = rlist->changeItem(0, NULL);
2853 mlist->addItem(item1);
2858 if(disable_action == false)
2860 // Feed action to player inventory
2868 UpdateCrafting(player->peer_id);
2869 SendInventory(player->peer_id);
2874 dstream<<"TOSERVER_INVENTORY_ACTION: "
2875 <<"InventoryAction::deSerialize() returned NULL"
2879 else if(command == TOSERVER_CHAT_MESSAGE)
2887 std::string datastring((char*)&data[2], datasize-2);
2888 std::istringstream is(datastring, std::ios_base::binary);
2891 is.read((char*)buf, 2);
2892 u16 len = readU16(buf);
2894 std::wstring message;
2895 for(u16 i=0; i<len; i++)
2897 is.read((char*)buf, 2);
2898 message += (wchar_t)readU16(buf);
2901 // Get player name of this client
2902 std::wstring name = narrow_to_wide(player->getName());
2904 // Line to send to players
2906 // Whether to send to the player that sent the line
2907 bool send_to_sender = false;
2908 // Whether to send to other players
2909 bool send_to_others = false;
2912 std::wstring commandprefix = L"/#";
2913 if(message.substr(0, commandprefix.size()) == commandprefix)
2915 line += L"Server: ";
2917 message = message.substr(commandprefix.size());
2919 // Local player gets all privileges regardless of
2920 // what's set on their account.
2921 u64 privs = player->privs;
2922 if(g_settings.get("name") == player->getName())
2925 ServerCommandContext *ctx = new ServerCommandContext(
2926 str_split(message, L' '),
2932 line += processServerCommand(ctx);
2933 send_to_sender = ctx->flags & 1;
2934 send_to_others = ctx->flags & 2;
2946 send_to_others = true;
2951 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2954 Send the message to clients
2956 for(core::map<u16, RemoteClient*>::Iterator
2957 i = m_clients.getIterator();
2958 i.atEnd() == false; i++)
2960 // Get client and check that it is valid
2961 RemoteClient *client = i.getNode()->getValue();
2962 assert(client->peer_id == i.getNode()->getKey());
2963 if(client->serialization_version == SER_FMT_VER_INVALID)
2967 bool sender_selected = (peer_id == client->peer_id);
2968 if(sender_selected == true && send_to_sender == false)
2970 if(sender_selected == false && send_to_others == false)
2973 SendChatMessage(client->peer_id, line);
2977 else if(command == TOSERVER_DAMAGE)
2979 if(g_settings.getBool("enable_damage"))
2981 std::string datastring((char*)&data[2], datasize-2);
2982 std::istringstream is(datastring, std::ios_base::binary);
2983 u8 damage = readU8(is);
2984 if(player->hp > damage)
2986 player->hp -= damage;
2992 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
2995 v3f pos = findSpawnPos(m_env.getServerMap());
2996 player->setPosition(pos);
2998 SendMovePlayer(player);
2999 SendPlayerHP(player);
3001 //TODO: Throw items around
3005 SendPlayerHP(player);
3007 else if(command == TOSERVER_PASSWORD)
3010 [0] u16 TOSERVER_PASSWORD
3011 [2] u8[28] old password
3012 [30] u8[28] new password
3015 if(datasize != 2+PASSWORD_SIZE*2)
3017 char password[PASSWORD_SIZE];
3018 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3019 password[i] = data[2+i];
3020 password[PASSWORD_SIZE-1] = 0;
3021 if(strcmp(player->getPassword(),password))
3023 // Wrong old password supplied!!
3024 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3027 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3028 password[i] = data[30+i];
3029 player->updatePassword(password);
3030 SendChatMessage(peer_id, L"Password change successful");
3034 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3035 "unknown command "<<command<<std::endl;
3039 catch(SendFailedException &e)
3041 derr_server<<"Server::ProcessData(): SendFailedException: "
3047 void Server::onMapEditEvent(MapEditEvent *event)
3049 dstream<<"Server::onMapEditEvent()"<<std::endl;
3050 if(m_ignore_map_edit_events)
3052 MapEditEvent *e = event->clone();
3053 m_unsent_map_edit_queue.push_back(e);
3056 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3058 if(id == "current_player")
3060 assert(c->current_player);
3061 return &(c->current_player->inventory);
3065 std::string id0 = fn.next(":");
3067 if(id0 == "nodemeta")
3070 p.X = stoi(fn.next(","));
3071 p.Y = stoi(fn.next(","));
3072 p.Z = stoi(fn.next(","));
3073 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3075 return meta->getInventory();
3076 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3077 <<"no metadata found"<<std::endl;
3081 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3084 void Server::inventoryModified(InventoryContext *c, std::string id)
3086 if(id == "current_player")
3088 assert(c->current_player);
3090 UpdateCrafting(c->current_player->peer_id);
3091 SendInventory(c->current_player->peer_id);
3096 std::string id0 = fn.next(":");
3098 if(id0 == "nodemeta")
3101 p.X = stoi(fn.next(","));
3102 p.Y = stoi(fn.next(","));
3103 p.Z = stoi(fn.next(","));
3104 v3s16 blockpos = getNodeBlockPos(p);
3106 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3108 meta->inventoryModified();
3110 for(core::map<u16, RemoteClient*>::Iterator
3111 i = m_clients.getIterator();
3112 i.atEnd()==false; i++)
3114 RemoteClient *client = i.getNode()->getValue();
3115 client->SetBlockNotSent(blockpos);
3121 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3124 core::list<PlayerInfo> Server::getPlayerInfo()
3126 DSTACK(__FUNCTION_NAME);
3127 JMutexAutoLock envlock(m_env_mutex);
3128 JMutexAutoLock conlock(m_con_mutex);
3130 core::list<PlayerInfo> list;
3132 core::list<Player*> players = m_env.getPlayers();
3134 core::list<Player*>::Iterator i;
3135 for(i = players.begin();
3136 i != players.end(); i++)
3140 Player *player = *i;
3143 con::Peer *peer = m_con.GetPeer(player->peer_id);
3144 // Copy info from peer to info struct
3146 info.address = peer->address;
3147 info.avg_rtt = peer->avg_rtt;
3149 catch(con::PeerNotFoundException &e)
3151 // Set dummy peer info
3153 info.address = Address(0,0,0,0,0);
3157 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3158 info.position = player->getPosition();
3160 list.push_back(info);
3167 void Server::peerAdded(con::Peer *peer)
3169 DSTACK(__FUNCTION_NAME);
3170 dout_server<<"Server::peerAdded(): peer->id="
3171 <<peer->id<<std::endl;
3174 c.type = PEER_ADDED;
3175 c.peer_id = peer->id;
3177 m_peer_change_queue.push_back(c);
3180 void Server::deletingPeer(con::Peer *peer, bool timeout)
3182 DSTACK(__FUNCTION_NAME);
3183 dout_server<<"Server::deletingPeer(): peer->id="
3184 <<peer->id<<", timeout="<<timeout<<std::endl;
3187 c.type = PEER_REMOVED;
3188 c.peer_id = peer->id;
3189 c.timeout = timeout;
3190 m_peer_change_queue.push_back(c);
3197 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3199 DSTACK(__FUNCTION_NAME);
3200 std::ostringstream os(std::ios_base::binary);
3202 writeU16(os, TOCLIENT_HP);
3206 std::string s = os.str();
3207 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3209 con.Send(peer_id, 0, data, true);
3212 void Server::SendAccessDenied(con::Connection &con, u16 peer_id)
3214 DSTACK(__FUNCTION_NAME);
3215 std::ostringstream os(std::ios_base::binary);
3217 writeU16(os, TOCLIENT_ACCESS_DENIED);
3220 std::string s = os.str();
3221 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3223 con.Send(peer_id, 0, data, true);
3227 Non-static send methods
3230 void Server::SendObjectData(float dtime)
3232 DSTACK(__FUNCTION_NAME);
3234 core::map<v3s16, bool> stepped_blocks;
3236 for(core::map<u16, RemoteClient*>::Iterator
3237 i = m_clients.getIterator();
3238 i.atEnd() == false; i++)
3240 u16 peer_id = i.getNode()->getKey();
3241 RemoteClient *client = i.getNode()->getValue();
3242 assert(client->peer_id == peer_id);
3244 if(client->serialization_version == SER_FMT_VER_INVALID)
3247 client->SendObjectData(this, dtime, stepped_blocks);
3251 void Server::SendPlayerInfos()
3253 DSTACK(__FUNCTION_NAME);
3255 //JMutexAutoLock envlock(m_env_mutex);
3257 // Get connected players
3258 core::list<Player*> players = m_env.getPlayers(true);
3260 u32 player_count = players.getSize();
3261 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3263 SharedBuffer<u8> data(datasize);
3264 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3267 core::list<Player*>::Iterator i;
3268 for(i = players.begin();
3269 i != players.end(); i++)
3271 Player *player = *i;
3273 /*dstream<<"Server sending player info for player with "
3274 "peer_id="<<player->peer_id<<std::endl;*/
3276 writeU16(&data[start], player->peer_id);
3277 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3278 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3279 start += 2+PLAYERNAME_SIZE;
3282 //JMutexAutoLock conlock(m_con_mutex);
3285 m_con.SendToAll(0, data, true);
3288 void Server::SendInventory(u16 peer_id)
3290 DSTACK(__FUNCTION_NAME);
3292 Player* player = m_env.getPlayer(peer_id);
3299 std::ostringstream os;
3300 //os.imbue(std::locale("C"));
3302 player->inventory.serialize(os);
3304 std::string s = os.str();
3306 SharedBuffer<u8> data(s.size()+2);
3307 writeU16(&data[0], TOCLIENT_INVENTORY);
3308 memcpy(&data[2], s.c_str(), s.size());
3311 m_con.Send(peer_id, 0, data, true);
3314 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3316 DSTACK(__FUNCTION_NAME);
3318 std::ostringstream os(std::ios_base::binary);
3322 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3323 os.write((char*)buf, 2);
3326 writeU16(buf, message.size());
3327 os.write((char*)buf, 2);
3330 for(u32 i=0; i<message.size(); i++)
3334 os.write((char*)buf, 2);
3338 std::string s = os.str();
3339 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3341 m_con.Send(peer_id, 0, data, true);
3344 void Server::BroadcastChatMessage(const std::wstring &message)
3346 for(core::map<u16, RemoteClient*>::Iterator
3347 i = m_clients.getIterator();
3348 i.atEnd() == false; i++)
3350 // Get client and check that it is valid
3351 RemoteClient *client = i.getNode()->getValue();
3352 assert(client->peer_id == i.getNode()->getKey());
3353 if(client->serialization_version == SER_FMT_VER_INVALID)
3356 SendChatMessage(client->peer_id, message);
3360 void Server::SendPlayerHP(Player *player)
3362 SendHP(m_con, player->peer_id, player->hp);
3365 void Server::SendMovePlayer(Player *player)
3367 DSTACK(__FUNCTION_NAME);
3368 std::ostringstream os(std::ios_base::binary);
3370 writeU16(os, TOCLIENT_MOVE_PLAYER);
3371 writeV3F1000(os, player->getPosition());
3372 writeF1000(os, player->getPitch());
3373 writeF1000(os, player->getYaw());
3376 v3f pos = player->getPosition();
3377 f32 pitch = player->getPitch();
3378 f32 yaw = player->getYaw();
3379 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3380 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3387 std::string s = os.str();
3388 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3390 m_con.Send(player->peer_id, 0, data, true);
3393 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3394 core::list<u16> *far_players, float far_d_nodes)
3396 float maxd = far_d_nodes*BS;
3397 v3f p_f = intToFloat(p, BS);
3401 SharedBuffer<u8> reply(replysize);
3402 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3403 writeS16(&reply[2], p.X);
3404 writeS16(&reply[4], p.Y);
3405 writeS16(&reply[6], p.Z);
3407 for(core::map<u16, RemoteClient*>::Iterator
3408 i = m_clients.getIterator();
3409 i.atEnd() == false; i++)
3411 // Get client and check that it is valid
3412 RemoteClient *client = i.getNode()->getValue();
3413 assert(client->peer_id == i.getNode()->getKey());
3414 if(client->serialization_version == SER_FMT_VER_INVALID)
3417 // Don't send if it's the same one
3418 if(client->peer_id == ignore_id)
3424 Player *player = m_env.getPlayer(client->peer_id);
3427 // If player is far away, only set modified blocks not sent
3428 v3f player_pos = player->getPosition();
3429 if(player_pos.getDistanceFrom(p_f) > maxd)
3431 far_players->push_back(client->peer_id);
3438 m_con.Send(client->peer_id, 0, reply, true);
3442 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3443 core::list<u16> *far_players, float far_d_nodes)
3445 float maxd = far_d_nodes*BS;
3446 v3f p_f = intToFloat(p, BS);
3448 for(core::map<u16, RemoteClient*>::Iterator
3449 i = m_clients.getIterator();
3450 i.atEnd() == false; i++)
3452 // Get client and check that it is valid
3453 RemoteClient *client = i.getNode()->getValue();
3454 assert(client->peer_id == i.getNode()->getKey());
3455 if(client->serialization_version == SER_FMT_VER_INVALID)
3458 // Don't send if it's the same one
3459 if(client->peer_id == ignore_id)
3465 Player *player = m_env.getPlayer(client->peer_id);
3468 // If player is far away, only set modified blocks not sent
3469 v3f player_pos = player->getPosition();
3470 if(player_pos.getDistanceFrom(p_f) > maxd)
3472 far_players->push_back(client->peer_id);
3479 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3480 SharedBuffer<u8> reply(replysize);
3481 writeU16(&reply[0], TOCLIENT_ADDNODE);
3482 writeS16(&reply[2], p.X);
3483 writeS16(&reply[4], p.Y);
3484 writeS16(&reply[6], p.Z);
3485 n.serialize(&reply[8], client->serialization_version);
3488 m_con.Send(client->peer_id, 0, reply, true);
3492 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3494 DSTACK(__FUNCTION_NAME);
3496 v3s16 p = block->getPos();
3500 bool completely_air = true;
3501 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3502 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3503 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3505 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3507 completely_air = false;
3508 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3513 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3515 dstream<<"[completely air] ";
3520 Create a packet with the block in the right format
3523 std::ostringstream os(std::ios_base::binary);
3524 block->serialize(os, ver);
3525 std::string s = os.str();
3526 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3528 u32 replysize = 8 + blockdata.getSize();
3529 SharedBuffer<u8> reply(replysize);
3530 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3531 writeS16(&reply[2], p.X);
3532 writeS16(&reply[4], p.Y);
3533 writeS16(&reply[6], p.Z);
3534 memcpy(&reply[8], *blockdata, blockdata.getSize());
3536 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3537 <<": \tpacket size: "<<replysize<<std::endl;*/
3542 m_con.Send(peer_id, 1, reply, true);
3545 void Server::SendBlocks(float dtime)
3547 DSTACK(__FUNCTION_NAME);
3549 JMutexAutoLock envlock(m_env_mutex);
3550 JMutexAutoLock conlock(m_con_mutex);
3552 //TimeTaker timer("Server::SendBlocks");
3554 core::array<PrioritySortedBlockTransfer> queue;
3556 s32 total_sending = 0;
3558 for(core::map<u16, RemoteClient*>::Iterator
3559 i = m_clients.getIterator();
3560 i.atEnd() == false; i++)
3562 RemoteClient *client = i.getNode()->getValue();
3563 assert(client->peer_id == i.getNode()->getKey());
3565 total_sending += client->SendingCount();
3567 if(client->serialization_version == SER_FMT_VER_INVALID)
3570 client->GetNextBlocks(this, dtime, queue);
3574 // Lowest priority number comes first.
3575 // Lowest is most important.
3578 for(u32 i=0; i<queue.size(); i++)
3580 //TODO: Calculate limit dynamically
3581 if(total_sending >= g_settings.getS32
3582 ("max_simultaneous_block_sends_server_total"))
3585 PrioritySortedBlockTransfer q = queue[i];
3587 MapBlock *block = NULL;
3590 block = m_env.getMap().getBlockNoCreate(q.pos);
3592 catch(InvalidPositionException &e)
3597 RemoteClient *client = getClient(q.peer_id);
3599 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3601 client->SentBlock(q.pos);
3611 void Server::UpdateCrafting(u16 peer_id)
3613 DSTACK(__FUNCTION_NAME);
3615 Player* player = m_env.getPlayer(peer_id);
3619 Calculate crafting stuff
3621 if(g_settings.getBool("creative_mode") == false)
3623 InventoryList *clist = player->inventory.getList("craft");
3624 InventoryList *rlist = player->inventory.getList("craftresult");
3626 if(rlist->getUsedSlots() == 0)
3627 player->craftresult_is_preview = true;
3629 if(rlist && player->craftresult_is_preview)
3631 rlist->clearItems();
3633 if(clist && rlist && player->craftresult_is_preview)
3635 InventoryItem *items[9];
3636 for(u16 i=0; i<9; i++)
3638 items[i] = clist->getItem(i);
3647 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3648 if(checkItemCombination(items, specs))
3650 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3659 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3660 if(checkItemCombination(items, specs))
3662 rlist->addItem(new CraftItem("Stick", 4));
3671 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3672 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3673 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3674 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3675 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3676 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3677 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3678 if(checkItemCombination(items, specs))
3680 //rlist->addItem(new MapBlockObjectItem("Sign"));
3681 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3690 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3691 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3692 if(checkItemCombination(items, specs))
3694 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3703 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3704 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3705 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3706 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3707 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3708 if(checkItemCombination(items, specs))
3710 rlist->addItem(new ToolItem("WPick", 0));
3719 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3720 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3721 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3722 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3723 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3724 if(checkItemCombination(items, specs))
3726 rlist->addItem(new ToolItem("STPick", 0));
3735 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3736 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3737 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3738 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3739 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3740 if(checkItemCombination(items, specs))
3742 rlist->addItem(new ToolItem("SteelPick", 0));
3751 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3752 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3753 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3754 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3755 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3756 if(checkItemCombination(items, specs))
3758 rlist->addItem(new ToolItem("MesePick", 0));
3767 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3768 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3769 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3770 if(checkItemCombination(items, specs))
3772 rlist->addItem(new ToolItem("WShovel", 0));
3781 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3782 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3783 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3784 if(checkItemCombination(items, specs))
3786 rlist->addItem(new ToolItem("STShovel", 0));
3795 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3796 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3797 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3798 if(checkItemCombination(items, specs))
3800 rlist->addItem(new ToolItem("SteelShovel", 0));
3809 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3810 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3811 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3812 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3813 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3814 if(checkItemCombination(items, specs))
3816 rlist->addItem(new ToolItem("WAxe", 0));
3825 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3826 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3827 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3828 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3829 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3830 if(checkItemCombination(items, specs))
3832 rlist->addItem(new ToolItem("STAxe", 0));
3841 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3842 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3843 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3844 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3845 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3846 if(checkItemCombination(items, specs))
3848 rlist->addItem(new ToolItem("SteelAxe", 0));
3857 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3858 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3859 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3860 if(checkItemCombination(items, specs))
3862 rlist->addItem(new ToolItem("WSword", 0));
3871 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3872 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3873 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3874 if(checkItemCombination(items, specs))
3876 rlist->addItem(new ToolItem("STSword", 0));
3885 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3886 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3887 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3888 if(checkItemCombination(items, specs))
3890 rlist->addItem(new ToolItem("SteelSword", 0));
3899 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3900 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3901 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3902 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3903 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3904 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3905 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3906 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3907 if(checkItemCombination(items, specs))
3909 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3918 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3919 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3920 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3921 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3922 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3923 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3924 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3925 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3926 if(checkItemCombination(items, specs))
3928 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3937 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3938 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3939 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3940 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3941 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3942 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3943 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3944 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3945 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3946 if(checkItemCombination(items, specs))
3948 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3954 } // if creative_mode == false
3957 RemoteClient* Server::getClient(u16 peer_id)
3959 DSTACK(__FUNCTION_NAME);
3960 //JMutexAutoLock lock(m_con_mutex);
3961 core::map<u16, RemoteClient*>::Node *n;
3962 n = m_clients.find(peer_id);
3963 // A client should exist for all peers
3965 return n->getValue();
3968 std::wstring Server::getStatusString()
3970 std::wostringstream os(std::ios_base::binary);
3973 os<<L"version="<<narrow_to_wide(VERSION_STRING);
3975 os<<L", uptime="<<m_uptime.get();
3976 // Information about clients
3978 for(core::map<u16, RemoteClient*>::Iterator
3979 i = m_clients.getIterator();
3980 i.atEnd() == false; i++)
3982 // Get client and check that it is valid
3983 RemoteClient *client = i.getNode()->getValue();
3984 assert(client->peer_id == i.getNode()->getKey());
3985 if(client->serialization_version == SER_FMT_VER_INVALID)
3988 Player *player = m_env.getPlayer(client->peer_id);
3989 // Get name of player
3990 std::wstring name = L"unknown";
3992 name = narrow_to_wide(player->getName());
3993 // Add name to information string
3997 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3998 os<<" WARNING: Map saving is disabled."<<std::endl;
4003 void setCreativeInventory(Player *player)
4005 player->resetInventory();
4007 // Give some good tools
4009 InventoryItem *item = new ToolItem("MesePick", 0);
4010 void* r = player->inventory.addItem("main", item);
4014 InventoryItem *item = new ToolItem("SteelPick", 0);
4015 void* r = player->inventory.addItem("main", item);
4019 InventoryItem *item = new ToolItem("SteelAxe", 0);
4020 void* r = player->inventory.addItem("main", item);
4024 InventoryItem *item = new ToolItem("SteelShovel", 0);
4025 void* r = player->inventory.addItem("main", item);
4033 // CONTENT_IGNORE-terminated list
4034 u8 material_items[] = {
4044 CONTENT_WATERSOURCE,
4052 u8 *mip = material_items;
4053 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4055 if(*mip == CONTENT_IGNORE)
4058 InventoryItem *item = new MaterialItem(*mip, 1);
4059 player->inventory.addItem("main", item);
4065 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4068 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4069 player->inventory.addItem("main", item);
4072 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4074 // Skip some materials
4075 if(i == CONTENT_WATER || i == CONTENT_TORCH
4076 || i == CONTENT_COALSTONE)
4079 InventoryItem *item = new MaterialItem(i, 1);
4080 player->inventory.addItem("main", item);
4086 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4087 void* r = player->inventory.addItem("main", item);
4092 v3f findSpawnPos(ServerMap &map)
4094 //return v3f(50,50,50)*BS;
4097 s16 groundheight = 0;
4099 // Try to find a good place a few times
4100 for(s32 i=0; i<1000; i++)
4103 // We're going to try to throw the player to this position
4104 nodepos = v2s16(-range + (myrand()%(range*2)),
4105 -range + (myrand()%(range*2)));
4106 v2s16 sectorpos = getNodeSectorPos(nodepos);
4107 // Get sector (NOTE: Don't get because it's slow)
4108 //m_env.getMap().emergeSector(sectorpos);
4109 // Get ground height at point (fallbacks to heightmap function)
4110 groundheight = map.findGroundLevel(nodepos);
4111 // Don't go underwater
4112 if(groundheight < WATER_LEVEL)
4114 //dstream<<"-> Underwater"<<std::endl;
4117 // Don't go to high places
4118 if(groundheight > WATER_LEVEL + 4)
4120 //dstream<<"-> Underwater"<<std::endl;
4124 // Found a good place
4125 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4129 // If no suitable place was not found, go above water at least.
4130 if(groundheight < WATER_LEVEL)
4131 groundheight = WATER_LEVEL;
4133 return intToFloat(v3s16(
4140 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4143 Try to get an existing player
4145 Player *player = m_env.getPlayer(name);
4148 // If player is already connected, cancel
4149 if(player->peer_id != 0)
4151 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4156 player->peer_id = peer_id;
4158 // Reset inventory to creative if in creative mode
4159 if(g_settings.getBool("creative_mode"))
4161 setCreativeInventory(player);
4168 If player with the wanted peer_id already exists, cancel.
4170 if(m_env.getPlayer(peer_id) != NULL)
4172 dstream<<"emergePlayer(): Player with wrong name but same"
4173 " peer_id already exists"<<std::endl;
4181 player = new ServerRemotePlayer();
4182 //player->peer_id = c.peer_id;
4183 //player->peer_id = PEER_ID_INEXISTENT;
4184 player->peer_id = peer_id;
4185 player->updateName(name);
4186 player->updatePassword(password);
4192 dstream<<"Server: Finding spawn place for player \""
4193 <<player->getName()<<"\""<<std::endl;
4195 v3f pos = findSpawnPos(m_env.getServerMap());
4197 player->setPosition(pos);
4200 Add player to environment
4203 m_env.addPlayer(player);
4206 Add stuff to inventory
4209 if(g_settings.getBool("creative_mode"))
4211 setCreativeInventory(player);
4213 else if(g_settings.getBool("give_initial_stuff"))
4216 InventoryItem *item = new ToolItem("SteelPick", 0);
4217 void* r = player->inventory.addItem("main", item);
4221 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4222 void* r = player->inventory.addItem("main", item);
4226 InventoryItem *item = new ToolItem("SteelAxe", 0);
4227 void* r = player->inventory.addItem("main", item);
4231 InventoryItem *item = new ToolItem("SteelShovel", 0);
4232 void* r = player->inventory.addItem("main", item);
4236 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4237 void* r = player->inventory.addItem("main", item);
4241 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4242 void* r = player->inventory.addItem("main", item);
4246 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4247 void* r = player->inventory.addItem("main", item);
4251 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4252 void* r = player->inventory.addItem("main", item);
4256 InventoryItem *item = new CraftItem("Stick", 4);
4257 void* r = player->inventory.addItem("main", item);
4261 InventoryItem *item = new ToolItem("WPick", 32000);
4262 void* r = player->inventory.addItem("main", item);
4266 InventoryItem *item = new ToolItem("STPick", 32000);
4267 void* r = player->inventory.addItem("main", item);
4271 for(u16 i=0; i<4; i++)
4273 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4274 bool r = player->inventory.addItem("main", item);
4277 /*// Give some other stuff
4279 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4280 bool r = player->inventory.addItem("main", item);
4287 } // create new player
4290 void Server::handlePeerChange(PeerChange &c)
4292 JMutexAutoLock envlock(m_env_mutex);
4293 JMutexAutoLock conlock(m_con_mutex);
4295 if(c.type == PEER_ADDED)
4302 core::map<u16, RemoteClient*>::Node *n;
4303 n = m_clients.find(c.peer_id);
4304 // The client shouldn't already exist
4308 RemoteClient *client = new RemoteClient();
4309 client->peer_id = c.peer_id;
4310 m_clients.insert(client->peer_id, client);
4313 else if(c.type == PEER_REMOVED)
4320 core::map<u16, RemoteClient*>::Node *n;
4321 n = m_clients.find(c.peer_id);
4322 // The client should exist
4326 Mark objects to be not known by the client
4328 RemoteClient *client = n->getValue();
4330 for(core::map<u16, bool>::Iterator
4331 i = client->m_known_objects.getIterator();
4332 i.atEnd()==false; i++)
4335 u16 id = i.getNode()->getKey();
4336 ServerActiveObject* obj = m_env.getActiveObject(id);
4338 if(obj && obj->m_known_by_count > 0)
4339 obj->m_known_by_count--;
4342 // Collect information about leaving in chat
4343 std::wstring message;
4345 std::wstring name = L"unknown";
4346 Player *player = m_env.getPlayer(c.peer_id);
4348 name = narrow_to_wide(player->getName());
4352 message += L" left game";
4354 message += L" (timed out)";
4359 m_env.removePlayer(c.peer_id);
4362 // Set player client disconnected
4364 Player *player = m_env.getPlayer(c.peer_id);
4366 player->peer_id = 0;
4370 delete m_clients[c.peer_id];
4371 m_clients.remove(c.peer_id);
4373 // Send player info to all remaining clients
4376 // Send leave chat message to all remaining clients
4377 BroadcastChatMessage(message);
4386 void Server::handlePeerChanges()
4388 while(m_peer_change_queue.size() > 0)
4390 PeerChange c = m_peer_change_queue.pop_front();
4392 dout_server<<"Server: Handling peer change: "
4393 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4396 handlePeerChange(c);
4400 void dedicated_server_loop(Server &server, bool &kill)
4402 DSTACK(__FUNCTION_NAME);
4404 std::cout<<DTIME<<std::endl;
4405 std::cout<<"========================"<<std::endl;
4406 std::cout<<"Running dedicated server"<<std::endl;
4407 std::cout<<"========================"<<std::endl;
4408 std::cout<<std::endl;
4412 // This is kind of a hack but can be done like this
4413 // because server.step() is very light
4417 if(server.getShutdownRequested() || kill)
4419 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4423 static int counter = 0;
4429 core::list<PlayerInfo> list = server.getPlayerInfo();
4430 core::list<PlayerInfo>::Iterator i;
4431 static u32 sum_old = 0;
4432 u32 sum = PIChecksum(list);
4435 std::cout<<DTIME<<"Player info:"<<std::endl;
4436 for(i=list.begin(); i!=list.end(); i++)
4438 i->PrintLine(&std::cout);