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;
2911 // Local player gets all privileges regardless of
2912 // what's set on their account.
2913 u64 privs = player->privs;
2914 if(g_settings.get("name") == player->getName())
2918 std::wstring commandprefix = L"/#";
2919 if(message.substr(0, commandprefix.size()) == commandprefix)
2921 line += L"Server: ";
2923 message = message.substr(commandprefix.size());
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;
2940 if(privs & PRIV_SHOUT)
2946 send_to_others = true;
2950 line += L"Server: You are not allowed to shout";
2951 send_to_sender = true;
2957 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2960 Send the message to clients
2962 for(core::map<u16, RemoteClient*>::Iterator
2963 i = m_clients.getIterator();
2964 i.atEnd() == false; i++)
2966 // Get client and check that it is valid
2967 RemoteClient *client = i.getNode()->getValue();
2968 assert(client->peer_id == i.getNode()->getKey());
2969 if(client->serialization_version == SER_FMT_VER_INVALID)
2973 bool sender_selected = (peer_id == client->peer_id);
2974 if(sender_selected == true && send_to_sender == false)
2976 if(sender_selected == false && send_to_others == false)
2979 SendChatMessage(client->peer_id, line);
2983 else if(command == TOSERVER_DAMAGE)
2985 if(g_settings.getBool("enable_damage"))
2987 std::string datastring((char*)&data[2], datasize-2);
2988 std::istringstream is(datastring, std::ios_base::binary);
2989 u8 damage = readU8(is);
2990 if(player->hp > damage)
2992 player->hp -= damage;
2998 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3001 v3f pos = findSpawnPos(m_env.getServerMap());
3002 player->setPosition(pos);
3004 SendMovePlayer(player);
3005 SendPlayerHP(player);
3007 //TODO: Throw items around
3011 SendPlayerHP(player);
3013 else if(command == TOSERVER_PASSWORD)
3016 [0] u16 TOSERVER_PASSWORD
3017 [2] u8[28] old password
3018 [30] u8[28] new password
3021 if(datasize != 2+PASSWORD_SIZE*2)
3023 char password[PASSWORD_SIZE];
3024 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3025 password[i] = data[2+i];
3026 password[PASSWORD_SIZE-1] = 0;
3027 if(strcmp(player->getPassword(),password))
3029 // Wrong old password supplied!!
3030 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3033 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3034 password[i] = data[30+i];
3035 player->updatePassword(password);
3036 SendChatMessage(peer_id, L"Password change successful");
3040 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3041 "unknown command "<<command<<std::endl;
3045 catch(SendFailedException &e)
3047 derr_server<<"Server::ProcessData(): SendFailedException: "
3053 void Server::onMapEditEvent(MapEditEvent *event)
3055 dstream<<"Server::onMapEditEvent()"<<std::endl;
3056 if(m_ignore_map_edit_events)
3058 MapEditEvent *e = event->clone();
3059 m_unsent_map_edit_queue.push_back(e);
3062 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3064 if(id == "current_player")
3066 assert(c->current_player);
3067 return &(c->current_player->inventory);
3071 std::string id0 = fn.next(":");
3073 if(id0 == "nodemeta")
3076 p.X = stoi(fn.next(","));
3077 p.Y = stoi(fn.next(","));
3078 p.Z = stoi(fn.next(","));
3079 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3081 return meta->getInventory();
3082 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3083 <<"no metadata found"<<std::endl;
3087 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3090 void Server::inventoryModified(InventoryContext *c, std::string id)
3092 if(id == "current_player")
3094 assert(c->current_player);
3096 UpdateCrafting(c->current_player->peer_id);
3097 SendInventory(c->current_player->peer_id);
3102 std::string id0 = fn.next(":");
3104 if(id0 == "nodemeta")
3107 p.X = stoi(fn.next(","));
3108 p.Y = stoi(fn.next(","));
3109 p.Z = stoi(fn.next(","));
3110 v3s16 blockpos = getNodeBlockPos(p);
3112 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3114 meta->inventoryModified();
3116 for(core::map<u16, RemoteClient*>::Iterator
3117 i = m_clients.getIterator();
3118 i.atEnd()==false; i++)
3120 RemoteClient *client = i.getNode()->getValue();
3121 client->SetBlockNotSent(blockpos);
3127 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3130 core::list<PlayerInfo> Server::getPlayerInfo()
3132 DSTACK(__FUNCTION_NAME);
3133 JMutexAutoLock envlock(m_env_mutex);
3134 JMutexAutoLock conlock(m_con_mutex);
3136 core::list<PlayerInfo> list;
3138 core::list<Player*> players = m_env.getPlayers();
3140 core::list<Player*>::Iterator i;
3141 for(i = players.begin();
3142 i != players.end(); i++)
3146 Player *player = *i;
3149 con::Peer *peer = m_con.GetPeer(player->peer_id);
3150 // Copy info from peer to info struct
3152 info.address = peer->address;
3153 info.avg_rtt = peer->avg_rtt;
3155 catch(con::PeerNotFoundException &e)
3157 // Set dummy peer info
3159 info.address = Address(0,0,0,0,0);
3163 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3164 info.position = player->getPosition();
3166 list.push_back(info);
3173 void Server::peerAdded(con::Peer *peer)
3175 DSTACK(__FUNCTION_NAME);
3176 dout_server<<"Server::peerAdded(): peer->id="
3177 <<peer->id<<std::endl;
3180 c.type = PEER_ADDED;
3181 c.peer_id = peer->id;
3183 m_peer_change_queue.push_back(c);
3186 void Server::deletingPeer(con::Peer *peer, bool timeout)
3188 DSTACK(__FUNCTION_NAME);
3189 dout_server<<"Server::deletingPeer(): peer->id="
3190 <<peer->id<<", timeout="<<timeout<<std::endl;
3193 c.type = PEER_REMOVED;
3194 c.peer_id = peer->id;
3195 c.timeout = timeout;
3196 m_peer_change_queue.push_back(c);
3203 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3205 DSTACK(__FUNCTION_NAME);
3206 std::ostringstream os(std::ios_base::binary);
3208 writeU16(os, TOCLIENT_HP);
3212 std::string s = os.str();
3213 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3215 con.Send(peer_id, 0, data, true);
3218 void Server::SendAccessDenied(con::Connection &con, u16 peer_id)
3220 DSTACK(__FUNCTION_NAME);
3221 std::ostringstream os(std::ios_base::binary);
3223 writeU16(os, TOCLIENT_ACCESS_DENIED);
3226 std::string s = os.str();
3227 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3229 con.Send(peer_id, 0, data, true);
3233 Non-static send methods
3236 void Server::SendObjectData(float dtime)
3238 DSTACK(__FUNCTION_NAME);
3240 core::map<v3s16, bool> stepped_blocks;
3242 for(core::map<u16, RemoteClient*>::Iterator
3243 i = m_clients.getIterator();
3244 i.atEnd() == false; i++)
3246 u16 peer_id = i.getNode()->getKey();
3247 RemoteClient *client = i.getNode()->getValue();
3248 assert(client->peer_id == peer_id);
3250 if(client->serialization_version == SER_FMT_VER_INVALID)
3253 client->SendObjectData(this, dtime, stepped_blocks);
3257 void Server::SendPlayerInfos()
3259 DSTACK(__FUNCTION_NAME);
3261 //JMutexAutoLock envlock(m_env_mutex);
3263 // Get connected players
3264 core::list<Player*> players = m_env.getPlayers(true);
3266 u32 player_count = players.getSize();
3267 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3269 SharedBuffer<u8> data(datasize);
3270 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3273 core::list<Player*>::Iterator i;
3274 for(i = players.begin();
3275 i != players.end(); i++)
3277 Player *player = *i;
3279 /*dstream<<"Server sending player info for player with "
3280 "peer_id="<<player->peer_id<<std::endl;*/
3282 writeU16(&data[start], player->peer_id);
3283 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3284 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3285 start += 2+PLAYERNAME_SIZE;
3288 //JMutexAutoLock conlock(m_con_mutex);
3291 m_con.SendToAll(0, data, true);
3294 void Server::SendInventory(u16 peer_id)
3296 DSTACK(__FUNCTION_NAME);
3298 Player* player = m_env.getPlayer(peer_id);
3305 std::ostringstream os;
3306 //os.imbue(std::locale("C"));
3308 player->inventory.serialize(os);
3310 std::string s = os.str();
3312 SharedBuffer<u8> data(s.size()+2);
3313 writeU16(&data[0], TOCLIENT_INVENTORY);
3314 memcpy(&data[2], s.c_str(), s.size());
3317 m_con.Send(peer_id, 0, data, true);
3320 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3322 DSTACK(__FUNCTION_NAME);
3324 std::ostringstream os(std::ios_base::binary);
3328 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3329 os.write((char*)buf, 2);
3332 writeU16(buf, message.size());
3333 os.write((char*)buf, 2);
3336 for(u32 i=0; i<message.size(); i++)
3340 os.write((char*)buf, 2);
3344 std::string s = os.str();
3345 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3347 m_con.Send(peer_id, 0, data, true);
3350 void Server::BroadcastChatMessage(const std::wstring &message)
3352 for(core::map<u16, RemoteClient*>::Iterator
3353 i = m_clients.getIterator();
3354 i.atEnd() == false; i++)
3356 // Get client and check that it is valid
3357 RemoteClient *client = i.getNode()->getValue();
3358 assert(client->peer_id == i.getNode()->getKey());
3359 if(client->serialization_version == SER_FMT_VER_INVALID)
3362 SendChatMessage(client->peer_id, message);
3366 void Server::SendPlayerHP(Player *player)
3368 SendHP(m_con, player->peer_id, player->hp);
3371 void Server::SendMovePlayer(Player *player)
3373 DSTACK(__FUNCTION_NAME);
3374 std::ostringstream os(std::ios_base::binary);
3376 writeU16(os, TOCLIENT_MOVE_PLAYER);
3377 writeV3F1000(os, player->getPosition());
3378 writeF1000(os, player->getPitch());
3379 writeF1000(os, player->getYaw());
3382 v3f pos = player->getPosition();
3383 f32 pitch = player->getPitch();
3384 f32 yaw = player->getYaw();
3385 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3386 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3393 std::string s = os.str();
3394 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3396 m_con.Send(player->peer_id, 0, data, true);
3399 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3400 core::list<u16> *far_players, float far_d_nodes)
3402 float maxd = far_d_nodes*BS;
3403 v3f p_f = intToFloat(p, BS);
3407 SharedBuffer<u8> reply(replysize);
3408 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3409 writeS16(&reply[2], p.X);
3410 writeS16(&reply[4], p.Y);
3411 writeS16(&reply[6], p.Z);
3413 for(core::map<u16, RemoteClient*>::Iterator
3414 i = m_clients.getIterator();
3415 i.atEnd() == false; i++)
3417 // Get client and check that it is valid
3418 RemoteClient *client = i.getNode()->getValue();
3419 assert(client->peer_id == i.getNode()->getKey());
3420 if(client->serialization_version == SER_FMT_VER_INVALID)
3423 // Don't send if it's the same one
3424 if(client->peer_id == ignore_id)
3430 Player *player = m_env.getPlayer(client->peer_id);
3433 // If player is far away, only set modified blocks not sent
3434 v3f player_pos = player->getPosition();
3435 if(player_pos.getDistanceFrom(p_f) > maxd)
3437 far_players->push_back(client->peer_id);
3444 m_con.Send(client->peer_id, 0, reply, true);
3448 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3449 core::list<u16> *far_players, float far_d_nodes)
3451 float maxd = far_d_nodes*BS;
3452 v3f p_f = intToFloat(p, BS);
3454 for(core::map<u16, RemoteClient*>::Iterator
3455 i = m_clients.getIterator();
3456 i.atEnd() == false; i++)
3458 // Get client and check that it is valid
3459 RemoteClient *client = i.getNode()->getValue();
3460 assert(client->peer_id == i.getNode()->getKey());
3461 if(client->serialization_version == SER_FMT_VER_INVALID)
3464 // Don't send if it's the same one
3465 if(client->peer_id == ignore_id)
3471 Player *player = m_env.getPlayer(client->peer_id);
3474 // If player is far away, only set modified blocks not sent
3475 v3f player_pos = player->getPosition();
3476 if(player_pos.getDistanceFrom(p_f) > maxd)
3478 far_players->push_back(client->peer_id);
3485 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3486 SharedBuffer<u8> reply(replysize);
3487 writeU16(&reply[0], TOCLIENT_ADDNODE);
3488 writeS16(&reply[2], p.X);
3489 writeS16(&reply[4], p.Y);
3490 writeS16(&reply[6], p.Z);
3491 n.serialize(&reply[8], client->serialization_version);
3494 m_con.Send(client->peer_id, 0, reply, true);
3498 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3500 DSTACK(__FUNCTION_NAME);
3502 v3s16 p = block->getPos();
3506 bool completely_air = true;
3507 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3508 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3509 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3511 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3513 completely_air = false;
3514 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3519 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3521 dstream<<"[completely air] ";
3526 Create a packet with the block in the right format
3529 std::ostringstream os(std::ios_base::binary);
3530 block->serialize(os, ver);
3531 std::string s = os.str();
3532 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3534 u32 replysize = 8 + blockdata.getSize();
3535 SharedBuffer<u8> reply(replysize);
3536 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3537 writeS16(&reply[2], p.X);
3538 writeS16(&reply[4], p.Y);
3539 writeS16(&reply[6], p.Z);
3540 memcpy(&reply[8], *blockdata, blockdata.getSize());
3542 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3543 <<": \tpacket size: "<<replysize<<std::endl;*/
3548 m_con.Send(peer_id, 1, reply, true);
3551 void Server::SendBlocks(float dtime)
3553 DSTACK(__FUNCTION_NAME);
3555 JMutexAutoLock envlock(m_env_mutex);
3556 JMutexAutoLock conlock(m_con_mutex);
3558 //TimeTaker timer("Server::SendBlocks");
3560 core::array<PrioritySortedBlockTransfer> queue;
3562 s32 total_sending = 0;
3564 for(core::map<u16, RemoteClient*>::Iterator
3565 i = m_clients.getIterator();
3566 i.atEnd() == false; i++)
3568 RemoteClient *client = i.getNode()->getValue();
3569 assert(client->peer_id == i.getNode()->getKey());
3571 total_sending += client->SendingCount();
3573 if(client->serialization_version == SER_FMT_VER_INVALID)
3576 client->GetNextBlocks(this, dtime, queue);
3580 // Lowest priority number comes first.
3581 // Lowest is most important.
3584 for(u32 i=0; i<queue.size(); i++)
3586 //TODO: Calculate limit dynamically
3587 if(total_sending >= g_settings.getS32
3588 ("max_simultaneous_block_sends_server_total"))
3591 PrioritySortedBlockTransfer q = queue[i];
3593 MapBlock *block = NULL;
3596 block = m_env.getMap().getBlockNoCreate(q.pos);
3598 catch(InvalidPositionException &e)
3603 RemoteClient *client = getClient(q.peer_id);
3605 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3607 client->SentBlock(q.pos);
3617 void Server::UpdateCrafting(u16 peer_id)
3619 DSTACK(__FUNCTION_NAME);
3621 Player* player = m_env.getPlayer(peer_id);
3625 Calculate crafting stuff
3627 if(g_settings.getBool("creative_mode") == false)
3629 InventoryList *clist = player->inventory.getList("craft");
3630 InventoryList *rlist = player->inventory.getList("craftresult");
3632 if(rlist->getUsedSlots() == 0)
3633 player->craftresult_is_preview = true;
3635 if(rlist && player->craftresult_is_preview)
3637 rlist->clearItems();
3639 if(clist && rlist && player->craftresult_is_preview)
3641 InventoryItem *items[9];
3642 for(u16 i=0; i<9; i++)
3644 items[i] = clist->getItem(i);
3653 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3654 if(checkItemCombination(items, specs))
3656 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3665 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3666 if(checkItemCombination(items, specs))
3668 rlist->addItem(new CraftItem("Stick", 4));
3677 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3678 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3679 specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
3680 specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
3681 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3682 specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
3683 if(checkItemCombination(items, specs))
3685 rlist->addItem(new MaterialItem(CONTENT_FENCE, 2));
3694 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3695 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3696 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3697 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3698 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3699 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3700 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3701 if(checkItemCombination(items, specs))
3703 //rlist->addItem(new MapBlockObjectItem("Sign"));
3704 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3713 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3714 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3715 if(checkItemCombination(items, specs))
3717 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3726 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3727 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3728 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3729 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3730 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3731 if(checkItemCombination(items, specs))
3733 rlist->addItem(new ToolItem("WPick", 0));
3742 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3743 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3744 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3745 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3746 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3747 if(checkItemCombination(items, specs))
3749 rlist->addItem(new ToolItem("STPick", 0));
3758 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3759 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3760 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3761 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3762 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3763 if(checkItemCombination(items, specs))
3765 rlist->addItem(new ToolItem("SteelPick", 0));
3774 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3775 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3776 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3777 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3778 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3779 if(checkItemCombination(items, specs))
3781 rlist->addItem(new ToolItem("MesePick", 0));
3790 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3791 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3792 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3793 if(checkItemCombination(items, specs))
3795 rlist->addItem(new ToolItem("WShovel", 0));
3804 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3805 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3806 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3807 if(checkItemCombination(items, specs))
3809 rlist->addItem(new ToolItem("STShovel", 0));
3818 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3819 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3820 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3821 if(checkItemCombination(items, specs))
3823 rlist->addItem(new ToolItem("SteelShovel", 0));
3832 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3833 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3834 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3835 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3836 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3837 if(checkItemCombination(items, specs))
3839 rlist->addItem(new ToolItem("WAxe", 0));
3848 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3849 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3850 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3851 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3852 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3853 if(checkItemCombination(items, specs))
3855 rlist->addItem(new ToolItem("STAxe", 0));
3864 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3865 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3866 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3867 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3868 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3869 if(checkItemCombination(items, specs))
3871 rlist->addItem(new ToolItem("SteelAxe", 0));
3880 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3881 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3882 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3883 if(checkItemCombination(items, specs))
3885 rlist->addItem(new ToolItem("WSword", 0));
3894 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3895 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3896 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3897 if(checkItemCombination(items, specs))
3899 rlist->addItem(new ToolItem("STSword", 0));
3908 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3909 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3910 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3911 if(checkItemCombination(items, specs))
3913 rlist->addItem(new ToolItem("SteelSword", 0));
3922 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3923 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3924 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3925 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3926 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3927 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3928 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3929 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3930 if(checkItemCombination(items, specs))
3932 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3941 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3942 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3943 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3944 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3945 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3946 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3947 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3948 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3949 if(checkItemCombination(items, specs))
3951 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3960 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3961 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3962 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3963 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3964 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3965 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3966 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3967 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3968 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3969 if(checkItemCombination(items, specs))
3971 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3980 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
3981 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
3982 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
3983 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
3984 if(checkItemCombination(items, specs))
3986 rlist->addItem(new MaterialItem(CONTENT_SANDSTONE, 1));
3995 specs[3] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
3996 specs[4] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
3997 specs[6] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
3998 specs[7] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
3999 if(checkItemCombination(items, specs))
4001 rlist->addItem(new MaterialItem(CONTENT_CLAY, 1));
4010 specs[3] = ItemSpec(ITEM_CRAFT, "clay_brick");
4011 specs[4] = ItemSpec(ITEM_CRAFT, "clay_brick");
4012 specs[6] = ItemSpec(ITEM_CRAFT, "clay_brick");
4013 specs[7] = ItemSpec(ITEM_CRAFT, "clay_brick");
4014 if(checkItemCombination(items, specs))
4016 rlist->addItem(new MaterialItem(CONTENT_BRICK, 1));
4022 } // if creative_mode == false
4025 RemoteClient* Server::getClient(u16 peer_id)
4027 DSTACK(__FUNCTION_NAME);
4028 //JMutexAutoLock lock(m_con_mutex);
4029 core::map<u16, RemoteClient*>::Node *n;
4030 n = m_clients.find(peer_id);
4031 // A client should exist for all peers
4033 return n->getValue();
4036 std::wstring Server::getStatusString()
4038 std::wostringstream os(std::ios_base::binary);
4041 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4043 os<<L", uptime="<<m_uptime.get();
4044 // Information about clients
4046 for(core::map<u16, RemoteClient*>::Iterator
4047 i = m_clients.getIterator();
4048 i.atEnd() == false; i++)
4050 // Get client and check that it is valid
4051 RemoteClient *client = i.getNode()->getValue();
4052 assert(client->peer_id == i.getNode()->getKey());
4053 if(client->serialization_version == SER_FMT_VER_INVALID)
4056 Player *player = m_env.getPlayer(client->peer_id);
4057 // Get name of player
4058 std::wstring name = L"unknown";
4060 name = narrow_to_wide(player->getName());
4061 // Add name to information string
4065 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4066 os<<" WARNING: Map saving is disabled."<<std::endl;
4071 void setCreativeInventory(Player *player)
4073 player->resetInventory();
4075 // Give some good tools
4077 InventoryItem *item = new ToolItem("MesePick", 0);
4078 void* r = player->inventory.addItem("main", item);
4082 InventoryItem *item = new ToolItem("SteelPick", 0);
4083 void* r = player->inventory.addItem("main", item);
4087 InventoryItem *item = new ToolItem("SteelAxe", 0);
4088 void* r = player->inventory.addItem("main", item);
4092 InventoryItem *item = new ToolItem("SteelShovel", 0);
4093 void* r = player->inventory.addItem("main", item);
4101 // CONTENT_IGNORE-terminated list
4102 u8 material_items[] = {
4118 CONTENT_WATERSOURCE,
4126 u8 *mip = material_items;
4127 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4129 if(*mip == CONTENT_IGNORE)
4132 InventoryItem *item = new MaterialItem(*mip, 1);
4133 player->inventory.addItem("main", item);
4139 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4142 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4143 player->inventory.addItem("main", item);
4146 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4148 // Skip some materials
4149 if(i == CONTENT_WATER || i == CONTENT_TORCH
4150 || i == CONTENT_COALSTONE)
4153 InventoryItem *item = new MaterialItem(i, 1);
4154 player->inventory.addItem("main", item);
4160 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4161 void* r = player->inventory.addItem("main", item);
4166 v3f findSpawnPos(ServerMap &map)
4168 //return v3f(50,50,50)*BS;
4171 s16 groundheight = 0;
4173 // Try to find a good place a few times
4174 for(s32 i=0; i<1000; i++)
4177 // We're going to try to throw the player to this position
4178 nodepos = v2s16(-range + (myrand()%(range*2)),
4179 -range + (myrand()%(range*2)));
4180 v2s16 sectorpos = getNodeSectorPos(nodepos);
4181 // Get sector (NOTE: Don't get because it's slow)
4182 //m_env.getMap().emergeSector(sectorpos);
4183 // Get ground height at point (fallbacks to heightmap function)
4184 groundheight = map.findGroundLevel(nodepos);
4185 // Don't go underwater
4186 if(groundheight < WATER_LEVEL)
4188 //dstream<<"-> Underwater"<<std::endl;
4191 // Don't go to high places
4192 if(groundheight > WATER_LEVEL + 4)
4194 //dstream<<"-> Underwater"<<std::endl;
4198 // Found a good place
4199 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4203 // If no suitable place was not found, go above water at least.
4204 if(groundheight < WATER_LEVEL)
4205 groundheight = WATER_LEVEL;
4207 return intToFloat(v3s16(
4214 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4217 Try to get an existing player
4219 Player *player = m_env.getPlayer(name);
4222 // If player is already connected, cancel
4223 if(player->peer_id != 0)
4225 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4230 player->peer_id = peer_id;
4232 // Reset inventory to creative if in creative mode
4233 if(g_settings.getBool("creative_mode"))
4235 setCreativeInventory(player);
4242 If player with the wanted peer_id already exists, cancel.
4244 if(m_env.getPlayer(peer_id) != NULL)
4246 dstream<<"emergePlayer(): Player with wrong name but same"
4247 " peer_id already exists"<<std::endl;
4255 player = new ServerRemotePlayer();
4256 //player->peer_id = c.peer_id;
4257 //player->peer_id = PEER_ID_INEXISTENT;
4258 player->peer_id = peer_id;
4259 player->updateName(name);
4260 player->updatePassword(password);
4262 if(g_settings.exists("default_privs"))
4263 player->privs = g_settings.getU64("default_privs");
4269 dstream<<"Server: Finding spawn place for player \""
4270 <<player->getName()<<"\""<<std::endl;
4272 v3f pos = findSpawnPos(m_env.getServerMap());
4274 player->setPosition(pos);
4277 Add player to environment
4280 m_env.addPlayer(player);
4283 Add stuff to inventory
4286 if(g_settings.getBool("creative_mode"))
4288 setCreativeInventory(player);
4290 else if(g_settings.getBool("give_initial_stuff"))
4293 InventoryItem *item = new ToolItem("SteelPick", 0);
4294 void* r = player->inventory.addItem("main", item);
4298 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4299 void* r = player->inventory.addItem("main", item);
4303 InventoryItem *item = new ToolItem("SteelAxe", 0);
4304 void* r = player->inventory.addItem("main", item);
4308 InventoryItem *item = new ToolItem("SteelShovel", 0);
4309 void* r = player->inventory.addItem("main", item);
4313 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4314 void* r = player->inventory.addItem("main", item);
4318 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4319 void* r = player->inventory.addItem("main", item);
4323 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4324 void* r = player->inventory.addItem("main", item);
4328 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4329 void* r = player->inventory.addItem("main", item);
4333 InventoryItem *item = new CraftItem("Stick", 4);
4334 void* r = player->inventory.addItem("main", item);
4338 InventoryItem *item = new ToolItem("WPick", 32000);
4339 void* r = player->inventory.addItem("main", item);
4343 InventoryItem *item = new ToolItem("STPick", 32000);
4344 void* r = player->inventory.addItem("main", item);
4348 for(u16 i=0; i<4; i++)
4350 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4351 bool r = player->inventory.addItem("main", item);
4354 /*// Give some other stuff
4356 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4357 bool r = player->inventory.addItem("main", item);
4364 } // create new player
4367 void Server::handlePeerChange(PeerChange &c)
4369 JMutexAutoLock envlock(m_env_mutex);
4370 JMutexAutoLock conlock(m_con_mutex);
4372 if(c.type == PEER_ADDED)
4379 core::map<u16, RemoteClient*>::Node *n;
4380 n = m_clients.find(c.peer_id);
4381 // The client shouldn't already exist
4385 RemoteClient *client = new RemoteClient();
4386 client->peer_id = c.peer_id;
4387 m_clients.insert(client->peer_id, client);
4390 else if(c.type == PEER_REMOVED)
4397 core::map<u16, RemoteClient*>::Node *n;
4398 n = m_clients.find(c.peer_id);
4399 // The client should exist
4403 Mark objects to be not known by the client
4405 RemoteClient *client = n->getValue();
4407 for(core::map<u16, bool>::Iterator
4408 i = client->m_known_objects.getIterator();
4409 i.atEnd()==false; i++)
4412 u16 id = i.getNode()->getKey();
4413 ServerActiveObject* obj = m_env.getActiveObject(id);
4415 if(obj && obj->m_known_by_count > 0)
4416 obj->m_known_by_count--;
4419 // Collect information about leaving in chat
4420 std::wstring message;
4422 std::wstring name = L"unknown";
4423 Player *player = m_env.getPlayer(c.peer_id);
4425 name = narrow_to_wide(player->getName());
4429 message += L" left game";
4431 message += L" (timed out)";
4436 m_env.removePlayer(c.peer_id);
4439 // Set player client disconnected
4441 Player *player = m_env.getPlayer(c.peer_id);
4443 player->peer_id = 0;
4447 delete m_clients[c.peer_id];
4448 m_clients.remove(c.peer_id);
4450 // Send player info to all remaining clients
4453 // Send leave chat message to all remaining clients
4454 BroadcastChatMessage(message);
4463 void Server::handlePeerChanges()
4465 while(m_peer_change_queue.size() > 0)
4467 PeerChange c = m_peer_change_queue.pop_front();
4469 dout_server<<"Server: Handling peer change: "
4470 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4473 handlePeerChange(c);
4477 void dedicated_server_loop(Server &server, bool &kill)
4479 DSTACK(__FUNCTION_NAME);
4481 std::cout<<DTIME<<std::endl;
4482 std::cout<<"========================"<<std::endl;
4483 std::cout<<"Running dedicated server"<<std::endl;
4484 std::cout<<"========================"<<std::endl;
4485 std::cout<<std::endl;
4489 // This is kind of a hack but can be done like this
4490 // because server.step() is very light
4494 if(server.getShutdownRequested() || kill)
4496 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4500 static int counter = 0;
4506 core::list<PlayerInfo> list = server.getPlayerInfo();
4507 core::list<PlayerInfo>::Iterator i;
4508 static u32 sum_old = 0;
4509 u32 sum = PIChecksum(list);
4512 std::cout<<DTIME<<"Player info:"<<std::endl;
4513 for(i=list.begin(); i!=list.end(); i++)
4515 i->PrintLine(&std::cout);