3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
29 #include "materials.h"
32 #include "servercommand.h"
34 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
36 void * ServerThread::Thread()
40 DSTACK(__FUNCTION_NAME);
42 BEGIN_DEBUG_EXCEPTION_HANDLER
47 //TimeTaker timer("AsyncRunStep() + Receive()");
50 //TimeTaker timer("AsyncRunStep()");
51 m_server->AsyncRunStep();
54 //dout_server<<"Running m_server->Receive()"<<std::endl;
57 catch(con::NoIncomingDataException &e)
60 catch(con::PeerNotFoundException &e)
62 dout_server<<"Server: PeerNotFoundException"<<std::endl;
66 END_DEBUG_EXCEPTION_HANDLER
71 void * EmergeThread::Thread()
75 DSTACK(__FUNCTION_NAME);
79 BEGIN_DEBUG_EXCEPTION_HANDLER
82 Get block info from queue, emerge them and send them
85 After queue is empty, exit.
89 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
93 SharedPtr<QueuedBlockEmerge> q(qptr);
99 Do not generate over-limit
101 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
102 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
103 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
104 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
105 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
106 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
109 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
111 //TimeTaker timer("block emerge");
114 Try to emerge it from somewhere.
116 If it is only wanted as optional, only loading from disk
121 Check if any peer wants it as non-optional. In that case it
124 Also decrement the emerge queue count in clients.
127 bool optional = true;
130 core::map<u16, u8>::Iterator i;
131 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
133 //u16 peer_id = i.getNode()->getKey();
136 u8 flags = i.getNode()->getValue();
137 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
143 /*dstream<<"EmergeThread: p="
144 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
145 <<"optional="<<optional<<std::endl;*/
147 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
149 core::map<v3s16, MapBlock*> changed_blocks;
150 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
152 MapBlock *block = NULL;
153 bool got_block = true;
154 core::map<v3s16, MapBlock*> modified_blocks;
156 bool only_from_disk = false;
159 only_from_disk = true;
161 v2s16 chunkpos = map.sector_to_chunk(p2d);
163 bool generate_chunk = false;
164 if(only_from_disk == false)
166 JMutexAutoLock envlock(m_server->m_env_mutex);
167 if(map.chunkNonVolatile(chunkpos) == false)
168 generate_chunk = true;
175 JMutexAutoLock envlock(m_server->m_env_mutex);
176 map.initChunkMake(data, chunkpos);
182 JMutexAutoLock envlock(m_server->m_env_mutex);
183 map.finishChunkMake(data, changed_blocks);
188 Fetch block from map or generate a single block
191 JMutexAutoLock envlock(m_server->m_env_mutex);
193 // Load sector if it isn't loaded
194 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
195 map.loadSectorFull(p2d);
197 block = map.getBlockNoCreateNoEx(p);
198 if(!block || block->isDummy())
206 // Get, load or create sector
207 ServerMapSector *sector =
208 (ServerMapSector*)map.createSector(p2d);
210 block = map.generateBlock(p, block, sector, changed_blocks,
211 lighting_invalidated_blocks);
218 if(block->getLightingExpired()){
219 lighting_invalidated_blocks[block->getPos()] = block;
223 // TODO: Some additional checking and lighting updating,
228 JMutexAutoLock envlock(m_server->m_env_mutex);
233 Collect a list of blocks that have been modified in
234 addition to the fetched one.
237 if(lighting_invalidated_blocks.size() > 0)
239 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
240 <<" blocks"<<std::endl;*/
242 // 50-100ms for single block generation
243 //TimeTaker timer("** EmergeThread updateLighting");
245 // Update lighting without locking the environment mutex,
246 // add modified blocks to changed blocks
247 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
250 // Add all from changed_blocks to modified_blocks
251 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
252 i.atEnd() == false; i++)
254 MapBlock *block = i.getNode()->getValue();
255 modified_blocks.insert(block->getPos(), block);
258 // If we got no block, there should be no invalidated blocks
261 assert(lighting_invalidated_blocks.size() == 0);
267 Set sent status of modified blocks on clients
270 // NOTE: Server's clients are also behind the connection mutex
271 JMutexAutoLock lock(m_server->m_con_mutex);
274 Add the originally fetched block to the modified list
278 modified_blocks.insert(p, block);
282 Set the modified blocks unsent for all the clients
285 for(core::map<u16, RemoteClient*>::Iterator
286 i = m_server->m_clients.getIterator();
287 i.atEnd() == false; i++)
289 RemoteClient *client = i.getNode()->getValue();
291 if(modified_blocks.size() > 0)
293 // Remove block from sent history
294 client->SetBlocksNotSent(modified_blocks);
300 END_DEBUG_EXCEPTION_HANDLER
305 void RemoteClient::GetNextBlocks(Server *server, float dtime,
306 core::array<PrioritySortedBlockTransfer> &dest)
308 DSTACK(__FUNCTION_NAME);
311 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
314 m_nearest_unsent_reset_timer += dtime;
315 m_nothing_to_send_pause_timer -= dtime;
317 if(m_nothing_to_send_pause_timer >= 0)
320 // Won't send anything if already sending
321 if(m_blocks_sending.size() >= g_settings.getU16
322 ("max_simultaneous_block_sends_per_client"))
324 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
328 Player *player = server->m_env.getPlayer(peer_id);
330 assert(player != NULL);
332 v3f playerpos = player->getPosition();
333 v3f playerspeed = player->getSpeed();
335 v3s16 center_nodepos = floatToInt(playerpos, BS);
337 v3s16 center = getNodeBlockPos(center_nodepos);
339 // Camera position and direction
341 playerpos + v3f(0, BS+BS/2, 0);
342 v3f camera_dir = v3f(0,0,1);
343 camera_dir.rotateYZBy(player->getPitch());
344 camera_dir.rotateXZBy(player->getYaw());
347 Get the starting value of the block finder radius.
350 if(m_last_center != center)
352 m_nearest_unsent_d = 0;
353 m_last_center = center;
356 /*dstream<<"m_nearest_unsent_reset_timer="
357 <<m_nearest_unsent_reset_timer<<std::endl;*/
358 if(m_nearest_unsent_reset_timer > 5.0)
360 m_nearest_unsent_reset_timer = 0;
361 m_nearest_unsent_d = 0;
362 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
365 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
366 s16 d_start = m_nearest_unsent_d;
368 //dstream<<"d_start="<<d_start<<std::endl;
370 u16 max_simul_sends_setting = g_settings.getU16
371 ("max_simultaneous_block_sends_per_client");
372 u16 max_simul_sends_usually = max_simul_sends_setting;
375 Check the time from last addNode/removeNode.
377 Decrease send rate if player is building stuff.
379 m_time_from_building += dtime;
380 if(m_time_from_building < g_settings.getFloat(
381 "full_block_send_enable_min_time_from_building"))
383 max_simul_sends_usually
384 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
388 Number of blocks sending + number of blocks selected for sending
390 u32 num_blocks_selected = m_blocks_sending.size();
393 next time d will be continued from the d from which the nearest
394 unsent block was found this time.
396 This is because not necessarily any of the blocks found this
397 time are actually sent.
399 s32 new_nearest_unsent_d = -1;
401 s16 d_max = g_settings.getS16("max_block_send_distance");
402 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
404 //dstream<<"Starting from "<<d_start<<std::endl;
406 bool sending_something = false;
408 for(s16 d = d_start; d <= d_max; d++)
410 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
413 If m_nearest_unsent_d was changed by the EmergeThread
414 (it can change it to 0 through SetBlockNotSent),
416 Else update m_nearest_unsent_d
418 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
420 d = m_nearest_unsent_d;
421 last_nearest_unsent_d = m_nearest_unsent_d;
425 Get the border/face dot coordinates of a "d-radiused"
428 core::list<v3s16> list;
429 getFacePositions(list, d);
431 core::list<v3s16>::Iterator li;
432 for(li=list.begin(); li!=list.end(); li++)
434 v3s16 p = *li + center;
438 - Don't allow too many simultaneous transfers
439 - EXCEPT when the blocks are very close
441 Also, don't send blocks that are already flying.
444 // Start with the usual maximum
445 u16 max_simul_dynamic = max_simul_sends_usually;
447 // If block is very close, allow full maximum
448 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
449 max_simul_dynamic = max_simul_sends_setting;
451 // Don't select too many blocks for sending
452 if(num_blocks_selected >= max_simul_dynamic)
455 // Don't send blocks that are currently being transferred
456 if(m_blocks_sending.find(p) != NULL)
462 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
463 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
464 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
465 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
466 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
467 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
470 // If this is true, inexistent block will be made from scratch
471 bool generate = d <= d_max_gen;
474 /*// Limit the generating area vertically to 2/3
475 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
478 // Limit the send area vertically to 2/3
479 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
485 If block is far away, don't generate it unless it is
491 // Block center y in nodes
492 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
493 // Don't generate if it's very high or very low
494 if(y < -64 || y > 64)
498 v2s16 p2d_nodes_center(
502 // Get ground height in nodes
503 s16 gh = server->m_env.getServerMap().findGroundLevel(
506 // If differs a lot, don't generate
507 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
509 // Actually, don't even send it
516 Don't generate or send if not in sight
519 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
525 Don't send already sent blocks
528 if(m_blocks_sent.find(p) != NULL)
533 Check if map has this block
535 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
537 bool surely_not_found_on_disk = false;
538 bool block_is_invalid = false;
543 surely_not_found_on_disk = true;
546 if(block->isValid() == false)
548 block_is_invalid = true;
551 /*if(block->isFullyGenerated() == false)
553 block_is_invalid = true;
557 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
558 v2s16 chunkpos = map->sector_to_chunk(p2d);
559 if(map->chunkNonVolatile(chunkpos) == false)
560 block_is_invalid = true;
563 If block is not close, don't send it unless it is near
566 Block is not near ground level if night-time mesh
567 doesn't differ from day-time mesh.
571 if(block->dayNightDiffed() == false)
578 If block has been marked to not exist on disk (dummy)
579 and generating new ones is not wanted, skip block.
581 if(generate == false && surely_not_found_on_disk == true)
588 Record the lowest d from which a a block has been
589 found being not sent and possibly to exist
591 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
594 new_nearest_unsent_d = d;
598 Add inexistent block to emerge queue.
600 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
602 //TODO: Get value from somewhere
603 // Allow only one block in emerge queue
604 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
605 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
607 //dstream<<"Adding block to emerge queue"<<std::endl;
609 // Add it to the emerge queue and trigger the thread
612 if(generate == false)
613 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
615 server->m_emerge_queue.addBlock(peer_id, p, flags);
616 server->m_emergethread.trigger();
624 Add block to send queue
627 PrioritySortedBlockTransfer q((float)d, p, peer_id);
631 num_blocks_selected += 1;
632 sending_something = true;
637 if(new_nearest_unsent_d != -1)
639 m_nearest_unsent_d = new_nearest_unsent_d;
642 if(sending_something == false)
644 m_nothing_to_send_counter++;
645 if(m_nothing_to_send_counter >= 3)
647 // Pause time in seconds
648 m_nothing_to_send_pause_timer = 2.0;
653 m_nothing_to_send_counter = 0;
656 /*timer_result = timer.stop(true);
657 if(timer_result != 0)
658 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
661 void RemoteClient::SendObjectData(
664 core::map<v3s16, bool> &stepped_blocks
667 DSTACK(__FUNCTION_NAME);
669 // Can't send anything without knowing version
670 if(serialization_version == SER_FMT_VER_INVALID)
672 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
678 Send a TOCLIENT_OBJECTDATA packet.
682 u16 number of player positions
693 std::ostringstream os(std::ios_base::binary);
697 writeU16(buf, TOCLIENT_OBJECTDATA);
698 os.write((char*)buf, 2);
701 Get and write player data
704 // Get connected players
705 core::list<Player*> players = server->m_env.getPlayers(true);
707 // Write player count
708 u16 playercount = players.size();
709 writeU16(buf, playercount);
710 os.write((char*)buf, 2);
712 core::list<Player*>::Iterator i;
713 for(i = players.begin();
714 i != players.end(); i++)
718 v3f pf = player->getPosition();
719 v3f sf = player->getSpeed();
721 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
722 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
723 s32 pitch_i (player->getPitch() * 100);
724 s32 yaw_i (player->getYaw() * 100);
726 writeU16(buf, player->peer_id);
727 os.write((char*)buf, 2);
728 writeV3S32(buf, position_i);
729 os.write((char*)buf, 12);
730 writeV3S32(buf, speed_i);
731 os.write((char*)buf, 12);
732 writeS32(buf, pitch_i);
733 os.write((char*)buf, 4);
734 writeS32(buf, yaw_i);
735 os.write((char*)buf, 4);
739 Get and write object data
745 For making players to be able to build to their nearby
746 environment (building is not possible on blocks that are not
749 - Add blocks to emerge queue if they are not found
751 SUGGESTION: These could be ignored from the backside of the player
754 Player *player = server->m_env.getPlayer(peer_id);
758 v3f playerpos = player->getPosition();
759 v3f playerspeed = player->getSpeed();
761 v3s16 center_nodepos = floatToInt(playerpos, BS);
762 v3s16 center = getNodeBlockPos(center_nodepos);
764 s16 d_max = g_settings.getS16("active_object_range");
766 // Number of blocks whose objects were written to bos
769 std::ostringstream bos(std::ios_base::binary);
771 for(s16 d = 0; d <= d_max; d++)
773 core::list<v3s16> list;
774 getFacePositions(list, d);
776 core::list<v3s16>::Iterator li;
777 for(li=list.begin(); li!=list.end(); li++)
779 v3s16 p = *li + center;
782 Ignore blocks that haven't been sent to the client
785 if(m_blocks_sent.find(p) == NULL)
789 // Try stepping block and add it to a send queue
794 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
797 Step block if not in stepped_blocks and add to stepped_blocks.
799 if(stepped_blocks.find(p) == NULL)
801 block->stepObjects(dtime, true, server->getDayNightRatio());
802 stepped_blocks.insert(p, true);
803 block->setChangedFlag();
806 // Skip block if there are no objects
807 if(block->getObjectCount() == 0)
816 bos.write((char*)buf, 6);
819 block->serializeObjects(bos, serialization_version);
824 Stop collecting objects if data is already too big
826 // Sum of player and object data sizes
827 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
828 // break out if data too big
829 if(sum > MAX_OBJECTDATA_SIZE)
831 goto skip_subsequent;
835 catch(InvalidPositionException &e)
838 // Add it to the emerge queue and trigger the thread.
839 // Fetch the block only if it is on disk.
841 // Grab and increment counter
842 /*SharedPtr<JMutexAutoLock> lock
843 (m_num_blocks_in_emerge_queue.getLock());
844 m_num_blocks_in_emerge_queue.m_value++;*/
846 // Add to queue as an anonymous fetch from disk
847 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
848 server->m_emerge_queue.addBlock(0, p, flags);
849 server->m_emergethread.trigger();
857 writeU16(buf, blockcount);
858 os.write((char*)buf, 2);
860 // Write block objects
867 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
870 std::string s = os.str();
871 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
872 // Send as unreliable
873 server->m_con.Send(peer_id, 0, data, false);
876 void RemoteClient::GotBlock(v3s16 p)
878 if(m_blocks_sending.find(p) != NULL)
879 m_blocks_sending.remove(p);
882 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
883 " m_blocks_sending"<<std::endl;*/
884 m_excess_gotblocks++;
886 m_blocks_sent.insert(p, true);
889 void RemoteClient::SentBlock(v3s16 p)
891 if(m_blocks_sending.find(p) == NULL)
892 m_blocks_sending.insert(p, 0.0);
894 dstream<<"RemoteClient::SentBlock(): Sent block"
895 " already in m_blocks_sending"<<std::endl;
898 void RemoteClient::SetBlockNotSent(v3s16 p)
900 m_nearest_unsent_d = 0;
902 if(m_blocks_sending.find(p) != NULL)
903 m_blocks_sending.remove(p);
904 if(m_blocks_sent.find(p) != NULL)
905 m_blocks_sent.remove(p);
908 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
910 m_nearest_unsent_d = 0;
912 for(core::map<v3s16, MapBlock*>::Iterator
913 i = blocks.getIterator();
914 i.atEnd()==false; i++)
916 v3s16 p = i.getNode()->getKey();
918 if(m_blocks_sending.find(p) != NULL)
919 m_blocks_sending.remove(p);
920 if(m_blocks_sent.find(p) != NULL)
921 m_blocks_sent.remove(p);
929 PlayerInfo::PlayerInfo()
935 void PlayerInfo::PrintLine(std::ostream *s)
938 (*s)<<"\""<<name<<"\" ("
939 <<(position.X/10)<<","<<(position.Y/10)
940 <<","<<(position.Z/10)<<") ";
942 (*s)<<" avg_rtt="<<avg_rtt;
946 u32 PIChecksum(core::list<PlayerInfo> &l)
948 core::list<PlayerInfo>::Iterator i;
951 for(i=l.begin(); i!=l.end(); i++)
953 checksum += a * (i->id+1);
954 checksum ^= 0x435aafcd;
965 std::string mapsavedir
967 m_env(new ServerMap(mapsavedir), this),
968 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
970 m_emergethread(this),
973 m_time_of_day_send_timer(0),
975 m_mapsavedir(mapsavedir),
976 m_shutdown_requested(false),
977 m_ignore_map_edit_events(false),
978 m_ignore_map_edit_events_peer_id(0)
980 m_liquid_transform_timer = 0.0;
981 m_print_info_timer = 0.0;
982 m_objectdata_timer = 0.0;
983 m_emergethread_trigger_timer = 0.0;
984 m_savemap_timer = 0.0;
988 m_step_dtime_mutex.Init();
991 m_env.getMap().addEventReceiver(this);
994 m_env.deSerializePlayers(m_mapsavedir);
999 dstream<<"Server::~Server()"<<std::endl;
1002 Send shutdown message
1005 JMutexAutoLock conlock(m_con_mutex);
1007 std::wstring line = L"*** Server shutting down";
1010 Send the message to clients
1012 for(core::map<u16, RemoteClient*>::Iterator
1013 i = m_clients.getIterator();
1014 i.atEnd() == false; i++)
1016 // Get client and check that it is valid
1017 RemoteClient *client = i.getNode()->getValue();
1018 assert(client->peer_id == i.getNode()->getKey());
1019 if(client->serialization_version == SER_FMT_VER_INVALID)
1023 SendChatMessage(client->peer_id, line);
1025 catch(con::PeerNotFoundException &e)
1033 dstream<<"Server: Saving players"<<std::endl;
1034 m_env.serializePlayers(m_mapsavedir);
1045 JMutexAutoLock clientslock(m_con_mutex);
1047 for(core::map<u16, RemoteClient*>::Iterator
1048 i = m_clients.getIterator();
1049 i.atEnd() == false; i++)
1052 // NOTE: These are removed by env destructor
1054 u16 peer_id = i.getNode()->getKey();
1055 JMutexAutoLock envlock(m_env_mutex);
1056 m_env.removePlayer(peer_id);
1060 delete i.getNode()->getValue();
1065 void Server::start(unsigned short port)
1067 DSTACK(__FUNCTION_NAME);
1068 // Stop thread if already running
1071 // Initialize connection
1072 m_con.setTimeoutMs(30);
1076 m_thread.setRun(true);
1079 dout_server<<"Server: Started on port "<<port<<std::endl;
1084 DSTACK(__FUNCTION_NAME);
1086 // Stop threads (set run=false first so both start stopping)
1087 m_thread.setRun(false);
1088 m_emergethread.setRun(false);
1090 m_emergethread.stop();
1092 dout_server<<"Server: Threads stopped"<<std::endl;
1095 void Server::step(float dtime)
1097 DSTACK(__FUNCTION_NAME);
1102 JMutexAutoLock lock(m_step_dtime_mutex);
1103 m_step_dtime += dtime;
1107 void Server::AsyncRunStep()
1109 DSTACK(__FUNCTION_NAME);
1113 JMutexAutoLock lock1(m_step_dtime_mutex);
1114 dtime = m_step_dtime;
1117 // Send blocks to clients
1123 //dstream<<"Server steps "<<dtime<<std::endl;
1124 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1127 JMutexAutoLock lock1(m_step_dtime_mutex);
1128 m_step_dtime -= dtime;
1135 m_uptime.set(m_uptime.get() + dtime);
1139 Update m_time_of_day
1142 m_time_counter += dtime;
1143 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1144 u32 units = (u32)(m_time_counter*speed);
1145 m_time_counter -= (f32)units / speed;
1146 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1148 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1151 Send to clients at constant intervals
1154 m_time_of_day_send_timer -= dtime;
1155 if(m_time_of_day_send_timer < 0.0)
1157 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1159 //JMutexAutoLock envlock(m_env_mutex);
1160 JMutexAutoLock conlock(m_con_mutex);
1162 for(core::map<u16, RemoteClient*>::Iterator
1163 i = m_clients.getIterator();
1164 i.atEnd() == false; i++)
1166 RemoteClient *client = i.getNode()->getValue();
1167 //Player *player = m_env.getPlayer(client->peer_id);
1169 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1170 m_time_of_day.get());
1172 m_con.Send(client->peer_id, 0, data, true);
1178 // Process connection's timeouts
1179 JMutexAutoLock lock2(m_con_mutex);
1180 m_con.RunTimeouts(dtime);
1184 // This has to be called so that the client list gets synced
1185 // with the peer list of the connection
1186 handlePeerChanges();
1191 // This also runs Map's timers
1192 JMutexAutoLock lock(m_env_mutex);
1203 m_liquid_transform_timer += dtime;
1204 if(m_liquid_transform_timer >= 1.00)
1206 m_liquid_transform_timer -= 1.00;
1208 JMutexAutoLock lock(m_env_mutex);
1210 core::map<v3s16, MapBlock*> modified_blocks;
1211 m_env.getMap().transformLiquids(modified_blocks);
1216 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1217 ServerMap &map = ((ServerMap&)m_env.getMap());
1218 map.updateLighting(modified_blocks, lighting_modified_blocks);
1220 // Add blocks modified by lighting to modified_blocks
1221 for(core::map<v3s16, MapBlock*>::Iterator
1222 i = lighting_modified_blocks.getIterator();
1223 i.atEnd() == false; i++)
1225 MapBlock *block = i.getNode()->getValue();
1226 modified_blocks.insert(block->getPos(), block);
1230 Set the modified blocks unsent for all the clients
1233 JMutexAutoLock lock2(m_con_mutex);
1235 for(core::map<u16, RemoteClient*>::Iterator
1236 i = m_clients.getIterator();
1237 i.atEnd() == false; i++)
1239 RemoteClient *client = i.getNode()->getValue();
1241 if(modified_blocks.size() > 0)
1243 // Remove block from sent history
1244 client->SetBlocksNotSent(modified_blocks);
1249 // Periodically print some info
1251 float &counter = m_print_info_timer;
1257 JMutexAutoLock lock2(m_con_mutex);
1259 for(core::map<u16, RemoteClient*>::Iterator
1260 i = m_clients.getIterator();
1261 i.atEnd() == false; i++)
1263 //u16 peer_id = i.getNode()->getKey();
1264 RemoteClient *client = i.getNode()->getValue();
1265 Player *player = m_env.getPlayer(client->peer_id);
1268 std::cout<<player->getName()<<"\t";
1269 client->PrintInfo(std::cout);
1274 //if(g_settings.getBool("enable_experimental"))
1278 Check added and deleted active objects
1281 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1283 JMutexAutoLock envlock(m_env_mutex);
1284 JMutexAutoLock conlock(m_con_mutex);
1286 // Radius inside which objects are active
1289 for(core::map<u16, RemoteClient*>::Iterator
1290 i = m_clients.getIterator();
1291 i.atEnd() == false; i++)
1293 RemoteClient *client = i.getNode()->getValue();
1294 Player *player = m_env.getPlayer(client->peer_id);
1297 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1298 <<" has no associated player"<<std::endl;
1301 v3s16 pos = floatToInt(player->getPosition(), BS);
1303 core::map<u16, bool> removed_objects;
1304 core::map<u16, bool> added_objects;
1305 m_env.getRemovedActiveObjects(pos, radius,
1306 client->m_known_objects, removed_objects);
1307 m_env.getAddedActiveObjects(pos, radius,
1308 client->m_known_objects, added_objects);
1310 // Ignore if nothing happened
1311 if(removed_objects.size() == 0 && added_objects.size() == 0)
1313 //dstream<<"INFO: active objects: none changed"<<std::endl;
1317 std::string data_buffer;
1321 // Handle removed objects
1322 writeU16((u8*)buf, removed_objects.size());
1323 data_buffer.append(buf, 2);
1324 for(core::map<u16, bool>::Iterator
1325 i = removed_objects.getIterator();
1326 i.atEnd()==false; i++)
1329 u16 id = i.getNode()->getKey();
1330 ServerActiveObject* obj = m_env.getActiveObject(id);
1332 // Add to data buffer for sending
1333 writeU16((u8*)buf, i.getNode()->getKey());
1334 data_buffer.append(buf, 2);
1336 // Remove from known objects
1337 client->m_known_objects.remove(i.getNode()->getKey());
1339 if(obj && obj->m_known_by_count > 0)
1340 obj->m_known_by_count--;
1343 // Handle added objects
1344 writeU16((u8*)buf, added_objects.size());
1345 data_buffer.append(buf, 2);
1346 for(core::map<u16, bool>::Iterator
1347 i = added_objects.getIterator();
1348 i.atEnd()==false; i++)
1351 u16 id = i.getNode()->getKey();
1352 ServerActiveObject* obj = m_env.getActiveObject(id);
1355 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1357 dstream<<"WARNING: "<<__FUNCTION_NAME
1358 <<": NULL object"<<std::endl;
1360 type = obj->getType();
1362 // Add to data buffer for sending
1363 writeU16((u8*)buf, id);
1364 data_buffer.append(buf, 2);
1365 writeU8((u8*)buf, type);
1366 data_buffer.append(buf, 1);
1369 data_buffer.append(serializeLongString(
1370 obj->getClientInitializationData()));
1372 data_buffer.append(serializeLongString(""));
1374 // Add to known objects
1375 client->m_known_objects.insert(i.getNode()->getKey(), false);
1378 obj->m_known_by_count++;
1382 SharedBuffer<u8> reply(2 + data_buffer.size());
1383 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1384 memcpy((char*)&reply[2], data_buffer.c_str(),
1385 data_buffer.size());
1387 m_con.Send(client->peer_id, 0, reply, true);
1389 dstream<<"INFO: Server: Sent object remove/add: "
1390 <<removed_objects.size()<<" removed, "
1391 <<added_objects.size()<<" added, "
1392 <<"packet size is "<<reply.getSize()<<std::endl;
1397 Collect a list of all the objects known by the clients
1398 and report it back to the environment.
1401 core::map<u16, bool> all_known_objects;
1403 for(core::map<u16, RemoteClient*>::Iterator
1404 i = m_clients.getIterator();
1405 i.atEnd() == false; i++)
1407 RemoteClient *client = i.getNode()->getValue();
1408 // Go through all known objects of client
1409 for(core::map<u16, bool>::Iterator
1410 i = client->m_known_objects.getIterator();
1411 i.atEnd()==false; i++)
1413 u16 id = i.getNode()->getKey();
1414 all_known_objects[id] = true;
1418 m_env.setKnownActiveObjects(whatever);
1424 Send object messages
1427 JMutexAutoLock envlock(m_env_mutex);
1428 JMutexAutoLock conlock(m_con_mutex);
1431 // Value = data sent by object
1432 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1434 // Get active object messages from environment
1437 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1441 core::list<ActiveObjectMessage>* message_list = NULL;
1442 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1443 n = buffered_messages.find(aom.id);
1446 message_list = new core::list<ActiveObjectMessage>;
1447 buffered_messages.insert(aom.id, message_list);
1451 message_list = n->getValue();
1453 message_list->push_back(aom);
1456 // Route data to every client
1457 for(core::map<u16, RemoteClient*>::Iterator
1458 i = m_clients.getIterator();
1459 i.atEnd()==false; i++)
1461 RemoteClient *client = i.getNode()->getValue();
1462 std::string reliable_data;
1463 std::string unreliable_data;
1464 // Go through all objects in message buffer
1465 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1466 j = buffered_messages.getIterator();
1467 j.atEnd()==false; j++)
1469 // If object is not known by client, skip it
1470 u16 id = j.getNode()->getKey();
1471 if(client->m_known_objects.find(id) == NULL)
1473 // Get message list of object
1474 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1475 // Go through every message
1476 for(core::list<ActiveObjectMessage>::Iterator
1477 k = list->begin(); k != list->end(); k++)
1479 // Compose the full new data with header
1480 ActiveObjectMessage aom = *k;
1481 std::string new_data;
1484 writeU16((u8*)&buf[0], aom.id);
1485 new_data.append(buf, 2);
1487 new_data += serializeString(aom.datastring);
1488 // Add data to buffer
1490 reliable_data += new_data;
1492 unreliable_data += new_data;
1496 reliable_data and unreliable_data are now ready.
1499 if(reliable_data.size() > 0)
1501 SharedBuffer<u8> reply(2 + reliable_data.size());
1502 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1503 memcpy((char*)&reply[2], reliable_data.c_str(),
1504 reliable_data.size());
1506 m_con.Send(client->peer_id, 0, reply, true);
1508 if(unreliable_data.size() > 0)
1510 SharedBuffer<u8> reply(2 + unreliable_data.size());
1511 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1512 memcpy((char*)&reply[2], unreliable_data.c_str(),
1513 unreliable_data.size());
1514 // Send as unreliable
1515 m_con.Send(client->peer_id, 0, reply, false);
1518 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1520 dstream<<"INFO: Server: Size of object message data: "
1521 <<"reliable: "<<reliable_data.size()
1522 <<", unreliable: "<<unreliable_data.size()
1527 // Clear buffered_messages
1528 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1529 i = buffered_messages.getIterator();
1530 i.atEnd()==false; i++)
1532 delete i.getNode()->getValue();
1536 } // enable_experimental
1539 Send queued-for-sending map edit events.
1542 while(m_unsent_map_edit_queue.size() != 0)
1544 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1546 if(event->type == MEET_ADDNODE)
1548 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1549 sendAddNode(event->p, event->n, event->already_known_by_peer);
1551 else if(event->type == MEET_REMOVENODE)
1553 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1554 sendRemoveNode(event->p, event->already_known_by_peer);
1556 else if(event->type == MEET_OTHER)
1558 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1563 dstream<<"WARNING: Server: Unknown MapEditEvent "
1564 <<((u32)event->type)<<std::endl;
1572 Send object positions
1573 TODO: Get rid of MapBlockObjects
1576 float &counter = m_objectdata_timer;
1578 if(counter >= g_settings.getFloat("objectdata_interval"))
1580 JMutexAutoLock lock1(m_env_mutex);
1581 JMutexAutoLock lock2(m_con_mutex);
1582 SendObjectData(counter);
1592 //TimeTaker timer("Step node metadata");
1594 JMutexAutoLock envlock(m_env_mutex);
1595 JMutexAutoLock conlock(m_con_mutex);
1597 core::map<v3s16, MapBlock*> changed_blocks;
1598 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1600 for(core::map<v3s16, MapBlock*>::Iterator
1601 i = changed_blocks.getIterator();
1602 i.atEnd() == false; i++)
1604 MapBlock *block = i.getNode()->getValue();
1606 for(core::map<u16, RemoteClient*>::Iterator
1607 i = m_clients.getIterator();
1608 i.atEnd()==false; i++)
1610 RemoteClient *client = i.getNode()->getValue();
1611 client->SetBlockNotSent(block->getPos());
1617 Trigger emergethread (it somehow gets to a non-triggered but
1618 bysy state sometimes)
1621 float &counter = m_emergethread_trigger_timer;
1627 m_emergethread.trigger();
1633 float &counter = m_savemap_timer;
1635 if(counter >= g_settings.getFloat("server_map_save_interval"))
1639 JMutexAutoLock lock(m_env_mutex);
1641 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1643 // Save only changed parts
1644 m_env.getMap().save(true);
1646 // Delete unused sectors
1647 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1648 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1649 if(deleted_count > 0)
1651 dout_server<<"Server: Unloaded "<<deleted_count
1652 <<" sectors from memory"<<std::endl;
1656 m_env.serializePlayers(m_mapsavedir);
1662 void Server::Receive()
1664 DSTACK(__FUNCTION_NAME);
1665 u32 data_maxsize = 10000;
1666 Buffer<u8> data(data_maxsize);
1671 JMutexAutoLock conlock(m_con_mutex);
1672 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1675 // This has to be called so that the client list gets synced
1676 // with the peer list of the connection
1677 handlePeerChanges();
1679 ProcessData(*data, datasize, peer_id);
1681 catch(con::InvalidIncomingDataException &e)
1683 derr_server<<"Server::Receive(): "
1684 "InvalidIncomingDataException: what()="
1685 <<e.what()<<std::endl;
1687 catch(con::PeerNotFoundException &e)
1689 //NOTE: This is not needed anymore
1691 // The peer has been disconnected.
1692 // Find the associated player and remove it.
1694 /*JMutexAutoLock envlock(m_env_mutex);
1696 dout_server<<"ServerThread: peer_id="<<peer_id
1697 <<" has apparently closed connection. "
1698 <<"Removing player."<<std::endl;
1700 m_env.removePlayer(peer_id);*/
1704 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1706 DSTACK(__FUNCTION_NAME);
1707 // Environment is locked first.
1708 JMutexAutoLock envlock(m_env_mutex);
1709 JMutexAutoLock conlock(m_con_mutex);
1713 peer = m_con.GetPeer(peer_id);
1715 catch(con::PeerNotFoundException &e)
1717 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1718 <<peer_id<<" not found"<<std::endl;
1722 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1730 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1732 if(command == TOSERVER_INIT)
1734 // [0] u16 TOSERVER_INIT
1735 // [2] u8 SER_FMT_VER_HIGHEST
1736 // [3] u8[20] player_name
1737 // [23] u8[28] password <--- can be sent without this, from old versions
1739 if(datasize < 2+1+PLAYERNAME_SIZE)
1742 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1743 <<peer->id<<std::endl;
1745 // First byte after command is maximum supported
1746 // serialization version
1747 u8 client_max = data[2];
1748 u8 our_max = SER_FMT_VER_HIGHEST;
1749 // Use the highest version supported by both
1750 u8 deployed = core::min_(client_max, our_max);
1751 // If it's lower than the lowest supported, give up.
1752 if(deployed < SER_FMT_VER_LOWEST)
1753 deployed = SER_FMT_VER_INVALID;
1755 //peer->serialization_version = deployed;
1756 getClient(peer->id)->pending_serialization_version = deployed;
1758 if(deployed == SER_FMT_VER_INVALID)
1760 derr_server<<DTIME<<"Server: Cannot negotiate "
1761 "serialization version with peer "
1762 <<peer_id<<std::endl;
1771 char playername[PLAYERNAME_SIZE];
1772 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1774 playername[i] = data[3+i];
1776 playername[PLAYERNAME_SIZE-1] = 0;
1779 char password[PASSWORD_SIZE];
1780 if(datasize == 2+1+PLAYERNAME_SIZE)
1782 // old version - assume blank password
1787 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1789 password[i] = data[23+i];
1791 password[PASSWORD_SIZE-1] = 0;
1793 Player *checkplayer = m_env.getPlayer(playername);
1794 if(checkplayer != NULL && strcmp(checkplayer->getPassword(),password))
1796 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1797 <<": supplied invalid password for "
1798 <<playername<<std::endl;
1799 SendAccessDenied(m_con, peer_id);
1804 Player *player = emergePlayer(playername, password, peer_id);
1808 // DEBUG: Test serialization
1809 std::ostringstream test_os;
1810 player->serialize(test_os);
1811 dstream<<"Player serialization test: \""<<test_os.str()
1813 std::istringstream test_is(test_os.str());
1814 player->deSerialize(test_is);
1817 // If failed, cancel
1820 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1821 <<": failed to emerge player"<<std::endl;
1826 // If a client is already connected to the player, cancel
1827 if(player->peer_id != 0)
1829 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1830 <<" tried to connect to "
1831 "an already connected player (peer_id="
1832 <<player->peer_id<<")"<<std::endl;
1835 // Set client of player
1836 player->peer_id = peer_id;
1839 // Check if player doesn't exist
1841 throw con::InvalidIncomingDataException
1842 ("Server::ProcessData(): INIT: Player doesn't exist");
1844 /*// update name if it was supplied
1845 if(datasize >= 20+3)
1848 player->updateName((const char*)&data[3]);
1852 Answer with a TOCLIENT_INIT
1855 SharedBuffer<u8> reply(2+1+6+8);
1856 writeU16(&reply[0], TOCLIENT_INIT);
1857 writeU8(&reply[2], deployed);
1858 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1859 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1860 writeU64(&reply[2+1+6], 0); // no seed
1863 m_con.Send(peer_id, 0, reply, true);
1867 Send complete position information
1869 SendMovePlayer(player);
1874 if(command == TOSERVER_INIT2)
1876 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1877 <<peer->id<<std::endl;
1880 getClient(peer->id)->serialization_version
1881 = getClient(peer->id)->pending_serialization_version;
1884 Send some initialization data
1887 // Send player info to all players
1890 // Send inventory to player
1891 UpdateCrafting(peer->id);
1892 SendInventory(peer->id);
1896 Player *player = m_env.getPlayer(peer_id);
1897 SendPlayerHP(player);
1902 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1903 m_time_of_day.get());
1904 m_con.Send(peer->id, 0, data, true);
1907 // Send information about server to player in chat
1908 SendChatMessage(peer_id, getStatusString());
1910 // Send information about joining in chat
1912 std::wstring name = L"unknown";
1913 Player *player = m_env.getPlayer(peer_id);
1915 name = narrow_to_wide(player->getName());
1917 std::wstring message;
1920 message += L" joined game";
1921 BroadcastChatMessage(message);
1927 if(peer_ser_ver == SER_FMT_VER_INVALID)
1929 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1930 " serialization format invalid or not initialized."
1931 " Skipping incoming command="<<command<<std::endl;
1935 Player *player = m_env.getPlayer(peer_id);
1938 derr_server<<"Server::ProcessData(): Cancelling: "
1939 "No player for peer_id="<<peer_id
1943 if(command == TOSERVER_PLAYERPOS)
1945 if(datasize < 2+12+12+4+4)
1949 v3s32 ps = readV3S32(&data[start+2]);
1950 v3s32 ss = readV3S32(&data[start+2+12]);
1951 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1952 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1953 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1954 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1955 pitch = wrapDegrees(pitch);
1956 yaw = wrapDegrees(yaw);
1957 player->setPosition(position);
1958 player->setSpeed(speed);
1959 player->setPitch(pitch);
1960 player->setYaw(yaw);
1962 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1963 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1964 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1966 else if(command == TOSERVER_GOTBLOCKS)
1979 u16 count = data[2];
1980 for(u16 i=0; i<count; i++)
1982 if((s16)datasize < 2+1+(i+1)*6)
1983 throw con::InvalidIncomingDataException
1984 ("GOTBLOCKS length is too short");
1985 v3s16 p = readV3S16(&data[2+1+i*6]);
1986 /*dstream<<"Server: GOTBLOCKS ("
1987 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1988 RemoteClient *client = getClient(peer_id);
1989 client->GotBlock(p);
1992 else if(command == TOSERVER_DELETEDBLOCKS)
2005 u16 count = data[2];
2006 for(u16 i=0; i<count; i++)
2008 if((s16)datasize < 2+1+(i+1)*6)
2009 throw con::InvalidIncomingDataException
2010 ("DELETEDBLOCKS length is too short");
2011 v3s16 p = readV3S16(&data[2+1+i*6]);
2012 /*dstream<<"Server: DELETEDBLOCKS ("
2013 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2014 RemoteClient *client = getClient(peer_id);
2015 client->SetBlockNotSent(p);
2018 else if(command == TOSERVER_CLICK_OBJECT)
2023 if((player->privs & PRIV_BUILD) == 0)
2028 [2] u8 button (0=left, 1=right)
2033 u8 button = readU8(&data[2]);
2035 p.X = readS16(&data[3]);
2036 p.Y = readS16(&data[5]);
2037 p.Z = readS16(&data[7]);
2038 s16 id = readS16(&data[9]);
2039 //u16 item_i = readU16(&data[11]);
2041 MapBlock *block = NULL;
2044 block = m_env.getMap().getBlockNoCreate(p);
2046 catch(InvalidPositionException &e)
2048 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2052 MapBlockObject *obj = block->getObject(id);
2056 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2060 //TODO: Check that object is reasonably close
2065 InventoryList *ilist = player->inventory.getList("main");
2066 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2069 // Skip if inventory has no free space
2070 if(ilist->getUsedSlots() == ilist->getSize())
2072 dout_server<<"Player inventory has no free space"<<std::endl;
2077 Create the inventory item
2079 InventoryItem *item = NULL;
2080 // If it is an item-object, take the item from it
2081 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2083 item = ((ItemObject*)obj)->createInventoryItem();
2085 // Else create an item of the object
2088 item = new MapBlockObjectItem
2089 (obj->getInventoryString());
2092 // Add to inventory and send inventory
2093 ilist->addItem(item);
2094 UpdateCrafting(player->peer_id);
2095 SendInventory(player->peer_id);
2098 // Remove from block
2099 block->removeObject(id);
2102 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2107 if((player->privs & PRIV_BUILD) == 0)
2113 [2] u8 button (0=left, 1=right)
2117 u8 button = readU8(&data[2]);
2118 u16 id = readS16(&data[3]);
2119 u16 item_i = readU16(&data[11]);
2121 ServerActiveObject *obj = m_env.getActiveObject(id);
2125 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2130 //TODO: Check that object is reasonably close
2132 // Left click, pick object up (usually)
2135 InventoryList *ilist = player->inventory.getList("main");
2136 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2139 // Skip if inventory has no free space
2140 if(ilist->getUsedSlots() == ilist->getSize())
2142 dout_server<<"Player inventory has no free space"<<std::endl;
2146 // Skip if object has been removed
2151 Create the inventory item
2153 InventoryItem *item = obj->createPickedUpItem();
2157 // Add to inventory and send inventory
2158 ilist->addItem(item);
2159 UpdateCrafting(player->peer_id);
2160 SendInventory(player->peer_id);
2162 // Remove object from environment
2163 obj->m_removed = true;
2168 Item cannot be picked up. Punch it instead.
2171 ToolItem *titem = NULL;
2172 std::string toolname = "";
2174 InventoryList *mlist = player->inventory.getList("main");
2177 InventoryItem *item = mlist->getItem(item_i);
2178 if(item && (std::string)item->getName() == "ToolItem")
2180 titem = (ToolItem*)item;
2181 toolname = titem->getToolName();
2185 u16 wear = obj->punch(toolname);
2189 bool weared_out = titem->addWear(wear);
2191 mlist->deleteItem(item_i);
2192 SendInventory(player->peer_id);
2198 else if(command == TOSERVER_GROUND_ACTION)
2206 [3] v3s16 nodepos_undersurface
2207 [9] v3s16 nodepos_abovesurface
2212 2: stop digging (all parameters ignored)
2213 3: digging completed
2215 u8 action = readU8(&data[2]);
2217 p_under.X = readS16(&data[3]);
2218 p_under.Y = readS16(&data[5]);
2219 p_under.Z = readS16(&data[7]);
2221 p_over.X = readS16(&data[9]);
2222 p_over.Y = readS16(&data[11]);
2223 p_over.Z = readS16(&data[13]);
2224 u16 item_i = readU16(&data[15]);
2226 //TODO: Check that target is reasonably close
2234 NOTE: This can be used in the future to check if
2235 somebody is cheating, by checking the timing.
2242 else if(action == 2)
2245 RemoteClient *client = getClient(peer->id);
2246 JMutexAutoLock digmutex(client->m_dig_mutex);
2247 client->m_dig_tool_item = -1;
2252 3: Digging completed
2254 else if(action == 3)
2256 // Mandatory parameter; actually used for nothing
2257 core::map<v3s16, MapBlock*> modified_blocks;
2259 u8 material = CONTENT_IGNORE;
2260 u8 mineral = MINERAL_NONE;
2262 bool cannot_remove_node = false;
2266 MapNode n = m_env.getMap().getNode(p_under);
2268 mineral = n.getMineral();
2269 // Get material at position
2271 // If not yet cancelled
2272 if(cannot_remove_node == false)
2274 // If it's not diggable, do nothing
2275 if(content_diggable(material) == false)
2277 derr_server<<"Server: Not finishing digging: "
2278 <<"Node not diggable"
2280 cannot_remove_node = true;
2283 // If not yet cancelled
2284 if(cannot_remove_node == false)
2286 // Get node metadata
2287 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2288 if(meta && meta->nodeRemovalDisabled() == true)
2290 derr_server<<"Server: Not finishing digging: "
2291 <<"Node metadata disables removal"
2293 cannot_remove_node = true;
2297 catch(InvalidPositionException &e)
2299 derr_server<<"Server: Not finishing digging: Node not found."
2300 <<" Adding block to emerge queue."
2302 m_emerge_queue.addBlock(peer_id,
2303 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2304 cannot_remove_node = true;
2307 // Make sure the player is allowed to do it
2308 if((player->privs & PRIV_BUILD) == 0)
2309 cannot_remove_node = true;
2312 If node can't be removed, set block to be re-sent to
2315 if(cannot_remove_node)
2317 derr_server<<"Server: Not finishing digging."<<std::endl;
2319 // Client probably has wrong data.
2320 // Set block not sent, so that client will get
2322 dstream<<"Client "<<peer_id<<" tried to dig "
2323 <<"node; but node cannot be removed."
2324 <<" setting MapBlock not sent."<<std::endl;
2325 RemoteClient *client = getClient(peer_id);
2326 v3s16 blockpos = getNodeBlockPos(p_under);
2327 client->SetBlockNotSent(blockpos);
2333 Send the removal to all other clients.
2334 - If other player is close, send REMOVENODE
2335 - Otherwise set blocks not sent
2337 core::list<u16> far_players;
2338 sendRemoveNode(p_under, peer_id, &far_players, 100);
2341 Update and send inventory
2344 if(g_settings.getBool("creative_mode") == false)
2349 InventoryList *mlist = player->inventory.getList("main");
2352 InventoryItem *item = mlist->getItem(item_i);
2353 if(item && (std::string)item->getName() == "ToolItem")
2355 ToolItem *titem = (ToolItem*)item;
2356 std::string toolname = titem->getToolName();
2358 // Get digging properties for material and tool
2359 DiggingProperties prop =
2360 getDiggingProperties(material, toolname);
2362 if(prop.diggable == false)
2364 derr_server<<"Server: WARNING: Player digged"
2365 <<" with impossible material + tool"
2366 <<" combination"<<std::endl;
2369 bool weared_out = titem->addWear(prop.wear);
2373 mlist->deleteItem(item_i);
2379 Add dug item to inventory
2382 InventoryItem *item = NULL;
2384 if(mineral != MINERAL_NONE)
2385 item = getDiggedMineralItem(mineral);
2390 std::string &dug_s = content_features(material).dug_item;
2393 std::istringstream is(dug_s, std::ios::binary);
2394 item = InventoryItem::deSerialize(is);
2400 // Add a item to inventory
2401 player->inventory.addItem("main", item);
2404 UpdateCrafting(player->peer_id);
2405 SendInventory(player->peer_id);
2411 (this takes some time so it is done after the quick stuff)
2413 m_ignore_map_edit_events = true;
2414 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2415 m_ignore_map_edit_events = false;
2418 Set blocks not sent to far players
2420 for(core::list<u16>::Iterator
2421 i = far_players.begin();
2422 i != far_players.end(); i++)
2425 RemoteClient *client = getClient(peer_id);
2428 client->SetBlocksNotSent(modified_blocks);
2435 else if(action == 1)
2438 InventoryList *ilist = player->inventory.getList("main");
2443 InventoryItem *item = ilist->getItem(item_i);
2445 // If there is no item, it is not possible to add it anywhere
2450 Handle material items
2452 if(std::string("MaterialItem") == item->getName())
2455 // Don't add a node if this is not a free space
2456 MapNode n2 = m_env.getMap().getNode(p_over);
2457 if(content_buildable_to(n2.d) == false
2458 || (player->privs & PRIV_BUILD) ==0)
2460 // Client probably has wrong data.
2461 // Set block not sent, so that client will get
2463 dstream<<"Client "<<peer_id<<" tried to place"
2464 <<" node in invalid position; setting"
2465 <<" MapBlock not sent."<<std::endl;
2466 RemoteClient *client = getClient(peer_id);
2467 v3s16 blockpos = getNodeBlockPos(p_over);
2468 client->SetBlockNotSent(blockpos);
2472 catch(InvalidPositionException &e)
2474 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2475 <<" Adding block to emerge queue."
2477 m_emerge_queue.addBlock(peer_id,
2478 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2482 // Reset build time counter
2483 getClient(peer->id)->m_time_from_building = 0.0;
2486 MaterialItem *mitem = (MaterialItem*)item;
2488 n.d = mitem->getMaterial();
2489 if(content_features(n.d).wall_mounted)
2490 n.dir = packDir(p_under - p_over);
2495 core::list<u16> far_players;
2496 sendAddNode(p_over, n, 0, &far_players, 100);
2501 InventoryList *ilist = player->inventory.getList("main");
2502 if(g_settings.getBool("creative_mode") == false && ilist)
2504 // Remove from inventory and send inventory
2505 if(mitem->getCount() == 1)
2506 ilist->deleteItem(item_i);
2510 UpdateCrafting(peer_id);
2511 SendInventory(peer_id);
2517 This takes some time so it is done after the quick stuff
2519 core::map<v3s16, MapBlock*> modified_blocks;
2520 m_ignore_map_edit_events = true;
2521 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2522 m_ignore_map_edit_events = false;
2525 Set blocks not sent to far players
2527 for(core::list<u16>::Iterator
2528 i = far_players.begin();
2529 i != far_players.end(); i++)
2532 RemoteClient *client = getClient(peer_id);
2535 client->SetBlocksNotSent(modified_blocks);
2539 Calculate special events
2542 /*if(n.d == CONTENT_MESE)
2545 for(s16 z=-1; z<=1; z++)
2546 for(s16 y=-1; y<=1; y++)
2547 for(s16 x=-1; x<=1; x++)
2554 Place other item (not a block)
2558 v3s16 blockpos = getNodeBlockPos(p_over);
2561 Check that the block is loaded so that the item
2562 can properly be added to the static list too
2564 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2567 derr_server<<"Error while placing object: "
2568 "block not found"<<std::endl;
2572 dout_server<<"Placing a miscellaneous item on map"
2575 // Calculate a position for it
2576 v3f pos = intToFloat(p_over, BS);
2578 pos.Y -= BS*0.25; // let it drop a bit
2580 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2581 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2586 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2590 derr_server<<"WARNING: item resulted in NULL object, "
2591 <<"not placing onto map"
2596 // Add the object to the environment
2597 m_env.addActiveObject(obj);
2599 dout_server<<"Placed object"<<std::endl;
2601 if(g_settings.getBool("creative_mode") == false)
2603 // Delete the right amount of items from the slot
2604 u16 dropcount = item->getDropCount();
2606 // Delete item if all gone
2607 if(item->getCount() <= dropcount)
2609 if(item->getCount() < dropcount)
2610 dstream<<"WARNING: Server: dropped more items"
2611 <<" than the slot contains"<<std::endl;
2613 InventoryList *ilist = player->inventory.getList("main");
2615 // Remove from inventory and send inventory
2616 ilist->deleteItem(item_i);
2618 // Else decrement it
2620 item->remove(dropcount);
2623 UpdateCrafting(peer_id);
2624 SendInventory(peer_id);
2632 Catch invalid actions
2636 derr_server<<"WARNING: Server: Invalid action "
2637 <<action<<std::endl;
2641 else if(command == TOSERVER_RELEASE)
2650 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2653 else if(command == TOSERVER_SIGNTEXT)
2655 if((player->privs & PRIV_BUILD) == 0)
2664 std::string datastring((char*)&data[2], datasize-2);
2665 std::istringstream is(datastring, std::ios_base::binary);
2668 is.read((char*)buf, 6);
2669 v3s16 blockpos = readV3S16(buf);
2670 is.read((char*)buf, 2);
2671 s16 id = readS16(buf);
2672 is.read((char*)buf, 2);
2673 u16 textlen = readU16(buf);
2675 for(u16 i=0; i<textlen; i++)
2677 is.read((char*)buf, 1);
2678 text += (char)buf[0];
2681 MapBlock *block = NULL;
2684 block = m_env.getMap().getBlockNoCreate(blockpos);
2686 catch(InvalidPositionException &e)
2688 derr_server<<"Error while setting sign text: "
2689 "block not found"<<std::endl;
2693 MapBlockObject *obj = block->getObject(id);
2696 derr_server<<"Error while setting sign text: "
2697 "object not found"<<std::endl;
2701 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2703 derr_server<<"Error while setting sign text: "
2704 "object is not a sign"<<std::endl;
2708 ((SignObject*)obj)->setText(text);
2710 obj->getBlock()->setChangedFlag();
2712 else if(command == TOSERVER_SIGNNODETEXT)
2714 if((player->privs & PRIV_BUILD) == 0)
2722 std::string datastring((char*)&data[2], datasize-2);
2723 std::istringstream is(datastring, std::ios_base::binary);
2726 is.read((char*)buf, 6);
2727 v3s16 p = readV3S16(buf);
2728 is.read((char*)buf, 2);
2729 u16 textlen = readU16(buf);
2731 for(u16 i=0; i<textlen; i++)
2733 is.read((char*)buf, 1);
2734 text += (char)buf[0];
2737 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2740 if(meta->typeId() != CONTENT_SIGN_WALL)
2742 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2743 signmeta->setText(text);
2745 v3s16 blockpos = getNodeBlockPos(p);
2746 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2749 block->setChangedFlag();
2752 for(core::map<u16, RemoteClient*>::Iterator
2753 i = m_clients.getIterator();
2754 i.atEnd()==false; i++)
2756 RemoteClient *client = i.getNode()->getValue();
2757 client->SetBlockNotSent(blockpos);
2760 else if(command == TOSERVER_INVENTORY_ACTION)
2762 /*// Ignore inventory changes if in creative mode
2763 if(g_settings.getBool("creative_mode") == true)
2765 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2769 // Strip command and create a stream
2770 std::string datastring((char*)&data[2], datasize-2);
2771 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2772 std::istringstream is(datastring, std::ios_base::binary);
2774 InventoryAction *a = InventoryAction::deSerialize(is);
2779 c.current_player = player;
2782 Handle craftresult specially if not in creative mode
2784 bool disable_action = false;
2785 if(a->getType() == IACTION_MOVE
2786 && g_settings.getBool("creative_mode") == false)
2788 IMoveAction *ma = (IMoveAction*)a;
2789 if(ma->to_inv == "current_player" &&
2790 ma->from_inv == "current_player")
2792 InventoryList *rlist = player->inventory.getList("craftresult");
2794 InventoryList *clist = player->inventory.getList("craft");
2796 InventoryList *mlist = player->inventory.getList("main");
2799 Craftresult is no longer preview if something
2802 if(ma->to_list == "craftresult"
2803 && ma->from_list != "craftresult")
2805 // If it currently is a preview, remove
2807 if(player->craftresult_is_preview)
2809 rlist->deleteItem(0);
2811 player->craftresult_is_preview = false;
2814 Crafting takes place if this condition is true.
2816 if(player->craftresult_is_preview &&
2817 ma->from_list == "craftresult")
2819 player->craftresult_is_preview = false;
2820 clist->decrementMaterials(1);
2823 If the craftresult is placed on itself, move it to
2824 main inventory instead of doing the action
2826 if(ma->to_list == "craftresult"
2827 && ma->from_list == "craftresult")
2829 disable_action = true;
2831 InventoryItem *item1 = rlist->changeItem(0, NULL);
2832 mlist->addItem(item1);
2837 if(disable_action == false)
2839 // Feed action to player inventory
2847 UpdateCrafting(player->peer_id);
2848 SendInventory(player->peer_id);
2853 dstream<<"TOSERVER_INVENTORY_ACTION: "
2854 <<"InventoryAction::deSerialize() returned NULL"
2858 else if(command == TOSERVER_CHAT_MESSAGE)
2866 std::string datastring((char*)&data[2], datasize-2);
2867 std::istringstream is(datastring, std::ios_base::binary);
2870 is.read((char*)buf, 2);
2871 u16 len = readU16(buf);
2873 std::wstring message;
2874 for(u16 i=0; i<len; i++)
2876 is.read((char*)buf, 2);
2877 message += (wchar_t)readU16(buf);
2880 // Get player name of this client
2881 std::wstring name = narrow_to_wide(player->getName());
2883 // Line to send to players
2885 // Whether to send to the player that sent the line
2886 bool send_to_sender = false;
2887 // Whether to send to other players
2888 bool send_to_others = false;
2891 std::wstring commandprefix = L"/#";
2892 if(message.substr(0, commandprefix.size()) == commandprefix)
2894 line += L"Server: ";
2896 message = message.substr(commandprefix.size());
2898 // Local player gets all privileges regardless of
2899 // what's set on their account.
2900 u64 privs = player->privs;
2901 if(g_settings.get("name") == player->getName())
2904 ServerCommandContext *ctx = new ServerCommandContext(
2905 str_split(message, L' '),
2911 line += processServerCommand(ctx);
2912 send_to_sender = ctx->flags & 1;
2913 send_to_others = ctx->flags & 2;
2925 send_to_others = true;
2930 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2933 Send the message to clients
2935 for(core::map<u16, RemoteClient*>::Iterator
2936 i = m_clients.getIterator();
2937 i.atEnd() == false; i++)
2939 // Get client and check that it is valid
2940 RemoteClient *client = i.getNode()->getValue();
2941 assert(client->peer_id == i.getNode()->getKey());
2942 if(client->serialization_version == SER_FMT_VER_INVALID)
2946 bool sender_selected = (peer_id == client->peer_id);
2947 if(sender_selected == true && send_to_sender == false)
2949 if(sender_selected == false && send_to_others == false)
2952 SendChatMessage(client->peer_id, line);
2956 else if(command == TOSERVER_DAMAGE)
2958 if(g_settings.getBool("enable_damage"))
2960 std::string datastring((char*)&data[2], datasize-2);
2961 std::istringstream is(datastring, std::ios_base::binary);
2962 u8 damage = readU8(is);
2963 if(player->hp > damage)
2965 player->hp -= damage;
2971 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
2974 v3f pos = findSpawnPos(m_env.getServerMap());
2975 player->setPosition(pos);
2977 SendMovePlayer(player);
2978 SendPlayerHP(player);
2980 //TODO: Throw items around
2984 SendPlayerHP(player);
2988 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2989 "unknown command "<<command<<std::endl;
2993 catch(SendFailedException &e)
2995 derr_server<<"Server::ProcessData(): SendFailedException: "
3001 void Server::onMapEditEvent(MapEditEvent *event)
3003 dstream<<"Server::onMapEditEvent()"<<std::endl;
3004 if(m_ignore_map_edit_events)
3006 MapEditEvent *e = event->clone();
3007 m_unsent_map_edit_queue.push_back(e);
3010 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3012 if(id == "current_player")
3014 assert(c->current_player);
3015 return &(c->current_player->inventory);
3019 std::string id0 = fn.next(":");
3021 if(id0 == "nodemeta")
3024 p.X = stoi(fn.next(","));
3025 p.Y = stoi(fn.next(","));
3026 p.Z = stoi(fn.next(","));
3027 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3029 return meta->getInventory();
3030 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3031 <<"no metadata found"<<std::endl;
3035 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3038 void Server::inventoryModified(InventoryContext *c, std::string id)
3040 if(id == "current_player")
3042 assert(c->current_player);
3044 UpdateCrafting(c->current_player->peer_id);
3045 SendInventory(c->current_player->peer_id);
3050 std::string id0 = fn.next(":");
3052 if(id0 == "nodemeta")
3055 p.X = stoi(fn.next(","));
3056 p.Y = stoi(fn.next(","));
3057 p.Z = stoi(fn.next(","));
3058 v3s16 blockpos = getNodeBlockPos(p);
3060 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3062 meta->inventoryModified();
3064 for(core::map<u16, RemoteClient*>::Iterator
3065 i = m_clients.getIterator();
3066 i.atEnd()==false; i++)
3068 RemoteClient *client = i.getNode()->getValue();
3069 client->SetBlockNotSent(blockpos);
3075 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3078 core::list<PlayerInfo> Server::getPlayerInfo()
3080 DSTACK(__FUNCTION_NAME);
3081 JMutexAutoLock envlock(m_env_mutex);
3082 JMutexAutoLock conlock(m_con_mutex);
3084 core::list<PlayerInfo> list;
3086 core::list<Player*> players = m_env.getPlayers();
3088 core::list<Player*>::Iterator i;
3089 for(i = players.begin();
3090 i != players.end(); i++)
3094 Player *player = *i;
3097 con::Peer *peer = m_con.GetPeer(player->peer_id);
3098 // Copy info from peer to info struct
3100 info.address = peer->address;
3101 info.avg_rtt = peer->avg_rtt;
3103 catch(con::PeerNotFoundException &e)
3105 // Set dummy peer info
3107 info.address = Address(0,0,0,0,0);
3111 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3112 info.position = player->getPosition();
3114 list.push_back(info);
3121 void Server::peerAdded(con::Peer *peer)
3123 DSTACK(__FUNCTION_NAME);
3124 dout_server<<"Server::peerAdded(): peer->id="
3125 <<peer->id<<std::endl;
3128 c.type = PEER_ADDED;
3129 c.peer_id = peer->id;
3131 m_peer_change_queue.push_back(c);
3134 void Server::deletingPeer(con::Peer *peer, bool timeout)
3136 DSTACK(__FUNCTION_NAME);
3137 dout_server<<"Server::deletingPeer(): peer->id="
3138 <<peer->id<<", timeout="<<timeout<<std::endl;
3141 c.type = PEER_REMOVED;
3142 c.peer_id = peer->id;
3143 c.timeout = timeout;
3144 m_peer_change_queue.push_back(c);
3151 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3153 DSTACK(__FUNCTION_NAME);
3154 std::ostringstream os(std::ios_base::binary);
3156 writeU16(os, TOCLIENT_HP);
3160 std::string s = os.str();
3161 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3163 con.Send(peer_id, 0, data, true);
3166 void Server::SendAccessDenied(con::Connection &con, u16 peer_id)
3168 DSTACK(__FUNCTION_NAME);
3169 std::ostringstream os(std::ios_base::binary);
3171 writeU16(os, TOCLIENT_ACCESS_DENIED);
3174 std::string s = os.str();
3175 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3177 con.Send(peer_id, 0, data, true);
3181 Non-static send methods
3184 void Server::SendObjectData(float dtime)
3186 DSTACK(__FUNCTION_NAME);
3188 core::map<v3s16, bool> stepped_blocks;
3190 for(core::map<u16, RemoteClient*>::Iterator
3191 i = m_clients.getIterator();
3192 i.atEnd() == false; i++)
3194 u16 peer_id = i.getNode()->getKey();
3195 RemoteClient *client = i.getNode()->getValue();
3196 assert(client->peer_id == peer_id);
3198 if(client->serialization_version == SER_FMT_VER_INVALID)
3201 client->SendObjectData(this, dtime, stepped_blocks);
3205 void Server::SendPlayerInfos()
3207 DSTACK(__FUNCTION_NAME);
3209 //JMutexAutoLock envlock(m_env_mutex);
3211 // Get connected players
3212 core::list<Player*> players = m_env.getPlayers(true);
3214 u32 player_count = players.getSize();
3215 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3217 SharedBuffer<u8> data(datasize);
3218 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3221 core::list<Player*>::Iterator i;
3222 for(i = players.begin();
3223 i != players.end(); i++)
3225 Player *player = *i;
3227 /*dstream<<"Server sending player info for player with "
3228 "peer_id="<<player->peer_id<<std::endl;*/
3230 writeU16(&data[start], player->peer_id);
3231 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3232 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3233 start += 2+PLAYERNAME_SIZE;
3236 //JMutexAutoLock conlock(m_con_mutex);
3239 m_con.SendToAll(0, data, true);
3242 void Server::SendInventory(u16 peer_id)
3244 DSTACK(__FUNCTION_NAME);
3246 Player* player = m_env.getPlayer(peer_id);
3253 std::ostringstream os;
3254 //os.imbue(std::locale("C"));
3256 player->inventory.serialize(os);
3258 std::string s = os.str();
3260 SharedBuffer<u8> data(s.size()+2);
3261 writeU16(&data[0], TOCLIENT_INVENTORY);
3262 memcpy(&data[2], s.c_str(), s.size());
3265 m_con.Send(peer_id, 0, data, true);
3268 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3270 DSTACK(__FUNCTION_NAME);
3272 std::ostringstream os(std::ios_base::binary);
3276 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3277 os.write((char*)buf, 2);
3280 writeU16(buf, message.size());
3281 os.write((char*)buf, 2);
3284 for(u32 i=0; i<message.size(); i++)
3288 os.write((char*)buf, 2);
3292 std::string s = os.str();
3293 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3295 m_con.Send(peer_id, 0, data, true);
3298 void Server::BroadcastChatMessage(const std::wstring &message)
3300 for(core::map<u16, RemoteClient*>::Iterator
3301 i = m_clients.getIterator();
3302 i.atEnd() == false; i++)
3304 // Get client and check that it is valid
3305 RemoteClient *client = i.getNode()->getValue();
3306 assert(client->peer_id == i.getNode()->getKey());
3307 if(client->serialization_version == SER_FMT_VER_INVALID)
3310 SendChatMessage(client->peer_id, message);
3314 void Server::SendPlayerHP(Player *player)
3316 SendHP(m_con, player->peer_id, player->hp);
3319 void Server::SendMovePlayer(Player *player)
3321 DSTACK(__FUNCTION_NAME);
3322 std::ostringstream os(std::ios_base::binary);
3324 writeU16(os, TOCLIENT_MOVE_PLAYER);
3325 writeV3F1000(os, player->getPosition());
3326 writeF1000(os, player->getPitch());
3327 writeF1000(os, player->getYaw());
3330 v3f pos = player->getPosition();
3331 f32 pitch = player->getPitch();
3332 f32 yaw = player->getYaw();
3333 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3334 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3341 std::string s = os.str();
3342 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3344 m_con.Send(player->peer_id, 0, data, true);
3347 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3348 core::list<u16> *far_players, float far_d_nodes)
3350 float maxd = far_d_nodes*BS;
3351 v3f p_f = intToFloat(p, BS);
3355 SharedBuffer<u8> reply(replysize);
3356 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3357 writeS16(&reply[2], p.X);
3358 writeS16(&reply[4], p.Y);
3359 writeS16(&reply[6], p.Z);
3361 for(core::map<u16, RemoteClient*>::Iterator
3362 i = m_clients.getIterator();
3363 i.atEnd() == false; i++)
3365 // Get client and check that it is valid
3366 RemoteClient *client = i.getNode()->getValue();
3367 assert(client->peer_id == i.getNode()->getKey());
3368 if(client->serialization_version == SER_FMT_VER_INVALID)
3371 // Don't send if it's the same one
3372 if(client->peer_id == ignore_id)
3378 Player *player = m_env.getPlayer(client->peer_id);
3381 // If player is far away, only set modified blocks not sent
3382 v3f player_pos = player->getPosition();
3383 if(player_pos.getDistanceFrom(p_f) > maxd)
3385 far_players->push_back(client->peer_id);
3392 m_con.Send(client->peer_id, 0, reply, true);
3396 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3397 core::list<u16> *far_players, float far_d_nodes)
3399 float maxd = far_d_nodes*BS;
3400 v3f p_f = intToFloat(p, BS);
3402 for(core::map<u16, RemoteClient*>::Iterator
3403 i = m_clients.getIterator();
3404 i.atEnd() == false; i++)
3406 // Get client and check that it is valid
3407 RemoteClient *client = i.getNode()->getValue();
3408 assert(client->peer_id == i.getNode()->getKey());
3409 if(client->serialization_version == SER_FMT_VER_INVALID)
3412 // Don't send if it's the same one
3413 if(client->peer_id == ignore_id)
3419 Player *player = m_env.getPlayer(client->peer_id);
3422 // If player is far away, only set modified blocks not sent
3423 v3f player_pos = player->getPosition();
3424 if(player_pos.getDistanceFrom(p_f) > maxd)
3426 far_players->push_back(client->peer_id);
3433 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3434 SharedBuffer<u8> reply(replysize);
3435 writeU16(&reply[0], TOCLIENT_ADDNODE);
3436 writeS16(&reply[2], p.X);
3437 writeS16(&reply[4], p.Y);
3438 writeS16(&reply[6], p.Z);
3439 n.serialize(&reply[8], client->serialization_version);
3442 m_con.Send(client->peer_id, 0, reply, true);
3446 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3448 DSTACK(__FUNCTION_NAME);
3450 v3s16 p = block->getPos();
3454 bool completely_air = true;
3455 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3456 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3457 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3459 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3461 completely_air = false;
3462 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3467 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3469 dstream<<"[completely air] ";
3474 Create a packet with the block in the right format
3477 std::ostringstream os(std::ios_base::binary);
3478 block->serialize(os, ver);
3479 std::string s = os.str();
3480 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3482 u32 replysize = 8 + blockdata.getSize();
3483 SharedBuffer<u8> reply(replysize);
3484 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3485 writeS16(&reply[2], p.X);
3486 writeS16(&reply[4], p.Y);
3487 writeS16(&reply[6], p.Z);
3488 memcpy(&reply[8], *blockdata, blockdata.getSize());
3490 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3491 <<": \tpacket size: "<<replysize<<std::endl;*/
3496 m_con.Send(peer_id, 1, reply, true);
3499 void Server::SendBlocks(float dtime)
3501 DSTACK(__FUNCTION_NAME);
3503 JMutexAutoLock envlock(m_env_mutex);
3504 JMutexAutoLock conlock(m_con_mutex);
3506 //TimeTaker timer("Server::SendBlocks");
3508 core::array<PrioritySortedBlockTransfer> queue;
3510 s32 total_sending = 0;
3512 for(core::map<u16, RemoteClient*>::Iterator
3513 i = m_clients.getIterator();
3514 i.atEnd() == false; i++)
3516 RemoteClient *client = i.getNode()->getValue();
3517 assert(client->peer_id == i.getNode()->getKey());
3519 total_sending += client->SendingCount();
3521 if(client->serialization_version == SER_FMT_VER_INVALID)
3524 client->GetNextBlocks(this, dtime, queue);
3528 // Lowest priority number comes first.
3529 // Lowest is most important.
3532 for(u32 i=0; i<queue.size(); i++)
3534 //TODO: Calculate limit dynamically
3535 if(total_sending >= g_settings.getS32
3536 ("max_simultaneous_block_sends_server_total"))
3539 PrioritySortedBlockTransfer q = queue[i];
3541 MapBlock *block = NULL;
3544 block = m_env.getMap().getBlockNoCreate(q.pos);
3546 catch(InvalidPositionException &e)
3551 RemoteClient *client = getClient(q.peer_id);
3553 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3555 client->SentBlock(q.pos);
3565 void Server::UpdateCrafting(u16 peer_id)
3567 DSTACK(__FUNCTION_NAME);
3569 Player* player = m_env.getPlayer(peer_id);
3573 Calculate crafting stuff
3575 if(g_settings.getBool("creative_mode") == false)
3577 InventoryList *clist = player->inventory.getList("craft");
3578 InventoryList *rlist = player->inventory.getList("craftresult");
3580 if(rlist->getUsedSlots() == 0)
3581 player->craftresult_is_preview = true;
3583 if(rlist && player->craftresult_is_preview)
3585 rlist->clearItems();
3587 if(clist && rlist && player->craftresult_is_preview)
3589 InventoryItem *items[9];
3590 for(u16 i=0; i<9; i++)
3592 items[i] = clist->getItem(i);
3601 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3602 if(checkItemCombination(items, specs))
3604 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3613 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3614 if(checkItemCombination(items, specs))
3616 rlist->addItem(new CraftItem("Stick", 4));
3625 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3626 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3627 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3628 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3629 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3630 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3631 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3632 if(checkItemCombination(items, specs))
3634 //rlist->addItem(new MapBlockObjectItem("Sign"));
3635 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3644 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3645 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3646 if(checkItemCombination(items, specs))
3648 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3657 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3658 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3659 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3660 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3661 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3662 if(checkItemCombination(items, specs))
3664 rlist->addItem(new ToolItem("WPick", 0));
3673 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3674 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3675 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3676 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3677 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3678 if(checkItemCombination(items, specs))
3680 rlist->addItem(new ToolItem("STPick", 0));
3689 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3690 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3691 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3692 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3693 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3694 if(checkItemCombination(items, specs))
3696 rlist->addItem(new ToolItem("SteelPick", 0));
3705 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3706 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3707 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3708 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3709 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3710 if(checkItemCombination(items, specs))
3712 rlist->addItem(new ToolItem("MesePick", 0));
3721 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3722 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3723 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3724 if(checkItemCombination(items, specs))
3726 rlist->addItem(new ToolItem("WShovel", 0));
3735 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3736 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3737 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3738 if(checkItemCombination(items, specs))
3740 rlist->addItem(new ToolItem("STShovel", 0));
3749 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3750 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3751 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3752 if(checkItemCombination(items, specs))
3754 rlist->addItem(new ToolItem("SteelShovel", 0));
3763 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3764 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3765 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3766 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3767 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3768 if(checkItemCombination(items, specs))
3770 rlist->addItem(new ToolItem("WAxe", 0));
3779 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3780 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3781 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3782 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3783 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3784 if(checkItemCombination(items, specs))
3786 rlist->addItem(new ToolItem("STAxe", 0));
3795 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3796 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3797 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3798 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3799 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3800 if(checkItemCombination(items, specs))
3802 rlist->addItem(new ToolItem("SteelAxe", 0));
3811 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3812 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3813 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3814 if(checkItemCombination(items, specs))
3816 rlist->addItem(new ToolItem("WSword", 0));
3825 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3826 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3827 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3828 if(checkItemCombination(items, specs))
3830 rlist->addItem(new ToolItem("STSword", 0));
3839 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3840 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3841 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3842 if(checkItemCombination(items, specs))
3844 rlist->addItem(new ToolItem("SteelSword", 0));
3853 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3854 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3855 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3856 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3857 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3858 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3859 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3860 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3861 if(checkItemCombination(items, specs))
3863 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3872 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3873 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3874 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3875 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3876 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3877 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3878 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3879 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3880 if(checkItemCombination(items, specs))
3882 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3891 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3892 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3893 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3894 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3895 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3896 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3897 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3898 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3899 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3900 if(checkItemCombination(items, specs))
3902 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3908 } // if creative_mode == false
3911 RemoteClient* Server::getClient(u16 peer_id)
3913 DSTACK(__FUNCTION_NAME);
3914 //JMutexAutoLock lock(m_con_mutex);
3915 core::map<u16, RemoteClient*>::Node *n;
3916 n = m_clients.find(peer_id);
3917 // A client should exist for all peers
3919 return n->getValue();
3922 std::wstring Server::getStatusString()
3924 std::wostringstream os(std::ios_base::binary);
3927 os<<L"version="<<narrow_to_wide(VERSION_STRING);
3929 os<<L", uptime="<<m_uptime.get();
3930 // Information about clients
3932 for(core::map<u16, RemoteClient*>::Iterator
3933 i = m_clients.getIterator();
3934 i.atEnd() == false; i++)
3936 // Get client and check that it is valid
3937 RemoteClient *client = i.getNode()->getValue();
3938 assert(client->peer_id == i.getNode()->getKey());
3939 if(client->serialization_version == SER_FMT_VER_INVALID)
3942 Player *player = m_env.getPlayer(client->peer_id);
3943 // Get name of player
3944 std::wstring name = L"unknown";
3946 name = narrow_to_wide(player->getName());
3947 // Add name to information string
3951 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3952 os<<" WARNING: Map saving is disabled."<<std::endl;
3957 void setCreativeInventory(Player *player)
3959 player->resetInventory();
3961 // Give some good tools
3963 InventoryItem *item = new ToolItem("MesePick", 0);
3964 void* r = player->inventory.addItem("main", item);
3968 InventoryItem *item = new ToolItem("SteelPick", 0);
3969 void* r = player->inventory.addItem("main", item);
3973 InventoryItem *item = new ToolItem("SteelAxe", 0);
3974 void* r = player->inventory.addItem("main", item);
3978 InventoryItem *item = new ToolItem("SteelShovel", 0);
3979 void* r = player->inventory.addItem("main", item);
3987 // CONTENT_IGNORE-terminated list
3988 u8 material_items[] = {
3998 CONTENT_WATERSOURCE,
4006 u8 *mip = material_items;
4007 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4009 if(*mip == CONTENT_IGNORE)
4012 InventoryItem *item = new MaterialItem(*mip, 1);
4013 player->inventory.addItem("main", item);
4019 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4022 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4023 player->inventory.addItem("main", item);
4026 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4028 // Skip some materials
4029 if(i == CONTENT_WATER || i == CONTENT_TORCH
4030 || i == CONTENT_COALSTONE)
4033 InventoryItem *item = new MaterialItem(i, 1);
4034 player->inventory.addItem("main", item);
4040 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4041 void* r = player->inventory.addItem("main", item);
4046 v3f findSpawnPos(ServerMap &map)
4048 //return v3f(50,50,50)*BS;
4051 s16 groundheight = 0;
4053 // Try to find a good place a few times
4054 for(s32 i=0; i<1000; i++)
4057 // We're going to try to throw the player to this position
4058 nodepos = v2s16(-range + (myrand()%(range*2)),
4059 -range + (myrand()%(range*2)));
4060 v2s16 sectorpos = getNodeSectorPos(nodepos);
4061 // Get sector (NOTE: Don't get because it's slow)
4062 //m_env.getMap().emergeSector(sectorpos);
4063 // Get ground height at point (fallbacks to heightmap function)
4064 groundheight = map.findGroundLevel(nodepos);
4065 // Don't go underwater
4066 if(groundheight < WATER_LEVEL)
4068 //dstream<<"-> Underwater"<<std::endl;
4071 // Don't go to high places
4072 if(groundheight > WATER_LEVEL + 4)
4074 //dstream<<"-> Underwater"<<std::endl;
4078 // Found a good place
4079 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4083 // If no suitable place was not found, go above water at least.
4084 if(groundheight < WATER_LEVEL)
4085 groundheight = WATER_LEVEL;
4087 return intToFloat(v3s16(
4094 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4097 Try to get an existing player
4099 Player *player = m_env.getPlayer(name);
4102 // If player is already connected, cancel
4103 if(player->peer_id != 0)
4105 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4110 player->peer_id = peer_id;
4112 // Reset inventory to creative if in creative mode
4113 if(g_settings.getBool("creative_mode"))
4115 setCreativeInventory(player);
4122 If player with the wanted peer_id already exists, cancel.
4124 if(m_env.getPlayer(peer_id) != NULL)
4126 dstream<<"emergePlayer(): Player with wrong name but same"
4127 " peer_id already exists"<<std::endl;
4135 player = new ServerRemotePlayer();
4136 //player->peer_id = c.peer_id;
4137 //player->peer_id = PEER_ID_INEXISTENT;
4138 player->peer_id = peer_id;
4139 player->updateName(name);
4140 player->updatePassword(password);
4146 dstream<<"Server: Finding spawn place for player \""
4147 <<player->getName()<<"\""<<std::endl;
4149 v3f pos = findSpawnPos(m_env.getServerMap());
4151 player->setPosition(pos);
4154 Add player to environment
4157 m_env.addPlayer(player);
4160 Add stuff to inventory
4163 if(g_settings.getBool("creative_mode"))
4165 setCreativeInventory(player);
4167 else if(g_settings.getBool("give_initial_stuff"))
4170 InventoryItem *item = new ToolItem("SteelPick", 0);
4171 void* r = player->inventory.addItem("main", item);
4175 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4176 void* r = player->inventory.addItem("main", item);
4180 InventoryItem *item = new ToolItem("SteelAxe", 0);
4181 void* r = player->inventory.addItem("main", item);
4185 InventoryItem *item = new ToolItem("SteelShovel", 0);
4186 void* r = player->inventory.addItem("main", item);
4190 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4191 void* r = player->inventory.addItem("main", item);
4195 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4196 void* r = player->inventory.addItem("main", item);
4200 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4201 void* r = player->inventory.addItem("main", item);
4205 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4206 void* r = player->inventory.addItem("main", item);
4210 InventoryItem *item = new CraftItem("Stick", 4);
4211 void* r = player->inventory.addItem("main", item);
4215 InventoryItem *item = new ToolItem("WPick", 32000);
4216 void* r = player->inventory.addItem("main", item);
4220 InventoryItem *item = new ToolItem("STPick", 32000);
4221 void* r = player->inventory.addItem("main", item);
4225 for(u16 i=0; i<4; i++)
4227 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4228 bool r = player->inventory.addItem("main", item);
4231 /*// Give some other stuff
4233 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4234 bool r = player->inventory.addItem("main", item);
4241 } // create new player
4244 void Server::handlePeerChange(PeerChange &c)
4246 JMutexAutoLock envlock(m_env_mutex);
4247 JMutexAutoLock conlock(m_con_mutex);
4249 if(c.type == PEER_ADDED)
4256 core::map<u16, RemoteClient*>::Node *n;
4257 n = m_clients.find(c.peer_id);
4258 // The client shouldn't already exist
4262 RemoteClient *client = new RemoteClient();
4263 client->peer_id = c.peer_id;
4264 m_clients.insert(client->peer_id, client);
4267 else if(c.type == PEER_REMOVED)
4274 core::map<u16, RemoteClient*>::Node *n;
4275 n = m_clients.find(c.peer_id);
4276 // The client should exist
4280 Mark objects to be not known by the client
4282 RemoteClient *client = n->getValue();
4284 for(core::map<u16, bool>::Iterator
4285 i = client->m_known_objects.getIterator();
4286 i.atEnd()==false; i++)
4289 u16 id = i.getNode()->getKey();
4290 ServerActiveObject* obj = m_env.getActiveObject(id);
4292 if(obj && obj->m_known_by_count > 0)
4293 obj->m_known_by_count--;
4296 // Collect information about leaving in chat
4297 std::wstring message;
4299 std::wstring name = L"unknown";
4300 Player *player = m_env.getPlayer(c.peer_id);
4302 name = narrow_to_wide(player->getName());
4306 message += L" left game";
4308 message += L" (timed out)";
4313 m_env.removePlayer(c.peer_id);
4316 // Set player client disconnected
4318 Player *player = m_env.getPlayer(c.peer_id);
4320 player->peer_id = 0;
4324 delete m_clients[c.peer_id];
4325 m_clients.remove(c.peer_id);
4327 // Send player info to all remaining clients
4330 // Send leave chat message to all remaining clients
4331 BroadcastChatMessage(message);
4340 void Server::handlePeerChanges()
4342 while(m_peer_change_queue.size() > 0)
4344 PeerChange c = m_peer_change_queue.pop_front();
4346 dout_server<<"Server: Handling peer change: "
4347 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4350 handlePeerChange(c);
4354 void dedicated_server_loop(Server &server, bool &kill)
4356 DSTACK(__FUNCTION_NAME);
4358 std::cout<<DTIME<<std::endl;
4359 std::cout<<"========================"<<std::endl;
4360 std::cout<<"Running dedicated server"<<std::endl;
4361 std::cout<<"========================"<<std::endl;
4362 std::cout<<std::endl;
4366 // This is kind of a hack but can be done like this
4367 // because server.step() is very light
4371 if(server.getShutdownRequested() || kill)
4373 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4377 static int counter = 0;
4383 core::list<PlayerInfo> list = server.getPlayerInfo();
4384 core::list<PlayerInfo>::Iterator i;
4385 static u32 sum_old = 0;
4386 u32 sum = PIChecksum(list);
4389 std::cout<<DTIME<<"Player info:"<<std::endl;
4390 for(i=list.begin(); i!=list.end(); i++)
4392 i->PrintLine(&std::cout);