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),
970 m_authmanager(mapsavedir+"/auth.txt"),
972 m_emergethread(this),
974 m_time_of_day_send_timer(0),
976 m_mapsavedir(mapsavedir),
977 m_shutdown_requested(false),
978 m_ignore_map_edit_events(false),
979 m_ignore_map_edit_events_peer_id(0)
981 m_liquid_transform_timer = 0.0;
982 m_print_info_timer = 0.0;
983 m_objectdata_timer = 0.0;
984 m_emergethread_trigger_timer = 0.0;
985 m_savemap_timer = 0.0;
989 m_step_dtime_mutex.Init();
992 // Register us to receive map edit events
993 m_env.getMap().addEventReceiver(this);
995 // If file exists, load environment metadata
996 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
998 dstream<<"Server: Loading environment metadata"<<std::endl;
999 m_env.loadMeta(m_mapsavedir);
1003 dstream<<"Server: Loading players"<<std::endl;
1004 m_env.deSerializePlayers(m_mapsavedir);
1009 dstream<<"Server::~Server()"<<std::endl;
1012 Send shutdown message
1015 JMutexAutoLock conlock(m_con_mutex);
1017 std::wstring line = L"*** Server shutting down";
1020 Send the message to clients
1022 for(core::map<u16, RemoteClient*>::Iterator
1023 i = m_clients.getIterator();
1024 i.atEnd() == false; i++)
1026 // Get client and check that it is valid
1027 RemoteClient *client = i.getNode()->getValue();
1028 assert(client->peer_id == i.getNode()->getKey());
1029 if(client->serialization_version == SER_FMT_VER_INVALID)
1033 SendChatMessage(client->peer_id, line);
1035 catch(con::PeerNotFoundException &e)
1043 dstream<<"Server: Saving players"<<std::endl;
1044 m_env.serializePlayers(m_mapsavedir);
1047 Save environment metadata
1049 dstream<<"Server: Saving environment metadata"<<std::endl;
1050 m_env.saveMeta(m_mapsavedir);
1061 JMutexAutoLock clientslock(m_con_mutex);
1063 for(core::map<u16, RemoteClient*>::Iterator
1064 i = m_clients.getIterator();
1065 i.atEnd() == false; i++)
1068 // NOTE: These are removed by env destructor
1070 u16 peer_id = i.getNode()->getKey();
1071 JMutexAutoLock envlock(m_env_mutex);
1072 m_env.removePlayer(peer_id);
1076 delete i.getNode()->getValue();
1081 void Server::start(unsigned short port)
1083 DSTACK(__FUNCTION_NAME);
1084 // Stop thread if already running
1087 // Initialize connection
1088 m_con.setTimeoutMs(30);
1092 m_thread.setRun(true);
1095 dout_server<<"Server: Started on port "<<port<<std::endl;
1100 DSTACK(__FUNCTION_NAME);
1102 // Stop threads (set run=false first so both start stopping)
1103 m_thread.setRun(false);
1104 m_emergethread.setRun(false);
1106 m_emergethread.stop();
1108 dout_server<<"Server: Threads stopped"<<std::endl;
1111 void Server::step(float dtime)
1113 DSTACK(__FUNCTION_NAME);
1118 JMutexAutoLock lock(m_step_dtime_mutex);
1119 m_step_dtime += dtime;
1123 void Server::AsyncRunStep()
1125 DSTACK(__FUNCTION_NAME);
1129 JMutexAutoLock lock1(m_step_dtime_mutex);
1130 dtime = m_step_dtime;
1133 // Send blocks to clients
1139 //dstream<<"Server steps "<<dtime<<std::endl;
1140 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1143 JMutexAutoLock lock1(m_step_dtime_mutex);
1144 m_step_dtime -= dtime;
1151 m_uptime.set(m_uptime.get() + dtime);
1155 Update m_time_of_day and overall game time
1158 JMutexAutoLock envlock(m_env_mutex);
1160 m_time_counter += dtime;
1161 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1162 u32 units = (u32)(m_time_counter*speed);
1163 m_time_counter -= (f32)units / speed;
1165 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1167 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1170 Send to clients at constant intervals
1173 m_time_of_day_send_timer -= dtime;
1174 if(m_time_of_day_send_timer < 0.0)
1176 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1178 //JMutexAutoLock envlock(m_env_mutex);
1179 JMutexAutoLock conlock(m_con_mutex);
1181 for(core::map<u16, RemoteClient*>::Iterator
1182 i = m_clients.getIterator();
1183 i.atEnd() == false; i++)
1185 RemoteClient *client = i.getNode()->getValue();
1186 //Player *player = m_env.getPlayer(client->peer_id);
1188 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1189 m_env.getTimeOfDay());
1191 m_con.Send(client->peer_id, 0, data, true);
1197 // Process connection's timeouts
1198 JMutexAutoLock lock2(m_con_mutex);
1199 m_con.RunTimeouts(dtime);
1203 // This has to be called so that the client list gets synced
1204 // with the peer list of the connection
1205 handlePeerChanges();
1210 // This also runs Map's timers
1211 JMutexAutoLock lock(m_env_mutex);
1222 m_liquid_transform_timer += dtime;
1223 if(m_liquid_transform_timer >= 1.00)
1225 m_liquid_transform_timer -= 1.00;
1227 JMutexAutoLock lock(m_env_mutex);
1229 core::map<v3s16, MapBlock*> modified_blocks;
1230 m_env.getMap().transformLiquids(modified_blocks);
1235 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1236 ServerMap &map = ((ServerMap&)m_env.getMap());
1237 map.updateLighting(modified_blocks, lighting_modified_blocks);
1239 // Add blocks modified by lighting to modified_blocks
1240 for(core::map<v3s16, MapBlock*>::Iterator
1241 i = lighting_modified_blocks.getIterator();
1242 i.atEnd() == false; i++)
1244 MapBlock *block = i.getNode()->getValue();
1245 modified_blocks.insert(block->getPos(), block);
1249 Set the modified blocks unsent for all the clients
1252 JMutexAutoLock lock2(m_con_mutex);
1254 for(core::map<u16, RemoteClient*>::Iterator
1255 i = m_clients.getIterator();
1256 i.atEnd() == false; i++)
1258 RemoteClient *client = i.getNode()->getValue();
1260 if(modified_blocks.size() > 0)
1262 // Remove block from sent history
1263 client->SetBlocksNotSent(modified_blocks);
1268 // Periodically print some info
1270 float &counter = m_print_info_timer;
1276 JMutexAutoLock lock2(m_con_mutex);
1278 for(core::map<u16, RemoteClient*>::Iterator
1279 i = m_clients.getIterator();
1280 i.atEnd() == false; i++)
1282 //u16 peer_id = i.getNode()->getKey();
1283 RemoteClient *client = i.getNode()->getValue();
1284 Player *player = m_env.getPlayer(client->peer_id);
1287 std::cout<<player->getName()<<"\t";
1288 client->PrintInfo(std::cout);
1293 //if(g_settings.getBool("enable_experimental"))
1297 Check added and deleted active objects
1300 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1302 JMutexAutoLock envlock(m_env_mutex);
1303 JMutexAutoLock conlock(m_con_mutex);
1305 // Radius inside which objects are active
1308 for(core::map<u16, RemoteClient*>::Iterator
1309 i = m_clients.getIterator();
1310 i.atEnd() == false; i++)
1312 RemoteClient *client = i.getNode()->getValue();
1313 Player *player = m_env.getPlayer(client->peer_id);
1316 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1317 <<" has no associated player"<<std::endl;
1320 v3s16 pos = floatToInt(player->getPosition(), BS);
1322 core::map<u16, bool> removed_objects;
1323 core::map<u16, bool> added_objects;
1324 m_env.getRemovedActiveObjects(pos, radius,
1325 client->m_known_objects, removed_objects);
1326 m_env.getAddedActiveObjects(pos, radius,
1327 client->m_known_objects, added_objects);
1329 // Ignore if nothing happened
1330 if(removed_objects.size() == 0 && added_objects.size() == 0)
1332 //dstream<<"INFO: active objects: none changed"<<std::endl;
1336 std::string data_buffer;
1340 // Handle removed objects
1341 writeU16((u8*)buf, removed_objects.size());
1342 data_buffer.append(buf, 2);
1343 for(core::map<u16, bool>::Iterator
1344 i = removed_objects.getIterator();
1345 i.atEnd()==false; i++)
1348 u16 id = i.getNode()->getKey();
1349 ServerActiveObject* obj = m_env.getActiveObject(id);
1351 // Add to data buffer for sending
1352 writeU16((u8*)buf, i.getNode()->getKey());
1353 data_buffer.append(buf, 2);
1355 // Remove from known objects
1356 client->m_known_objects.remove(i.getNode()->getKey());
1358 if(obj && obj->m_known_by_count > 0)
1359 obj->m_known_by_count--;
1362 // Handle added objects
1363 writeU16((u8*)buf, added_objects.size());
1364 data_buffer.append(buf, 2);
1365 for(core::map<u16, bool>::Iterator
1366 i = added_objects.getIterator();
1367 i.atEnd()==false; i++)
1370 u16 id = i.getNode()->getKey();
1371 ServerActiveObject* obj = m_env.getActiveObject(id);
1374 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1376 dstream<<"WARNING: "<<__FUNCTION_NAME
1377 <<": NULL object"<<std::endl;
1379 type = obj->getType();
1381 // Add to data buffer for sending
1382 writeU16((u8*)buf, id);
1383 data_buffer.append(buf, 2);
1384 writeU8((u8*)buf, type);
1385 data_buffer.append(buf, 1);
1388 data_buffer.append(serializeLongString(
1389 obj->getClientInitializationData()));
1391 data_buffer.append(serializeLongString(""));
1393 // Add to known objects
1394 client->m_known_objects.insert(i.getNode()->getKey(), false);
1397 obj->m_known_by_count++;
1401 SharedBuffer<u8> reply(2 + data_buffer.size());
1402 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1403 memcpy((char*)&reply[2], data_buffer.c_str(),
1404 data_buffer.size());
1406 m_con.Send(client->peer_id, 0, reply, true);
1408 dstream<<"INFO: Server: Sent object remove/add: "
1409 <<removed_objects.size()<<" removed, "
1410 <<added_objects.size()<<" added, "
1411 <<"packet size is "<<reply.getSize()<<std::endl;
1416 Collect a list of all the objects known by the clients
1417 and report it back to the environment.
1420 core::map<u16, bool> all_known_objects;
1422 for(core::map<u16, RemoteClient*>::Iterator
1423 i = m_clients.getIterator();
1424 i.atEnd() == false; i++)
1426 RemoteClient *client = i.getNode()->getValue();
1427 // Go through all known objects of client
1428 for(core::map<u16, bool>::Iterator
1429 i = client->m_known_objects.getIterator();
1430 i.atEnd()==false; i++)
1432 u16 id = i.getNode()->getKey();
1433 all_known_objects[id] = true;
1437 m_env.setKnownActiveObjects(whatever);
1443 Send object messages
1446 JMutexAutoLock envlock(m_env_mutex);
1447 JMutexAutoLock conlock(m_con_mutex);
1450 // Value = data sent by object
1451 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1453 // Get active object messages from environment
1456 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1460 core::list<ActiveObjectMessage>* message_list = NULL;
1461 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1462 n = buffered_messages.find(aom.id);
1465 message_list = new core::list<ActiveObjectMessage>;
1466 buffered_messages.insert(aom.id, message_list);
1470 message_list = n->getValue();
1472 message_list->push_back(aom);
1475 // Route data to every client
1476 for(core::map<u16, RemoteClient*>::Iterator
1477 i = m_clients.getIterator();
1478 i.atEnd()==false; i++)
1480 RemoteClient *client = i.getNode()->getValue();
1481 std::string reliable_data;
1482 std::string unreliable_data;
1483 // Go through all objects in message buffer
1484 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1485 j = buffered_messages.getIterator();
1486 j.atEnd()==false; j++)
1488 // If object is not known by client, skip it
1489 u16 id = j.getNode()->getKey();
1490 if(client->m_known_objects.find(id) == NULL)
1492 // Get message list of object
1493 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1494 // Go through every message
1495 for(core::list<ActiveObjectMessage>::Iterator
1496 k = list->begin(); k != list->end(); k++)
1498 // Compose the full new data with header
1499 ActiveObjectMessage aom = *k;
1500 std::string new_data;
1503 writeU16((u8*)&buf[0], aom.id);
1504 new_data.append(buf, 2);
1506 new_data += serializeString(aom.datastring);
1507 // Add data to buffer
1509 reliable_data += new_data;
1511 unreliable_data += new_data;
1515 reliable_data and unreliable_data are now ready.
1518 if(reliable_data.size() > 0)
1520 SharedBuffer<u8> reply(2 + reliable_data.size());
1521 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1522 memcpy((char*)&reply[2], reliable_data.c_str(),
1523 reliable_data.size());
1525 m_con.Send(client->peer_id, 0, reply, true);
1527 if(unreliable_data.size() > 0)
1529 SharedBuffer<u8> reply(2 + unreliable_data.size());
1530 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1531 memcpy((char*)&reply[2], unreliable_data.c_str(),
1532 unreliable_data.size());
1533 // Send as unreliable
1534 m_con.Send(client->peer_id, 0, reply, false);
1537 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1539 dstream<<"INFO: Server: Size of object message data: "
1540 <<"reliable: "<<reliable_data.size()
1541 <<", unreliable: "<<unreliable_data.size()
1546 // Clear buffered_messages
1547 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1548 i = buffered_messages.getIterator();
1549 i.atEnd()==false; i++)
1551 delete i.getNode()->getValue();
1555 } // enable_experimental
1558 Send queued-for-sending map edit events.
1561 while(m_unsent_map_edit_queue.size() != 0)
1563 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1565 if(event->type == MEET_ADDNODE)
1567 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1568 sendAddNode(event->p, event->n, event->already_known_by_peer);
1570 else if(event->type == MEET_REMOVENODE)
1572 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1573 sendRemoveNode(event->p, event->already_known_by_peer);
1575 else if(event->type == MEET_OTHER)
1577 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1582 dstream<<"WARNING: Server: Unknown MapEditEvent "
1583 <<((u32)event->type)<<std::endl;
1591 Send object positions
1592 TODO: Get rid of MapBlockObjects
1595 float &counter = m_objectdata_timer;
1597 if(counter >= g_settings.getFloat("objectdata_interval"))
1599 JMutexAutoLock lock1(m_env_mutex);
1600 JMutexAutoLock lock2(m_con_mutex);
1601 SendObjectData(counter);
1611 //TimeTaker timer("Step node metadata");
1613 JMutexAutoLock envlock(m_env_mutex);
1614 JMutexAutoLock conlock(m_con_mutex);
1616 core::map<v3s16, MapBlock*> changed_blocks;
1617 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1619 for(core::map<v3s16, MapBlock*>::Iterator
1620 i = changed_blocks.getIterator();
1621 i.atEnd() == false; i++)
1623 MapBlock *block = i.getNode()->getValue();
1625 for(core::map<u16, RemoteClient*>::Iterator
1626 i = m_clients.getIterator();
1627 i.atEnd()==false; i++)
1629 RemoteClient *client = i.getNode()->getValue();
1630 client->SetBlockNotSent(block->getPos());
1636 Trigger emergethread (it somehow gets to a non-triggered but
1637 bysy state sometimes)
1640 float &counter = m_emergethread_trigger_timer;
1646 m_emergethread.trigger();
1650 // Save map, players and auth stuff
1652 float &counter = m_savemap_timer;
1654 if(counter >= g_settings.getFloat("server_map_save_interval"))
1659 m_authmanager.save();
1662 JMutexAutoLock lock(m_env_mutex);
1663 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1665 // Save only changed parts
1666 m_env.getMap().save(true);
1668 // Delete unused sectors
1669 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1670 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1671 if(deleted_count > 0)
1673 dout_server<<"Server: Unloaded "<<deleted_count
1674 <<" sectors from memory"<<std::endl;
1678 m_env.serializePlayers(m_mapsavedir);
1680 // Save environment metadata
1681 m_env.saveMeta(m_mapsavedir);
1687 void Server::Receive()
1689 DSTACK(__FUNCTION_NAME);
1690 u32 data_maxsize = 10000;
1691 Buffer<u8> data(data_maxsize);
1696 JMutexAutoLock conlock(m_con_mutex);
1697 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1700 // This has to be called so that the client list gets synced
1701 // with the peer list of the connection
1702 handlePeerChanges();
1704 ProcessData(*data, datasize, peer_id);
1706 catch(con::InvalidIncomingDataException &e)
1708 derr_server<<"Server::Receive(): "
1709 "InvalidIncomingDataException: what()="
1710 <<e.what()<<std::endl;
1712 catch(con::PeerNotFoundException &e)
1714 //NOTE: This is not needed anymore
1716 // The peer has been disconnected.
1717 // Find the associated player and remove it.
1719 /*JMutexAutoLock envlock(m_env_mutex);
1721 dout_server<<"ServerThread: peer_id="<<peer_id
1722 <<" has apparently closed connection. "
1723 <<"Removing player."<<std::endl;
1725 m_env.removePlayer(peer_id);*/
1729 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1731 DSTACK(__FUNCTION_NAME);
1732 // Environment is locked first.
1733 JMutexAutoLock envlock(m_env_mutex);
1734 JMutexAutoLock conlock(m_con_mutex);
1738 peer = m_con.GetPeer(peer_id);
1740 catch(con::PeerNotFoundException &e)
1742 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1743 <<peer_id<<" not found"<<std::endl;
1747 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1755 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1757 if(command == TOSERVER_INIT)
1759 // [0] u16 TOSERVER_INIT
1760 // [2] u8 SER_FMT_VER_HIGHEST
1761 // [3] u8[20] player_name
1762 // [23] u8[28] password <--- can be sent without this, from old versions
1764 if(datasize < 2+1+PLAYERNAME_SIZE)
1767 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1768 <<peer->id<<std::endl;
1770 // First byte after command is maximum supported
1771 // serialization version
1772 u8 client_max = data[2];
1773 u8 our_max = SER_FMT_VER_HIGHEST;
1774 // Use the highest version supported by both
1775 u8 deployed = core::min_(client_max, our_max);
1776 // If it's lower than the lowest supported, give up.
1777 if(deployed < SER_FMT_VER_LOWEST)
1778 deployed = SER_FMT_VER_INVALID;
1780 //peer->serialization_version = deployed;
1781 getClient(peer->id)->pending_serialization_version = deployed;
1783 if(deployed == SER_FMT_VER_INVALID)
1785 derr_server<<DTIME<<"Server: Cannot negotiate "
1786 "serialization version with peer "
1787 <<peer_id<<std::endl;
1796 char playername[PLAYERNAME_SIZE];
1797 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1799 playername[i] = data[3+i];
1801 playername[PLAYERNAME_SIZE-1] = 0;
1803 if(playername[0]=='\0')
1805 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1806 SendAccessDenied(m_con, peer_id,
1811 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1813 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1814 SendAccessDenied(m_con, peer_id,
1815 L"Name contains unallowed characters");
1820 char password[PASSWORD_SIZE];
1821 if(datasize == 2+1+PLAYERNAME_SIZE)
1823 // old version - assume blank password
1828 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1830 password[i] = data[23+i];
1832 password[PASSWORD_SIZE-1] = 0;
1835 std::string checkpwd;
1836 if(m_authmanager.exists(playername))
1838 checkpwd = m_authmanager.getPassword(playername);
1842 checkpwd = g_settings.get("default_password");
1845 if(password != checkpwd)
1847 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1848 <<": supplied invalid password for "
1849 <<playername<<std::endl;
1850 SendAccessDenied(m_con, peer_id, L"Invalid password");
1854 // Add player to auth manager
1855 if(m_authmanager.exists(playername) == false)
1857 derr_server<<DTIME<<"Server: adding player "<<playername
1858 <<" to auth manager"<<std::endl;
1859 m_authmanager.add(playername);
1860 m_authmanager.setPassword(playername, checkpwd);
1861 m_authmanager.setPrivs(playername,
1862 stringToPrivs(g_settings.get("default_privs")));
1863 m_authmanager.save();
1867 Player *player = emergePlayer(playername, password, peer_id);
1871 // DEBUG: Test serialization
1872 std::ostringstream test_os;
1873 player->serialize(test_os);
1874 dstream<<"Player serialization test: \""<<test_os.str()
1876 std::istringstream test_is(test_os.str());
1877 player->deSerialize(test_is);
1880 // If failed, cancel
1883 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1884 <<": failed to emerge player"<<std::endl;
1889 // If a client is already connected to the player, cancel
1890 if(player->peer_id != 0)
1892 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1893 <<" tried to connect to "
1894 "an already connected player (peer_id="
1895 <<player->peer_id<<")"<<std::endl;
1898 // Set client of player
1899 player->peer_id = peer_id;
1902 // Check if player doesn't exist
1904 throw con::InvalidIncomingDataException
1905 ("Server::ProcessData(): INIT: Player doesn't exist");
1907 /*// update name if it was supplied
1908 if(datasize >= 20+3)
1911 player->updateName((const char*)&data[3]);
1915 Answer with a TOCLIENT_INIT
1918 SharedBuffer<u8> reply(2+1+6+8);
1919 writeU16(&reply[0], TOCLIENT_INIT);
1920 writeU8(&reply[2], deployed);
1921 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1922 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1923 writeU64(&reply[2+1+6], 0); // no seed
1926 m_con.Send(peer_id, 0, reply, true);
1930 Send complete position information
1932 SendMovePlayer(player);
1937 if(command == TOSERVER_INIT2)
1939 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1940 <<peer->id<<std::endl;
1943 getClient(peer->id)->serialization_version
1944 = getClient(peer->id)->pending_serialization_version;
1947 Send some initialization data
1950 // Send player info to all players
1953 // Send inventory to player
1954 UpdateCrafting(peer->id);
1955 SendInventory(peer->id);
1959 Player *player = m_env.getPlayer(peer_id);
1960 SendPlayerHP(player);
1965 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1966 m_env.getTimeOfDay());
1967 m_con.Send(peer->id, 0, data, true);
1970 // Send information about server to player in chat
1971 SendChatMessage(peer_id, getStatusString());
1973 // Send information about joining in chat
1975 std::wstring name = L"unknown";
1976 Player *player = m_env.getPlayer(peer_id);
1978 name = narrow_to_wide(player->getName());
1980 std::wstring message;
1983 message += L" joined game";
1984 BroadcastChatMessage(message);
1990 if(peer_ser_ver == SER_FMT_VER_INVALID)
1992 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1993 " serialization format invalid or not initialized."
1994 " Skipping incoming command="<<command<<std::endl;
1998 Player *player = m_env.getPlayer(peer_id);
2001 derr_server<<"Server::ProcessData(): Cancelling: "
2002 "No player for peer_id="<<peer_id
2006 if(command == TOSERVER_PLAYERPOS)
2008 if(datasize < 2+12+12+4+4)
2012 v3s32 ps = readV3S32(&data[start+2]);
2013 v3s32 ss = readV3S32(&data[start+2+12]);
2014 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2015 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2016 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2017 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2018 pitch = wrapDegrees(pitch);
2019 yaw = wrapDegrees(yaw);
2020 player->setPosition(position);
2021 player->setSpeed(speed);
2022 player->setPitch(pitch);
2023 player->setYaw(yaw);
2025 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2026 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2027 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2029 else if(command == TOSERVER_GOTBLOCKS)
2042 u16 count = data[2];
2043 for(u16 i=0; i<count; i++)
2045 if((s16)datasize < 2+1+(i+1)*6)
2046 throw con::InvalidIncomingDataException
2047 ("GOTBLOCKS length is too short");
2048 v3s16 p = readV3S16(&data[2+1+i*6]);
2049 /*dstream<<"Server: GOTBLOCKS ("
2050 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2051 RemoteClient *client = getClient(peer_id);
2052 client->GotBlock(p);
2055 else if(command == TOSERVER_DELETEDBLOCKS)
2068 u16 count = data[2];
2069 for(u16 i=0; i<count; i++)
2071 if((s16)datasize < 2+1+(i+1)*6)
2072 throw con::InvalidIncomingDataException
2073 ("DELETEDBLOCKS length is too short");
2074 v3s16 p = readV3S16(&data[2+1+i*6]);
2075 /*dstream<<"Server: DELETEDBLOCKS ("
2076 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2077 RemoteClient *client = getClient(peer_id);
2078 client->SetBlockNotSent(p);
2081 else if(command == TOSERVER_CLICK_OBJECT)
2086 if((player->privs & PRIV_BUILD) == 0)
2091 [2] u8 button (0=left, 1=right)
2096 u8 button = readU8(&data[2]);
2098 p.X = readS16(&data[3]);
2099 p.Y = readS16(&data[5]);
2100 p.Z = readS16(&data[7]);
2101 s16 id = readS16(&data[9]);
2102 //u16 item_i = readU16(&data[11]);
2104 MapBlock *block = NULL;
2107 block = m_env.getMap().getBlockNoCreate(p);
2109 catch(InvalidPositionException &e)
2111 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2115 MapBlockObject *obj = block->getObject(id);
2119 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2123 //TODO: Check that object is reasonably close
2128 InventoryList *ilist = player->inventory.getList("main");
2129 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2132 // Skip if inventory has no free space
2133 if(ilist->getUsedSlots() == ilist->getSize())
2135 dout_server<<"Player inventory has no free space"<<std::endl;
2140 Create the inventory item
2142 InventoryItem *item = NULL;
2143 // If it is an item-object, take the item from it
2144 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2146 item = ((ItemObject*)obj)->createInventoryItem();
2148 // Else create an item of the object
2151 item = new MapBlockObjectItem
2152 (obj->getInventoryString());
2155 // Add to inventory and send inventory
2156 ilist->addItem(item);
2157 UpdateCrafting(player->peer_id);
2158 SendInventory(player->peer_id);
2161 // Remove from block
2162 block->removeObject(id);
2165 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2170 if((player->privs & PRIV_BUILD) == 0)
2176 [2] u8 button (0=left, 1=right)
2180 u8 button = readU8(&data[2]);
2181 u16 id = readS16(&data[3]);
2182 u16 item_i = readU16(&data[11]);
2184 ServerActiveObject *obj = m_env.getActiveObject(id);
2188 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2193 //TODO: Check that object is reasonably close
2195 // Left click, pick object up (usually)
2198 InventoryList *ilist = player->inventory.getList("main");
2199 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2202 // Skip if inventory has no free space
2203 if(ilist->getUsedSlots() == ilist->getSize())
2205 dout_server<<"Player inventory has no free space"<<std::endl;
2209 // Skip if object has been removed
2214 Create the inventory item
2216 InventoryItem *item = obj->createPickedUpItem();
2220 // Add to inventory and send inventory
2221 ilist->addItem(item);
2222 UpdateCrafting(player->peer_id);
2223 SendInventory(player->peer_id);
2225 // Remove object from environment
2226 obj->m_removed = true;
2231 Item cannot be picked up. Punch it instead.
2234 ToolItem *titem = NULL;
2235 std::string toolname = "";
2237 InventoryList *mlist = player->inventory.getList("main");
2240 InventoryItem *item = mlist->getItem(item_i);
2241 if(item && (std::string)item->getName() == "ToolItem")
2243 titem = (ToolItem*)item;
2244 toolname = titem->getToolName();
2248 u16 wear = obj->punch(toolname);
2252 bool weared_out = titem->addWear(wear);
2254 mlist->deleteItem(item_i);
2255 SendInventory(player->peer_id);
2261 else if(command == TOSERVER_GROUND_ACTION)
2269 [3] v3s16 nodepos_undersurface
2270 [9] v3s16 nodepos_abovesurface
2275 2: stop digging (all parameters ignored)
2276 3: digging completed
2278 u8 action = readU8(&data[2]);
2280 p_under.X = readS16(&data[3]);
2281 p_under.Y = readS16(&data[5]);
2282 p_under.Z = readS16(&data[7]);
2284 p_over.X = readS16(&data[9]);
2285 p_over.Y = readS16(&data[11]);
2286 p_over.Z = readS16(&data[13]);
2287 u16 item_i = readU16(&data[15]);
2289 //TODO: Check that target is reasonably close
2297 NOTE: This can be used in the future to check if
2298 somebody is cheating, by checking the timing.
2305 else if(action == 2)
2308 RemoteClient *client = getClient(peer->id);
2309 JMutexAutoLock digmutex(client->m_dig_mutex);
2310 client->m_dig_tool_item = -1;
2315 3: Digging completed
2317 else if(action == 3)
2319 // Mandatory parameter; actually used for nothing
2320 core::map<v3s16, MapBlock*> modified_blocks;
2322 u8 material = CONTENT_IGNORE;
2323 u8 mineral = MINERAL_NONE;
2325 bool cannot_remove_node = false;
2329 MapNode n = m_env.getMap().getNode(p_under);
2331 mineral = n.getMineral();
2332 // Get material at position
2334 // If not yet cancelled
2335 if(cannot_remove_node == false)
2337 // If it's not diggable, do nothing
2338 if(content_diggable(material) == false)
2340 derr_server<<"Server: Not finishing digging: "
2341 <<"Node not diggable"
2343 cannot_remove_node = true;
2346 // If not yet cancelled
2347 if(cannot_remove_node == false)
2349 // Get node metadata
2350 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2351 if(meta && meta->nodeRemovalDisabled() == true)
2353 derr_server<<"Server: Not finishing digging: "
2354 <<"Node metadata disables removal"
2356 cannot_remove_node = true;
2360 catch(InvalidPositionException &e)
2362 derr_server<<"Server: Not finishing digging: Node not found."
2363 <<" Adding block to emerge queue."
2365 m_emerge_queue.addBlock(peer_id,
2366 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2367 cannot_remove_node = true;
2370 // Make sure the player is allowed to do it
2371 if((player->privs & PRIV_BUILD) == 0)
2372 cannot_remove_node = true;
2375 If node can't be removed, set block to be re-sent to
2378 if(cannot_remove_node)
2380 derr_server<<"Server: Not finishing digging."<<std::endl;
2382 // Client probably has wrong data.
2383 // Set block not sent, so that client will get
2385 dstream<<"Client "<<peer_id<<" tried to dig "
2386 <<"node; but node cannot be removed."
2387 <<" setting MapBlock not sent."<<std::endl;
2388 RemoteClient *client = getClient(peer_id);
2389 v3s16 blockpos = getNodeBlockPos(p_under);
2390 client->SetBlockNotSent(blockpos);
2396 Send the removal to all other clients.
2397 - If other player is close, send REMOVENODE
2398 - Otherwise set blocks not sent
2400 core::list<u16> far_players;
2401 sendRemoveNode(p_under, peer_id, &far_players, 100);
2404 Update and send inventory
2407 if(g_settings.getBool("creative_mode") == false)
2412 InventoryList *mlist = player->inventory.getList("main");
2415 InventoryItem *item = mlist->getItem(item_i);
2416 if(item && (std::string)item->getName() == "ToolItem")
2418 ToolItem *titem = (ToolItem*)item;
2419 std::string toolname = titem->getToolName();
2421 // Get digging properties for material and tool
2422 DiggingProperties prop =
2423 getDiggingProperties(material, toolname);
2425 if(prop.diggable == false)
2427 derr_server<<"Server: WARNING: Player digged"
2428 <<" with impossible material + tool"
2429 <<" combination"<<std::endl;
2432 bool weared_out = titem->addWear(prop.wear);
2436 mlist->deleteItem(item_i);
2442 Add dug item to inventory
2445 InventoryItem *item = NULL;
2447 if(mineral != MINERAL_NONE)
2448 item = getDiggedMineralItem(mineral);
2453 std::string &dug_s = content_features(material).dug_item;
2456 std::istringstream is(dug_s, std::ios::binary);
2457 item = InventoryItem::deSerialize(is);
2463 // Add a item to inventory
2464 player->inventory.addItem("main", item);
2467 UpdateCrafting(player->peer_id);
2468 SendInventory(player->peer_id);
2474 (this takes some time so it is done after the quick stuff)
2476 m_ignore_map_edit_events = true;
2477 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2478 m_ignore_map_edit_events = false;
2481 Set blocks not sent to far players
2483 for(core::list<u16>::Iterator
2484 i = far_players.begin();
2485 i != far_players.end(); i++)
2488 RemoteClient *client = getClient(peer_id);
2491 client->SetBlocksNotSent(modified_blocks);
2498 else if(action == 1)
2501 InventoryList *ilist = player->inventory.getList("main");
2506 InventoryItem *item = ilist->getItem(item_i);
2508 // If there is no item, it is not possible to add it anywhere
2513 Handle material items
2515 if(std::string("MaterialItem") == item->getName())
2518 // Don't add a node if this is not a free space
2519 MapNode n2 = m_env.getMap().getNode(p_over);
2520 if(content_buildable_to(n2.d) == false
2521 || (player->privs & PRIV_BUILD) ==0)
2523 // Client probably has wrong data.
2524 // Set block not sent, so that client will get
2526 dstream<<"Client "<<peer_id<<" tried to place"
2527 <<" node in invalid position; setting"
2528 <<" MapBlock not sent."<<std::endl;
2529 RemoteClient *client = getClient(peer_id);
2530 v3s16 blockpos = getNodeBlockPos(p_over);
2531 client->SetBlockNotSent(blockpos);
2535 catch(InvalidPositionException &e)
2537 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2538 <<" Adding block to emerge queue."
2540 m_emerge_queue.addBlock(peer_id,
2541 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2545 // Reset build time counter
2546 getClient(peer->id)->m_time_from_building = 0.0;
2549 MaterialItem *mitem = (MaterialItem*)item;
2551 n.d = mitem->getMaterial();
2552 if(content_features(n.d).wall_mounted)
2553 n.dir = packDir(p_under - p_over);
2558 core::list<u16> far_players;
2559 sendAddNode(p_over, n, 0, &far_players, 100);
2564 InventoryList *ilist = player->inventory.getList("main");
2565 if(g_settings.getBool("creative_mode") == false && ilist)
2567 // Remove from inventory and send inventory
2568 if(mitem->getCount() == 1)
2569 ilist->deleteItem(item_i);
2573 UpdateCrafting(peer_id);
2574 SendInventory(peer_id);
2580 This takes some time so it is done after the quick stuff
2582 core::map<v3s16, MapBlock*> modified_blocks;
2583 m_ignore_map_edit_events = true;
2584 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2585 m_ignore_map_edit_events = false;
2588 Set blocks not sent to far players
2590 for(core::list<u16>::Iterator
2591 i = far_players.begin();
2592 i != far_players.end(); i++)
2595 RemoteClient *client = getClient(peer_id);
2598 client->SetBlocksNotSent(modified_blocks);
2602 Calculate special events
2605 /*if(n.d == CONTENT_MESE)
2608 for(s16 z=-1; z<=1; z++)
2609 for(s16 y=-1; y<=1; y++)
2610 for(s16 x=-1; x<=1; x++)
2617 Place other item (not a block)
2621 v3s16 blockpos = getNodeBlockPos(p_over);
2624 Check that the block is loaded so that the item
2625 can properly be added to the static list too
2627 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2630 derr_server<<"Error while placing object: "
2631 "block not found"<<std::endl;
2635 dout_server<<"Placing a miscellaneous item on map"
2638 // Calculate a position for it
2639 v3f pos = intToFloat(p_over, BS);
2641 pos.Y -= BS*0.25; // let it drop a bit
2643 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2644 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2649 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2653 derr_server<<"WARNING: item resulted in NULL object, "
2654 <<"not placing onto map"
2659 // Add the object to the environment
2660 m_env.addActiveObject(obj);
2662 dout_server<<"Placed object"<<std::endl;
2664 if(g_settings.getBool("creative_mode") == false)
2666 // Delete the right amount of items from the slot
2667 u16 dropcount = item->getDropCount();
2669 // Delete item if all gone
2670 if(item->getCount() <= dropcount)
2672 if(item->getCount() < dropcount)
2673 dstream<<"WARNING: Server: dropped more items"
2674 <<" than the slot contains"<<std::endl;
2676 InventoryList *ilist = player->inventory.getList("main");
2678 // Remove from inventory and send inventory
2679 ilist->deleteItem(item_i);
2681 // Else decrement it
2683 item->remove(dropcount);
2686 UpdateCrafting(peer_id);
2687 SendInventory(peer_id);
2695 Catch invalid actions
2699 derr_server<<"WARNING: Server: Invalid action "
2700 <<action<<std::endl;
2704 else if(command == TOSERVER_RELEASE)
2713 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2716 else if(command == TOSERVER_SIGNTEXT)
2718 if((player->privs & PRIV_BUILD) == 0)
2727 std::string datastring((char*)&data[2], datasize-2);
2728 std::istringstream is(datastring, std::ios_base::binary);
2731 is.read((char*)buf, 6);
2732 v3s16 blockpos = readV3S16(buf);
2733 is.read((char*)buf, 2);
2734 s16 id = readS16(buf);
2735 is.read((char*)buf, 2);
2736 u16 textlen = readU16(buf);
2738 for(u16 i=0; i<textlen; i++)
2740 is.read((char*)buf, 1);
2741 text += (char)buf[0];
2744 MapBlock *block = NULL;
2747 block = m_env.getMap().getBlockNoCreate(blockpos);
2749 catch(InvalidPositionException &e)
2751 derr_server<<"Error while setting sign text: "
2752 "block not found"<<std::endl;
2756 MapBlockObject *obj = block->getObject(id);
2759 derr_server<<"Error while setting sign text: "
2760 "object not found"<<std::endl;
2764 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2766 derr_server<<"Error while setting sign text: "
2767 "object is not a sign"<<std::endl;
2771 ((SignObject*)obj)->setText(text);
2773 obj->getBlock()->setChangedFlag();
2775 else if(command == TOSERVER_SIGNNODETEXT)
2777 if((player->privs & PRIV_BUILD) == 0)
2785 std::string datastring((char*)&data[2], datasize-2);
2786 std::istringstream is(datastring, std::ios_base::binary);
2789 is.read((char*)buf, 6);
2790 v3s16 p = readV3S16(buf);
2791 is.read((char*)buf, 2);
2792 u16 textlen = readU16(buf);
2794 for(u16 i=0; i<textlen; i++)
2796 is.read((char*)buf, 1);
2797 text += (char)buf[0];
2800 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2803 if(meta->typeId() != CONTENT_SIGN_WALL)
2805 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2806 signmeta->setText(text);
2808 v3s16 blockpos = getNodeBlockPos(p);
2809 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2812 block->setChangedFlag();
2815 for(core::map<u16, RemoteClient*>::Iterator
2816 i = m_clients.getIterator();
2817 i.atEnd()==false; i++)
2819 RemoteClient *client = i.getNode()->getValue();
2820 client->SetBlockNotSent(blockpos);
2823 else if(command == TOSERVER_INVENTORY_ACTION)
2825 /*// Ignore inventory changes if in creative mode
2826 if(g_settings.getBool("creative_mode") == true)
2828 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2832 // Strip command and create a stream
2833 std::string datastring((char*)&data[2], datasize-2);
2834 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2835 std::istringstream is(datastring, std::ios_base::binary);
2837 InventoryAction *a = InventoryAction::deSerialize(is);
2842 c.current_player = player;
2845 Handle craftresult specially if not in creative mode
2847 bool disable_action = false;
2848 if(a->getType() == IACTION_MOVE
2849 && g_settings.getBool("creative_mode") == false)
2851 IMoveAction *ma = (IMoveAction*)a;
2852 if(ma->to_inv == "current_player" &&
2853 ma->from_inv == "current_player")
2855 InventoryList *rlist = player->inventory.getList("craftresult");
2857 InventoryList *clist = player->inventory.getList("craft");
2859 InventoryList *mlist = player->inventory.getList("main");
2862 Craftresult is no longer preview if something
2865 if(ma->to_list == "craftresult"
2866 && ma->from_list != "craftresult")
2868 // If it currently is a preview, remove
2870 if(player->craftresult_is_preview)
2872 rlist->deleteItem(0);
2874 player->craftresult_is_preview = false;
2877 Crafting takes place if this condition is true.
2879 if(player->craftresult_is_preview &&
2880 ma->from_list == "craftresult")
2882 player->craftresult_is_preview = false;
2883 clist->decrementMaterials(1);
2886 If the craftresult is placed on itself, move it to
2887 main inventory instead of doing the action
2889 if(ma->to_list == "craftresult"
2890 && ma->from_list == "craftresult")
2892 disable_action = true;
2894 InventoryItem *item1 = rlist->changeItem(0, NULL);
2895 mlist->addItem(item1);
2900 if(disable_action == false)
2902 // Feed action to player inventory
2910 UpdateCrafting(player->peer_id);
2911 SendInventory(player->peer_id);
2916 dstream<<"TOSERVER_INVENTORY_ACTION: "
2917 <<"InventoryAction::deSerialize() returned NULL"
2921 else if(command == TOSERVER_CHAT_MESSAGE)
2929 std::string datastring((char*)&data[2], datasize-2);
2930 std::istringstream is(datastring, std::ios_base::binary);
2933 is.read((char*)buf, 2);
2934 u16 len = readU16(buf);
2936 std::wstring message;
2937 for(u16 i=0; i<len; i++)
2939 is.read((char*)buf, 2);
2940 message += (wchar_t)readU16(buf);
2943 // Get player name of this client
2944 std::wstring name = narrow_to_wide(player->getName());
2946 // Line to send to players
2948 // Whether to send to the player that sent the line
2949 bool send_to_sender = false;
2950 // Whether to send to other players
2951 bool send_to_others = false;
2953 // Local player gets all privileges regardless of
2954 // what's set on their account.
2955 u64 privs = player->privs;
2956 if(g_settings.get("name") == player->getName())
2960 std::wstring commandprefix = L"/#";
2961 if(message.substr(0, commandprefix.size()) == commandprefix)
2963 line += L"Server: ";
2965 message = message.substr(commandprefix.size());
2967 ServerCommandContext *ctx = new ServerCommandContext(
2968 str_split(message, L' '),
2974 line += processServerCommand(ctx);
2975 send_to_sender = ctx->flags & 1;
2976 send_to_others = ctx->flags & 2;
2982 if(privs & PRIV_SHOUT)
2988 send_to_others = true;
2992 line += L"Server: You are not allowed to shout";
2993 send_to_sender = true;
2999 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3002 Send the message to clients
3004 for(core::map<u16, RemoteClient*>::Iterator
3005 i = m_clients.getIterator();
3006 i.atEnd() == false; i++)
3008 // Get client and check that it is valid
3009 RemoteClient *client = i.getNode()->getValue();
3010 assert(client->peer_id == i.getNode()->getKey());
3011 if(client->serialization_version == SER_FMT_VER_INVALID)
3015 bool sender_selected = (peer_id == client->peer_id);
3016 if(sender_selected == true && send_to_sender == false)
3018 if(sender_selected == false && send_to_others == false)
3021 SendChatMessage(client->peer_id, line);
3025 else if(command == TOSERVER_DAMAGE)
3027 if(g_settings.getBool("enable_damage"))
3029 std::string datastring((char*)&data[2], datasize-2);
3030 std::istringstream is(datastring, std::ios_base::binary);
3031 u8 damage = readU8(is);
3032 if(player->hp > damage)
3034 player->hp -= damage;
3040 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3043 v3f pos = findSpawnPos(m_env.getServerMap());
3044 player->setPosition(pos);
3046 SendMovePlayer(player);
3047 SendPlayerHP(player);
3049 //TODO: Throw items around
3053 SendPlayerHP(player);
3055 else if(command == TOSERVER_PASSWORD)
3058 [0] u16 TOSERVER_PASSWORD
3059 [2] u8[28] old password
3060 [30] u8[28] new password
3063 if(datasize != 2+PASSWORD_SIZE*2)
3065 /*char password[PASSWORD_SIZE];
3066 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3067 password[i] = data[2+i];
3068 password[PASSWORD_SIZE-1] = 0;*/
3070 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3078 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3080 char c = data[2+PASSWORD_SIZE+i];
3086 std::string playername = player->getName();
3088 if(m_authmanager.exists(playername) == false)
3090 dstream<<"Server: playername not found in authmanager"<<std::endl;
3091 // Wrong old password supplied!!
3092 SendChatMessage(peer_id, L"playername not found in authmanager");
3096 std::string checkpwd = m_authmanager.getPassword(playername);
3098 if(oldpwd != checkpwd)
3100 dstream<<"Server: invalid old password"<<std::endl;
3101 // Wrong old password supplied!!
3102 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3106 m_authmanager.setPassword(playername, newpwd);
3108 dstream<<"Server: password change successful for "<<playername
3110 SendChatMessage(peer_id, L"Password change successful");
3114 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3115 "unknown command "<<command<<std::endl;
3119 catch(SendFailedException &e)
3121 derr_server<<"Server::ProcessData(): SendFailedException: "
3127 void Server::onMapEditEvent(MapEditEvent *event)
3129 dstream<<"Server::onMapEditEvent()"<<std::endl;
3130 if(m_ignore_map_edit_events)
3132 MapEditEvent *e = event->clone();
3133 m_unsent_map_edit_queue.push_back(e);
3136 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3138 if(id == "current_player")
3140 assert(c->current_player);
3141 return &(c->current_player->inventory);
3145 std::string id0 = fn.next(":");
3147 if(id0 == "nodemeta")
3150 p.X = stoi(fn.next(","));
3151 p.Y = stoi(fn.next(","));
3152 p.Z = stoi(fn.next(","));
3153 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3155 return meta->getInventory();
3156 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3157 <<"no metadata found"<<std::endl;
3161 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3164 void Server::inventoryModified(InventoryContext *c, std::string id)
3166 if(id == "current_player")
3168 assert(c->current_player);
3170 UpdateCrafting(c->current_player->peer_id);
3171 SendInventory(c->current_player->peer_id);
3176 std::string id0 = fn.next(":");
3178 if(id0 == "nodemeta")
3181 p.X = stoi(fn.next(","));
3182 p.Y = stoi(fn.next(","));
3183 p.Z = stoi(fn.next(","));
3184 v3s16 blockpos = getNodeBlockPos(p);
3186 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3188 meta->inventoryModified();
3190 for(core::map<u16, RemoteClient*>::Iterator
3191 i = m_clients.getIterator();
3192 i.atEnd()==false; i++)
3194 RemoteClient *client = i.getNode()->getValue();
3195 client->SetBlockNotSent(blockpos);
3201 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3204 core::list<PlayerInfo> Server::getPlayerInfo()
3206 DSTACK(__FUNCTION_NAME);
3207 JMutexAutoLock envlock(m_env_mutex);
3208 JMutexAutoLock conlock(m_con_mutex);
3210 core::list<PlayerInfo> list;
3212 core::list<Player*> players = m_env.getPlayers();
3214 core::list<Player*>::Iterator i;
3215 for(i = players.begin();
3216 i != players.end(); i++)
3220 Player *player = *i;
3223 con::Peer *peer = m_con.GetPeer(player->peer_id);
3224 // Copy info from peer to info struct
3226 info.address = peer->address;
3227 info.avg_rtt = peer->avg_rtt;
3229 catch(con::PeerNotFoundException &e)
3231 // Set dummy peer info
3233 info.address = Address(0,0,0,0,0);
3237 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3238 info.position = player->getPosition();
3240 list.push_back(info);
3247 void Server::peerAdded(con::Peer *peer)
3249 DSTACK(__FUNCTION_NAME);
3250 dout_server<<"Server::peerAdded(): peer->id="
3251 <<peer->id<<std::endl;
3254 c.type = PEER_ADDED;
3255 c.peer_id = peer->id;
3257 m_peer_change_queue.push_back(c);
3260 void Server::deletingPeer(con::Peer *peer, bool timeout)
3262 DSTACK(__FUNCTION_NAME);
3263 dout_server<<"Server::deletingPeer(): peer->id="
3264 <<peer->id<<", timeout="<<timeout<<std::endl;
3267 c.type = PEER_REMOVED;
3268 c.peer_id = peer->id;
3269 c.timeout = timeout;
3270 m_peer_change_queue.push_back(c);
3277 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3279 DSTACK(__FUNCTION_NAME);
3280 std::ostringstream os(std::ios_base::binary);
3282 writeU16(os, TOCLIENT_HP);
3286 std::string s = os.str();
3287 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3289 con.Send(peer_id, 0, data, true);
3292 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3293 const std::wstring &reason)
3295 DSTACK(__FUNCTION_NAME);
3296 std::ostringstream os(std::ios_base::binary);
3298 writeU16(os, TOCLIENT_ACCESS_DENIED);
3299 os<<serializeWideString(reason);
3302 std::string s = os.str();
3303 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3305 con.Send(peer_id, 0, data, true);
3309 Non-static send methods
3312 void Server::SendObjectData(float dtime)
3314 DSTACK(__FUNCTION_NAME);
3316 core::map<v3s16, bool> stepped_blocks;
3318 for(core::map<u16, RemoteClient*>::Iterator
3319 i = m_clients.getIterator();
3320 i.atEnd() == false; i++)
3322 u16 peer_id = i.getNode()->getKey();
3323 RemoteClient *client = i.getNode()->getValue();
3324 assert(client->peer_id == peer_id);
3326 if(client->serialization_version == SER_FMT_VER_INVALID)
3329 client->SendObjectData(this, dtime, stepped_blocks);
3333 void Server::SendPlayerInfos()
3335 DSTACK(__FUNCTION_NAME);
3337 //JMutexAutoLock envlock(m_env_mutex);
3339 // Get connected players
3340 core::list<Player*> players = m_env.getPlayers(true);
3342 u32 player_count = players.getSize();
3343 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3345 SharedBuffer<u8> data(datasize);
3346 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3349 core::list<Player*>::Iterator i;
3350 for(i = players.begin();
3351 i != players.end(); i++)
3353 Player *player = *i;
3355 /*dstream<<"Server sending player info for player with "
3356 "peer_id="<<player->peer_id<<std::endl;*/
3358 writeU16(&data[start], player->peer_id);
3359 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3360 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3361 start += 2+PLAYERNAME_SIZE;
3364 //JMutexAutoLock conlock(m_con_mutex);
3367 m_con.SendToAll(0, data, true);
3370 void Server::SendInventory(u16 peer_id)
3372 DSTACK(__FUNCTION_NAME);
3374 Player* player = m_env.getPlayer(peer_id);
3381 std::ostringstream os;
3382 //os.imbue(std::locale("C"));
3384 player->inventory.serialize(os);
3386 std::string s = os.str();
3388 SharedBuffer<u8> data(s.size()+2);
3389 writeU16(&data[0], TOCLIENT_INVENTORY);
3390 memcpy(&data[2], s.c_str(), s.size());
3393 m_con.Send(peer_id, 0, data, true);
3396 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3398 DSTACK(__FUNCTION_NAME);
3400 std::ostringstream os(std::ios_base::binary);
3404 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3405 os.write((char*)buf, 2);
3408 writeU16(buf, message.size());
3409 os.write((char*)buf, 2);
3412 for(u32 i=0; i<message.size(); i++)
3416 os.write((char*)buf, 2);
3420 std::string s = os.str();
3421 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3423 m_con.Send(peer_id, 0, data, true);
3426 void Server::BroadcastChatMessage(const std::wstring &message)
3428 for(core::map<u16, RemoteClient*>::Iterator
3429 i = m_clients.getIterator();
3430 i.atEnd() == false; i++)
3432 // Get client and check that it is valid
3433 RemoteClient *client = i.getNode()->getValue();
3434 assert(client->peer_id == i.getNode()->getKey());
3435 if(client->serialization_version == SER_FMT_VER_INVALID)
3438 SendChatMessage(client->peer_id, message);
3442 void Server::SendPlayerHP(Player *player)
3444 SendHP(m_con, player->peer_id, player->hp);
3447 void Server::SendMovePlayer(Player *player)
3449 DSTACK(__FUNCTION_NAME);
3450 std::ostringstream os(std::ios_base::binary);
3452 writeU16(os, TOCLIENT_MOVE_PLAYER);
3453 writeV3F1000(os, player->getPosition());
3454 writeF1000(os, player->getPitch());
3455 writeF1000(os, player->getYaw());
3458 v3f pos = player->getPosition();
3459 f32 pitch = player->getPitch();
3460 f32 yaw = player->getYaw();
3461 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3462 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3469 std::string s = os.str();
3470 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3472 m_con.Send(player->peer_id, 0, data, true);
3475 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3476 core::list<u16> *far_players, float far_d_nodes)
3478 float maxd = far_d_nodes*BS;
3479 v3f p_f = intToFloat(p, BS);
3483 SharedBuffer<u8> reply(replysize);
3484 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3485 writeS16(&reply[2], p.X);
3486 writeS16(&reply[4], p.Y);
3487 writeS16(&reply[6], p.Z);
3489 for(core::map<u16, RemoteClient*>::Iterator
3490 i = m_clients.getIterator();
3491 i.atEnd() == false; i++)
3493 // Get client and check that it is valid
3494 RemoteClient *client = i.getNode()->getValue();
3495 assert(client->peer_id == i.getNode()->getKey());
3496 if(client->serialization_version == SER_FMT_VER_INVALID)
3499 // Don't send if it's the same one
3500 if(client->peer_id == ignore_id)
3506 Player *player = m_env.getPlayer(client->peer_id);
3509 // If player is far away, only set modified blocks not sent
3510 v3f player_pos = player->getPosition();
3511 if(player_pos.getDistanceFrom(p_f) > maxd)
3513 far_players->push_back(client->peer_id);
3520 m_con.Send(client->peer_id, 0, reply, true);
3524 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3525 core::list<u16> *far_players, float far_d_nodes)
3527 float maxd = far_d_nodes*BS;
3528 v3f p_f = intToFloat(p, BS);
3530 for(core::map<u16, RemoteClient*>::Iterator
3531 i = m_clients.getIterator();
3532 i.atEnd() == false; i++)
3534 // Get client and check that it is valid
3535 RemoteClient *client = i.getNode()->getValue();
3536 assert(client->peer_id == i.getNode()->getKey());
3537 if(client->serialization_version == SER_FMT_VER_INVALID)
3540 // Don't send if it's the same one
3541 if(client->peer_id == ignore_id)
3547 Player *player = m_env.getPlayer(client->peer_id);
3550 // If player is far away, only set modified blocks not sent
3551 v3f player_pos = player->getPosition();
3552 if(player_pos.getDistanceFrom(p_f) > maxd)
3554 far_players->push_back(client->peer_id);
3561 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3562 SharedBuffer<u8> reply(replysize);
3563 writeU16(&reply[0], TOCLIENT_ADDNODE);
3564 writeS16(&reply[2], p.X);
3565 writeS16(&reply[4], p.Y);
3566 writeS16(&reply[6], p.Z);
3567 n.serialize(&reply[8], client->serialization_version);
3570 m_con.Send(client->peer_id, 0, reply, true);
3574 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3576 DSTACK(__FUNCTION_NAME);
3578 v3s16 p = block->getPos();
3582 bool completely_air = true;
3583 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3584 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3585 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3587 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3589 completely_air = false;
3590 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3595 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3597 dstream<<"[completely air] ";
3602 Create a packet with the block in the right format
3605 std::ostringstream os(std::ios_base::binary);
3606 block->serialize(os, ver);
3607 std::string s = os.str();
3608 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3610 u32 replysize = 8 + blockdata.getSize();
3611 SharedBuffer<u8> reply(replysize);
3612 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3613 writeS16(&reply[2], p.X);
3614 writeS16(&reply[4], p.Y);
3615 writeS16(&reply[6], p.Z);
3616 memcpy(&reply[8], *blockdata, blockdata.getSize());
3618 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3619 <<": \tpacket size: "<<replysize<<std::endl;*/
3624 m_con.Send(peer_id, 1, reply, true);
3627 void Server::SendBlocks(float dtime)
3629 DSTACK(__FUNCTION_NAME);
3631 JMutexAutoLock envlock(m_env_mutex);
3632 JMutexAutoLock conlock(m_con_mutex);
3634 //TimeTaker timer("Server::SendBlocks");
3636 core::array<PrioritySortedBlockTransfer> queue;
3638 s32 total_sending = 0;
3640 for(core::map<u16, RemoteClient*>::Iterator
3641 i = m_clients.getIterator();
3642 i.atEnd() == false; i++)
3644 RemoteClient *client = i.getNode()->getValue();
3645 assert(client->peer_id == i.getNode()->getKey());
3647 total_sending += client->SendingCount();
3649 if(client->serialization_version == SER_FMT_VER_INVALID)
3652 client->GetNextBlocks(this, dtime, queue);
3656 // Lowest priority number comes first.
3657 // Lowest is most important.
3660 for(u32 i=0; i<queue.size(); i++)
3662 //TODO: Calculate limit dynamically
3663 if(total_sending >= g_settings.getS32
3664 ("max_simultaneous_block_sends_server_total"))
3667 PrioritySortedBlockTransfer q = queue[i];
3669 MapBlock *block = NULL;
3672 block = m_env.getMap().getBlockNoCreate(q.pos);
3674 catch(InvalidPositionException &e)
3679 RemoteClient *client = getClient(q.peer_id);
3681 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3683 client->SentBlock(q.pos);
3693 void Server::UpdateCrafting(u16 peer_id)
3695 DSTACK(__FUNCTION_NAME);
3697 Player* player = m_env.getPlayer(peer_id);
3701 Calculate crafting stuff
3703 if(g_settings.getBool("creative_mode") == false)
3705 InventoryList *clist = player->inventory.getList("craft");
3706 InventoryList *rlist = player->inventory.getList("craftresult");
3708 if(rlist->getUsedSlots() == 0)
3709 player->craftresult_is_preview = true;
3711 if(rlist && player->craftresult_is_preview)
3713 rlist->clearItems();
3715 if(clist && rlist && player->craftresult_is_preview)
3717 InventoryItem *items[9];
3718 for(u16 i=0; i<9; i++)
3720 items[i] = clist->getItem(i);
3729 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3730 if(checkItemCombination(items, specs))
3732 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3741 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3742 if(checkItemCombination(items, specs))
3744 rlist->addItem(new CraftItem("Stick", 4));
3753 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3754 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3755 specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
3756 specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
3757 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3758 specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
3759 if(checkItemCombination(items, specs))
3761 rlist->addItem(new MaterialItem(CONTENT_FENCE, 2));
3770 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3771 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3772 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3773 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3774 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3775 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3776 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3777 if(checkItemCombination(items, specs))
3779 //rlist->addItem(new MapBlockObjectItem("Sign"));
3780 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3789 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3790 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3791 if(checkItemCombination(items, specs))
3793 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3802 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3803 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3804 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3805 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3806 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3807 if(checkItemCombination(items, specs))
3809 rlist->addItem(new ToolItem("WPick", 0));
3818 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3819 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3820 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3821 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3822 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3823 if(checkItemCombination(items, specs))
3825 rlist->addItem(new ToolItem("STPick", 0));
3834 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3835 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3836 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3837 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3838 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3839 if(checkItemCombination(items, specs))
3841 rlist->addItem(new ToolItem("SteelPick", 0));
3850 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3851 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3852 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3853 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3854 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3855 if(checkItemCombination(items, specs))
3857 rlist->addItem(new ToolItem("MesePick", 0));
3866 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3867 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3868 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3869 if(checkItemCombination(items, specs))
3871 rlist->addItem(new ToolItem("WShovel", 0));
3880 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3881 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3882 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3883 if(checkItemCombination(items, specs))
3885 rlist->addItem(new ToolItem("STShovel", 0));
3894 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3895 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3896 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3897 if(checkItemCombination(items, specs))
3899 rlist->addItem(new ToolItem("SteelShovel", 0));
3908 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3909 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3910 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3911 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3912 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3913 if(checkItemCombination(items, specs))
3915 rlist->addItem(new ToolItem("WAxe", 0));
3924 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3925 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3926 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3927 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3928 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3929 if(checkItemCombination(items, specs))
3931 rlist->addItem(new ToolItem("STAxe", 0));
3940 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3941 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3942 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3943 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3944 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3945 if(checkItemCombination(items, specs))
3947 rlist->addItem(new ToolItem("SteelAxe", 0));
3956 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3957 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3958 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3959 if(checkItemCombination(items, specs))
3961 rlist->addItem(new ToolItem("WSword", 0));
3970 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3971 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3972 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3973 if(checkItemCombination(items, specs))
3975 rlist->addItem(new ToolItem("STSword", 0));
3984 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3985 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3986 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3987 if(checkItemCombination(items, specs))
3989 rlist->addItem(new ToolItem("SteelSword", 0));
3998 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3999 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4000 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4001 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4002 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4003 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4004 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4005 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4006 if(checkItemCombination(items, specs))
4008 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
4017 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4018 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4019 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4020 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4021 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4022 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4023 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4024 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4025 if(checkItemCombination(items, specs))
4027 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
4036 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4037 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4038 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4039 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4040 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4041 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4042 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4043 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4044 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4045 if(checkItemCombination(items, specs))
4047 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
4053 } // if creative_mode == false
4056 RemoteClient* Server::getClient(u16 peer_id)
4058 DSTACK(__FUNCTION_NAME);
4059 //JMutexAutoLock lock(m_con_mutex);
4060 core::map<u16, RemoteClient*>::Node *n;
4061 n = m_clients.find(peer_id);
4062 // A client should exist for all peers
4064 return n->getValue();
4067 std::wstring Server::getStatusString()
4069 std::wostringstream os(std::ios_base::binary);
4072 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4074 os<<L", uptime="<<m_uptime.get();
4075 // Information about clients
4077 for(core::map<u16, RemoteClient*>::Iterator
4078 i = m_clients.getIterator();
4079 i.atEnd() == false; i++)
4081 // Get client and check that it is valid
4082 RemoteClient *client = i.getNode()->getValue();
4083 assert(client->peer_id == i.getNode()->getKey());
4084 if(client->serialization_version == SER_FMT_VER_INVALID)
4087 Player *player = m_env.getPlayer(client->peer_id);
4088 // Get name of player
4089 std::wstring name = L"unknown";
4091 name = narrow_to_wide(player->getName());
4092 // Add name to information string
4096 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4097 os<<" WARNING: Map saving is disabled."<<std::endl;
4102 void setCreativeInventory(Player *player)
4104 player->resetInventory();
4106 // Give some good tools
4108 InventoryItem *item = new ToolItem("MesePick", 0);
4109 void* r = player->inventory.addItem("main", item);
4113 InventoryItem *item = new ToolItem("SteelPick", 0);
4114 void* r = player->inventory.addItem("main", item);
4118 InventoryItem *item = new ToolItem("SteelAxe", 0);
4119 void* r = player->inventory.addItem("main", item);
4123 InventoryItem *item = new ToolItem("SteelShovel", 0);
4124 void* r = player->inventory.addItem("main", item);
4132 // CONTENT_IGNORE-terminated list
4133 u8 material_items[] = {
4144 CONTENT_WATERSOURCE,
4152 u8 *mip = material_items;
4153 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4155 if(*mip == CONTENT_IGNORE)
4158 InventoryItem *item = new MaterialItem(*mip, 1);
4159 player->inventory.addItem("main", item);
4165 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4168 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4169 player->inventory.addItem("main", item);
4172 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4174 // Skip some materials
4175 if(i == CONTENT_WATER || i == CONTENT_TORCH
4176 || i == CONTENT_COALSTONE)
4179 InventoryItem *item = new MaterialItem(i, 1);
4180 player->inventory.addItem("main", item);
4186 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4187 void* r = player->inventory.addItem("main", item);
4192 v3f findSpawnPos(ServerMap &map)
4194 //return v3f(50,50,50)*BS;
4197 s16 groundheight = 0;
4199 // Try to find a good place a few times
4200 for(s32 i=0; i<1000; i++)
4203 // We're going to try to throw the player to this position
4204 nodepos = v2s16(-range + (myrand()%(range*2)),
4205 -range + (myrand()%(range*2)));
4206 v2s16 sectorpos = getNodeSectorPos(nodepos);
4207 // Get sector (NOTE: Don't get because it's slow)
4208 //m_env.getMap().emergeSector(sectorpos);
4209 // Get ground height at point (fallbacks to heightmap function)
4210 groundheight = map.findGroundLevel(nodepos);
4211 // Don't go underwater
4212 if(groundheight < WATER_LEVEL)
4214 //dstream<<"-> Underwater"<<std::endl;
4217 // Don't go to high places
4218 if(groundheight > WATER_LEVEL + 4)
4220 //dstream<<"-> Underwater"<<std::endl;
4224 // Found a good place
4225 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4229 // If no suitable place was not found, go above water at least.
4230 if(groundheight < WATER_LEVEL)
4231 groundheight = WATER_LEVEL;
4233 return intToFloat(v3s16(
4240 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4243 Try to get an existing player
4245 Player *player = m_env.getPlayer(name);
4248 // If player is already connected, cancel
4249 if(player->peer_id != 0)
4251 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4256 player->peer_id = peer_id;
4258 // Reset inventory to creative if in creative mode
4259 if(g_settings.getBool("creative_mode"))
4261 setCreativeInventory(player);
4268 If player with the wanted peer_id already exists, cancel.
4270 if(m_env.getPlayer(peer_id) != NULL)
4272 dstream<<"emergePlayer(): Player with wrong name but same"
4273 " peer_id already exists"<<std::endl;
4281 player = new ServerRemotePlayer();
4282 //player->peer_id = c.peer_id;
4283 //player->peer_id = PEER_ID_INEXISTENT;
4284 player->peer_id = peer_id;
4285 player->updateName(name);
4286 m_authmanager.add(name);
4287 m_authmanager.setPassword(name, password);
4288 m_authmanager.setPrivs(name,
4289 stringToPrivs(g_settings.get("default_privs")));
4291 if(g_settings.exists("default_privs"))
4292 player->privs = g_settings.getU64("default_privs");
4298 dstream<<"Server: Finding spawn place for player \""
4299 <<player->getName()<<"\""<<std::endl;
4301 v3f pos = findSpawnPos(m_env.getServerMap());
4303 player->setPosition(pos);
4306 Add player to environment
4309 m_env.addPlayer(player);
4312 Add stuff to inventory
4315 if(g_settings.getBool("creative_mode"))
4317 setCreativeInventory(player);
4319 else if(g_settings.getBool("give_initial_stuff"))
4322 InventoryItem *item = new ToolItem("SteelPick", 0);
4323 void* r = player->inventory.addItem("main", item);
4327 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4328 void* r = player->inventory.addItem("main", item);
4332 InventoryItem *item = new ToolItem("SteelAxe", 0);
4333 void* r = player->inventory.addItem("main", item);
4337 InventoryItem *item = new ToolItem("SteelShovel", 0);
4338 void* r = player->inventory.addItem("main", item);
4342 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4343 void* r = player->inventory.addItem("main", item);
4347 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4348 void* r = player->inventory.addItem("main", item);
4352 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4353 void* r = player->inventory.addItem("main", item);
4357 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4358 void* r = player->inventory.addItem("main", item);
4362 InventoryItem *item = new CraftItem("Stick", 4);
4363 void* r = player->inventory.addItem("main", item);
4367 InventoryItem *item = new ToolItem("WPick", 32000);
4368 void* r = player->inventory.addItem("main", item);
4372 InventoryItem *item = new ToolItem("STPick", 32000);
4373 void* r = player->inventory.addItem("main", item);
4377 for(u16 i=0; i<4; i++)
4379 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4380 bool r = player->inventory.addItem("main", item);
4383 /*// Give some other stuff
4385 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4386 bool r = player->inventory.addItem("main", item);
4393 } // create new player
4396 void Server::handlePeerChange(PeerChange &c)
4398 JMutexAutoLock envlock(m_env_mutex);
4399 JMutexAutoLock conlock(m_con_mutex);
4401 if(c.type == PEER_ADDED)
4408 core::map<u16, RemoteClient*>::Node *n;
4409 n = m_clients.find(c.peer_id);
4410 // The client shouldn't already exist
4414 RemoteClient *client = new RemoteClient();
4415 client->peer_id = c.peer_id;
4416 m_clients.insert(client->peer_id, client);
4419 else if(c.type == PEER_REMOVED)
4426 core::map<u16, RemoteClient*>::Node *n;
4427 n = m_clients.find(c.peer_id);
4428 // The client should exist
4432 Mark objects to be not known by the client
4434 RemoteClient *client = n->getValue();
4436 for(core::map<u16, bool>::Iterator
4437 i = client->m_known_objects.getIterator();
4438 i.atEnd()==false; i++)
4441 u16 id = i.getNode()->getKey();
4442 ServerActiveObject* obj = m_env.getActiveObject(id);
4444 if(obj && obj->m_known_by_count > 0)
4445 obj->m_known_by_count--;
4448 // Collect information about leaving in chat
4449 std::wstring message;
4451 std::wstring name = L"unknown";
4452 Player *player = m_env.getPlayer(c.peer_id);
4454 name = narrow_to_wide(player->getName());
4458 message += L" left game";
4460 message += L" (timed out)";
4465 m_env.removePlayer(c.peer_id);
4468 // Set player client disconnected
4470 Player *player = m_env.getPlayer(c.peer_id);
4472 player->peer_id = 0;
4476 delete m_clients[c.peer_id];
4477 m_clients.remove(c.peer_id);
4479 // Send player info to all remaining clients
4482 // Send leave chat message to all remaining clients
4483 BroadcastChatMessage(message);
4492 void Server::handlePeerChanges()
4494 while(m_peer_change_queue.size() > 0)
4496 PeerChange c = m_peer_change_queue.pop_front();
4498 dout_server<<"Server: Handling peer change: "
4499 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4502 handlePeerChange(c);
4506 void dedicated_server_loop(Server &server, bool &kill)
4508 DSTACK(__FUNCTION_NAME);
4510 std::cout<<DTIME<<std::endl;
4511 std::cout<<"========================"<<std::endl;
4512 std::cout<<"Running dedicated server"<<std::endl;
4513 std::cout<<"========================"<<std::endl;
4514 std::cout<<std::endl;
4518 // This is kind of a hack but can be done like this
4519 // because server.step() is very light
4523 if(server.getShutdownRequested() || kill)
4525 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4529 static int counter = 0;
4535 core::list<PlayerInfo> list = server.getPlayerInfo();
4536 core::list<PlayerInfo>::Iterator i;
4537 static u32 sum_old = 0;
4538 u32 sum = PIChecksum(list);
4541 std::cout<<DTIME<<"Player info:"<<std::endl;
4542 for(i=list.begin(); i!=list.end(); i++)
4544 i->PrintLine(&std::cout);