3 Copyright (C) 2010 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.
21 (c) 2010 Perttu Ahola <celeron55@gmail.com>
27 #include "clientserver.h"
29 #include "jmutexautolock.h"
31 #include "constants.h"
33 #include "materials.h"
36 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
38 void * ServerThread::Thread()
42 DSTACK(__FUNCTION_NAME);
44 BEGIN_DEBUG_EXCEPTION_HANDLER
49 m_server->AsyncRunStep();
51 //dout_server<<"Running m_server->Receive()"<<std::endl;
54 catch(con::NoIncomingDataException &e)
57 catch(con::PeerNotFoundException &e)
59 dout_server<<"Server: PeerNotFoundException"<<std::endl;
63 END_DEBUG_EXCEPTION_HANDLER
68 void * EmergeThread::Thread()
72 DSTACK(__FUNCTION_NAME);
76 BEGIN_DEBUG_EXCEPTION_HANDLER
79 Get block info from queue, emerge them and send them
82 After queue is empty, exit.
86 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
90 SharedPtr<QueuedBlockEmerge> q(qptr);
94 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
96 //TimeTaker timer("block emerge");
99 Try to emerge it from somewhere.
101 If it is only wanted as optional, only loading from disk
106 Check if any peer wants it as non-optional. In that case it
109 Also decrement the emerge queue count in clients.
112 bool optional = true;
115 core::map<u16, u8>::Iterator i;
116 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
118 //u16 peer_id = i.getNode()->getKey();
121 u8 flags = i.getNode()->getValue();
122 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
128 /*dstream<<"EmergeThread: p="
129 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
130 <<"optional="<<optional<<std::endl;*/
132 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
134 core::map<v3s16, MapBlock*> changed_blocks;
135 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
137 MapBlock *block = NULL;
138 bool got_block = true;
139 core::map<v3s16, MapBlock*> modified_blocks;
143 //TimeTaker envlockwaittimer("block emerge envlock wait time");
146 JMutexAutoLock envlock(m_server->m_env_mutex);
148 //envlockwaittimer.stop();
150 //TimeTaker timer("block emerge (while env locked)");
153 bool only_from_disk = false;
156 only_from_disk = true;
158 // First check if the block already exists
161 block = map.getBlockNoCreate(p);
166 block = map.emergeBlock(
170 lighting_invalidated_blocks);
174 EXPERIMENTAL: Create a few other blocks too
181 lighting_invalidated_blocks);
187 lighting_invalidated_blocks);
193 lighting_invalidated_blocks);
199 lighting_invalidated_blocks);
204 // If it is a dummy, block was not found on disk
207 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
211 catch(InvalidPositionException &e)
214 // This happens when position is over limit.
220 if(debug && changed_blocks.size() > 0)
222 dout_server<<DTIME<<"Got changed_blocks: ";
223 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
224 i.atEnd() == false; i++)
226 MapBlock *block = i.getNode()->getValue();
227 v3s16 p = block->getPos();
228 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
230 dout_server<<std::endl;
234 Collect a list of blocks that have been modified in
235 addition to the fetched one.
238 // Add all the "changed blocks" to modified_blocks
239 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
240 i.atEnd() == false; i++)
242 MapBlock *block = i.getNode()->getValue();
243 modified_blocks.insert(block->getPos(), block);
246 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
247 <<" blocks"<<std::endl;*/
249 //TimeTaker timer("** updateLighting");
251 // Update lighting without locking the environment mutex,
252 // add modified blocks to changed blocks
253 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
255 // If we got no block, there should be no invalidated blocks
258 assert(lighting_invalidated_blocks.size() == 0);
264 Set sent status of modified blocks on clients
267 // NOTE: Server's clients are also behind the connection mutex
268 JMutexAutoLock lock(m_server->m_con_mutex);
271 Add the originally fetched block to the modified list
275 modified_blocks.insert(p, block);
279 Set the modified blocks unsent for all the clients
282 for(core::map<u16, RemoteClient*>::Iterator
283 i = m_server->m_clients.getIterator();
284 i.atEnd() == false; i++)
286 RemoteClient *client = i.getNode()->getValue();
288 if(modified_blocks.size() > 0)
290 // Remove block from sent history
291 client->SetBlocksNotSent(modified_blocks);
297 END_DEBUG_EXCEPTION_HANDLER
302 void RemoteClient::GetNextBlocks(Server *server, float dtime,
303 core::array<PrioritySortedBlockTransfer> &dest)
305 DSTACK(__FUNCTION_NAME);
309 JMutexAutoLock lock(m_blocks_sent_mutex);
310 m_nearest_unsent_reset_timer += dtime;
313 // Won't send anything if already sending
315 JMutexAutoLock lock(m_blocks_sending_mutex);
317 if(m_blocks_sending.size() >= g_settings.getU16
318 ("max_simultaneous_block_sends_per_client"))
320 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
325 bool haxmode = g_settings.getBool("haxmode");
327 Player *player = server->m_env.getPlayer(peer_id);
329 assert(player != NULL);
331 v3f playerpos = player->getPosition();
332 v3f playerspeed = player->getSpeed();
334 v3s16 center_nodepos = floatToInt(playerpos);
336 v3s16 center = getNodeBlockPos(center_nodepos);
338 // Camera position and direction
340 playerpos + v3f(0, BS+BS/2, 0);
341 v3f camera_dir = v3f(0,0,1);
342 camera_dir.rotateYZBy(player->getPitch());
343 camera_dir.rotateXZBy(player->getYaw());
346 Get the starting value of the block finder radius.
348 s16 last_nearest_unsent_d;
351 JMutexAutoLock lock(m_blocks_sent_mutex);
353 if(m_last_center != center)
355 m_nearest_unsent_d = 0;
356 m_last_center = center;
359 /*dstream<<"m_nearest_unsent_reset_timer="
360 <<m_nearest_unsent_reset_timer<<std::endl;*/
361 if(m_nearest_unsent_reset_timer > 5.0)
363 m_nearest_unsent_reset_timer = 0;
364 m_nearest_unsent_d = 0;
365 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
368 last_nearest_unsent_d = m_nearest_unsent_d;
370 d_start = m_nearest_unsent_d;
373 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
374 ("max_simultaneous_block_sends_per_client");
375 u16 maximum_simultaneous_block_sends =
376 maximum_simultaneous_block_sends_setting;
379 Check the time from last addNode/removeNode.
381 Decrease send rate if player is building stuff.
384 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
385 m_time_from_building.m_value += dtime;
386 /*if(m_time_from_building.m_value
387 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
388 if(m_time_from_building.m_value < g_settings.getFloat(
389 "full_block_send_enable_min_time_from_building"))
391 maximum_simultaneous_block_sends
392 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
396 u32 num_blocks_selected;
398 JMutexAutoLock lock(m_blocks_sending_mutex);
399 num_blocks_selected = m_blocks_sending.size();
403 next time d will be continued from the d from which the nearest
404 unsent block was found this time.
406 This is because not necessarily any of the blocks found this
407 time are actually sent.
409 s32 new_nearest_unsent_d = -1;
411 // Serialization version used
412 //u8 ser_version = serialization_version;
414 //bool has_incomplete_blocks = false;
416 s16 d_max = g_settings.getS16("max_block_send_distance");
417 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
419 //dstream<<"Starting from "<<d_start<<std::endl;
421 for(s16 d = d_start; d <= d_max; d++)
423 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
425 //if(has_incomplete_blocks == false)
427 JMutexAutoLock lock(m_blocks_sent_mutex);
429 If m_nearest_unsent_d was changed by the EmergeThread
430 (it can change it to 0 through SetBlockNotSent),
432 Else update m_nearest_unsent_d
434 if(m_nearest_unsent_d != last_nearest_unsent_d)
436 d = m_nearest_unsent_d;
437 last_nearest_unsent_d = m_nearest_unsent_d;
442 Get the border/face dot coordinates of a "d-radiused"
445 core::list<v3s16> list;
446 getFacePositions(list, d);
448 core::list<v3s16>::Iterator li;
449 for(li=list.begin(); li!=list.end(); li++)
451 v3s16 p = *li + center;
455 - Don't allow too many simultaneous transfers
456 - EXCEPT when the blocks are very close
458 Also, don't send blocks that are already flying.
461 u16 maximum_simultaneous_block_sends_now =
462 maximum_simultaneous_block_sends;
464 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
466 maximum_simultaneous_block_sends_now =
467 maximum_simultaneous_block_sends_setting;
471 JMutexAutoLock lock(m_blocks_sending_mutex);
473 // Limit is dynamically lowered when building
474 if(num_blocks_selected
475 >= maximum_simultaneous_block_sends_now)
477 /*dstream<<"Not sending more blocks. Queue full. "
478 <<m_blocks_sending.size()
483 if(m_blocks_sending.find(p) != NULL)
490 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
491 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
492 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
493 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
494 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
495 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
498 // If this is true, inexistent block will be made from scratch
499 bool generate = d <= d_max_gen;
503 // Don't generate above player
509 // Limit the generating area vertically to 2/3
510 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
515 If block is far away, don't generate it unless it is
521 MapSector *sector = NULL;
524 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
526 catch(InvalidPositionException &e)
532 // Get center ground height in nodes
533 f32 gh = sector->getGroundHeight(
534 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
535 // Block center y in nodes
536 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
537 // If differs a lot, don't generate
538 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
544 Don't draw if not in sight
547 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
553 Don't send already sent blocks
556 JMutexAutoLock lock(m_blocks_sent_mutex);
558 if(m_blocks_sent.find(p) != NULL)
565 Ignore block if it is not at ground surface
566 but don't ignore water surface blocks
568 v2s16 p2d(p.X*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2,
569 p.Z*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
570 f32 y = server->m_env.getMap().getGroundHeight(p2d);
571 // The sector might not exist yet, thus no heightmap
572 if(y > GROUNDHEIGHT_VALID_MINVALUE)
574 f32 by = p.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;
575 if(fabs(by - y) > MAP_BLOCKSIZE + MAP_BLOCKSIZE/3
576 && fabs(by - WATER_LEVEL) >= MAP_BLOCKSIZE)
582 Check if map has this block
584 MapBlock *block = NULL;
587 block = server->m_env.getMap().getBlockNoCreate(p);
589 catch(InvalidPositionException &e)
593 bool surely_not_found_on_disk = false;
596 /*if(block->isIncomplete())
598 has_incomplete_blocks = true;
604 surely_not_found_on_disk = true;
609 If block has been marked to not exist on disk (dummy)
610 and generating new ones is not wanted, skip block.
612 if(generate == false && surely_not_found_on_disk == true)
619 Record the lowest d from which a a block has been
620 found being not sent and possibly to exist
622 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
624 new_nearest_unsent_d = d;
628 Add inexistent block to emerge queue.
630 if(block == NULL || surely_not_found_on_disk)
632 /*SharedPtr<JMutexAutoLock> lock
633 (m_num_blocks_in_emerge_queue.getLock());*/
635 //TODO: Get value from somewhere
636 // Allow only one block in emerge queue
637 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
639 // Add it to the emerge queue and trigger the thread
642 if(generate == false)
643 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
645 server->m_emerge_queue.addBlock(peer_id, p, flags);
646 server->m_emergethread.trigger();
657 PrioritySortedBlockTransfer q((float)d, p, peer_id);
661 num_blocks_selected += 1;
666 if(new_nearest_unsent_d != -1)
668 JMutexAutoLock lock(m_blocks_sent_mutex);
669 m_nearest_unsent_d = new_nearest_unsent_d;
673 void RemoteClient::SendObjectData(
676 core::map<v3s16, bool> &stepped_blocks
679 DSTACK(__FUNCTION_NAME);
681 // Can't send anything without knowing version
682 if(serialization_version == SER_FMT_VER_INVALID)
684 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
690 Send a TOCLIENT_OBJECTDATA packet.
694 u16 number of player positions
705 std::ostringstream os(std::ios_base::binary);
709 writeU16(buf, TOCLIENT_OBJECTDATA);
710 os.write((char*)buf, 2);
713 Get and write player data
716 // Get connected players
717 core::list<Player*> players = server->m_env.getPlayers(true);
719 // Write player count
720 u16 playercount = players.size();
721 writeU16(buf, playercount);
722 os.write((char*)buf, 2);
724 core::list<Player*>::Iterator i;
725 for(i = players.begin();
726 i != players.end(); i++)
730 v3f pf = player->getPosition();
731 v3f sf = player->getSpeed();
733 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
734 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
735 s32 pitch_i (player->getPitch() * 100);
736 s32 yaw_i (player->getYaw() * 100);
738 writeU16(buf, player->peer_id);
739 os.write((char*)buf, 2);
740 writeV3S32(buf, position_i);
741 os.write((char*)buf, 12);
742 writeV3S32(buf, speed_i);
743 os.write((char*)buf, 12);
744 writeS32(buf, pitch_i);
745 os.write((char*)buf, 4);
746 writeS32(buf, yaw_i);
747 os.write((char*)buf, 4);
751 Get and write object data
757 For making players to be able to build to their nearby
758 environment (building is not possible on blocks that are not
761 - Add blocks to emerge queue if they are not found
763 SUGGESTION: These could be ignored from the backside of the player
766 Player *player = server->m_env.getPlayer(peer_id);
770 v3f playerpos = player->getPosition();
771 v3f playerspeed = player->getSpeed();
773 v3s16 center_nodepos = floatToInt(playerpos);
774 v3s16 center = getNodeBlockPos(center_nodepos);
776 s16 d_max = g_settings.getS16("active_object_range");
778 // Number of blocks whose objects were written to bos
781 std::ostringstream bos(std::ios_base::binary);
783 for(s16 d = 0; d <= d_max; d++)
785 core::list<v3s16> list;
786 getFacePositions(list, d);
788 core::list<v3s16>::Iterator li;
789 for(li=list.begin(); li!=list.end(); li++)
791 v3s16 p = *li + center;
794 Ignore blocks that haven't been sent to the client
797 JMutexAutoLock sentlock(m_blocks_sent_mutex);
798 if(m_blocks_sent.find(p) == NULL)
802 // Try stepping block and add it to a send queue
807 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
810 Step block if not in stepped_blocks and add to stepped_blocks.
812 if(stepped_blocks.find(p) == NULL)
814 block->stepObjects(dtime, true, server->getDayNightRatio());
815 stepped_blocks.insert(p, true);
816 block->setChangedFlag();
819 // Skip block if there are no objects
820 if(block->getObjectCount() == 0)
829 bos.write((char*)buf, 6);
832 block->serializeObjects(bos, serialization_version);
837 Stop collecting objects if data is already too big
839 // Sum of player and object data sizes
840 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
841 // break out if data too big
842 if(sum > MAX_OBJECTDATA_SIZE)
844 goto skip_subsequent;
848 catch(InvalidPositionException &e)
851 // Add it to the emerge queue and trigger the thread.
852 // Fetch the block only if it is on disk.
854 // Grab and increment counter
855 /*SharedPtr<JMutexAutoLock> lock
856 (m_num_blocks_in_emerge_queue.getLock());
857 m_num_blocks_in_emerge_queue.m_value++;*/
859 // Add to queue as an anonymous fetch from disk
860 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
861 server->m_emerge_queue.addBlock(0, p, flags);
862 server->m_emergethread.trigger();
870 writeU16(buf, blockcount);
871 os.write((char*)buf, 2);
873 // Write block objects
880 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
883 std::string s = os.str();
884 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
885 // Send as unreliable
886 server->m_con.Send(peer_id, 0, data, false);
889 void RemoteClient::GotBlock(v3s16 p)
891 JMutexAutoLock lock(m_blocks_sending_mutex);
892 JMutexAutoLock lock2(m_blocks_sent_mutex);
893 if(m_blocks_sending.find(p) != NULL)
894 m_blocks_sending.remove(p);
897 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
898 " m_blocks_sending"<<std::endl;*/
899 m_excess_gotblocks++;
901 m_blocks_sent.insert(p, true);
904 void RemoteClient::SentBlock(v3s16 p)
906 JMutexAutoLock lock(m_blocks_sending_mutex);
907 /*if(m_blocks_sending.size() > 15)
909 dstream<<"RemoteClient::SentBlock(): "
910 <<"m_blocks_sending.size()="
911 <<m_blocks_sending.size()<<std::endl;
913 if(m_blocks_sending.find(p) == NULL)
914 m_blocks_sending.insert(p, 0.0);
916 dstream<<"RemoteClient::SentBlock(): Sent block"
917 " already in m_blocks_sending"<<std::endl;
920 void RemoteClient::SetBlockNotSent(v3s16 p)
922 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
923 JMutexAutoLock sentlock(m_blocks_sent_mutex);
925 m_nearest_unsent_d = 0;
927 if(m_blocks_sending.find(p) != NULL)
928 m_blocks_sending.remove(p);
929 if(m_blocks_sent.find(p) != NULL)
930 m_blocks_sent.remove(p);
933 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
935 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
936 JMutexAutoLock sentlock(m_blocks_sent_mutex);
938 m_nearest_unsent_d = 0;
940 for(core::map<v3s16, MapBlock*>::Iterator
941 i = blocks.getIterator();
942 i.atEnd()==false; i++)
944 v3s16 p = i.getNode()->getKey();
946 if(m_blocks_sending.find(p) != NULL)
947 m_blocks_sending.remove(p);
948 if(m_blocks_sent.find(p) != NULL)
949 m_blocks_sent.remove(p);
957 PlayerInfo::PlayerInfo()
962 void PlayerInfo::PrintLine(std::ostream *s)
965 (*s)<<"\""<<name<<"\" ("
966 <<position.X<<","<<position.Y
967 <<","<<position.Z<<") ";
969 (*s)<<" avg_rtt="<<avg_rtt;
973 u32 PIChecksum(core::list<PlayerInfo> &l)
975 core::list<PlayerInfo>::Iterator i;
978 for(i=l.begin(); i!=l.end(); i++)
980 checksum += a * (i->id+1);
981 checksum ^= 0x435aafcd;
992 std::string mapsavedir,
996 m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
997 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
999 m_emergethread(this),
1000 m_time_of_day(9000),
1002 m_time_of_day_send_timer(0),
1004 m_mapsavedir(mapsavedir)
1006 //m_flowwater_timer = 0.0;
1007 m_liquid_transform_timer = 0.0;
1008 m_print_info_timer = 0.0;
1009 m_objectdata_timer = 0.0;
1010 m_emergethread_trigger_timer = 0.0;
1011 m_savemap_timer = 0.0;
1015 m_step_dtime_mutex.Init();
1019 m_env.deSerializePlayers(m_mapsavedir);
1025 m_env.serializePlayers(m_mapsavedir);
1030 JMutexAutoLock clientslock(m_con_mutex);
1032 for(core::map<u16, RemoteClient*>::Iterator
1033 i = m_clients.getIterator();
1034 i.atEnd() == false; i++)
1037 // NOTE: These are removed by env destructor
1039 u16 peer_id = i.getNode()->getKey();
1040 JMutexAutoLock envlock(m_env_mutex);
1041 m_env.removePlayer(peer_id);
1045 delete i.getNode()->getValue();
1049 void Server::start(unsigned short port)
1051 DSTACK(__FUNCTION_NAME);
1052 // Stop thread if already running
1055 // Initialize connection
1056 m_con.setTimeoutMs(30);
1060 m_thread.setRun(true);
1063 dout_server<<"Server started on port "<<port<<std::endl;
1068 DSTACK(__FUNCTION_NAME);
1069 // Stop threads (set run=false first so both start stopping)
1070 m_thread.setRun(false);
1071 m_emergethread.setRun(false);
1073 m_emergethread.stop();
1075 dout_server<<"Server threads stopped"<<std::endl;
1078 void Server::step(float dtime)
1080 DSTACK(__FUNCTION_NAME);
1085 JMutexAutoLock lock(m_step_dtime_mutex);
1086 m_step_dtime += dtime;
1090 void Server::AsyncRunStep()
1092 DSTACK(__FUNCTION_NAME);
1096 JMutexAutoLock lock1(m_step_dtime_mutex);
1097 dtime = m_step_dtime;
1100 // Send blocks to clients
1106 //dstream<<"Server steps "<<dtime<<std::endl;
1107 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1110 JMutexAutoLock lock1(m_step_dtime_mutex);
1111 m_step_dtime -= dtime;
1118 m_uptime.set(m_uptime.get() + dtime);
1122 Update m_time_of_day
1125 m_time_counter += dtime;
1126 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1127 u32 units = (u32)(m_time_counter*speed);
1128 m_time_counter -= (f32)units / speed;
1129 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1131 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1134 Send to clients at constant intervals
1137 m_time_of_day_send_timer -= dtime;
1138 if(m_time_of_day_send_timer < 0.0)
1140 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1142 //JMutexAutoLock envlock(m_env_mutex);
1143 JMutexAutoLock conlock(m_con_mutex);
1145 for(core::map<u16, RemoteClient*>::Iterator
1146 i = m_clients.getIterator();
1147 i.atEnd() == false; i++)
1149 RemoteClient *client = i.getNode()->getValue();
1150 //Player *player = m_env.getPlayer(client->peer_id);
1152 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1153 m_time_of_day.get());
1155 m_con.Send(client->peer_id, 0, data, true);
1161 // Process connection's timeouts
1162 JMutexAutoLock lock2(m_con_mutex);
1163 m_con.RunTimeouts(dtime);
1167 // This has to be called so that the client list gets synced
1168 // with the peer list of the connection
1169 handlePeerChanges();
1174 // This also runs Map's timers
1175 JMutexAutoLock lock(m_env_mutex);
1186 m_liquid_transform_timer += dtime;
1187 if(m_liquid_transform_timer >= 1.00)
1189 m_liquid_transform_timer -= 1.00;
1191 JMutexAutoLock lock(m_env_mutex);
1193 core::map<v3s16, MapBlock*> modified_blocks;
1194 m_env.getMap().transformLiquids(modified_blocks);
1199 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1200 ServerMap &map = ((ServerMap&)m_env.getMap());
1201 map.updateLighting(modified_blocks, lighting_modified_blocks);
1203 // Add blocks modified by lighting to modified_blocks
1204 for(core::map<v3s16, MapBlock*>::Iterator
1205 i = lighting_modified_blocks.getIterator();
1206 i.atEnd() == false; i++)
1208 MapBlock *block = i.getNode()->getValue();
1209 modified_blocks.insert(block->getPos(), block);
1213 Set the modified blocks unsent for all the clients
1216 JMutexAutoLock lock2(m_con_mutex);
1218 for(core::map<u16, RemoteClient*>::Iterator
1219 i = m_clients.getIterator();
1220 i.atEnd() == false; i++)
1222 RemoteClient *client = i.getNode()->getValue();
1224 if(modified_blocks.size() > 0)
1226 // Remove block from sent history
1227 client->SetBlocksNotSent(modified_blocks);
1232 // Periodically print some info
1234 float &counter = m_print_info_timer;
1240 JMutexAutoLock lock2(m_con_mutex);
1242 for(core::map<u16, RemoteClient*>::Iterator
1243 i = m_clients.getIterator();
1244 i.atEnd() == false; i++)
1246 //u16 peer_id = i.getNode()->getKey();
1247 RemoteClient *client = i.getNode()->getValue();
1248 client->PrintInfo(std::cout);
1256 NOTE: Some of this could be moved to RemoteClient
1260 JMutexAutoLock envlock(m_env_mutex);
1261 JMutexAutoLock conlock(m_con_mutex);
1263 for(core::map<u16, RemoteClient*>::Iterator
1264 i = m_clients.getIterator();
1265 i.atEnd() == false; i++)
1267 RemoteClient *client = i.getNode()->getValue();
1268 Player *player = m_env.getPlayer(client->peer_id);
1270 JMutexAutoLock digmutex(client->m_dig_mutex);
1272 if(client->m_dig_tool_item == -1)
1275 client->m_dig_time_remaining -= dtime;
1277 if(client->m_dig_time_remaining > 0)
1279 client->m_time_from_building.set(0.0);
1283 v3s16 p_under = client->m_dig_position;
1285 // Mandatory parameter; actually used for nothing
1286 core::map<v3s16, MapBlock*> modified_blocks;
1292 // Get material at position
1293 material = m_env.getMap().getNode(p_under).d;
1294 // If it's not diggable, do nothing
1295 if(content_diggable(material) == false)
1297 derr_server<<"Server: Not finishing digging: Node not diggable"
1299 client->m_dig_tool_item = -1;
1303 catch(InvalidPositionException &e)
1305 derr_server<<"Server: Not finishing digging: Node not found"
1307 client->m_dig_tool_item = -1;
1313 SharedBuffer<u8> reply(replysize);
1314 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1315 writeS16(&reply[2], p_under.X);
1316 writeS16(&reply[4], p_under.Y);
1317 writeS16(&reply[6], p_under.Z);
1319 m_con.SendToAll(0, reply, true);
1321 if(g_settings.getBool("creative_mode") == false)
1323 // Add to inventory and send inventory
1324 InventoryItem *item = new MaterialItem(material, 1);
1325 player->inventory.addItem("main", item);
1326 SendInventory(player->peer_id);
1331 (this takes some time so it is done after the quick stuff)
1333 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1339 // Update water pressure around modification
1340 // This also adds it to m_flow_active_nodes if appropriate
1342 MapVoxelManipulator v(&m_env.getMap());
1343 v.m_disable_water_climb =
1344 g_settings.getBool("disable_water_climb");
1346 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1350 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1352 catch(ProcessingLimitException &e)
1354 dstream<<"Processing limit reached (1)"<<std::endl;
1357 v.blitBack(modified_blocks);
1362 // Send object positions
1364 float &counter = m_objectdata_timer;
1366 if(counter >= g_settings.getFloat("objectdata_interval"))
1368 JMutexAutoLock lock1(m_env_mutex);
1369 JMutexAutoLock lock2(m_con_mutex);
1370 SendObjectData(counter);
1376 // Trigger emergethread (it gets somehow gets to a
1377 // non-triggered but bysy state sometimes)
1379 float &counter = m_emergethread_trigger_timer;
1385 m_emergethread.trigger();
1391 float &counter = m_savemap_timer;
1393 if(counter >= g_settings.getFloat("server_map_save_interval"))
1397 JMutexAutoLock lock(m_env_mutex);
1399 // Save only changed parts
1400 m_env.getMap().save(true);
1402 // Delete unused sectors
1403 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1404 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1405 if(deleted_count > 0)
1407 dout_server<<"Server: Unloaded "<<deleted_count
1408 <<" sectors from memory"<<std::endl;
1412 m_env.serializePlayers(m_mapsavedir);
1417 void Server::Receive()
1419 DSTACK(__FUNCTION_NAME);
1420 u32 data_maxsize = 10000;
1421 Buffer<u8> data(data_maxsize);
1426 JMutexAutoLock conlock(m_con_mutex);
1427 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1430 // This has to be called so that the client list gets synced
1431 // with the peer list of the connection
1432 handlePeerChanges();
1434 ProcessData(*data, datasize, peer_id);
1436 catch(con::InvalidIncomingDataException &e)
1438 derr_server<<"Server::Receive(): "
1439 "InvalidIncomingDataException: what()="
1440 <<e.what()<<std::endl;
1442 catch(con::PeerNotFoundException &e)
1444 //NOTE: This is not needed anymore
1446 // The peer has been disconnected.
1447 // Find the associated player and remove it.
1449 /*JMutexAutoLock envlock(m_env_mutex);
1451 dout_server<<"ServerThread: peer_id="<<peer_id
1452 <<" has apparently closed connection. "
1453 <<"Removing player."<<std::endl;
1455 m_env.removePlayer(peer_id);*/
1459 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1461 DSTACK(__FUNCTION_NAME);
1462 // Environment is locked first.
1463 JMutexAutoLock envlock(m_env_mutex);
1464 JMutexAutoLock conlock(m_con_mutex);
1468 peer = m_con.GetPeer(peer_id);
1470 catch(con::PeerNotFoundException &e)
1472 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1473 <<peer_id<<" not found"<<std::endl;
1477 //u8 peer_ser_ver = peer->serialization_version;
1478 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1486 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1488 if(command == TOSERVER_INIT)
1490 // [0] u16 TOSERVER_INIT
1491 // [2] u8 SER_FMT_VER_HIGHEST
1492 // [3] u8[20] player_name
1497 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1498 <<peer->id<<std::endl;
1500 // First byte after command is maximum supported
1501 // serialization version
1502 u8 client_max = data[2];
1503 u8 our_max = SER_FMT_VER_HIGHEST;
1504 // Use the highest version supported by both
1505 u8 deployed = core::min_(client_max, our_max);
1506 // If it's lower than the lowest supported, give up.
1507 if(deployed < SER_FMT_VER_LOWEST)
1508 deployed = SER_FMT_VER_INVALID;
1510 //peer->serialization_version = deployed;
1511 getClient(peer->id)->pending_serialization_version = deployed;
1513 if(deployed == SER_FMT_VER_INVALID)
1515 derr_server<<DTIME<<"Server: Cannot negotiate "
1516 "serialization version with peer "
1517 <<peer_id<<std::endl;
1526 const u32 playername_size = 20;
1527 char playername[playername_size];
1528 for(u32 i=0; i<playername_size-1; i++)
1530 playername[i] = data[3+i];
1532 playername[playername_size-1] = 0;
1535 Player *player = emergePlayer(playername, "", peer_id);
1536 //Player *player = m_env.getPlayer(peer_id);
1539 // DEBUG: Test serialization
1540 std::ostringstream test_os;
1541 player->serialize(test_os);
1542 dstream<<"Player serialization test: \""<<test_os.str()
1544 std::istringstream test_is(test_os.str());
1545 player->deSerialize(test_is);
1548 // If failed, cancel
1551 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1552 <<": failed to emerge player"<<std::endl;
1557 // If a client is already connected to the player, cancel
1558 if(player->peer_id != 0)
1560 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1561 <<" tried to connect to "
1562 "an already connected player (peer_id="
1563 <<player->peer_id<<")"<<std::endl;
1566 // Set client of player
1567 player->peer_id = peer_id;
1570 // Check if player doesn't exist
1572 throw con::InvalidIncomingDataException
1573 ("Server::ProcessData(): INIT: Player doesn't exist");
1575 /*// update name if it was supplied
1576 if(datasize >= 20+3)
1579 player->updateName((const char*)&data[3]);
1582 // Now answer with a TOCLIENT_INIT
1584 SharedBuffer<u8> reply(2+1+6);
1585 writeU16(&reply[0], TOCLIENT_INIT);
1586 writeU8(&reply[2], deployed);
1587 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1589 m_con.Send(peer_id, 0, reply, true);
1593 if(command == TOSERVER_INIT2)
1595 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1596 <<peer->id<<std::endl;
1599 getClient(peer->id)->serialization_version
1600 = getClient(peer->id)->pending_serialization_version;
1603 Send some initialization data
1606 // Send player info to all players
1609 // Send inventory to player
1610 SendInventory(peer->id);
1614 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1615 m_time_of_day.get());
1616 m_con.Send(peer->id, 0, data, true);
1619 // Send information about server to player in chat
1621 std::wostringstream os(std::ios_base::binary);
1624 os<<L"uptime="<<m_uptime.get();
1625 // Information about clients
1627 for(core::map<u16, RemoteClient*>::Iterator
1628 i = m_clients.getIterator();
1629 i.atEnd() == false; i++)
1631 // Get client and check that it is valid
1632 RemoteClient *client = i.getNode()->getValue();
1633 assert(client->peer_id == i.getNode()->getKey());
1634 if(client->serialization_version == SER_FMT_VER_INVALID)
1637 Player *player = m_env.getPlayer(client->peer_id);
1638 // Get name of player
1639 std::wstring name = L"unknown";
1641 name = narrow_to_wide(player->getName());
1642 // Add name to information string
1647 SendChatMessage(peer_id, os.str());
1650 // Send information about joining in chat
1652 std::wstring name = L"unknown";
1653 Player *player = m_env.getPlayer(peer_id);
1655 name = narrow_to_wide(player->getName());
1657 std::wstring message;
1660 message += L" joined game";
1661 BroadcastChatMessage(message);
1667 if(peer_ser_ver == SER_FMT_VER_INVALID)
1669 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1670 " serialization format invalid or not initialized."
1671 " Skipping incoming command="<<command<<std::endl;
1675 Player *player = m_env.getPlayer(peer_id);
1678 derr_server<<"Server::ProcessData(): Cancelling: "
1679 "No player for peer_id="<<peer_id
1683 if(command == TOSERVER_PLAYERPOS)
1685 if(datasize < 2+12+12+4+4)
1689 v3s32 ps = readV3S32(&data[start+2]);
1690 v3s32 ss = readV3S32(&data[start+2+12]);
1691 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1692 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1693 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1694 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1695 pitch = wrapDegrees(pitch);
1696 yaw = wrapDegrees(yaw);
1697 player->setPosition(position);
1698 player->setSpeed(speed);
1699 player->setPitch(pitch);
1700 player->setYaw(yaw);
1702 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1703 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1704 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1706 else if(command == TOSERVER_GOTBLOCKS)
1719 u16 count = data[2];
1720 for(u16 i=0; i<count; i++)
1722 if((s16)datasize < 2+1+(i+1)*6)
1723 throw con::InvalidIncomingDataException
1724 ("GOTBLOCKS length is too short");
1725 v3s16 p = readV3S16(&data[2+1+i*6]);
1726 /*dstream<<"Server: GOTBLOCKS ("
1727 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1728 RemoteClient *client = getClient(peer_id);
1729 client->GotBlock(p);
1732 else if(command == TOSERVER_DELETEDBLOCKS)
1745 u16 count = data[2];
1746 for(u16 i=0; i<count; i++)
1748 if((s16)datasize < 2+1+(i+1)*6)
1749 throw con::InvalidIncomingDataException
1750 ("DELETEDBLOCKS length is too short");
1751 v3s16 p = readV3S16(&data[2+1+i*6]);
1752 /*dstream<<"Server: DELETEDBLOCKS ("
1753 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1754 RemoteClient *client = getClient(peer_id);
1755 client->SetBlockNotSent(p);
1758 else if(command == TOSERVER_CLICK_OBJECT)
1765 [2] u8 button (0=left, 1=right)
1770 u8 button = readU8(&data[2]);
1772 p.X = readS16(&data[3]);
1773 p.Y = readS16(&data[5]);
1774 p.Z = readS16(&data[7]);
1775 s16 id = readS16(&data[9]);
1776 //u16 item_i = readU16(&data[11]);
1778 MapBlock *block = NULL;
1781 block = m_env.getMap().getBlockNoCreate(p);
1783 catch(InvalidPositionException &e)
1785 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1789 MapBlockObject *obj = block->getObject(id);
1793 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1797 //TODO: Check that object is reasonably close
1802 InventoryList *ilist = player->inventory.getList("main");
1803 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1806 // Skip if inventory has no free space
1807 if(ilist->getUsedSlots() == ilist->getSize())
1809 dout_server<<"Player inventory has no free space"<<std::endl;
1814 Create the inventory item
1816 InventoryItem *item = NULL;
1817 // If it is an item-object, take the item from it
1818 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1820 item = ((ItemObject*)obj)->createInventoryItem();
1822 // Else create an item of the object
1825 item = new MapBlockObjectItem
1826 (obj->getInventoryString());
1829 // Add to inventory and send inventory
1830 ilist->addItem(item);
1831 SendInventory(player->peer_id);
1834 // Remove from block
1835 block->removeObject(id);
1838 else if(command == TOSERVER_GROUND_ACTION)
1846 [3] v3s16 nodepos_undersurface
1847 [9] v3s16 nodepos_abovesurface
1852 2: stop digging (all parameters ignored)
1854 u8 action = readU8(&data[2]);
1856 p_under.X = readS16(&data[3]);
1857 p_under.Y = readS16(&data[5]);
1858 p_under.Z = readS16(&data[7]);
1860 p_over.X = readS16(&data[9]);
1861 p_over.Y = readS16(&data[11]);
1862 p_over.Z = readS16(&data[13]);
1863 u16 item_i = readU16(&data[15]);
1865 //TODO: Check that target is reasonably close
1873 NOTE: This can be used in the future to check if
1874 somebody is cheating, by checking the timing.
1881 else if(action == 2)
1884 RemoteClient *client = getClient(peer->id);
1885 JMutexAutoLock digmutex(client->m_dig_mutex);
1886 client->m_dig_tool_item = -1;
1891 3: Digging completed
1893 else if(action == 3)
1895 // Mandatory parameter; actually used for nothing
1896 core::map<v3s16, MapBlock*> modified_blocks;
1899 u8 mineral = MINERAL_NONE;
1903 MapNode n = m_env.getMap().getNode(p_under);
1904 // Get material at position
1906 // If it's not diggable, do nothing
1907 if(content_diggable(material) == false)
1909 derr_server<<"Server: Not finishing digging: Node not diggable"
1914 mineral = n.getMineral();
1916 catch(InvalidPositionException &e)
1918 derr_server<<"Server: Not finishing digging: Node not found."
1919 <<" Adding block to emerge queue."
1921 m_emerge_queue.addBlock(peer_id,
1922 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1927 Send the removal to all other clients
1932 SharedBuffer<u8> reply(replysize);
1933 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1934 writeS16(&reply[2], p_under.X);
1935 writeS16(&reply[4], p_under.Y);
1936 writeS16(&reply[6], p_under.Z);
1938 for(core::map<u16, RemoteClient*>::Iterator
1939 i = m_clients.getIterator();
1940 i.atEnd() == false; i++)
1942 // Get client and check that it is valid
1943 RemoteClient *client = i.getNode()->getValue();
1944 assert(client->peer_id == i.getNode()->getKey());
1945 if(client->serialization_version == SER_FMT_VER_INVALID)
1948 // Don't send if it's the same one
1949 if(peer_id == client->peer_id)
1953 m_con.Send(client->peer_id, 0, reply, true);
1957 Update and send inventory
1960 if(g_settings.getBool("creative_mode") == false)
1965 InventoryList *mlist = player->inventory.getList("main");
1968 InventoryItem *item = mlist->getItem(item_i);
1969 if(item && (std::string)item->getName() == "ToolItem")
1971 ToolItem *titem = (ToolItem*)item;
1972 std::string toolname = titem->getToolName();
1974 // Get digging properties for material and tool
1975 DiggingProperties prop =
1976 getDiggingProperties(material, toolname);
1978 if(prop.diggable == false)
1980 derr_server<<"Server: WARNING: Player digged"
1981 <<" with impossible material + tool"
1982 <<" combination"<<std::endl;
1985 bool weared_out = titem->addWear(prop.wear);
1989 mlist->deleteItem(item_i);
1995 Add digged item to inventory
1998 InventoryItem *item = NULL;
2000 if(mineral != MINERAL_NONE)
2001 item = getDiggedMineralItem(mineral);
2004 item = new MaterialItem(material, 1);
2006 player->inventory.addItem("main", item);
2011 SendInventory(player->peer_id);
2016 (this takes some time so it is done after the quick stuff)
2018 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2025 // Update water pressure around modification
2026 // This also adds it to m_flow_active_nodes if appropriate
2028 MapVoxelManipulator v(&m_env.getMap());
2029 v.m_disable_water_climb =
2030 g_settings.getBool("disable_water_climb");
2032 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2036 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2038 catch(ProcessingLimitException &e)
2040 dstream<<"Processing limit reached (1)"<<std::endl;
2043 v.blitBack(modified_blocks);
2050 else if(action == 1)
2053 InventoryList *ilist = player->inventory.getList("main");
2058 InventoryItem *item = ilist->getItem(item_i);
2060 // If there is no item, it is not possible to add it anywhere
2065 Handle material items
2067 if(std::string("MaterialItem") == item->getName())
2070 // Don't add a node if this is not a free space
2071 MapNode n2 = m_env.getMap().getNode(p_over);
2072 if(content_buildable_to(n2.d) == false)
2075 catch(InvalidPositionException &e)
2077 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2078 <<" Adding block to emerge queue."
2080 m_emerge_queue.addBlock(peer_id,
2081 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2085 // Reset build time counter
2086 getClient(peer->id)->m_time_from_building.set(0.0);
2089 MaterialItem *mitem = (MaterialItem*)item;
2091 n.d = mitem->getMaterial();
2092 if(content_features(n.d).wall_mounted)
2093 n.dir = packDir(p_under - p_over);
2097 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2098 SharedBuffer<u8> reply(replysize);
2099 writeU16(&reply[0], TOCLIENT_ADDNODE);
2100 writeS16(&reply[2], p_over.X);
2101 writeS16(&reply[4], p_over.Y);
2102 writeS16(&reply[6], p_over.Z);
2103 n.serialize(&reply[8], peer_ser_ver);
2105 m_con.SendToAll(0, reply, true);
2110 InventoryList *ilist = player->inventory.getList("main");
2111 if(g_settings.getBool("creative_mode") == false && ilist)
2113 // Remove from inventory and send inventory
2114 if(mitem->getCount() == 1)
2115 ilist->deleteItem(item_i);
2119 SendInventory(peer_id);
2125 This takes some time so it is done after the quick stuff
2127 core::map<v3s16, MapBlock*> modified_blocks;
2128 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2134 InventoryList *ilist = player->inventory.getList("main");
2135 if(g_settings.getBool("creative_mode") == false && ilist)
2137 // Remove from inventory and send inventory
2138 if(mitem->getCount() == 1)
2139 ilist->deleteItem(item_i);
2143 SendInventory(peer_id);
2149 This takes some time so it is done after the quick stuff
2151 core::map<v3s16, MapBlock*> modified_blocks;
2152 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2155 Set the modified blocks unsent for all the clients
2158 //JMutexAutoLock lock2(m_con_mutex);
2160 for(core::map<u16, RemoteClient*>::Iterator
2161 i = m_clients.getIterator();
2162 i.atEnd() == false; i++)
2164 RemoteClient *client = i.getNode()->getValue();
2166 if(modified_blocks.size() > 0)
2168 // Remove block from sent history
2169 client->SetBlocksNotSent(modified_blocks);
2179 // Update water pressure around modification
2180 // This also adds it to m_flow_active_nodes if appropriate
2182 MapVoxelManipulator v(&m_env.getMap());
2183 v.m_disable_water_climb =
2184 g_settings.getBool("disable_water_climb");
2186 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2190 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2192 catch(ProcessingLimitException &e)
2194 dstream<<"Processing limit reached (1)"<<std::endl;
2197 v.blitBack(modified_blocks);
2205 v3s16 blockpos = getNodeBlockPos(p_over);
2207 MapBlock *block = NULL;
2210 block = m_env.getMap().getBlockNoCreate(blockpos);
2212 catch(InvalidPositionException &e)
2214 derr_server<<"Error while placing object: "
2215 "block not found"<<std::endl;
2219 v3s16 block_pos_i_on_map = block->getPosRelative();
2220 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2222 v3f pos = intToFloat(p_over);
2223 pos -= block_pos_f_on_map;
2225 /*dout_server<<"pos="
2226 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2229 MapBlockObject *obj = NULL;
2232 Handle block object items
2234 if(std::string("MBOItem") == item->getName())
2236 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2238 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2239 "inventorystring=\""
2240 <<oitem->getInventoryString()
2241 <<"\""<<std::endl;*/
2243 obj = oitem->createObject
2244 (pos, player->getYaw(), player->getPitch());
2251 dout_server<<"Placing a miscellaneous item on map"
2254 Create an ItemObject that contains the item.
2256 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2257 std::ostringstream os(std::ios_base::binary);
2258 item->serialize(os);
2259 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2260 iobj->setItemString(os.str());
2266 derr_server<<"WARNING: item resulted in NULL object, "
2267 <<"not placing onto map"
2272 block->addObject(obj);
2274 dout_server<<"Placed object"<<std::endl;
2276 InventoryList *ilist = player->inventory.getList("main");
2277 if(g_settings.getBool("creative_mode") == false && ilist)
2279 // Remove from inventory and send inventory
2280 ilist->deleteItem(item_i);
2282 SendInventory(peer_id);
2290 Catch invalid actions
2294 derr_server<<"WARNING: Server: Invalid action "
2295 <<action<<std::endl;
2299 else if(command == TOSERVER_RELEASE)
2308 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2311 else if(command == TOSERVER_SIGNTEXT)
2320 std::string datastring((char*)&data[2], datasize-2);
2321 std::istringstream is(datastring, std::ios_base::binary);
2324 is.read((char*)buf, 6);
2325 v3s16 blockpos = readV3S16(buf);
2326 is.read((char*)buf, 2);
2327 s16 id = readS16(buf);
2328 is.read((char*)buf, 2);
2329 u16 textlen = readU16(buf);
2331 for(u16 i=0; i<textlen; i++)
2333 is.read((char*)buf, 1);
2334 text += (char)buf[0];
2337 MapBlock *block = NULL;
2340 block = m_env.getMap().getBlockNoCreate(blockpos);
2342 catch(InvalidPositionException &e)
2344 derr_server<<"Error while setting sign text: "
2345 "block not found"<<std::endl;
2349 MapBlockObject *obj = block->getObject(id);
2352 derr_server<<"Error while setting sign text: "
2353 "object not found"<<std::endl;
2357 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2359 derr_server<<"Error while setting sign text: "
2360 "object is not a sign"<<std::endl;
2364 ((SignObject*)obj)->setText(text);
2366 obj->getBlock()->setChangedFlag();
2368 else if(command == TOSERVER_INVENTORY_ACTION)
2370 /*// Ignore inventory changes if in creative mode
2371 if(g_settings.getBool("creative_mode") == true)
2373 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2377 // Strip command and create a stream
2378 std::string datastring((char*)&data[2], datasize-2);
2379 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2380 std::istringstream is(datastring, std::ios_base::binary);
2382 InventoryAction *a = InventoryAction::deSerialize(is);
2386 Handle craftresult specially if not in creative mode
2388 bool disable_action = false;
2389 if(a->getType() == IACTION_MOVE
2390 && g_settings.getBool("creative_mode") == false)
2392 IMoveAction *ma = (IMoveAction*)a;
2393 // Don't allow moving anything to craftresult
2394 if(ma->to_name == "craftresult")
2397 disable_action = true;
2399 // When something is removed from craftresult
2400 if(ma->from_name == "craftresult")
2402 disable_action = true;
2403 // Remove stuff from craft
2404 InventoryList *clist = player->inventory.getList("craft");
2407 u16 count = ma->count;
2410 clist->decrementMaterials(count);
2413 // Feed action to player inventory
2414 a->apply(&player->inventory);
2417 // If something appeared in craftresult, throw it
2419 InventoryList *rlist = player->inventory.getList("craftresult");
2420 InventoryList *mlist = player->inventory.getList("main");
2421 if(rlist && mlist && rlist->getUsedSlots() == 1)
2423 InventoryItem *item1 = rlist->changeItem(0, NULL);
2424 mlist->addItem(item1);
2428 if(disable_action == false)
2430 // Feed action to player inventory
2431 a->apply(&player->inventory);
2436 SendInventory(player->peer_id);
2440 dstream<<"TOSERVER_INVENTORY_ACTION: "
2441 <<"InventoryAction::deSerialize() returned NULL"
2445 else if(command == TOSERVER_CHAT_MESSAGE)
2453 std::string datastring((char*)&data[2], datasize-2);
2454 std::istringstream is(datastring, std::ios_base::binary);
2457 is.read((char*)buf, 2);
2458 u16 len = readU16(buf);
2460 std::wstring message;
2461 for(u16 i=0; i<len; i++)
2463 is.read((char*)buf, 2);
2464 message += (wchar_t)readU16(buf);
2467 // Get player name of this client
2468 std::wstring name = narrow_to_wide(player->getName());
2470 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2472 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2475 Send the message to all other clients
2477 for(core::map<u16, RemoteClient*>::Iterator
2478 i = m_clients.getIterator();
2479 i.atEnd() == false; i++)
2481 // Get client and check that it is valid
2482 RemoteClient *client = i.getNode()->getValue();
2483 assert(client->peer_id == i.getNode()->getKey());
2484 if(client->serialization_version == SER_FMT_VER_INVALID)
2487 // Don't send if it's the same one
2488 if(peer_id == client->peer_id)
2491 SendChatMessage(client->peer_id, line);
2496 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2497 "unknown command "<<command<<std::endl;
2501 catch(SendFailedException &e)
2503 derr_server<<"Server::ProcessData(): SendFailedException: "
2509 /*void Server::Send(u16 peer_id, u16 channelnum,
2510 SharedBuffer<u8> data, bool reliable)
2512 JMutexAutoLock lock(m_con_mutex);
2513 m_con.Send(peer_id, channelnum, data, reliable);
2516 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2518 DSTACK(__FUNCTION_NAME);
2520 Create a packet with the block in the right format
2523 std::ostringstream os(std::ios_base::binary);
2524 block->serialize(os, ver);
2525 std::string s = os.str();
2526 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2528 u32 replysize = 8 + blockdata.getSize();
2529 SharedBuffer<u8> reply(replysize);
2530 v3s16 p = block->getPos();
2531 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2532 writeS16(&reply[2], p.X);
2533 writeS16(&reply[4], p.Y);
2534 writeS16(&reply[6], p.Z);
2535 memcpy(&reply[8], *blockdata, blockdata.getSize());
2537 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2538 <<": \tpacket size: "<<replysize<<std::endl;*/
2543 m_con.Send(peer_id, 1, reply, true);
2546 core::list<PlayerInfo> Server::getPlayerInfo()
2548 DSTACK(__FUNCTION_NAME);
2549 JMutexAutoLock envlock(m_env_mutex);
2550 JMutexAutoLock conlock(m_con_mutex);
2552 core::list<PlayerInfo> list;
2554 core::list<Player*> players = m_env.getPlayers();
2556 core::list<Player*>::Iterator i;
2557 for(i = players.begin();
2558 i != players.end(); i++)
2562 Player *player = *i;
2565 con::Peer *peer = m_con.GetPeer(player->peer_id);
2566 // Copy info from peer to info struct
2568 info.address = peer->address;
2569 info.avg_rtt = peer->avg_rtt;
2571 catch(con::PeerNotFoundException &e)
2573 // Set dummy peer info
2575 info.address = Address(0,0,0,0,0);
2579 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2580 info.position = player->getPosition();
2582 list.push_back(info);
2588 void Server::peerAdded(con::Peer *peer)
2590 DSTACK(__FUNCTION_NAME);
2591 dout_server<<"Server::peerAdded(): peer->id="
2592 <<peer->id<<std::endl;
2595 c.type = PEER_ADDED;
2596 c.peer_id = peer->id;
2598 m_peer_change_queue.push_back(c);
2601 void Server::deletingPeer(con::Peer *peer, bool timeout)
2603 DSTACK(__FUNCTION_NAME);
2604 dout_server<<"Server::deletingPeer(): peer->id="
2605 <<peer->id<<", timeout="<<timeout<<std::endl;
2608 c.type = PEER_REMOVED;
2609 c.peer_id = peer->id;
2610 c.timeout = timeout;
2611 m_peer_change_queue.push_back(c);
2614 void Server::SendObjectData(float dtime)
2616 DSTACK(__FUNCTION_NAME);
2618 core::map<v3s16, bool> stepped_blocks;
2620 for(core::map<u16, RemoteClient*>::Iterator
2621 i = m_clients.getIterator();
2622 i.atEnd() == false; i++)
2624 u16 peer_id = i.getNode()->getKey();
2625 RemoteClient *client = i.getNode()->getValue();
2626 assert(client->peer_id == peer_id);
2628 if(client->serialization_version == SER_FMT_VER_INVALID)
2631 client->SendObjectData(this, dtime, stepped_blocks);
2635 void Server::SendPlayerInfos()
2637 DSTACK(__FUNCTION_NAME);
2639 //JMutexAutoLock envlock(m_env_mutex);
2641 // Get connected players
2642 core::list<Player*> players = m_env.getPlayers(true);
2644 u32 player_count = players.getSize();
2645 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2647 SharedBuffer<u8> data(datasize);
2648 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2651 core::list<Player*>::Iterator i;
2652 for(i = players.begin();
2653 i != players.end(); i++)
2655 Player *player = *i;
2657 /*dstream<<"Server sending player info for player with "
2658 "peer_id="<<player->peer_id<<std::endl;*/
2660 writeU16(&data[start], player->peer_id);
2661 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2662 start += 2+PLAYERNAME_SIZE;
2665 //JMutexAutoLock conlock(m_con_mutex);
2668 m_con.SendToAll(0, data, true);
2686 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2692 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2698 enum ItemSpecType type;
2699 // Only other one of these is used
2705 items: a pointer to an array of 9 pointers to items
2706 specs: a pointer to an array of 9 ItemSpecs
2708 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2710 u16 items_min_x = 100;
2711 u16 items_max_x = 100;
2712 u16 items_min_y = 100;
2713 u16 items_max_y = 100;
2714 for(u16 y=0; y<3; y++)
2715 for(u16 x=0; x<3; x++)
2717 if(items[y*3 + x] == NULL)
2719 if(items_min_x == 100 || x < items_min_x)
2721 if(items_min_y == 100 || y < items_min_y)
2723 if(items_max_x == 100 || x > items_max_x)
2725 if(items_max_y == 100 || y > items_max_y)
2728 // No items at all, just return false
2729 if(items_min_x == 100)
2732 u16 items_w = items_max_x - items_min_x + 1;
2733 u16 items_h = items_max_y - items_min_y + 1;
2735 u16 specs_min_x = 100;
2736 u16 specs_max_x = 100;
2737 u16 specs_min_y = 100;
2738 u16 specs_max_y = 100;
2739 for(u16 y=0; y<3; y++)
2740 for(u16 x=0; x<3; x++)
2742 if(specs[y*3 + x].type == ITEM_NONE)
2744 if(specs_min_x == 100 || x < specs_min_x)
2746 if(specs_min_y == 100 || y < specs_min_y)
2748 if(specs_max_x == 100 || x > specs_max_x)
2750 if(specs_max_y == 100 || y > specs_max_y)
2753 // No specs at all, just return false
2754 if(specs_min_x == 100)
2757 u16 specs_w = specs_max_x - specs_min_x + 1;
2758 u16 specs_h = specs_max_y - specs_min_y + 1;
2761 if(items_w != specs_w || items_h != specs_h)
2764 for(u16 y=0; y<specs_h; y++)
2765 for(u16 x=0; x<specs_w; x++)
2767 u16 items_x = items_min_x + x;
2768 u16 items_y = items_min_y + y;
2769 u16 specs_x = specs_min_x + x;
2770 u16 specs_y = specs_min_y + y;
2771 InventoryItem *item = items[items_y * 3 + items_x];
2772 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2774 if(spec.type == ITEM_NONE)
2776 // Has to be no item
2782 // There should be an item
2786 std::string itemname = item->getName();
2788 if(spec.type == ITEM_MATERIAL)
2790 if(itemname != "MaterialItem")
2792 MaterialItem *mitem = (MaterialItem*)item;
2793 if(mitem->getMaterial() != spec.num)
2796 else if(spec.type == ITEM_CRAFT)
2798 if(itemname != "CraftItem")
2800 CraftItem *mitem = (CraftItem*)item;
2801 if(mitem->getSubName() != spec.name)
2804 else if(spec.type == ITEM_TOOL)
2806 // Not supported yet
2809 else if(spec.type == ITEM_MBO)
2811 // Not supported yet
2816 // Not supported yet
2824 void Server::SendInventory(u16 peer_id)
2826 DSTACK(__FUNCTION_NAME);
2828 Player* player = m_env.getPlayer(peer_id);
2831 Calculate crafting stuff
2833 if(g_settings.getBool("creative_mode") == false)
2835 InventoryList *clist = player->inventory.getList("craft");
2836 InventoryList *rlist = player->inventory.getList("craftresult");
2839 rlist->clearItems();
2843 InventoryItem *items[9];
2844 for(u16 i=0; i<9; i++)
2846 items[i] = clist->getItem(i);
2855 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2856 if(checkItemCombination(items, specs))
2858 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2867 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2868 if(checkItemCombination(items, specs))
2870 rlist->addItem(new CraftItem("Stick", 4));
2879 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2880 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2881 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2882 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2883 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2884 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2885 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2886 if(checkItemCombination(items, specs))
2888 rlist->addItem(new MapBlockObjectItem("Sign"));
2897 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2898 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2899 if(checkItemCombination(items, specs))
2901 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2910 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2911 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2912 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2913 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2914 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2915 if(checkItemCombination(items, specs))
2917 rlist->addItem(new ToolItem("WPick", 0));
2926 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2927 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2928 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2929 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2930 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2931 if(checkItemCombination(items, specs))
2933 rlist->addItem(new ToolItem("STPick", 0));
2942 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2943 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2944 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2945 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2946 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2947 if(checkItemCombination(items, specs))
2949 rlist->addItem(new ToolItem("MesePick", 0));
2954 } // if creative_mode == false
2960 std::ostringstream os;
2961 //os.imbue(std::locale("C"));
2963 player->inventory.serialize(os);
2965 std::string s = os.str();
2967 SharedBuffer<u8> data(s.size()+2);
2968 writeU16(&data[0], TOCLIENT_INVENTORY);
2969 memcpy(&data[2], s.c_str(), s.size());
2972 m_con.Send(peer_id, 0, data, true);
2975 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2977 DSTACK(__FUNCTION_NAME);
2979 std::ostringstream os(std::ios_base::binary);
2983 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2984 os.write((char*)buf, 2);
2987 writeU16(buf, message.size());
2988 os.write((char*)buf, 2);
2991 for(u32 i=0; i<message.size(); i++)
2995 os.write((char*)buf, 2);
2999 std::string s = os.str();
3000 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3002 m_con.Send(peer_id, 0, data, true);
3005 void Server::BroadcastChatMessage(const std::wstring &message)
3007 for(core::map<u16, RemoteClient*>::Iterator
3008 i = m_clients.getIterator();
3009 i.atEnd() == false; i++)
3011 // Get client and check that it is valid
3012 RemoteClient *client = i.getNode()->getValue();
3013 assert(client->peer_id == i.getNode()->getKey());
3014 if(client->serialization_version == SER_FMT_VER_INVALID)
3017 SendChatMessage(client->peer_id, message);
3021 void Server::SendBlocks(float dtime)
3023 DSTACK(__FUNCTION_NAME);
3025 JMutexAutoLock envlock(m_env_mutex);
3027 core::array<PrioritySortedBlockTransfer> queue;
3029 s32 total_sending = 0;
3031 for(core::map<u16, RemoteClient*>::Iterator
3032 i = m_clients.getIterator();
3033 i.atEnd() == false; i++)
3035 RemoteClient *client = i.getNode()->getValue();
3036 assert(client->peer_id == i.getNode()->getKey());
3038 total_sending += client->SendingCount();
3040 if(client->serialization_version == SER_FMT_VER_INVALID)
3043 client->GetNextBlocks(this, dtime, queue);
3047 // Lowest priority number comes first.
3048 // Lowest is most important.
3051 JMutexAutoLock conlock(m_con_mutex);
3053 for(u32 i=0; i<queue.size(); i++)
3055 //TODO: Calculate limit dynamically
3056 if(total_sending >= g_settings.getS32
3057 ("max_simultaneous_block_sends_server_total"))
3060 PrioritySortedBlockTransfer q = queue[i];
3062 MapBlock *block = NULL;
3065 block = m_env.getMap().getBlockNoCreate(q.pos);
3067 catch(InvalidPositionException &e)
3072 RemoteClient *client = getClient(q.peer_id);
3074 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3076 client->SentBlock(q.pos);
3083 RemoteClient* Server::getClient(u16 peer_id)
3085 DSTACK(__FUNCTION_NAME);
3086 //JMutexAutoLock lock(m_con_mutex);
3087 core::map<u16, RemoteClient*>::Node *n;
3088 n = m_clients.find(peer_id);
3089 // A client should exist for all peers
3091 return n->getValue();
3094 void setCreativeInventory(Player *player)
3096 player->resetInventory();
3098 // Give some good picks
3100 InventoryItem *item = new ToolItem("STPick", 0);
3101 void* r = player->inventory.addItem("main", item);
3105 InventoryItem *item = new ToolItem("MesePick", 0);
3106 void* r = player->inventory.addItem("main", item);
3113 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3116 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3117 player->inventory.addItem("main", item);
3120 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3122 // Skip some materials
3123 if(i == CONTENT_WATER || i == CONTENT_TORCH
3124 || i == CONTENT_COALSTONE)
3127 InventoryItem *item = new MaterialItem(i, 1);
3128 player->inventory.addItem("main", item);
3132 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3133 void* r = player->inventory.addItem("main", item);
3138 Player *Server::emergePlayer(const char *name, const char *password,
3142 Try to get an existing player
3144 Player *player = m_env.getPlayer(name);
3147 // If player is already connected, cancel
3148 if(player->peer_id != 0)
3150 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3155 player->peer_id = peer_id;
3157 // Reset inventory to creative if in creative mode
3158 if(g_settings.getBool("creative_mode"))
3160 setCreativeInventory(player);
3167 If player with the wanted peer_id already exists, cancel.
3169 if(m_env.getPlayer(peer_id) != NULL)
3171 dstream<<"emergePlayer(): Player with wrong name but same"
3172 " peer_id already exists"<<std::endl;
3180 player = new ServerRemotePlayer();
3181 //player->peer_id = c.peer_id;
3182 //player->peer_id = PEER_ID_INEXISTENT;
3183 player->peer_id = peer_id;
3184 player->updateName(name);
3190 dstream<<"Server: Finding spawn place for player \""
3191 <<player->getName()<<"\""<<std::endl;
3195 f32 groundheight = 0;
3196 // Try to find a good place a few times
3197 for(s32 i=0; i<500; i++)
3200 // We're going to try to throw the player to this position
3201 nodepos = v2s16(-range + (myrand()%(range*2)),
3202 -range + (myrand()%(range*2)));
3203 v2s16 sectorpos = getNodeSectorPos(nodepos);
3205 m_env.getMap().emergeSector(sectorpos);
3206 // Get ground height at point
3207 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3208 // The sector should have been generated -> groundheight exists
3209 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3210 // Don't go underwater
3211 if(groundheight < WATER_LEVEL)
3213 //dstream<<"-> Underwater"<<std::endl;
3216 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3217 // Get block at point
3219 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3220 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3221 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3222 // Don't go inside ground
3224 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3225 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3226 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3227 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3228 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3229 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3231 dstream<<"-> Inside ground"<<std::endl;
3235 }catch(InvalidPositionException &e)
3237 dstream<<"-> Invalid position"<<std::endl;
3238 // Ignore invalid position
3242 // Found a good place
3243 dstream<<"Searched through "<<i<<" places."<<std::endl;
3248 // If no suitable place was not found, go above water at least.
3249 if(groundheight < WATER_LEVEL)
3250 groundheight = WATER_LEVEL;
3252 player->setPosition(intToFloat(v3s16(
3259 Add player to environment
3262 m_env.addPlayer(player);
3265 Add stuff to inventory
3268 if(g_settings.getBool("creative_mode"))
3270 setCreativeInventory(player);
3275 InventoryItem *item = new ToolItem("WPick", 32000);
3276 void* r = player->inventory.addItem("main", item);
3280 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3281 void* r = player->inventory.addItem("main", item);
3285 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3286 void* r = player->inventory.addItem("main", item);
3290 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3291 void* r = player->inventory.addItem("main", item);
3295 InventoryItem *item = new CraftItem("Stick", 4);
3296 void* r = player->inventory.addItem("main", item);
3300 InventoryItem *item = new ToolItem("WPick", 32000);
3301 void* r = player->inventory.addItem("main", item);
3305 InventoryItem *item = new ToolItem("STPick", 32000);
3306 void* r = player->inventory.addItem("main", item);
3309 /*// Give some lights
3311 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3312 bool r = player->inventory.addItem("main", item);
3316 for(u16 i=0; i<4; i++)
3318 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3319 bool r = player->inventory.addItem("main", item);
3322 /*// Give some other stuff
3324 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3325 bool r = player->inventory.addItem("main", item);
3332 } // create new player
3336 void Server::UpdateBlockWaterPressure(MapBlock *block,
3337 core::map<v3s16, MapBlock*> &modified_blocks)
3339 MapVoxelManipulator v(&m_env.getMap());
3340 v.m_disable_water_climb =
3341 g_settings.getBool("disable_water_climb");
3343 VoxelArea area(block->getPosRelative(),
3344 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3348 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3350 catch(ProcessingLimitException &e)
3352 dstream<<"Processing limit reached (1)"<<std::endl;
3355 v.blitBack(modified_blocks);
3359 void Server::handlePeerChange(PeerChange &c)
3361 JMutexAutoLock envlock(m_env_mutex);
3362 JMutexAutoLock conlock(m_con_mutex);
3364 if(c.type == PEER_ADDED)
3371 core::map<u16, RemoteClient*>::Node *n;
3372 n = m_clients.find(c.peer_id);
3373 // The client shouldn't already exist
3377 RemoteClient *client = new RemoteClient();
3378 client->peer_id = c.peer_id;
3379 m_clients.insert(client->peer_id, client);
3382 else if(c.type == PEER_REMOVED)
3389 core::map<u16, RemoteClient*>::Node *n;
3390 n = m_clients.find(c.peer_id);
3391 // The client should exist
3394 // Collect information about leaving in chat
3395 std::wstring message;
3397 std::wstring name = L"unknown";
3398 Player *player = m_env.getPlayer(c.peer_id);
3400 name = narrow_to_wide(player->getName());
3404 message += L" left game";
3406 message += L" (timed out)";
3411 m_env.removePlayer(c.peer_id);
3414 // Set player client disconnected
3416 Player *player = m_env.getPlayer(c.peer_id);
3418 player->peer_id = 0;
3422 delete m_clients[c.peer_id];
3423 m_clients.remove(c.peer_id);
3425 // Send player info to all remaining clients
3428 // Send leave chat message to all remaining clients
3429 BroadcastChatMessage(message);
3438 void Server::handlePeerChanges()
3440 while(m_peer_change_queue.size() > 0)
3442 PeerChange c = m_peer_change_queue.pop_front();
3444 dout_server<<"Server: Handling peer change: "
3445 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3448 handlePeerChange(c);
3452 void dedicated_server_loop(Server &server)
3454 DSTACK(__FUNCTION_NAME);
3456 std::cout<<std::endl;
3457 std::cout<<"========================"<<std::endl;
3458 std::cout<<"Running dedicated server"<<std::endl;
3459 std::cout<<"========================"<<std::endl;
3460 std::cout<<std::endl;
3464 // This is kind of a hack but can be done like this
3465 // because server.step() is very light
3469 static int counter = 0;
3475 core::list<PlayerInfo> list = server.getPlayerInfo();
3476 core::list<PlayerInfo>::Iterator i;
3477 static u32 sum_old = 0;
3478 u32 sum = PIChecksum(list);
3481 std::cout<<DTIME<<"Player info:"<<std::endl;
3482 for(i=list.begin(); i!=list.end(); i++)
3484 i->PrintLine(&std::cout);