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((getPlayerPrivs(player) & 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((getPlayerPrivs(player) & 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((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2373 dstream<<"Player "<<player->getName()<<" cannot remove node"
2374 <<" because privileges are "<<getPlayerPrivs(player)
2376 cannot_remove_node = true;
2380 If node can't be removed, set block to be re-sent to
2383 if(cannot_remove_node)
2385 derr_server<<"Server: Not finishing digging."<<std::endl;
2387 // Client probably has wrong data.
2388 // Set block not sent, so that client will get
2390 dstream<<"Client "<<peer_id<<" tried to dig "
2391 <<"node; but node cannot be removed."
2392 <<" setting MapBlock not sent."<<std::endl;
2393 RemoteClient *client = getClient(peer_id);
2394 v3s16 blockpos = getNodeBlockPos(p_under);
2395 client->SetBlockNotSent(blockpos);
2401 Send the removal to all other clients.
2402 - If other player is close, send REMOVENODE
2403 - Otherwise set blocks not sent
2405 core::list<u16> far_players;
2406 sendRemoveNode(p_under, peer_id, &far_players, 100);
2409 Update and send inventory
2412 if(g_settings.getBool("creative_mode") == false)
2417 InventoryList *mlist = player->inventory.getList("main");
2420 InventoryItem *item = mlist->getItem(item_i);
2421 if(item && (std::string)item->getName() == "ToolItem")
2423 ToolItem *titem = (ToolItem*)item;
2424 std::string toolname = titem->getToolName();
2426 // Get digging properties for material and tool
2427 DiggingProperties prop =
2428 getDiggingProperties(material, toolname);
2430 if(prop.diggable == false)
2432 derr_server<<"Server: WARNING: Player digged"
2433 <<" with impossible material + tool"
2434 <<" combination"<<std::endl;
2437 bool weared_out = titem->addWear(prop.wear);
2441 mlist->deleteItem(item_i);
2447 Add dug item to inventory
2450 InventoryItem *item = NULL;
2452 if(mineral != MINERAL_NONE)
2453 item = getDiggedMineralItem(mineral);
2458 std::string &dug_s = content_features(material).dug_item;
2461 std::istringstream is(dug_s, std::ios::binary);
2462 item = InventoryItem::deSerialize(is);
2468 // Add a item to inventory
2469 player->inventory.addItem("main", item);
2472 UpdateCrafting(player->peer_id);
2473 SendInventory(player->peer_id);
2479 (this takes some time so it is done after the quick stuff)
2481 m_ignore_map_edit_events = true;
2482 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2483 m_ignore_map_edit_events = false;
2486 Set blocks not sent to far players
2488 for(core::list<u16>::Iterator
2489 i = far_players.begin();
2490 i != far_players.end(); i++)
2493 RemoteClient *client = getClient(peer_id);
2496 client->SetBlocksNotSent(modified_blocks);
2503 else if(action == 1)
2506 InventoryList *ilist = player->inventory.getList("main");
2511 InventoryItem *item = ilist->getItem(item_i);
2513 // If there is no item, it is not possible to add it anywhere
2518 Handle material items
2520 if(std::string("MaterialItem") == item->getName())
2523 // Don't add a node if this is not a free space
2524 MapNode n2 = m_env.getMap().getNode(p_over);
2525 bool no_enough_privs =
2526 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2528 dstream<<"Player "<<player->getName()<<" cannot add node"
2529 <<" because privileges are "<<getPlayerPrivs(player)
2532 if(content_buildable_to(n2.d) == false
2535 // Client probably has wrong data.
2536 // Set block not sent, so that client will get
2538 dstream<<"Client "<<peer_id<<" tried to place"
2539 <<" node in invalid position; setting"
2540 <<" MapBlock not sent."<<std::endl;
2541 RemoteClient *client = getClient(peer_id);
2542 v3s16 blockpos = getNodeBlockPos(p_over);
2543 client->SetBlockNotSent(blockpos);
2547 catch(InvalidPositionException &e)
2549 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2550 <<" Adding block to emerge queue."
2552 m_emerge_queue.addBlock(peer_id,
2553 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2557 // Reset build time counter
2558 getClient(peer->id)->m_time_from_building = 0.0;
2561 MaterialItem *mitem = (MaterialItem*)item;
2563 n.d = mitem->getMaterial();
2564 if(content_features(n.d).wall_mounted)
2565 n.dir = packDir(p_under - p_over);
2570 core::list<u16> far_players;
2571 sendAddNode(p_over, n, 0, &far_players, 100);
2576 InventoryList *ilist = player->inventory.getList("main");
2577 if(g_settings.getBool("creative_mode") == false && ilist)
2579 // Remove from inventory and send inventory
2580 if(mitem->getCount() == 1)
2581 ilist->deleteItem(item_i);
2585 UpdateCrafting(peer_id);
2586 SendInventory(peer_id);
2592 This takes some time so it is done after the quick stuff
2594 core::map<v3s16, MapBlock*> modified_blocks;
2595 m_ignore_map_edit_events = true;
2596 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2597 m_ignore_map_edit_events = false;
2600 Set blocks not sent to far players
2602 for(core::list<u16>::Iterator
2603 i = far_players.begin();
2604 i != far_players.end(); i++)
2607 RemoteClient *client = getClient(peer_id);
2610 client->SetBlocksNotSent(modified_blocks);
2614 Calculate special events
2617 /*if(n.d == CONTENT_MESE)
2620 for(s16 z=-1; z<=1; z++)
2621 for(s16 y=-1; y<=1; y++)
2622 for(s16 x=-1; x<=1; x++)
2629 Place other item (not a block)
2633 v3s16 blockpos = getNodeBlockPos(p_over);
2636 Check that the block is loaded so that the item
2637 can properly be added to the static list too
2639 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2642 derr_server<<"Error while placing object: "
2643 "block not found"<<std::endl;
2647 dout_server<<"Placing a miscellaneous item on map"
2650 // Calculate a position for it
2651 v3f pos = intToFloat(p_over, BS);
2653 pos.Y -= BS*0.25; // let it drop a bit
2655 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2656 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2661 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2665 derr_server<<"WARNING: item resulted in NULL object, "
2666 <<"not placing onto map"
2671 // Add the object to the environment
2672 m_env.addActiveObject(obj);
2674 dout_server<<"Placed object"<<std::endl;
2676 if(g_settings.getBool("creative_mode") == false)
2678 // Delete the right amount of items from the slot
2679 u16 dropcount = item->getDropCount();
2681 // Delete item if all gone
2682 if(item->getCount() <= dropcount)
2684 if(item->getCount() < dropcount)
2685 dstream<<"WARNING: Server: dropped more items"
2686 <<" than the slot contains"<<std::endl;
2688 InventoryList *ilist = player->inventory.getList("main");
2690 // Remove from inventory and send inventory
2691 ilist->deleteItem(item_i);
2693 // Else decrement it
2695 item->remove(dropcount);
2698 UpdateCrafting(peer_id);
2699 SendInventory(peer_id);
2707 Catch invalid actions
2711 derr_server<<"WARNING: Server: Invalid action "
2712 <<action<<std::endl;
2716 else if(command == TOSERVER_RELEASE)
2725 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2728 else if(command == TOSERVER_SIGNTEXT)
2730 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2739 std::string datastring((char*)&data[2], datasize-2);
2740 std::istringstream is(datastring, std::ios_base::binary);
2743 is.read((char*)buf, 6);
2744 v3s16 blockpos = readV3S16(buf);
2745 is.read((char*)buf, 2);
2746 s16 id = readS16(buf);
2747 is.read((char*)buf, 2);
2748 u16 textlen = readU16(buf);
2750 for(u16 i=0; i<textlen; i++)
2752 is.read((char*)buf, 1);
2753 text += (char)buf[0];
2756 MapBlock *block = NULL;
2759 block = m_env.getMap().getBlockNoCreate(blockpos);
2761 catch(InvalidPositionException &e)
2763 derr_server<<"Error while setting sign text: "
2764 "block not found"<<std::endl;
2768 MapBlockObject *obj = block->getObject(id);
2771 derr_server<<"Error while setting sign text: "
2772 "object not found"<<std::endl;
2776 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2778 derr_server<<"Error while setting sign text: "
2779 "object is not a sign"<<std::endl;
2783 ((SignObject*)obj)->setText(text);
2785 obj->getBlock()->setChangedFlag();
2787 else if(command == TOSERVER_SIGNNODETEXT)
2789 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2797 std::string datastring((char*)&data[2], datasize-2);
2798 std::istringstream is(datastring, std::ios_base::binary);
2801 is.read((char*)buf, 6);
2802 v3s16 p = readV3S16(buf);
2803 is.read((char*)buf, 2);
2804 u16 textlen = readU16(buf);
2806 for(u16 i=0; i<textlen; i++)
2808 is.read((char*)buf, 1);
2809 text += (char)buf[0];
2812 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2815 if(meta->typeId() != CONTENT_SIGN_WALL)
2817 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2818 signmeta->setText(text);
2820 v3s16 blockpos = getNodeBlockPos(p);
2821 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2824 block->setChangedFlag();
2827 for(core::map<u16, RemoteClient*>::Iterator
2828 i = m_clients.getIterator();
2829 i.atEnd()==false; i++)
2831 RemoteClient *client = i.getNode()->getValue();
2832 client->SetBlockNotSent(blockpos);
2835 else if(command == TOSERVER_INVENTORY_ACTION)
2837 /*// Ignore inventory changes if in creative mode
2838 if(g_settings.getBool("creative_mode") == true)
2840 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2844 // Strip command and create a stream
2845 std::string datastring((char*)&data[2], datasize-2);
2846 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2847 std::istringstream is(datastring, std::ios_base::binary);
2849 InventoryAction *a = InventoryAction::deSerialize(is);
2854 c.current_player = player;
2857 Handle craftresult specially if not in creative mode
2859 bool disable_action = false;
2860 if(a->getType() == IACTION_MOVE
2861 && g_settings.getBool("creative_mode") == false)
2863 IMoveAction *ma = (IMoveAction*)a;
2864 if(ma->to_inv == "current_player" &&
2865 ma->from_inv == "current_player")
2867 InventoryList *rlist = player->inventory.getList("craftresult");
2869 InventoryList *clist = player->inventory.getList("craft");
2871 InventoryList *mlist = player->inventory.getList("main");
2874 Craftresult is no longer preview if something
2877 if(ma->to_list == "craftresult"
2878 && ma->from_list != "craftresult")
2880 // If it currently is a preview, remove
2882 if(player->craftresult_is_preview)
2884 rlist->deleteItem(0);
2886 player->craftresult_is_preview = false;
2889 Crafting takes place if this condition is true.
2891 if(player->craftresult_is_preview &&
2892 ma->from_list == "craftresult")
2894 player->craftresult_is_preview = false;
2895 clist->decrementMaterials(1);
2898 If the craftresult is placed on itself, move it to
2899 main inventory instead of doing the action
2901 if(ma->to_list == "craftresult"
2902 && ma->from_list == "craftresult")
2904 disable_action = true;
2906 InventoryItem *item1 = rlist->changeItem(0, NULL);
2907 mlist->addItem(item1);
2912 if(disable_action == false)
2914 // Feed action to player inventory
2922 UpdateCrafting(player->peer_id);
2923 SendInventory(player->peer_id);
2928 dstream<<"TOSERVER_INVENTORY_ACTION: "
2929 <<"InventoryAction::deSerialize() returned NULL"
2933 else if(command == TOSERVER_CHAT_MESSAGE)
2941 std::string datastring((char*)&data[2], datasize-2);
2942 std::istringstream is(datastring, std::ios_base::binary);
2945 is.read((char*)buf, 2);
2946 u16 len = readU16(buf);
2948 std::wstring message;
2949 for(u16 i=0; i<len; i++)
2951 is.read((char*)buf, 2);
2952 message += (wchar_t)readU16(buf);
2955 // Get player name of this client
2956 std::wstring name = narrow_to_wide(player->getName());
2958 // Line to send to players
2960 // Whether to send to the player that sent the line
2961 bool send_to_sender = false;
2962 // Whether to send to other players
2963 bool send_to_others = false;
2965 // Local player gets all privileges regardless of
2966 // what's set on their account.
2967 u64 privs = getPlayerPrivs(player);
2970 std::wstring commandprefix = L"/#";
2971 if(message.substr(0, commandprefix.size()) == commandprefix)
2973 line += L"Server: ";
2975 message = message.substr(commandprefix.size());
2977 ServerCommandContext *ctx = new ServerCommandContext(
2978 str_split(message, L' '),
2984 line += processServerCommand(ctx);
2985 send_to_sender = ctx->flags & 1;
2986 send_to_others = ctx->flags & 2;
2992 if(privs & PRIV_SHOUT)
2998 send_to_others = true;
3002 line += L"Server: You are not allowed to shout";
3003 send_to_sender = true;
3009 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3012 Send the message to clients
3014 for(core::map<u16, RemoteClient*>::Iterator
3015 i = m_clients.getIterator();
3016 i.atEnd() == false; i++)
3018 // Get client and check that it is valid
3019 RemoteClient *client = i.getNode()->getValue();
3020 assert(client->peer_id == i.getNode()->getKey());
3021 if(client->serialization_version == SER_FMT_VER_INVALID)
3025 bool sender_selected = (peer_id == client->peer_id);
3026 if(sender_selected == true && send_to_sender == false)
3028 if(sender_selected == false && send_to_others == false)
3031 SendChatMessage(client->peer_id, line);
3035 else if(command == TOSERVER_DAMAGE)
3037 if(g_settings.getBool("enable_damage"))
3039 std::string datastring((char*)&data[2], datasize-2);
3040 std::istringstream is(datastring, std::ios_base::binary);
3041 u8 damage = readU8(is);
3042 if(player->hp > damage)
3044 player->hp -= damage;
3050 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3053 v3f pos = findSpawnPos(m_env.getServerMap());
3054 player->setPosition(pos);
3056 SendMovePlayer(player);
3057 SendPlayerHP(player);
3059 //TODO: Throw items around
3063 SendPlayerHP(player);
3065 else if(command == TOSERVER_PASSWORD)
3068 [0] u16 TOSERVER_PASSWORD
3069 [2] u8[28] old password
3070 [30] u8[28] new password
3073 if(datasize != 2+PASSWORD_SIZE*2)
3075 /*char password[PASSWORD_SIZE];
3076 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3077 password[i] = data[2+i];
3078 password[PASSWORD_SIZE-1] = 0;*/
3080 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3088 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3090 char c = data[2+PASSWORD_SIZE+i];
3096 std::string playername = player->getName();
3098 if(m_authmanager.exists(playername) == false)
3100 dstream<<"Server: playername not found in authmanager"<<std::endl;
3101 // Wrong old password supplied!!
3102 SendChatMessage(peer_id, L"playername not found in authmanager");
3106 std::string checkpwd = m_authmanager.getPassword(playername);
3108 if(oldpwd != checkpwd)
3110 dstream<<"Server: invalid old password"<<std::endl;
3111 // Wrong old password supplied!!
3112 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3116 m_authmanager.setPassword(playername, newpwd);
3118 dstream<<"Server: password change successful for "<<playername
3120 SendChatMessage(peer_id, L"Password change successful");
3124 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3125 "unknown command "<<command<<std::endl;
3129 catch(SendFailedException &e)
3131 derr_server<<"Server::ProcessData(): SendFailedException: "
3137 void Server::onMapEditEvent(MapEditEvent *event)
3139 dstream<<"Server::onMapEditEvent()"<<std::endl;
3140 if(m_ignore_map_edit_events)
3142 MapEditEvent *e = event->clone();
3143 m_unsent_map_edit_queue.push_back(e);
3146 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3148 if(id == "current_player")
3150 assert(c->current_player);
3151 return &(c->current_player->inventory);
3155 std::string id0 = fn.next(":");
3157 if(id0 == "nodemeta")
3160 p.X = stoi(fn.next(","));
3161 p.Y = stoi(fn.next(","));
3162 p.Z = stoi(fn.next(","));
3163 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3165 return meta->getInventory();
3166 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3167 <<"no metadata found"<<std::endl;
3171 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3174 void Server::inventoryModified(InventoryContext *c, std::string id)
3176 if(id == "current_player")
3178 assert(c->current_player);
3180 UpdateCrafting(c->current_player->peer_id);
3181 SendInventory(c->current_player->peer_id);
3186 std::string id0 = fn.next(":");
3188 if(id0 == "nodemeta")
3191 p.X = stoi(fn.next(","));
3192 p.Y = stoi(fn.next(","));
3193 p.Z = stoi(fn.next(","));
3194 v3s16 blockpos = getNodeBlockPos(p);
3196 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3198 meta->inventoryModified();
3200 for(core::map<u16, RemoteClient*>::Iterator
3201 i = m_clients.getIterator();
3202 i.atEnd()==false; i++)
3204 RemoteClient *client = i.getNode()->getValue();
3205 client->SetBlockNotSent(blockpos);
3211 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3214 core::list<PlayerInfo> Server::getPlayerInfo()
3216 DSTACK(__FUNCTION_NAME);
3217 JMutexAutoLock envlock(m_env_mutex);
3218 JMutexAutoLock conlock(m_con_mutex);
3220 core::list<PlayerInfo> list;
3222 core::list<Player*> players = m_env.getPlayers();
3224 core::list<Player*>::Iterator i;
3225 for(i = players.begin();
3226 i != players.end(); i++)
3230 Player *player = *i;
3233 con::Peer *peer = m_con.GetPeer(player->peer_id);
3234 // Copy info from peer to info struct
3236 info.address = peer->address;
3237 info.avg_rtt = peer->avg_rtt;
3239 catch(con::PeerNotFoundException &e)
3241 // Set dummy peer info
3243 info.address = Address(0,0,0,0,0);
3247 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3248 info.position = player->getPosition();
3250 list.push_back(info);
3257 void Server::peerAdded(con::Peer *peer)
3259 DSTACK(__FUNCTION_NAME);
3260 dout_server<<"Server::peerAdded(): peer->id="
3261 <<peer->id<<std::endl;
3264 c.type = PEER_ADDED;
3265 c.peer_id = peer->id;
3267 m_peer_change_queue.push_back(c);
3270 void Server::deletingPeer(con::Peer *peer, bool timeout)
3272 DSTACK(__FUNCTION_NAME);
3273 dout_server<<"Server::deletingPeer(): peer->id="
3274 <<peer->id<<", timeout="<<timeout<<std::endl;
3277 c.type = PEER_REMOVED;
3278 c.peer_id = peer->id;
3279 c.timeout = timeout;
3280 m_peer_change_queue.push_back(c);
3287 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3289 DSTACK(__FUNCTION_NAME);
3290 std::ostringstream os(std::ios_base::binary);
3292 writeU16(os, TOCLIENT_HP);
3296 std::string s = os.str();
3297 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3299 con.Send(peer_id, 0, data, true);
3302 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3303 const std::wstring &reason)
3305 DSTACK(__FUNCTION_NAME);
3306 std::ostringstream os(std::ios_base::binary);
3308 writeU16(os, TOCLIENT_ACCESS_DENIED);
3309 os<<serializeWideString(reason);
3312 std::string s = os.str();
3313 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3315 con.Send(peer_id, 0, data, true);
3319 Non-static send methods
3322 void Server::SendObjectData(float dtime)
3324 DSTACK(__FUNCTION_NAME);
3326 core::map<v3s16, bool> stepped_blocks;
3328 for(core::map<u16, RemoteClient*>::Iterator
3329 i = m_clients.getIterator();
3330 i.atEnd() == false; i++)
3332 u16 peer_id = i.getNode()->getKey();
3333 RemoteClient *client = i.getNode()->getValue();
3334 assert(client->peer_id == peer_id);
3336 if(client->serialization_version == SER_FMT_VER_INVALID)
3339 client->SendObjectData(this, dtime, stepped_blocks);
3343 void Server::SendPlayerInfos()
3345 DSTACK(__FUNCTION_NAME);
3347 //JMutexAutoLock envlock(m_env_mutex);
3349 // Get connected players
3350 core::list<Player*> players = m_env.getPlayers(true);
3352 u32 player_count = players.getSize();
3353 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3355 SharedBuffer<u8> data(datasize);
3356 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3359 core::list<Player*>::Iterator i;
3360 for(i = players.begin();
3361 i != players.end(); i++)
3363 Player *player = *i;
3365 /*dstream<<"Server sending player info for player with "
3366 "peer_id="<<player->peer_id<<std::endl;*/
3368 writeU16(&data[start], player->peer_id);
3369 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3370 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3371 start += 2+PLAYERNAME_SIZE;
3374 //JMutexAutoLock conlock(m_con_mutex);
3377 m_con.SendToAll(0, data, true);
3380 void Server::SendInventory(u16 peer_id)
3382 DSTACK(__FUNCTION_NAME);
3384 Player* player = m_env.getPlayer(peer_id);
3391 std::ostringstream os;
3392 //os.imbue(std::locale("C"));
3394 player->inventory.serialize(os);
3396 std::string s = os.str();
3398 SharedBuffer<u8> data(s.size()+2);
3399 writeU16(&data[0], TOCLIENT_INVENTORY);
3400 memcpy(&data[2], s.c_str(), s.size());
3403 m_con.Send(peer_id, 0, data, true);
3406 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3408 DSTACK(__FUNCTION_NAME);
3410 std::ostringstream os(std::ios_base::binary);
3414 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3415 os.write((char*)buf, 2);
3418 writeU16(buf, message.size());
3419 os.write((char*)buf, 2);
3422 for(u32 i=0; i<message.size(); i++)
3426 os.write((char*)buf, 2);
3430 std::string s = os.str();
3431 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3433 m_con.Send(peer_id, 0, data, true);
3436 void Server::BroadcastChatMessage(const std::wstring &message)
3438 for(core::map<u16, RemoteClient*>::Iterator
3439 i = m_clients.getIterator();
3440 i.atEnd() == false; i++)
3442 // Get client and check that it is valid
3443 RemoteClient *client = i.getNode()->getValue();
3444 assert(client->peer_id == i.getNode()->getKey());
3445 if(client->serialization_version == SER_FMT_VER_INVALID)
3448 SendChatMessage(client->peer_id, message);
3452 void Server::SendPlayerHP(Player *player)
3454 SendHP(m_con, player->peer_id, player->hp);
3457 void Server::SendMovePlayer(Player *player)
3459 DSTACK(__FUNCTION_NAME);
3460 std::ostringstream os(std::ios_base::binary);
3462 writeU16(os, TOCLIENT_MOVE_PLAYER);
3463 writeV3F1000(os, player->getPosition());
3464 writeF1000(os, player->getPitch());
3465 writeF1000(os, player->getYaw());
3468 v3f pos = player->getPosition();
3469 f32 pitch = player->getPitch();
3470 f32 yaw = player->getYaw();
3471 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3472 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3479 std::string s = os.str();
3480 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3482 m_con.Send(player->peer_id, 0, data, true);
3485 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3486 core::list<u16> *far_players, float far_d_nodes)
3488 float maxd = far_d_nodes*BS;
3489 v3f p_f = intToFloat(p, BS);
3493 SharedBuffer<u8> reply(replysize);
3494 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3495 writeS16(&reply[2], p.X);
3496 writeS16(&reply[4], p.Y);
3497 writeS16(&reply[6], p.Z);
3499 for(core::map<u16, RemoteClient*>::Iterator
3500 i = m_clients.getIterator();
3501 i.atEnd() == false; i++)
3503 // Get client and check that it is valid
3504 RemoteClient *client = i.getNode()->getValue();
3505 assert(client->peer_id == i.getNode()->getKey());
3506 if(client->serialization_version == SER_FMT_VER_INVALID)
3509 // Don't send if it's the same one
3510 if(client->peer_id == ignore_id)
3516 Player *player = m_env.getPlayer(client->peer_id);
3519 // If player is far away, only set modified blocks not sent
3520 v3f player_pos = player->getPosition();
3521 if(player_pos.getDistanceFrom(p_f) > maxd)
3523 far_players->push_back(client->peer_id);
3530 m_con.Send(client->peer_id, 0, reply, true);
3534 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3535 core::list<u16> *far_players, float far_d_nodes)
3537 float maxd = far_d_nodes*BS;
3538 v3f p_f = intToFloat(p, BS);
3540 for(core::map<u16, RemoteClient*>::Iterator
3541 i = m_clients.getIterator();
3542 i.atEnd() == false; i++)
3544 // Get client and check that it is valid
3545 RemoteClient *client = i.getNode()->getValue();
3546 assert(client->peer_id == i.getNode()->getKey());
3547 if(client->serialization_version == SER_FMT_VER_INVALID)
3550 // Don't send if it's the same one
3551 if(client->peer_id == ignore_id)
3557 Player *player = m_env.getPlayer(client->peer_id);
3560 // If player is far away, only set modified blocks not sent
3561 v3f player_pos = player->getPosition();
3562 if(player_pos.getDistanceFrom(p_f) > maxd)
3564 far_players->push_back(client->peer_id);
3571 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3572 SharedBuffer<u8> reply(replysize);
3573 writeU16(&reply[0], TOCLIENT_ADDNODE);
3574 writeS16(&reply[2], p.X);
3575 writeS16(&reply[4], p.Y);
3576 writeS16(&reply[6], p.Z);
3577 n.serialize(&reply[8], client->serialization_version);
3580 m_con.Send(client->peer_id, 0, reply, true);
3584 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3586 DSTACK(__FUNCTION_NAME);
3588 v3s16 p = block->getPos();
3592 bool completely_air = true;
3593 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3594 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3595 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3597 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3599 completely_air = false;
3600 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3605 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3607 dstream<<"[completely air] ";
3612 Create a packet with the block in the right format
3615 std::ostringstream os(std::ios_base::binary);
3616 block->serialize(os, ver);
3617 std::string s = os.str();
3618 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3620 u32 replysize = 8 + blockdata.getSize();
3621 SharedBuffer<u8> reply(replysize);
3622 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3623 writeS16(&reply[2], p.X);
3624 writeS16(&reply[4], p.Y);
3625 writeS16(&reply[6], p.Z);
3626 memcpy(&reply[8], *blockdata, blockdata.getSize());
3628 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3629 <<": \tpacket size: "<<replysize<<std::endl;*/
3634 m_con.Send(peer_id, 1, reply, true);
3637 void Server::SendBlocks(float dtime)
3639 DSTACK(__FUNCTION_NAME);
3641 JMutexAutoLock envlock(m_env_mutex);
3642 JMutexAutoLock conlock(m_con_mutex);
3644 //TimeTaker timer("Server::SendBlocks");
3646 core::array<PrioritySortedBlockTransfer> queue;
3648 s32 total_sending = 0;
3650 for(core::map<u16, RemoteClient*>::Iterator
3651 i = m_clients.getIterator();
3652 i.atEnd() == false; i++)
3654 RemoteClient *client = i.getNode()->getValue();
3655 assert(client->peer_id == i.getNode()->getKey());
3657 total_sending += client->SendingCount();
3659 if(client->serialization_version == SER_FMT_VER_INVALID)
3662 client->GetNextBlocks(this, dtime, queue);
3666 // Lowest priority number comes first.
3667 // Lowest is most important.
3670 for(u32 i=0; i<queue.size(); i++)
3672 //TODO: Calculate limit dynamically
3673 if(total_sending >= g_settings.getS32
3674 ("max_simultaneous_block_sends_server_total"))
3677 PrioritySortedBlockTransfer q = queue[i];
3679 MapBlock *block = NULL;
3682 block = m_env.getMap().getBlockNoCreate(q.pos);
3684 catch(InvalidPositionException &e)
3689 RemoteClient *client = getClient(q.peer_id);
3691 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3693 client->SentBlock(q.pos);
3703 void Server::UpdateCrafting(u16 peer_id)
3705 DSTACK(__FUNCTION_NAME);
3707 Player* player = m_env.getPlayer(peer_id);
3711 Calculate crafting stuff
3713 if(g_settings.getBool("creative_mode") == false)
3715 InventoryList *clist = player->inventory.getList("craft");
3716 InventoryList *rlist = player->inventory.getList("craftresult");
3718 if(rlist->getUsedSlots() == 0)
3719 player->craftresult_is_preview = true;
3721 if(rlist && player->craftresult_is_preview)
3723 rlist->clearItems();
3725 if(clist && rlist && player->craftresult_is_preview)
3727 InventoryItem *items[9];
3728 for(u16 i=0; i<9; i++)
3730 items[i] = clist->getItem(i);
3739 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3740 if(checkItemCombination(items, specs))
3742 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3751 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3752 if(checkItemCombination(items, specs))
3754 rlist->addItem(new CraftItem("Stick", 4));
3763 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3764 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3765 specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
3766 specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
3767 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3768 specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
3769 if(checkItemCombination(items, specs))
3771 rlist->addItem(new MaterialItem(CONTENT_FENCE, 2));
3780 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3781 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3782 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3783 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3784 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3785 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3786 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3787 if(checkItemCombination(items, specs))
3789 //rlist->addItem(new MapBlockObjectItem("Sign"));
3790 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3799 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3800 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3801 if(checkItemCombination(items, specs))
3803 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3812 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3813 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3814 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3815 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3816 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3817 if(checkItemCombination(items, specs))
3819 rlist->addItem(new ToolItem("WPick", 0));
3828 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3829 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3830 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3831 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3832 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3833 if(checkItemCombination(items, specs))
3835 rlist->addItem(new ToolItem("STPick", 0));
3844 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3845 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3846 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3847 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3848 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3849 if(checkItemCombination(items, specs))
3851 rlist->addItem(new ToolItem("SteelPick", 0));
3860 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3861 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3862 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3863 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3864 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3865 if(checkItemCombination(items, specs))
3867 rlist->addItem(new ToolItem("MesePick", 0));
3876 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3877 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3878 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3879 if(checkItemCombination(items, specs))
3881 rlist->addItem(new ToolItem("WShovel", 0));
3890 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3891 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3892 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3893 if(checkItemCombination(items, specs))
3895 rlist->addItem(new ToolItem("STShovel", 0));
3904 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3905 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3906 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3907 if(checkItemCombination(items, specs))
3909 rlist->addItem(new ToolItem("SteelShovel", 0));
3918 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3919 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3920 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3921 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3922 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3923 if(checkItemCombination(items, specs))
3925 rlist->addItem(new ToolItem("WAxe", 0));
3934 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3935 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3936 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3937 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3938 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3939 if(checkItemCombination(items, specs))
3941 rlist->addItem(new ToolItem("STAxe", 0));
3950 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3951 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3952 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3953 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3954 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3955 if(checkItemCombination(items, specs))
3957 rlist->addItem(new ToolItem("SteelAxe", 0));
3966 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3967 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3968 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3969 if(checkItemCombination(items, specs))
3971 rlist->addItem(new ToolItem("WSword", 0));
3980 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3981 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3982 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3983 if(checkItemCombination(items, specs))
3985 rlist->addItem(new ToolItem("STSword", 0));
3994 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3995 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3996 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3997 if(checkItemCombination(items, specs))
3999 rlist->addItem(new ToolItem("SteelSword", 0));
4008 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4009 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4010 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4011 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4012 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4013 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4014 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4015 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4016 if(checkItemCombination(items, specs))
4018 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
4027 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4028 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4029 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4030 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4031 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4032 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4033 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4034 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4035 if(checkItemCombination(items, specs))
4037 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
4046 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4047 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4048 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4049 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4050 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4051 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4052 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4053 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4054 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4055 if(checkItemCombination(items, specs))
4057 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
4063 } // if creative_mode == false
4066 RemoteClient* Server::getClient(u16 peer_id)
4068 DSTACK(__FUNCTION_NAME);
4069 //JMutexAutoLock lock(m_con_mutex);
4070 core::map<u16, RemoteClient*>::Node *n;
4071 n = m_clients.find(peer_id);
4072 // A client should exist for all peers
4074 return n->getValue();
4077 std::wstring Server::getStatusString()
4079 std::wostringstream os(std::ios_base::binary);
4082 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4084 os<<L", uptime="<<m_uptime.get();
4085 // Information about clients
4087 for(core::map<u16, RemoteClient*>::Iterator
4088 i = m_clients.getIterator();
4089 i.atEnd() == false; i++)
4091 // Get client and check that it is valid
4092 RemoteClient *client = i.getNode()->getValue();
4093 assert(client->peer_id == i.getNode()->getKey());
4094 if(client->serialization_version == SER_FMT_VER_INVALID)
4097 Player *player = m_env.getPlayer(client->peer_id);
4098 // Get name of player
4099 std::wstring name = L"unknown";
4101 name = narrow_to_wide(player->getName());
4102 // Add name to information string
4106 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4107 os<<" WARNING: Map saving is disabled."<<std::endl;
4112 void setCreativeInventory(Player *player)
4114 player->resetInventory();
4116 // Give some good tools
4118 InventoryItem *item = new ToolItem("MesePick", 0);
4119 void* r = player->inventory.addItem("main", item);
4123 InventoryItem *item = new ToolItem("SteelPick", 0);
4124 void* r = player->inventory.addItem("main", item);
4128 InventoryItem *item = new ToolItem("SteelAxe", 0);
4129 void* r = player->inventory.addItem("main", item);
4133 InventoryItem *item = new ToolItem("SteelShovel", 0);
4134 void* r = player->inventory.addItem("main", item);
4142 // CONTENT_IGNORE-terminated list
4143 u8 material_items[] = {
4154 CONTENT_WATERSOURCE,
4162 u8 *mip = material_items;
4163 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4165 if(*mip == CONTENT_IGNORE)
4168 InventoryItem *item = new MaterialItem(*mip, 1);
4169 player->inventory.addItem("main", item);
4175 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4178 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4179 player->inventory.addItem("main", item);
4182 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4184 // Skip some materials
4185 if(i == CONTENT_WATER || i == CONTENT_TORCH
4186 || i == CONTENT_COALSTONE)
4189 InventoryItem *item = new MaterialItem(i, 1);
4190 player->inventory.addItem("main", item);
4196 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4197 void* r = player->inventory.addItem("main", item);
4202 v3f findSpawnPos(ServerMap &map)
4204 //return v3f(50,50,50)*BS;
4207 s16 groundheight = 0;
4209 // Try to find a good place a few times
4210 for(s32 i=0; i<1000; i++)
4213 // We're going to try to throw the player to this position
4214 nodepos = v2s16(-range + (myrand()%(range*2)),
4215 -range + (myrand()%(range*2)));
4216 v2s16 sectorpos = getNodeSectorPos(nodepos);
4217 // Get sector (NOTE: Don't get because it's slow)
4218 //m_env.getMap().emergeSector(sectorpos);
4219 // Get ground height at point (fallbacks to heightmap function)
4220 groundheight = map.findGroundLevel(nodepos);
4221 // Don't go underwater
4222 if(groundheight < WATER_LEVEL)
4224 //dstream<<"-> Underwater"<<std::endl;
4227 // Don't go to high places
4228 if(groundheight > WATER_LEVEL + 4)
4230 //dstream<<"-> Underwater"<<std::endl;
4234 // Found a good place
4235 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4239 // If no suitable place was not found, go above water at least.
4240 if(groundheight < WATER_LEVEL)
4241 groundheight = WATER_LEVEL;
4243 return intToFloat(v3s16(
4250 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4253 Try to get an existing player
4255 Player *player = m_env.getPlayer(name);
4258 // If player is already connected, cancel
4259 if(player->peer_id != 0)
4261 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4266 player->peer_id = peer_id;
4268 // Reset inventory to creative if in creative mode
4269 if(g_settings.getBool("creative_mode"))
4271 setCreativeInventory(player);
4278 If player with the wanted peer_id already exists, cancel.
4280 if(m_env.getPlayer(peer_id) != NULL)
4282 dstream<<"emergePlayer(): Player with wrong name but same"
4283 " peer_id already exists"<<std::endl;
4291 player = new ServerRemotePlayer();
4292 //player->peer_id = c.peer_id;
4293 //player->peer_id = PEER_ID_INEXISTENT;
4294 player->peer_id = peer_id;
4295 player->updateName(name);
4296 m_authmanager.add(name);
4297 m_authmanager.setPassword(name, password);
4298 m_authmanager.setPrivs(name,
4299 stringToPrivs(g_settings.get("default_privs")));
4305 dstream<<"Server: Finding spawn place for player \""
4306 <<player->getName()<<"\""<<std::endl;
4308 v3f pos = findSpawnPos(m_env.getServerMap());
4310 player->setPosition(pos);
4313 Add player to environment
4316 m_env.addPlayer(player);
4319 Add stuff to inventory
4322 if(g_settings.getBool("creative_mode"))
4324 setCreativeInventory(player);
4326 else if(g_settings.getBool("give_initial_stuff"))
4329 InventoryItem *item = new ToolItem("SteelPick", 0);
4330 void* r = player->inventory.addItem("main", item);
4334 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4335 void* r = player->inventory.addItem("main", item);
4339 InventoryItem *item = new ToolItem("SteelAxe", 0);
4340 void* r = player->inventory.addItem("main", item);
4344 InventoryItem *item = new ToolItem("SteelShovel", 0);
4345 void* r = player->inventory.addItem("main", item);
4349 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4350 void* r = player->inventory.addItem("main", item);
4354 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4355 void* r = player->inventory.addItem("main", item);
4359 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4360 void* r = player->inventory.addItem("main", item);
4364 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4365 void* r = player->inventory.addItem("main", item);
4369 InventoryItem *item = new CraftItem("Stick", 4);
4370 void* r = player->inventory.addItem("main", item);
4374 InventoryItem *item = new ToolItem("WPick", 32000);
4375 void* r = player->inventory.addItem("main", item);
4379 InventoryItem *item = new ToolItem("STPick", 32000);
4380 void* r = player->inventory.addItem("main", item);
4384 for(u16 i=0; i<4; i++)
4386 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4387 bool r = player->inventory.addItem("main", item);
4390 /*// Give some other stuff
4392 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4393 bool r = player->inventory.addItem("main", item);
4400 } // create new player
4403 void Server::handlePeerChange(PeerChange &c)
4405 JMutexAutoLock envlock(m_env_mutex);
4406 JMutexAutoLock conlock(m_con_mutex);
4408 if(c.type == PEER_ADDED)
4415 core::map<u16, RemoteClient*>::Node *n;
4416 n = m_clients.find(c.peer_id);
4417 // The client shouldn't already exist
4421 RemoteClient *client = new RemoteClient();
4422 client->peer_id = c.peer_id;
4423 m_clients.insert(client->peer_id, client);
4426 else if(c.type == PEER_REMOVED)
4433 core::map<u16, RemoteClient*>::Node *n;
4434 n = m_clients.find(c.peer_id);
4435 // The client should exist
4439 Mark objects to be not known by the client
4441 RemoteClient *client = n->getValue();
4443 for(core::map<u16, bool>::Iterator
4444 i = client->m_known_objects.getIterator();
4445 i.atEnd()==false; i++)
4448 u16 id = i.getNode()->getKey();
4449 ServerActiveObject* obj = m_env.getActiveObject(id);
4451 if(obj && obj->m_known_by_count > 0)
4452 obj->m_known_by_count--;
4455 // Collect information about leaving in chat
4456 std::wstring message;
4458 std::wstring name = L"unknown";
4459 Player *player = m_env.getPlayer(c.peer_id);
4461 name = narrow_to_wide(player->getName());
4465 message += L" left game";
4467 message += L" (timed out)";
4472 m_env.removePlayer(c.peer_id);
4475 // Set player client disconnected
4477 Player *player = m_env.getPlayer(c.peer_id);
4479 player->peer_id = 0;
4483 delete m_clients[c.peer_id];
4484 m_clients.remove(c.peer_id);
4486 // Send player info to all remaining clients
4489 // Send leave chat message to all remaining clients
4490 BroadcastChatMessage(message);
4499 void Server::handlePeerChanges()
4501 while(m_peer_change_queue.size() > 0)
4503 PeerChange c = m_peer_change_queue.pop_front();
4505 dout_server<<"Server: Handling peer change: "
4506 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4509 handlePeerChange(c);
4513 u64 Server::getPlayerPrivs(Player *player)
4517 std::string playername = player->getName();
4518 // Local player gets all privileges regardless of
4519 // what's set on their account.
4520 if(g_settings.get("name") == playername)
4526 return getPlayerAuthPrivs(playername);
4530 void dedicated_server_loop(Server &server, bool &kill)
4532 DSTACK(__FUNCTION_NAME);
4534 std::cout<<DTIME<<std::endl;
4535 std::cout<<"========================"<<std::endl;
4536 std::cout<<"Running dedicated server"<<std::endl;
4537 std::cout<<"========================"<<std::endl;
4538 std::cout<<std::endl;
4542 // This is kind of a hack but can be done like this
4543 // because server.step() is very light
4547 if(server.getShutdownRequested() || kill)
4549 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4553 static int counter = 0;
4559 core::list<PlayerInfo> list = server.getPlayerInfo();
4560 core::list<PlayerInfo>::Iterator i;
4561 static u32 sum_old = 0;
4562 u32 sum = PIChecksum(list);
4565 std::cout<<DTIME<<"Player info:"<<std::endl;
4566 for(i=list.begin(); i!=list.end(); i++)
4568 i->PrintLine(&std::cout);