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"
35 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
37 void * ServerThread::Thread()
41 DSTACK(__FUNCTION_NAME);
43 BEGIN_DEBUG_EXCEPTION_HANDLER
48 m_server->AsyncRunStep();
50 //dout_server<<"Running m_server->Receive()"<<std::endl;
53 catch(con::NoIncomingDataException &e)
56 catch(con::PeerNotFoundException &e)
58 dout_server<<"Server: PeerNotFoundException"<<std::endl;
62 END_DEBUG_EXCEPTION_HANDLER
67 void * EmergeThread::Thread()
71 DSTACK(__FUNCTION_NAME);
75 BEGIN_DEBUG_EXCEPTION_HANDLER
78 Get block info from queue, emerge them and send them
81 After queue is empty, exit.
85 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
89 SharedPtr<QueuedBlockEmerge> q(qptr);
93 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
95 //TimeTaker timer("block emerge");
98 Try to emerge it from somewhere.
100 If it is only wanted as optional, only loading from disk
105 Check if any peer wants it as non-optional. In that case it
108 Also decrement the emerge queue count in clients.
111 bool optional = true;
114 core::map<u16, u8>::Iterator i;
115 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
117 //u16 peer_id = i.getNode()->getKey();
120 u8 flags = i.getNode()->getValue();
121 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
127 /*dstream<<"EmergeThread: p="
128 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
129 <<"optional="<<optional<<std::endl;*/
131 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
133 core::map<v3s16, MapBlock*> changed_blocks;
134 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
136 MapBlock *block = NULL;
137 bool got_block = true;
138 core::map<v3s16, MapBlock*> modified_blocks;
142 //TimeTaker envlockwaittimer("block emerge envlock wait time");
145 JMutexAutoLock envlock(m_server->m_env_mutex);
147 //envlockwaittimer.stop();
149 //TimeTaker timer("block emerge (while env locked)");
152 bool only_from_disk = false;
155 only_from_disk = true;
157 // First check if the block already exists
160 block = map.getBlockNoCreate(p);
165 block = map.emergeBlock(
169 lighting_invalidated_blocks);
173 EXPERIMENTAL: Create a few other blocks too
180 lighting_invalidated_blocks);
186 lighting_invalidated_blocks);
190 // If it is a dummy, block was not found on disk
193 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
197 catch(InvalidPositionException &e)
200 // This happens when position is over limit.
206 if(debug && changed_blocks.size() > 0)
208 dout_server<<DTIME<<"Got changed_blocks: ";
209 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
210 i.atEnd() == false; i++)
212 MapBlock *block = i.getNode()->getValue();
213 v3s16 p = block->getPos();
214 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
216 dout_server<<std::endl;
221 Update water pressure
224 m_server->UpdateBlockWaterPressure(block, modified_blocks);
226 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
227 i.atEnd() == false; i++)
229 MapBlock *block = i.getNode()->getValue();
230 m_server->UpdateBlockWaterPressure(block, modified_blocks);
231 //v3s16 p = i.getNode()->getKey();
232 //m_server->UpdateBlockWaterPressure(p, modified_blocks);
237 Collect a list of blocks that have been modified in
238 addition to the fetched one.
241 // Add all the "changed blocks" to modified_blocks
242 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
243 i.atEnd() == false; i++)
245 MapBlock *block = i.getNode()->getValue();
246 modified_blocks.insert(block->getPos(), block);
249 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
250 <<" blocks"<<std::endl;*/
252 //TimeTaker timer("** updateLighting", g_device);
254 // Update lighting without locking the environment mutex,
255 // add modified blocks to changed blocks
256 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
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);
312 JMutexAutoLock lock(m_blocks_sent_mutex);
313 m_nearest_unsent_reset_timer += dtime;
316 // Won't send anything if already sending
318 JMutexAutoLock lock(m_blocks_sending_mutex);
320 if(m_blocks_sending.size() >= g_settings.getU16
321 ("max_simultaneous_block_sends_per_client"))
323 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
328 bool haxmode = g_settings.getBool("haxmode");
330 Player *player = server->m_env.getPlayer(peer_id);
332 assert(player != NULL);
334 v3f playerpos = player->getPosition();
335 v3f playerspeed = player->getSpeed();
337 v3s16 center_nodepos = floatToInt(playerpos);
339 v3s16 center = getNodeBlockPos(center_nodepos);
341 // Camera position and direction
343 playerpos + v3f(0, BS+BS/2, 0);
344 v3f camera_dir = v3f(0,0,1);
345 camera_dir.rotateYZBy(player->getPitch());
346 camera_dir.rotateXZBy(player->getYaw());
349 Get the starting value of the block finder radius.
351 s16 last_nearest_unsent_d;
354 JMutexAutoLock lock(m_blocks_sent_mutex);
356 if(m_last_center != center)
358 m_nearest_unsent_d = 0;
359 m_last_center = center;
362 /*dstream<<"m_nearest_unsent_reset_timer="
363 <<m_nearest_unsent_reset_timer<<std::endl;*/
364 if(m_nearest_unsent_reset_timer > 5.0)
366 m_nearest_unsent_reset_timer = 0;
367 m_nearest_unsent_d = 0;
368 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
371 last_nearest_unsent_d = m_nearest_unsent_d;
373 d_start = m_nearest_unsent_d;
376 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
377 ("max_simultaneous_block_sends_per_client");
378 u16 maximum_simultaneous_block_sends =
379 maximum_simultaneous_block_sends_setting;
382 Check the time from last addNode/removeNode.
384 Decrease send rate if player is building stuff.
387 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
388 m_time_from_building.m_value += dtime;
389 /*if(m_time_from_building.m_value
390 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
391 if(m_time_from_building.m_value < g_settings.getFloat(
392 "full_block_send_enable_min_time_from_building"))
394 maximum_simultaneous_block_sends
395 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
399 u32 num_blocks_selected;
401 JMutexAutoLock lock(m_blocks_sending_mutex);
402 num_blocks_selected = m_blocks_sending.size();
406 next time d will be continued from the d from which the nearest
407 unsent block was found this time.
409 This is because not necessarily any of the blocks found this
410 time are actually sent.
412 s32 new_nearest_unsent_d = -1;
414 // Serialization version used
415 //u8 ser_version = serialization_version;
417 //bool has_incomplete_blocks = false;
419 s16 d_max = g_settings.getS16("max_block_send_distance");
420 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
422 //dstream<<"Starting from "<<d_start<<std::endl;
424 for(s16 d = d_start; d <= d_max; d++)
426 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
428 //if(has_incomplete_blocks == false)
430 JMutexAutoLock lock(m_blocks_sent_mutex);
432 If m_nearest_unsent_d was changed by the EmergeThread
433 (it can change it to 0 through SetBlockNotSent),
435 Else update m_nearest_unsent_d
437 if(m_nearest_unsent_d != last_nearest_unsent_d)
439 d = m_nearest_unsent_d;
440 last_nearest_unsent_d = m_nearest_unsent_d;
445 Get the border/face dot coordinates of a "d-radiused"
448 core::list<v3s16> list;
449 getFacePositions(list, d);
451 core::list<v3s16>::Iterator li;
452 for(li=list.begin(); li!=list.end(); li++)
454 v3s16 p = *li + center;
458 - Don't allow too many simultaneous transfers
459 - EXCEPT when the blocks are very close
461 Also, don't send blocks that are already flying.
464 u16 maximum_simultaneous_block_sends_now =
465 maximum_simultaneous_block_sends;
467 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
469 maximum_simultaneous_block_sends_now =
470 maximum_simultaneous_block_sends_setting;
474 JMutexAutoLock lock(m_blocks_sending_mutex);
476 // Limit is dynamically lowered when building
477 if(num_blocks_selected
478 >= maximum_simultaneous_block_sends_now)
480 /*dstream<<"Not sending more blocks. Queue full. "
481 <<m_blocks_sending.size()
486 if(m_blocks_sending.find(p) != NULL)
493 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
494 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
495 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
496 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
497 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
498 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
501 bool generate = d <= d_max_gen;
505 // Don't generate above player
511 // Limit the generating area vertically to 2/3
512 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
517 Don't draw if not in sight
520 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
526 Don't send already sent blocks
529 JMutexAutoLock lock(m_blocks_sent_mutex);
531 if(m_blocks_sent.find(p) != NULL)
538 Ignore block if it is not at ground surface
539 but don't ignore water surface blocks
541 v2s16 p2d(p.X*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2,
542 p.Z*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
543 f32 y = server->m_env.getMap().getGroundHeight(p2d);
544 // The sector might not exist yet, thus no heightmap
545 if(y > GROUNDHEIGHT_VALID_MINVALUE)
547 f32 by = p.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;
548 if(fabs(by - y) > MAP_BLOCKSIZE + MAP_BLOCKSIZE/3
549 && fabs(by - WATER_LEVEL) >= MAP_BLOCKSIZE)
555 Check if map has this block
557 MapBlock *block = NULL;
560 block = server->m_env.getMap().getBlockNoCreate(p);
562 catch(InvalidPositionException &e)
566 bool surely_not_found_on_disk = false;
569 /*if(block->isIncomplete())
571 has_incomplete_blocks = true;
577 surely_not_found_on_disk = true;
582 If block has been marked to not exist on disk (dummy)
583 and generating new ones is not wanted, skip block.
585 if(generate == false && surely_not_found_on_disk == true)
592 Record the lowest d from which a a block has been
593 found being not sent and possibly to exist
595 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
597 new_nearest_unsent_d = d;
601 Add inexistent block to emerge queue.
603 if(block == NULL || surely_not_found_on_disk)
605 /*SharedPtr<JMutexAutoLock> lock
606 (m_num_blocks_in_emerge_queue.getLock());*/
608 //TODO: Get value from somewhere
609 // Allow only one block in emerge queue
610 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
612 // Add it to the emerge queue and trigger the thread
615 if(generate == false)
616 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
618 server->m_emerge_queue.addBlock(peer_id, p, flags);
619 server->m_emergethread.trigger();
630 PrioritySortedBlockTransfer q((float)d, p, peer_id);
634 num_blocks_selected += 1;
639 if(new_nearest_unsent_d != -1)
641 JMutexAutoLock lock(m_blocks_sent_mutex);
642 m_nearest_unsent_d = new_nearest_unsent_d;
646 void RemoteClient::SendObjectData(
649 core::map<v3s16, bool> &stepped_blocks
652 DSTACK(__FUNCTION_NAME);
654 // Can't send anything without knowing version
655 if(serialization_version == SER_FMT_VER_INVALID)
657 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
663 Send a TOCLIENT_OBJECTDATA packet.
667 u16 number of player positions
678 std::ostringstream os(std::ios_base::binary);
682 writeU16(buf, TOCLIENT_OBJECTDATA);
683 os.write((char*)buf, 2);
686 Get and write player data
689 // Get connected players
690 core::list<Player*> players = server->m_env.getPlayers(true);
692 // Write player count
693 u16 playercount = players.size();
694 writeU16(buf, playercount);
695 os.write((char*)buf, 2);
697 core::list<Player*>::Iterator i;
698 for(i = players.begin();
699 i != players.end(); i++)
703 v3f pf = player->getPosition();
704 v3f sf = player->getSpeed();
706 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
707 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
708 s32 pitch_i (player->getPitch() * 100);
709 s32 yaw_i (player->getYaw() * 100);
711 writeU16(buf, player->peer_id);
712 os.write((char*)buf, 2);
713 writeV3S32(buf, position_i);
714 os.write((char*)buf, 12);
715 writeV3S32(buf, speed_i);
716 os.write((char*)buf, 12);
717 writeS32(buf, pitch_i);
718 os.write((char*)buf, 4);
719 writeS32(buf, yaw_i);
720 os.write((char*)buf, 4);
724 Get and write object data
730 For making players to be able to build to their nearby
731 environment (building is not possible on blocks that are not
734 - Add blocks to emerge queue if they are not found
736 SUGGESTION: These could be ignored from the backside of the player
739 Player *player = server->m_env.getPlayer(peer_id);
743 v3f playerpos = player->getPosition();
744 v3f playerspeed = player->getSpeed();
746 v3s16 center_nodepos = floatToInt(playerpos);
747 v3s16 center = getNodeBlockPos(center_nodepos);
749 s16 d_max = g_settings.getS16("active_object_range");
751 // Number of blocks whose objects were written to bos
754 std::ostringstream bos(std::ios_base::binary);
756 for(s16 d = 0; d <= d_max; d++)
758 core::list<v3s16> list;
759 getFacePositions(list, d);
761 core::list<v3s16>::Iterator li;
762 for(li=list.begin(); li!=list.end(); li++)
764 v3s16 p = *li + center;
767 Ignore blocks that haven't been sent to the client
770 JMutexAutoLock sentlock(m_blocks_sent_mutex);
771 if(m_blocks_sent.find(p) == NULL)
775 // Try stepping block and add it to a send queue
780 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
783 Step block if not in stepped_blocks and add to stepped_blocks.
785 if(stepped_blocks.find(p) == NULL)
787 block->stepObjects(dtime, true, server->getDayNightRatio());
788 stepped_blocks.insert(p, true);
789 block->setChangedFlag();
792 // Skip block if there are no objects
793 if(block->getObjectCount() == 0)
802 bos.write((char*)buf, 6);
805 block->serializeObjects(bos, serialization_version);
810 Stop collecting objects if data is already too big
812 // Sum of player and object data sizes
813 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
814 // break out if data too big
815 if(sum > MAX_OBJECTDATA_SIZE)
817 goto skip_subsequent;
821 catch(InvalidPositionException &e)
824 // Add it to the emerge queue and trigger the thread.
825 // Fetch the block only if it is on disk.
827 // Grab and increment counter
828 /*SharedPtr<JMutexAutoLock> lock
829 (m_num_blocks_in_emerge_queue.getLock());
830 m_num_blocks_in_emerge_queue.m_value++;*/
832 // Add to queue as an anonymous fetch from disk
833 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
834 server->m_emerge_queue.addBlock(0, p, flags);
835 server->m_emergethread.trigger();
843 writeU16(buf, blockcount);
844 os.write((char*)buf, 2);
846 // Write block objects
853 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
856 std::string s = os.str();
857 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
858 // Send as unreliable
859 server->m_con.Send(peer_id, 0, data, false);
862 void RemoteClient::GotBlock(v3s16 p)
864 JMutexAutoLock lock(m_blocks_sending_mutex);
865 JMutexAutoLock lock2(m_blocks_sent_mutex);
866 if(m_blocks_sending.find(p) != NULL)
867 m_blocks_sending.remove(p);
870 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
871 " m_blocks_sending"<<std::endl;*/
872 m_excess_gotblocks++;
874 m_blocks_sent.insert(p, true);
877 void RemoteClient::SentBlock(v3s16 p)
879 JMutexAutoLock lock(m_blocks_sending_mutex);
880 /*if(m_blocks_sending.size() > 15)
882 dstream<<"RemoteClient::SentBlock(): "
883 <<"m_blocks_sending.size()="
884 <<m_blocks_sending.size()<<std::endl;
886 if(m_blocks_sending.find(p) == NULL)
887 m_blocks_sending.insert(p, 0.0);
889 dstream<<"RemoteClient::SentBlock(): Sent block"
890 " already in m_blocks_sending"<<std::endl;
893 void RemoteClient::SetBlockNotSent(v3s16 p)
895 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
896 JMutexAutoLock sentlock(m_blocks_sent_mutex);
898 m_nearest_unsent_d = 0;
900 if(m_blocks_sending.find(p) != NULL)
901 m_blocks_sending.remove(p);
902 if(m_blocks_sent.find(p) != NULL)
903 m_blocks_sent.remove(p);
906 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
908 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
909 JMutexAutoLock sentlock(m_blocks_sent_mutex);
911 m_nearest_unsent_d = 0;
913 for(core::map<v3s16, MapBlock*>::Iterator
914 i = blocks.getIterator();
915 i.atEnd()==false; i++)
917 v3s16 p = i.getNode()->getKey();
919 if(m_blocks_sending.find(p) != NULL)
920 m_blocks_sending.remove(p);
921 if(m_blocks_sent.find(p) != NULL)
922 m_blocks_sent.remove(p);
930 PlayerInfo::PlayerInfo()
935 void PlayerInfo::PrintLine(std::ostream *s)
938 (*s)<<"\""<<name<<"\" ("
939 <<position.X<<","<<position.Y
940 <<","<<position.Z<<") ";
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,
969 m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
970 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
972 m_emergethread(this),
975 m_time_of_day_send_timer(0),
978 //m_flowwater_timer = 0.0;
979 m_liquid_transform_timer = 0.0;
980 m_print_info_timer = 0.0;
981 m_objectdata_timer = 0.0;
982 m_emergethread_trigger_timer = 0.0;
983 m_savemap_timer = 0.0;
987 m_step_dtime_mutex.Init();
996 JMutexAutoLock clientslock(m_con_mutex);
998 for(core::map<u16, RemoteClient*>::Iterator
999 i = m_clients.getIterator();
1000 i.atEnd() == false; i++)
1003 // NOTE: These are removed by env destructor
1005 u16 peer_id = i.getNode()->getKey();
1006 JMutexAutoLock envlock(m_env_mutex);
1007 m_env.removePlayer(peer_id);
1011 delete i.getNode()->getValue();
1015 void Server::start(unsigned short port)
1017 DSTACK(__FUNCTION_NAME);
1018 // Stop thread if already running
1021 // Initialize connection
1022 m_con.setTimeoutMs(30);
1026 m_thread.setRun(true);
1029 dout_server<<"Server started on port "<<port<<std::endl;
1034 DSTACK(__FUNCTION_NAME);
1035 // Stop threads (set run=false first so both start stopping)
1036 m_thread.setRun(false);
1037 m_emergethread.setRun(false);
1039 m_emergethread.stop();
1041 dout_server<<"Server threads stopped"<<std::endl;
1044 void Server::step(float dtime)
1046 DSTACK(__FUNCTION_NAME);
1051 JMutexAutoLock lock(m_step_dtime_mutex);
1052 m_step_dtime += dtime;
1056 void Server::AsyncRunStep()
1058 DSTACK(__FUNCTION_NAME);
1062 JMutexAutoLock lock1(m_step_dtime_mutex);
1063 dtime = m_step_dtime;
1066 // Send blocks to clients
1072 //dstream<<"Server steps "<<dtime<<std::endl;
1073 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1076 JMutexAutoLock lock1(m_step_dtime_mutex);
1077 m_step_dtime -= dtime;
1084 m_uptime.set(m_uptime.get() + dtime);
1088 Update m_time_of_day
1091 m_time_counter += dtime;
1092 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1093 u32 units = (u32)(m_time_counter*speed);
1094 m_time_counter -= (f32)units / speed;
1095 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1097 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1100 Send to clients at constant intervals
1103 m_time_of_day_send_timer -= dtime;
1104 if(m_time_of_day_send_timer < 0.0)
1106 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1108 //JMutexAutoLock envlock(m_env_mutex);
1109 JMutexAutoLock conlock(m_con_mutex);
1111 for(core::map<u16, RemoteClient*>::Iterator
1112 i = m_clients.getIterator();
1113 i.atEnd() == false; i++)
1115 RemoteClient *client = i.getNode()->getValue();
1116 //Player *player = m_env.getPlayer(client->peer_id);
1118 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1119 m_time_of_day.get());
1121 m_con.Send(client->peer_id, 0, data, true);
1127 // Process connection's timeouts
1128 JMutexAutoLock lock2(m_con_mutex);
1129 m_con.RunTimeouts(dtime);
1133 // This has to be called so that the client list gets synced
1134 // with the peer list of the connection
1135 handlePeerChanges();
1140 // This also runs Map's timers
1141 JMutexAutoLock lock(m_env_mutex);
1152 m_liquid_transform_timer += dtime;
1153 if(m_liquid_transform_timer >= 1.00)
1155 m_liquid_transform_timer -= 1.00;
1157 JMutexAutoLock lock(m_env_mutex);
1159 core::map<v3s16, MapBlock*> modified_blocks;
1160 m_env.getMap().transformLiquids(modified_blocks);
1165 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1166 ServerMap &map = ((ServerMap&)m_env.getMap());
1167 map.updateLighting(modified_blocks, lighting_modified_blocks);
1169 // Add blocks modified by lighting to modified_blocks
1170 for(core::map<v3s16, MapBlock*>::Iterator
1171 i = lighting_modified_blocks.getIterator();
1172 i.atEnd() == false; i++)
1174 MapBlock *block = i.getNode()->getValue();
1175 modified_blocks.insert(block->getPos(), block);
1179 Set the modified blocks unsent for all the clients
1182 JMutexAutoLock lock2(m_con_mutex);
1184 for(core::map<u16, RemoteClient*>::Iterator
1185 i = m_clients.getIterator();
1186 i.atEnd() == false; i++)
1188 RemoteClient *client = i.getNode()->getValue();
1190 if(modified_blocks.size() > 0)
1192 // Remove block from sent history
1193 client->SetBlocksNotSent(modified_blocks);
1202 if(g_settings.getBool("water_moves") == true)
1206 if(g_settings.getBool("endless_water") == false)
1211 float &counter = m_flowwater_timer;
1213 if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
1218 core::map<v3s16, MapBlock*> modified_blocks;
1222 JMutexAutoLock envlock(m_env_mutex);
1224 MapVoxelManipulator v(&m_env.getMap());
1225 v.m_disable_water_climb =
1226 g_settings.getBool("disable_water_climb");
1228 if(g_settings.getBool("endless_water") == false)
1229 v.flowWater(m_flow_active_nodes, 0, false, 250);
1231 v.flowWater(m_flow_active_nodes, 0, false, 50);
1233 v.blitBack(modified_blocks);
1235 ServerMap &map = ((ServerMap&)m_env.getMap());
1238 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1239 map.updateLighting(modified_blocks, lighting_modified_blocks);
1241 // Add blocks modified by lighting to modified_blocks
1242 for(core::map<v3s16, MapBlock*>::Iterator
1243 i = lighting_modified_blocks.getIterator();
1244 i.atEnd() == false; i++)
1246 MapBlock *block = i.getNode()->getValue();
1247 modified_blocks.insert(block->getPos(), block);
1252 Set the modified blocks unsent for all the clients
1255 JMutexAutoLock lock2(m_con_mutex);
1257 for(core::map<u16, RemoteClient*>::Iterator
1258 i = m_clients.getIterator();
1259 i.atEnd() == false; i++)
1261 RemoteClient *client = i.getNode()->getValue();
1263 if(modified_blocks.size() > 0)
1265 // Remove block from sent history
1266 client->SetBlocksNotSent(modified_blocks);
1270 } // interval counter
1274 // Periodically print some info
1276 float &counter = m_print_info_timer;
1282 JMutexAutoLock lock2(m_con_mutex);
1284 for(core::map<u16, RemoteClient*>::Iterator
1285 i = m_clients.getIterator();
1286 i.atEnd() == false; i++)
1288 //u16 peer_id = i.getNode()->getKey();
1289 RemoteClient *client = i.getNode()->getValue();
1290 client->PrintInfo(std::cout);
1298 NOTE: Some of this could be moved to RemoteClient
1302 JMutexAutoLock envlock(m_env_mutex);
1303 JMutexAutoLock conlock(m_con_mutex);
1305 for(core::map<u16, RemoteClient*>::Iterator
1306 i = m_clients.getIterator();
1307 i.atEnd() == false; i++)
1309 RemoteClient *client = i.getNode()->getValue();
1310 Player *player = m_env.getPlayer(client->peer_id);
1312 JMutexAutoLock digmutex(client->m_dig_mutex);
1314 if(client->m_dig_tool_item == -1)
1317 client->m_dig_time_remaining -= dtime;
1319 if(client->m_dig_time_remaining > 0)
1321 client->m_time_from_building.set(0.0);
1325 v3s16 p_under = client->m_dig_position;
1327 // Mandatory parameter; actually used for nothing
1328 core::map<v3s16, MapBlock*> modified_blocks;
1334 // Get material at position
1335 material = m_env.getMap().getNode(p_under).d;
1336 // If it's not diggable, do nothing
1337 if(content_diggable(material) == false)
1339 derr_server<<"Server: Not finishing digging: Node not diggable"
1341 client->m_dig_tool_item = -1;
1345 catch(InvalidPositionException &e)
1347 derr_server<<"Server: Not finishing digging: Node not found"
1349 client->m_dig_tool_item = -1;
1355 SharedBuffer<u8> reply(replysize);
1356 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1357 writeS16(&reply[2], p_under.X);
1358 writeS16(&reply[4], p_under.Y);
1359 writeS16(&reply[6], p_under.Z);
1361 m_con.SendToAll(0, reply, true);
1363 if(g_settings.getBool("creative_mode") == false)
1365 // Add to inventory and send inventory
1366 InventoryItem *item = new MaterialItem(material, 1);
1367 player->inventory.addItem("main", item);
1368 SendInventory(player->peer_id);
1373 (this takes some time so it is done after the quick stuff)
1375 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1381 // Update water pressure around modification
1382 // This also adds it to m_flow_active_nodes if appropriate
1384 MapVoxelManipulator v(&m_env.getMap());
1385 v.m_disable_water_climb =
1386 g_settings.getBool("disable_water_climb");
1388 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1392 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1394 catch(ProcessingLimitException &e)
1396 dstream<<"Processing limit reached (1)"<<std::endl;
1399 v.blitBack(modified_blocks);
1404 // Send object positions
1406 float &counter = m_objectdata_timer;
1408 if(counter >= g_settings.getFloat("objectdata_interval"))
1410 JMutexAutoLock lock1(m_env_mutex);
1411 JMutexAutoLock lock2(m_con_mutex);
1412 SendObjectData(counter);
1418 // Trigger emergethread (it gets somehow gets to a
1419 // non-triggered but bysy state sometimes)
1421 float &counter = m_emergethread_trigger_timer;
1427 m_emergethread.trigger();
1433 float &counter = m_savemap_timer;
1435 if(counter >= g_settings.getFloat("server_map_save_interval"))
1439 JMutexAutoLock lock(m_env_mutex);
1441 // Save only changed parts
1442 m_env.getMap().save(true);
1444 // Delete unused sectors
1445 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1446 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1447 if(deleted_count > 0)
1449 dout_server<<"Server: Unloaded "<<deleted_count
1450 <<" sectors from memory"<<std::endl;
1456 void Server::Receive()
1458 DSTACK(__FUNCTION_NAME);
1459 u32 data_maxsize = 10000;
1460 Buffer<u8> data(data_maxsize);
1465 JMutexAutoLock conlock(m_con_mutex);
1466 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1469 // This has to be called so that the client list gets synced
1470 // with the peer list of the connection
1471 handlePeerChanges();
1473 ProcessData(*data, datasize, peer_id);
1475 catch(con::InvalidIncomingDataException &e)
1477 derr_server<<"Server::Receive(): "
1478 "InvalidIncomingDataException: what()="
1479 <<e.what()<<std::endl;
1481 catch(con::PeerNotFoundException &e)
1483 //NOTE: This is not needed anymore
1485 // The peer has been disconnected.
1486 // Find the associated player and remove it.
1488 /*JMutexAutoLock envlock(m_env_mutex);
1490 dout_server<<"ServerThread: peer_id="<<peer_id
1491 <<" has apparently closed connection. "
1492 <<"Removing player."<<std::endl;
1494 m_env.removePlayer(peer_id);*/
1498 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1500 DSTACK(__FUNCTION_NAME);
1501 // Environment is locked first.
1502 JMutexAutoLock envlock(m_env_mutex);
1503 JMutexAutoLock conlock(m_con_mutex);
1507 peer = m_con.GetPeer(peer_id);
1509 catch(con::PeerNotFoundException &e)
1511 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1512 <<peer_id<<" not found"<<std::endl;
1516 //u8 peer_ser_ver = peer->serialization_version;
1517 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1525 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1527 if(command == TOSERVER_INIT)
1529 // [0] u16 TOSERVER_INIT
1530 // [2] u8 SER_FMT_VER_HIGHEST
1531 // [3] u8[20] player_name
1536 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1537 <<peer->id<<std::endl;
1539 // First byte after command is maximum supported
1540 // serialization version
1541 u8 client_max = data[2];
1542 u8 our_max = SER_FMT_VER_HIGHEST;
1543 // Use the highest version supported by both
1544 u8 deployed = core::min_(client_max, our_max);
1545 // If it's lower than the lowest supported, give up.
1546 if(deployed < SER_FMT_VER_LOWEST)
1547 deployed = SER_FMT_VER_INVALID;
1549 //peer->serialization_version = deployed;
1550 getClient(peer->id)->pending_serialization_version = deployed;
1552 if(deployed == SER_FMT_VER_INVALID)
1554 derr_server<<DTIME<<"Server: Cannot negotiate "
1555 "serialization version with peer "
1556 <<peer_id<<std::endl;
1565 const u32 playername_size = 20;
1566 char playername[playername_size];
1567 for(u32 i=0; i<playername_size-1; i++)
1569 playername[i] = data[3+i];
1571 playername[playername_size-1] = 0;
1574 Player *player = emergePlayer(playername, "", peer_id);
1575 //Player *player = m_env.getPlayer(peer_id);
1577 // If failed, cancel
1580 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1581 <<": failed to emerge player"<<std::endl;
1586 // If a client is already connected to the player, cancel
1587 if(player->peer_id != 0)
1589 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1590 <<" tried to connect to "
1591 "an already connected player (peer_id="
1592 <<player->peer_id<<")"<<std::endl;
1595 // Set client of player
1596 player->peer_id = peer_id;
1599 // Check if player doesn't exist
1601 throw con::InvalidIncomingDataException
1602 ("Server::ProcessData(): INIT: Player doesn't exist");
1604 /*// update name if it was supplied
1605 if(datasize >= 20+3)
1608 player->updateName((const char*)&data[3]);
1611 // Now answer with a TOCLIENT_INIT
1613 SharedBuffer<u8> reply(2+1+6);
1614 writeU16(&reply[0], TOCLIENT_INIT);
1615 writeU8(&reply[2], deployed);
1616 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1618 m_con.Send(peer_id, 0, reply, true);
1622 if(command == TOSERVER_INIT2)
1624 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1625 <<peer->id<<std::endl;
1628 getClient(peer->id)->serialization_version
1629 = getClient(peer->id)->pending_serialization_version;
1632 Send some initialization data
1635 // Send player info to all players
1638 // Send inventory to player
1639 SendInventory(peer->id);
1643 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1644 m_time_of_day.get());
1645 m_con.Send(peer->id, 0, data, true);
1648 // Send information about server to player in chat
1650 std::wostringstream os(std::ios_base::binary);
1653 os<<L"uptime="<<m_uptime.get();
1654 // Information about clients
1656 for(core::map<u16, RemoteClient*>::Iterator
1657 i = m_clients.getIterator();
1658 i.atEnd() == false; i++)
1660 // Get client and check that it is valid
1661 RemoteClient *client = i.getNode()->getValue();
1662 assert(client->peer_id == i.getNode()->getKey());
1663 if(client->serialization_version == SER_FMT_VER_INVALID)
1666 Player *player = m_env.getPlayer(client->peer_id);
1667 // Get name of player
1668 std::wstring name = L"unknown";
1670 name = narrow_to_wide(player->getName());
1671 // Add name to information string
1676 SendChatMessage(peer_id, os.str());
1679 // Send information about joining in chat
1681 std::wstring name = L"unknown";
1682 Player *player = m_env.getPlayer(peer_id);
1684 name = narrow_to_wide(player->getName());
1686 std::wstring message;
1689 message += L" joined game";
1690 BroadcastChatMessage(message);
1696 if(peer_ser_ver == SER_FMT_VER_INVALID)
1698 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1699 " serialization format invalid or not initialized."
1700 " Skipping incoming command="<<command<<std::endl;
1704 Player *player = m_env.getPlayer(peer_id);
1707 derr_server<<"Server::ProcessData(): Cancelling: "
1708 "No player for peer_id="<<peer_id
1712 if(command == TOSERVER_PLAYERPOS)
1714 if(datasize < 2+12+12+4+4)
1718 v3s32 ps = readV3S32(&data[start+2]);
1719 v3s32 ss = readV3S32(&data[start+2+12]);
1720 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1721 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1722 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1723 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1724 pitch = wrapDegrees(pitch);
1725 yaw = wrapDegrees(yaw);
1726 player->setPosition(position);
1727 player->setSpeed(speed);
1728 player->setPitch(pitch);
1729 player->setYaw(yaw);
1731 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1732 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1733 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1735 else if(command == TOSERVER_GOTBLOCKS)
1748 u16 count = data[2];
1749 for(u16 i=0; i<count; i++)
1751 if((s16)datasize < 2+1+(i+1)*6)
1752 throw con::InvalidIncomingDataException
1753 ("GOTBLOCKS length is too short");
1754 v3s16 p = readV3S16(&data[2+1+i*6]);
1755 /*dstream<<"Server: GOTBLOCKS ("
1756 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1757 RemoteClient *client = getClient(peer_id);
1758 client->GotBlock(p);
1761 else if(command == TOSERVER_DELETEDBLOCKS)
1774 u16 count = data[2];
1775 for(u16 i=0; i<count; i++)
1777 if((s16)datasize < 2+1+(i+1)*6)
1778 throw con::InvalidIncomingDataException
1779 ("DELETEDBLOCKS length is too short");
1780 v3s16 p = readV3S16(&data[2+1+i*6]);
1781 /*dstream<<"Server: DELETEDBLOCKS ("
1782 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1783 RemoteClient *client = getClient(peer_id);
1784 client->SetBlockNotSent(p);
1787 else if(command == TOSERVER_CLICK_OBJECT)
1794 [2] u8 button (0=left, 1=right)
1799 u8 button = readU8(&data[2]);
1801 p.X = readS16(&data[3]);
1802 p.Y = readS16(&data[5]);
1803 p.Z = readS16(&data[7]);
1804 s16 id = readS16(&data[9]);
1805 //u16 item_i = readU16(&data[11]);
1807 MapBlock *block = NULL;
1810 block = m_env.getMap().getBlockNoCreate(p);
1812 catch(InvalidPositionException &e)
1814 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1818 MapBlockObject *obj = block->getObject(id);
1822 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1826 //TODO: Check that object is reasonably close
1831 InventoryList *ilist = player->inventory.getList("main");
1832 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1835 // Skip if inventory has no free space
1836 if(ilist->getUsedSlots() == ilist->getSize())
1838 dout_server<<"Player inventory has no free space"<<std::endl;
1843 Create the inventory item
1845 InventoryItem *item = NULL;
1846 // If it is an item-object, take the item from it
1847 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1849 item = ((ItemObject*)obj)->createInventoryItem();
1851 // Else create an item of the object
1854 item = new MapBlockObjectItem
1855 (obj->getInventoryString());
1858 // Add to inventory and send inventory
1859 ilist->addItem(item);
1860 SendInventory(player->peer_id);
1863 // Remove from block
1864 block->removeObject(id);
1867 else if(command == TOSERVER_GROUND_ACTION)
1875 [3] v3s16 nodepos_undersurface
1876 [9] v3s16 nodepos_abovesurface
1881 2: stop digging (all parameters ignored)
1883 u8 action = readU8(&data[2]);
1885 p_under.X = readS16(&data[3]);
1886 p_under.Y = readS16(&data[5]);
1887 p_under.Z = readS16(&data[7]);
1889 p_over.X = readS16(&data[9]);
1890 p_over.Y = readS16(&data[11]);
1891 p_over.Z = readS16(&data[13]);
1892 u16 item_i = readU16(&data[15]);
1894 //TODO: Check that target is reasonably close
1902 NOTE: This can be used in the future to check if
1903 somebody is cheating, by checking the timing.
1910 else if(action == 2)
1913 RemoteClient *client = getClient(peer->id);
1914 JMutexAutoLock digmutex(client->m_dig_mutex);
1915 client->m_dig_tool_item = -1;
1920 3: Digging completed
1922 else if(action == 3)
1924 // Mandatory parameter; actually used for nothing
1925 core::map<v3s16, MapBlock*> modified_blocks;
1931 // Get material at position
1932 material = m_env.getMap().getNode(p_under).d;
1933 // If it's not diggable, do nothing
1934 if(content_diggable(material) == false)
1936 derr_server<<"Server: Not finishing digging: Node not diggable"
1941 catch(InvalidPositionException &e)
1943 derr_server<<"Server: Not finishing digging: Node not found"
1948 //TODO: Send to only other clients
1951 Send the removal to all other clients
1956 SharedBuffer<u8> reply(replysize);
1957 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1958 writeS16(&reply[2], p_under.X);
1959 writeS16(&reply[4], p_under.Y);
1960 writeS16(&reply[6], p_under.Z);
1962 for(core::map<u16, RemoteClient*>::Iterator
1963 i = m_clients.getIterator();
1964 i.atEnd() == false; i++)
1966 // Get client and check that it is valid
1967 RemoteClient *client = i.getNode()->getValue();
1968 assert(client->peer_id == i.getNode()->getKey());
1969 if(client->serialization_version == SER_FMT_VER_INVALID)
1972 // Don't send if it's the same one
1973 if(peer_id == client->peer_id)
1977 m_con.Send(client->peer_id, 0, reply, true);
1981 Update and send inventory
1984 if(g_settings.getBool("creative_mode") == false)
1989 InventoryList *mlist = player->inventory.getList("main");
1992 InventoryItem *item = mlist->getItem(item_i);
1993 if(item && (std::string)item->getName() == "ToolItem")
1995 ToolItem *titem = (ToolItem*)item;
1996 std::string toolname = titem->getToolName();
1998 // Get digging properties for material and tool
1999 DiggingProperties prop =
2000 getDiggingProperties(material, toolname);
2002 if(prop.diggable == false)
2004 derr_server<<"Server: WARNING: Player digged"
2005 <<" with impossible material + tool"
2006 <<" combination"<<std::endl;
2009 bool weared_out = titem->addWear(prop.wear);
2013 mlist->deleteItem(item_i);
2019 Add digged item to inventory
2021 InventoryItem *item = new MaterialItem(material, 1);
2022 player->inventory.addItem("main", item);
2027 SendInventory(player->peer_id);
2032 (this takes some time so it is done after the quick stuff)
2034 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2041 // Update water pressure around modification
2042 // This also adds it to m_flow_active_nodes if appropriate
2044 MapVoxelManipulator v(&m_env.getMap());
2045 v.m_disable_water_climb =
2046 g_settings.getBool("disable_water_climb");
2048 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2052 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2054 catch(ProcessingLimitException &e)
2056 dstream<<"Processing limit reached (1)"<<std::endl;
2059 v.blitBack(modified_blocks);
2066 else if(action == 1)
2069 InventoryList *ilist = player->inventory.getList("main");
2074 InventoryItem *item = ilist->getItem(item_i);
2076 // If there is no item, it is not possible to add it anywhere
2081 Handle material items
2083 if(std::string("MaterialItem") == item->getName())
2086 // Don't add a node if this is not a free space
2087 MapNode n2 = m_env.getMap().getNode(p_over);
2088 if(content_buildable_to(n2.d) == false)
2091 catch(InvalidPositionException &e)
2093 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2098 // Reset build time counter
2099 getClient(peer->id)->m_time_from_building.set(0.0);
2102 MaterialItem *mitem = (MaterialItem*)item;
2104 n.d = mitem->getMaterial();
2105 if(content_directional(n.d))
2106 n.dir = packDir(p_under - p_over);
2110 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2111 SharedBuffer<u8> reply(replysize);
2112 writeU16(&reply[0], TOCLIENT_ADDNODE);
2113 writeS16(&reply[2], p_over.X);
2114 writeS16(&reply[4], p_over.Y);
2115 writeS16(&reply[6], p_over.Z);
2116 n.serialize(&reply[8], peer_ser_ver);
2118 m_con.SendToAll(0, reply, true);
2123 InventoryList *ilist = player->inventory.getList("main");
2124 if(g_settings.getBool("creative_mode") == false && ilist)
2126 // Remove from inventory and send inventory
2127 if(mitem->getCount() == 1)
2128 ilist->deleteItem(item_i);
2132 SendInventory(peer_id);
2138 This takes some time so it is done after the quick stuff
2140 core::map<v3s16, MapBlock*> modified_blocks;
2141 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2147 InventoryList *ilist = player->inventory.getList("main");
2148 if(g_settings.getBool("creative_mode") == false && ilist)
2150 // Remove from inventory and send inventory
2151 if(mitem->getCount() == 1)
2152 ilist->deleteItem(item_i);
2156 SendInventory(peer_id);
2162 This takes some time so it is done after the quick stuff
2164 core::map<v3s16, MapBlock*> modified_blocks;
2165 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2168 Set the modified blocks unsent for all the clients
2171 //JMutexAutoLock lock2(m_con_mutex);
2173 for(core::map<u16, RemoteClient*>::Iterator
2174 i = m_clients.getIterator();
2175 i.atEnd() == false; i++)
2177 RemoteClient *client = i.getNode()->getValue();
2179 if(modified_blocks.size() > 0)
2181 // Remove block from sent history
2182 client->SetBlocksNotSent(modified_blocks);
2192 // Update water pressure around modification
2193 // This also adds it to m_flow_active_nodes if appropriate
2195 MapVoxelManipulator v(&m_env.getMap());
2196 v.m_disable_water_climb =
2197 g_settings.getBool("disable_water_climb");
2199 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2203 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2205 catch(ProcessingLimitException &e)
2207 dstream<<"Processing limit reached (1)"<<std::endl;
2210 v.blitBack(modified_blocks);
2218 v3s16 blockpos = getNodeBlockPos(p_over);
2220 MapBlock *block = NULL;
2223 block = m_env.getMap().getBlockNoCreate(blockpos);
2225 catch(InvalidPositionException &e)
2227 derr_server<<"Error while placing object: "
2228 "block not found"<<std::endl;
2232 v3s16 block_pos_i_on_map = block->getPosRelative();
2233 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2235 v3f pos = intToFloat(p_over);
2236 pos -= block_pos_f_on_map;
2238 /*dout_server<<"pos="
2239 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2242 MapBlockObject *obj = NULL;
2245 Handle block object items
2247 if(std::string("MBOItem") == item->getName())
2249 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2251 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2252 "inventorystring=\""
2253 <<oitem->getInventoryString()
2254 <<"\""<<std::endl;*/
2256 obj = oitem->createObject
2257 (pos, player->getYaw(), player->getPitch());
2264 dout_server<<"Placing a miscellaneous item on map"
2267 Create an ItemObject that contains the item.
2269 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2270 std::ostringstream os(std::ios_base::binary);
2271 item->serialize(os);
2272 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2273 iobj->setItemString(os.str());
2279 derr_server<<"WARNING: item resulted in NULL object, "
2280 <<"not placing onto map"
2285 block->addObject(obj);
2287 dout_server<<"Placed object"<<std::endl;
2289 InventoryList *ilist = player->inventory.getList("main");
2290 if(g_settings.getBool("creative_mode") == false && ilist)
2292 // Remove from inventory and send inventory
2293 ilist->deleteItem(item_i);
2295 SendInventory(peer_id);
2303 Catch invalid actions
2307 derr_server<<"WARNING: Server: Invalid action "
2308 <<action<<std::endl;
2312 else if(command == TOSERVER_RELEASE)
2321 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2324 else if(command == TOSERVER_SIGNTEXT)
2333 std::string datastring((char*)&data[2], datasize-2);
2334 std::istringstream is(datastring, std::ios_base::binary);
2337 is.read((char*)buf, 6);
2338 v3s16 blockpos = readV3S16(buf);
2339 is.read((char*)buf, 2);
2340 s16 id = readS16(buf);
2341 is.read((char*)buf, 2);
2342 u16 textlen = readU16(buf);
2344 for(u16 i=0; i<textlen; i++)
2346 is.read((char*)buf, 1);
2347 text += (char)buf[0];
2350 MapBlock *block = NULL;
2353 block = m_env.getMap().getBlockNoCreate(blockpos);
2355 catch(InvalidPositionException &e)
2357 derr_server<<"Error while setting sign text: "
2358 "block not found"<<std::endl;
2362 MapBlockObject *obj = block->getObject(id);
2365 derr_server<<"Error while setting sign text: "
2366 "object not found"<<std::endl;
2370 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2372 derr_server<<"Error while setting sign text: "
2373 "object is not a sign"<<std::endl;
2377 ((SignObject*)obj)->setText(text);
2379 obj->getBlock()->setChangedFlag();
2381 else if(command == TOSERVER_INVENTORY_ACTION)
2383 /*// Ignore inventory changes if in creative mode
2384 if(g_settings.getBool("creative_mode") == true)
2386 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2390 // Strip command and create a stream
2391 std::string datastring((char*)&data[2], datasize-2);
2392 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2393 std::istringstream is(datastring, std::ios_base::binary);
2395 InventoryAction *a = InventoryAction::deSerialize(is);
2399 Handle craftresult specially if not in creative mode
2401 bool disable_action = false;
2402 if(a->getType() == IACTION_MOVE
2403 && g_settings.getBool("creative_mode") == false)
2405 IMoveAction *ma = (IMoveAction*)a;
2406 // Don't allow moving anything to craftresult
2407 if(ma->to_name == "craftresult")
2410 disable_action = true;
2412 // When something is removed from craftresult
2413 if(ma->from_name == "craftresult")
2415 disable_action = true;
2416 // Remove stuff from craft
2417 InventoryList *clist = player->inventory.getList("craft");
2420 u16 count = ma->count;
2423 clist->decrementMaterials(count);
2426 // Feed action to player inventory
2427 a->apply(&player->inventory);
2430 // If something appeared in craftresult, throw it
2432 InventoryList *rlist = player->inventory.getList("craftresult");
2433 InventoryList *mlist = player->inventory.getList("main");
2434 if(rlist && mlist && rlist->getUsedSlots() == 1)
2436 InventoryItem *item1 = rlist->changeItem(0, NULL);
2437 mlist->addItem(item1);
2441 if(disable_action == false)
2443 // Feed action to player inventory
2444 a->apply(&player->inventory);
2449 SendInventory(player->peer_id);
2453 dstream<<"TOSERVER_INVENTORY_ACTION: "
2454 <<"InventoryAction::deSerialize() returned NULL"
2458 else if(command == TOSERVER_CHAT_MESSAGE)
2466 std::string datastring((char*)&data[2], datasize-2);
2467 std::istringstream is(datastring, std::ios_base::binary);
2470 is.read((char*)buf, 2);
2471 u16 len = readU16(buf);
2473 std::wstring message;
2474 for(u16 i=0; i<len; i++)
2476 is.read((char*)buf, 2);
2477 message += (wchar_t)readU16(buf);
2480 // Get player name of this client
2481 std::wstring name = narrow_to_wide(player->getName());
2483 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2485 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2488 Send the message to all other clients
2490 for(core::map<u16, RemoteClient*>::Iterator
2491 i = m_clients.getIterator();
2492 i.atEnd() == false; i++)
2494 // Get client and check that it is valid
2495 RemoteClient *client = i.getNode()->getValue();
2496 assert(client->peer_id == i.getNode()->getKey());
2497 if(client->serialization_version == SER_FMT_VER_INVALID)
2500 // Don't send if it's the same one
2501 if(peer_id == client->peer_id)
2504 SendChatMessage(client->peer_id, line);
2509 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2510 "unknown command "<<command<<std::endl;
2514 catch(SendFailedException &e)
2516 derr_server<<"Server::ProcessData(): SendFailedException: "
2522 /*void Server::Send(u16 peer_id, u16 channelnum,
2523 SharedBuffer<u8> data, bool reliable)
2525 JMutexAutoLock lock(m_con_mutex);
2526 m_con.Send(peer_id, channelnum, data, reliable);
2529 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2531 DSTACK(__FUNCTION_NAME);
2533 Create a packet with the block in the right format
2536 std::ostringstream os(std::ios_base::binary);
2537 block->serialize(os, ver);
2538 std::string s = os.str();
2539 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2541 u32 replysize = 8 + blockdata.getSize();
2542 SharedBuffer<u8> reply(replysize);
2543 v3s16 p = block->getPos();
2544 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2545 writeS16(&reply[2], p.X);
2546 writeS16(&reply[4], p.Y);
2547 writeS16(&reply[6], p.Z);
2548 memcpy(&reply[8], *blockdata, blockdata.getSize());
2550 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2551 <<": \tpacket size: "<<replysize<<std::endl;*/
2556 m_con.Send(peer_id, 1, reply, true);
2559 core::list<PlayerInfo> Server::getPlayerInfo()
2561 DSTACK(__FUNCTION_NAME);
2562 JMutexAutoLock envlock(m_env_mutex);
2563 JMutexAutoLock conlock(m_con_mutex);
2565 core::list<PlayerInfo> list;
2567 core::list<Player*> players = m_env.getPlayers();
2569 core::list<Player*>::Iterator i;
2570 for(i = players.begin();
2571 i != players.end(); i++)
2575 Player *player = *i;
2578 con::Peer *peer = m_con.GetPeer(player->peer_id);
2579 // Copy info from peer to info struct
2581 info.address = peer->address;
2582 info.avg_rtt = peer->avg_rtt;
2584 catch(con::PeerNotFoundException &e)
2586 // Set dummy peer info
2588 info.address = Address(0,0,0,0,0);
2592 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2593 info.position = player->getPosition();
2595 list.push_back(info);
2601 void Server::peerAdded(con::Peer *peer)
2603 DSTACK(__FUNCTION_NAME);
2604 dout_server<<"Server::peerAdded(): peer->id="
2605 <<peer->id<<std::endl;
2608 c.type = PEER_ADDED;
2609 c.peer_id = peer->id;
2611 m_peer_change_queue.push_back(c);
2614 void Server::deletingPeer(con::Peer *peer, bool timeout)
2616 DSTACK(__FUNCTION_NAME);
2617 dout_server<<"Server::deletingPeer(): peer->id="
2618 <<peer->id<<", timeout="<<timeout<<std::endl;
2621 c.type = PEER_REMOVED;
2622 c.peer_id = peer->id;
2623 c.timeout = timeout;
2624 m_peer_change_queue.push_back(c);
2627 void Server::SendObjectData(float dtime)
2629 DSTACK(__FUNCTION_NAME);
2631 core::map<v3s16, bool> stepped_blocks;
2633 for(core::map<u16, RemoteClient*>::Iterator
2634 i = m_clients.getIterator();
2635 i.atEnd() == false; i++)
2637 u16 peer_id = i.getNode()->getKey();
2638 RemoteClient *client = i.getNode()->getValue();
2639 assert(client->peer_id == peer_id);
2641 if(client->serialization_version == SER_FMT_VER_INVALID)
2644 client->SendObjectData(this, dtime, stepped_blocks);
2648 void Server::SendPlayerInfos()
2650 DSTACK(__FUNCTION_NAME);
2652 //JMutexAutoLock envlock(m_env_mutex);
2654 // Get connected players
2655 core::list<Player*> players = m_env.getPlayers(true);
2657 u32 player_count = players.getSize();
2658 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2660 SharedBuffer<u8> data(datasize);
2661 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2664 core::list<Player*>::Iterator i;
2665 for(i = players.begin();
2666 i != players.end(); i++)
2668 Player *player = *i;
2670 /*dstream<<"Server sending player info for player with "
2671 "peer_id="<<player->peer_id<<std::endl;*/
2673 writeU16(&data[start], player->peer_id);
2674 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2675 start += 2+PLAYERNAME_SIZE;
2678 //JMutexAutoLock conlock(m_con_mutex);
2681 m_con.SendToAll(0, data, true);
2699 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2705 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2711 enum ItemSpecType type;
2712 // Only other one of these is used
2718 items: a pointer to an array of 9 pointers to items
2719 specs: a pointer to an array of 9 ItemSpecs
2721 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2723 u16 items_min_x = 100;
2724 u16 items_max_x = 100;
2725 u16 items_min_y = 100;
2726 u16 items_max_y = 100;
2727 for(u16 y=0; y<3; y++)
2728 for(u16 x=0; x<3; x++)
2730 if(items[y*3 + x] == NULL)
2732 if(items_min_x == 100 || x < items_min_x)
2734 if(items_min_y == 100 || y < items_min_y)
2736 if(items_max_x == 100 || x > items_max_x)
2738 if(items_max_y == 100 || y > items_max_y)
2741 // No items at all, just return false
2742 if(items_min_x == 100)
2745 u16 items_w = items_max_x - items_min_x + 1;
2746 u16 items_h = items_max_y - items_min_y + 1;
2748 u16 specs_min_x = 100;
2749 u16 specs_max_x = 100;
2750 u16 specs_min_y = 100;
2751 u16 specs_max_y = 100;
2752 for(u16 y=0; y<3; y++)
2753 for(u16 x=0; x<3; x++)
2755 if(specs[y*3 + x].type == ITEM_NONE)
2757 if(specs_min_x == 100 || x < specs_min_x)
2759 if(specs_min_y == 100 || y < specs_min_y)
2761 if(specs_max_x == 100 || x > specs_max_x)
2763 if(specs_max_y == 100 || y > specs_max_y)
2766 // No specs at all, just return false
2767 if(specs_min_x == 100)
2770 u16 specs_w = specs_max_x - specs_min_x + 1;
2771 u16 specs_h = specs_max_y - specs_min_y + 1;
2774 if(items_w != specs_w || items_h != specs_h)
2777 for(u16 y=0; y<specs_h; y++)
2778 for(u16 x=0; x<specs_w; x++)
2780 u16 items_x = items_min_x + x;
2781 u16 items_y = items_min_y + y;
2782 u16 specs_x = specs_min_x + x;
2783 u16 specs_y = specs_min_y + y;
2784 InventoryItem *item = items[items_y * 3 + items_x];
2785 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2787 if(spec.type == ITEM_NONE)
2789 // Has to be no item
2795 // There should be an item
2799 std::string itemname = item->getName();
2801 if(spec.type == ITEM_MATERIAL)
2803 if(itemname != "MaterialItem")
2805 MaterialItem *mitem = (MaterialItem*)item;
2806 if(mitem->getMaterial() != spec.num)
2809 else if(spec.type == ITEM_CRAFT)
2811 if(itemname != "CraftItem")
2813 CraftItem *mitem = (CraftItem*)item;
2814 if(mitem->getSubName() != spec.name)
2817 else if(spec.type == ITEM_TOOL)
2819 // Not supported yet
2822 else if(spec.type == ITEM_MBO)
2824 // Not supported yet
2829 // Not supported yet
2837 void Server::SendInventory(u16 peer_id)
2839 DSTACK(__FUNCTION_NAME);
2841 Player* player = m_env.getPlayer(peer_id);
2844 Calculate crafting stuff
2846 if(g_settings.getBool("creative_mode") == false)
2848 InventoryList *clist = player->inventory.getList("craft");
2849 InventoryList *rlist = player->inventory.getList("craftresult");
2852 rlist->clearItems();
2856 InventoryItem *items[9];
2857 for(u16 i=0; i<9; i++)
2859 items[i] = clist->getItem(i);
2868 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2869 if(checkItemCombination(items, specs))
2871 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2880 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2881 if(checkItemCombination(items, specs))
2883 rlist->addItem(new CraftItem("Stick", 4));
2892 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2893 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2894 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2895 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2896 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2897 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2898 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2899 if(checkItemCombination(items, specs))
2901 rlist->addItem(new MapBlockObjectItem("Sign"));
2910 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COALSTONE);
2911 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2912 if(checkItemCombination(items, specs))
2914 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2923 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2924 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2925 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2926 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2927 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2928 if(checkItemCombination(items, specs))
2930 rlist->addItem(new ToolItem("WPick", 0));
2939 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2940 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2941 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2942 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2943 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2944 if(checkItemCombination(items, specs))
2946 rlist->addItem(new ToolItem("STPick", 0));
2955 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2956 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2957 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2958 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2959 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2960 if(checkItemCombination(items, specs))
2962 rlist->addItem(new ToolItem("MesePick", 0));
2967 } // if creative_mode == false
2973 std::ostringstream os;
2974 //os.imbue(std::locale("C"));
2976 player->inventory.serialize(os);
2978 std::string s = os.str();
2980 SharedBuffer<u8> data(s.size()+2);
2981 writeU16(&data[0], TOCLIENT_INVENTORY);
2982 memcpy(&data[2], s.c_str(), s.size());
2985 m_con.Send(peer_id, 0, data, true);
2988 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2990 DSTACK(__FUNCTION_NAME);
2992 std::ostringstream os(std::ios_base::binary);
2996 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2997 os.write((char*)buf, 2);
3000 writeU16(buf, message.size());
3001 os.write((char*)buf, 2);
3004 for(u32 i=0; i<message.size(); i++)
3008 os.write((char*)buf, 2);
3012 std::string s = os.str();
3013 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3015 m_con.Send(peer_id, 0, data, true);
3018 void Server::BroadcastChatMessage(const std::wstring &message)
3020 for(core::map<u16, RemoteClient*>::Iterator
3021 i = m_clients.getIterator();
3022 i.atEnd() == false; i++)
3024 // Get client and check that it is valid
3025 RemoteClient *client = i.getNode()->getValue();
3026 assert(client->peer_id == i.getNode()->getKey());
3027 if(client->serialization_version == SER_FMT_VER_INVALID)
3030 SendChatMessage(client->peer_id, message);
3034 void Server::SendBlocks(float dtime)
3036 DSTACK(__FUNCTION_NAME);
3038 JMutexAutoLock envlock(m_env_mutex);
3040 core::array<PrioritySortedBlockTransfer> queue;
3042 s32 total_sending = 0;
3044 for(core::map<u16, RemoteClient*>::Iterator
3045 i = m_clients.getIterator();
3046 i.atEnd() == false; i++)
3048 RemoteClient *client = i.getNode()->getValue();
3049 assert(client->peer_id == i.getNode()->getKey());
3051 total_sending += client->SendingCount();
3053 if(client->serialization_version == SER_FMT_VER_INVALID)
3056 client->GetNextBlocks(this, dtime, queue);
3060 // Lowest priority number comes first.
3061 // Lowest is most important.
3064 JMutexAutoLock conlock(m_con_mutex);
3066 for(u32 i=0; i<queue.size(); i++)
3068 //TODO: Calculate limit dynamically
3069 if(total_sending >= g_settings.getS32
3070 ("max_simultaneous_block_sends_server_total"))
3073 PrioritySortedBlockTransfer q = queue[i];
3075 MapBlock *block = NULL;
3078 block = m_env.getMap().getBlockNoCreate(q.pos);
3080 catch(InvalidPositionException &e)
3085 RemoteClient *client = getClient(q.peer_id);
3087 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3089 client->SentBlock(q.pos);
3096 RemoteClient* Server::getClient(u16 peer_id)
3098 DSTACK(__FUNCTION_NAME);
3099 //JMutexAutoLock lock(m_con_mutex);
3100 core::map<u16, RemoteClient*>::Node *n;
3101 n = m_clients.find(peer_id);
3102 // A client should exist for all peers
3104 return n->getValue();
3107 Player *Server::emergePlayer(const char *name, const char *password,
3111 Try to get an existing player
3113 Player *player = m_env.getPlayer(name);
3116 // If player is already connected, cancel
3117 if(player->peer_id != 0)
3119 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3123 player->peer_id = peer_id;
3128 If player with the wanted peer_id already exists, cancel.
3130 if(m_env.getPlayer(peer_id) != NULL)
3132 dstream<<"emergePlayer(): Player with wrong name but same"
3133 " peer_id already exists"<<std::endl;
3141 player = new ServerRemotePlayer();
3142 //player->peer_id = c.peer_id;
3143 //player->peer_id = PEER_ID_INEXISTENT;
3144 player->peer_id = peer_id;
3145 player->updateName(name);
3151 dstream<<"Server: Finding spawn place for player \""
3152 <<player->getName()<<"\""<<std::endl;
3156 f32 groundheight = 0;
3157 // Try to find a good place a few times
3158 for(s32 i=0; i<500; i++)
3161 // We're going to try to throw the player to this position
3162 nodepos = v2s16(-range + (myrand()%(range*2)),
3163 -range + (myrand()%(range*2)));
3164 v2s16 sectorpos = getNodeSectorPos(nodepos);
3166 m_env.getMap().emergeSector(sectorpos);
3167 // Get ground height at point
3168 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3169 // The sector should have been generated -> groundheight exists
3170 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3171 // Don't go underwater
3172 if(groundheight < WATER_LEVEL)
3174 //dstream<<"-> Underwater"<<std::endl;
3177 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3178 // Get block at point
3180 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3181 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3182 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3183 // Don't go inside ground
3185 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3186 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3187 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3188 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3189 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3190 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3192 dstream<<"-> Inside ground"<<std::endl;
3196 }catch(InvalidPositionException &e)
3198 dstream<<"-> Invalid position"<<std::endl;
3199 // Ignore invalid position
3203 // Found a good place
3204 dstream<<"Searched through "<<i<<" places."<<std::endl;
3209 // If no suitable place was not found, go above water at least.
3210 if(groundheight < WATER_LEVEL)
3211 groundheight = WATER_LEVEL;
3213 player->setPosition(intToFloat(v3s16(
3220 Add player to environment
3223 m_env.addPlayer(player);
3226 Add stuff to inventory
3229 if(g_settings.getBool("creative_mode"))
3231 // Give some good picks
3233 InventoryItem *item = new ToolItem("STPick", 0);
3234 void* r = player->inventory.addItem("main", item);
3238 InventoryItem *item = new ToolItem("MesePick", 0);
3239 void* r = player->inventory.addItem("main", item);
3246 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3249 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3250 player->inventory.addItem("main", item);
3253 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3255 // Skip some materials
3256 if(i == CONTENT_WATER || i == CONTENT_TORCH)
3259 InventoryItem *item = new MaterialItem(i, 1);
3260 player->inventory.addItem("main", item);
3264 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3265 void* r = player->inventory.addItem("main", item);
3272 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3273 void* r = player->inventory.addItem("main", item);
3277 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3278 void* r = player->inventory.addItem("main", item);
3282 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3283 void* r = player->inventory.addItem("main", item);
3287 InventoryItem *item = new CraftItem("Stick", 4);
3288 void* r = player->inventory.addItem("main", item);
3292 InventoryItem *item = new ToolItem("WPick", 32000);
3293 void* r = player->inventory.addItem("main", item);
3297 InventoryItem *item = new ToolItem("STPick", 32000);
3298 void* r = player->inventory.addItem("main", item);
3301 /*// Give some lights
3303 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3304 bool r = player->inventory.addItem("main", item);
3308 for(u16 i=0; i<4; i++)
3310 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3311 bool r = player->inventory.addItem("main", item);
3314 /*// Give some other stuff
3316 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3317 bool r = player->inventory.addItem("main", item);
3324 } // create new player
3328 void Server::UpdateBlockWaterPressure(MapBlock *block,
3329 core::map<v3s16, MapBlock*> &modified_blocks)
3331 MapVoxelManipulator v(&m_env.getMap());
3332 v.m_disable_water_climb =
3333 g_settings.getBool("disable_water_climb");
3335 VoxelArea area(block->getPosRelative(),
3336 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3340 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3342 catch(ProcessingLimitException &e)
3344 dstream<<"Processing limit reached (1)"<<std::endl;
3347 v.blitBack(modified_blocks);
3351 void Server::handlePeerChange(PeerChange &c)
3353 JMutexAutoLock envlock(m_env_mutex);
3354 JMutexAutoLock conlock(m_con_mutex);
3356 if(c.type == PEER_ADDED)
3363 core::map<u16, RemoteClient*>::Node *n;
3364 n = m_clients.find(c.peer_id);
3365 // The client shouldn't already exist
3369 RemoteClient *client = new RemoteClient();
3370 client->peer_id = c.peer_id;
3371 m_clients.insert(client->peer_id, client);
3374 else if(c.type == PEER_REMOVED)
3381 core::map<u16, RemoteClient*>::Node *n;
3382 n = m_clients.find(c.peer_id);
3383 // The client should exist
3386 // Collect information about leaving in chat
3387 std::wstring message;
3389 std::wstring name = L"unknown";
3390 Player *player = m_env.getPlayer(c.peer_id);
3392 name = narrow_to_wide(player->getName());
3396 message += L" left game";
3398 message += L" (timed out)";
3403 m_env.removePlayer(c.peer_id);
3406 // Set player client disconnected
3408 Player *player = m_env.getPlayer(c.peer_id);
3409 player->peer_id = 0;
3413 delete m_clients[c.peer_id];
3414 m_clients.remove(c.peer_id);
3416 // Send player info to all remaining clients
3419 // Send leave chat message to all remaining clients
3420 BroadcastChatMessage(message);
3429 void Server::handlePeerChanges()
3431 while(m_peer_change_queue.size() > 0)
3433 PeerChange c = m_peer_change_queue.pop_front();
3435 dout_server<<"Server: Handling peer change: "
3436 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3439 handlePeerChange(c);
3443 void dedicated_server_loop(Server &server)
3445 DSTACK(__FUNCTION_NAME);
3447 std::cout<<std::endl;
3448 std::cout<<"========================"<<std::endl;
3449 std::cout<<"Running dedicated server"<<std::endl;
3450 std::cout<<"========================"<<std::endl;
3451 std::cout<<std::endl;
3455 // This is kind of a hack but can be done like this
3456 // because server.step() is very light
3460 static int counter = 0;
3466 core::list<PlayerInfo> list = server.getPlayerInfo();
3467 core::list<PlayerInfo>::Iterator i;
3468 static u32 sum_old = 0;
3469 u32 sum = PIChecksum(list);
3472 std::cout<<DTIME<<"Player info:"<<std::endl;
3473 for(i=list.begin(); i!=list.end(); i++)
3475 i->PrintLine(&std::cout);