Clients inform server on wielded item
[oweals/minetest.git] / src / server.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #include "server.h"
21 #include "utility.h"
22 #include <iostream>
23 #include "clientserver.h"
24 #include "map.h"
25 #include "jmutexautolock.h"
26 #include "main.h"
27 #include "constants.h"
28 #include "voxel.h"
29 #include "materials.h"
30 #include "mineral.h"
31 #include "config.h"
32 #include "servercommand.h"
33 #include "filesys.h"
34 #include "content_mapnode.h"
35 #include "content_craft.h"
36 #include "content_nodemeta.h"
37 #include "mapblock.h"
38 #include "serverobject.h"
39
40 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
41
42 class MapEditEventIgnorer
43 {
44 public:
45         MapEditEventIgnorer(bool *flag):
46                 m_flag(flag)
47         {
48                 if(*m_flag == false)
49                         *m_flag = true;
50                 else
51                         m_flag = NULL;
52         }
53
54         ~MapEditEventIgnorer()
55         {
56                 if(m_flag)
57                 {
58                         assert(*m_flag);
59                         *m_flag = false;
60                 }
61         }
62         
63 private:
64         bool *m_flag;
65 };
66
67 void * ServerThread::Thread()
68 {
69         ThreadStarted();
70
71         DSTACK(__FUNCTION_NAME);
72
73         BEGIN_DEBUG_EXCEPTION_HANDLER
74
75         while(getRun())
76         {
77                 try{
78                         //TimeTaker timer("AsyncRunStep() + Receive()");
79
80                         {
81                                 //TimeTaker timer("AsyncRunStep()");
82                                 m_server->AsyncRunStep();
83                         }
84                 
85                         //dout_server<<"Running m_server->Receive()"<<std::endl;
86                         m_server->Receive();
87                 }
88                 catch(con::NoIncomingDataException &e)
89                 {
90                 }
91                 catch(con::PeerNotFoundException &e)
92                 {
93                         dout_server<<"Server: PeerNotFoundException"<<std::endl;
94                 }
95         }
96         
97         END_DEBUG_EXCEPTION_HANDLER
98
99         return NULL;
100 }
101
102 void * EmergeThread::Thread()
103 {
104         ThreadStarted();
105
106         DSTACK(__FUNCTION_NAME);
107
108         BEGIN_DEBUG_EXCEPTION_HANDLER
109
110         bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
111         
112         /*
113                 Get block info from queue, emerge them and send them
114                 to clients.
115
116                 After queue is empty, exit.
117         */
118         while(getRun())
119         {
120                 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
121                 if(qptr == NULL)
122                         break;
123                 
124                 SharedPtr<QueuedBlockEmerge> q(qptr);
125
126                 v3s16 &p = q->pos;
127                 v2s16 p2d(p.X,p.Z);
128
129                 /*
130                         Do not generate over-limit
131                 */
132                 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
133                 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
134                 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
135                 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
136                 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
137                 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
138                         continue;
139                         
140                 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
141
142                 //TimeTaker timer("block emerge");
143                 
144                 /*
145                         Try to emerge it from somewhere.
146
147                         If it is only wanted as optional, only loading from disk
148                         will be allowed.
149                 */
150                 
151                 /*
152                         Check if any peer wants it as non-optional. In that case it
153                         will be generated.
154
155                         Also decrement the emerge queue count in clients.
156                 */
157
158                 bool only_from_disk = true;
159
160                 {
161                         core::map<u16, u8>::Iterator i;
162                         for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
163                         {
164                                 //u16 peer_id = i.getNode()->getKey();
165
166                                 // Check flags
167                                 u8 flags = i.getNode()->getValue();
168                                 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
169                                         only_from_disk = false;
170                                 
171                         }
172                 }
173                 
174                 if(enable_mapgen_debug_info)
175                         dstream<<"EmergeThread: p="
176                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
177                                         <<"only_from_disk="<<only_from_disk<<std::endl;
178                 
179                 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
180                         
181                 //core::map<v3s16, MapBlock*> changed_blocks;
182                 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
183
184                 MapBlock *block = NULL;
185                 bool got_block = true;
186                 core::map<v3s16, MapBlock*> modified_blocks;
187                 
188                 /*
189                         Fetch block from map or generate a single block
190                 */
191                 {
192                         JMutexAutoLock envlock(m_server->m_env_mutex);
193                         
194                         // Load sector if it isn't loaded
195                         if(map.getSectorNoGenerateNoEx(p2d) == NULL)
196                                 //map.loadSectorFull(p2d);
197                                 map.loadSectorMeta(p2d);
198
199                         block = map.getBlockNoCreateNoEx(p);
200                         if(!block || block->isDummy() || !block->isGenerated())
201                         {
202                                 if(enable_mapgen_debug_info)
203                                         dstream<<"EmergeThread: not in memory, loading"<<std::endl;
204
205                                 // Get, load or create sector
206                                 /*ServerMapSector *sector =
207                                                 (ServerMapSector*)map.createSector(p2d);*/
208
209                                 // Load/generate block
210
211                                 /*block = map.emergeBlock(p, sector, changed_blocks,
212                                                 lighting_invalidated_blocks);*/
213
214                                 block = map.loadBlock(p);
215                                 
216                                 if(only_from_disk == false)
217                                 {
218                                         if(block == NULL || block->isGenerated() == false)
219                                         {
220                                                 if(enable_mapgen_debug_info)
221                                                         dstream<<"EmergeThread: generating"<<std::endl;
222                                                 block = map.generateBlock(p, modified_blocks);
223                                         }
224                                 }
225
226                                 if(enable_mapgen_debug_info)
227                                         dstream<<"EmergeThread: ended up with: "
228                                                         <<analyze_block(block)<<std::endl;
229
230                                 if(block == NULL)
231                                 {
232                                         got_block = false;
233                                 }
234                                 else
235                                 {
236                                         /*
237                                                 Ignore map edit events, they will not need to be
238                                                 sent to anybody because the block hasn't been sent
239                                                 to anybody
240                                         */
241                                         MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
242                                         
243                                         // Activate objects and stuff
244                                         m_server->m_env.activateBlock(block, 3600);
245                                 }
246                         }
247                         else
248                         {
249                                 /*if(block->getLightingExpired()){
250                                         lighting_invalidated_blocks[block->getPos()] = block;
251                                 }*/
252                         }
253
254                         // TODO: Some additional checking and lighting updating,
255                         //       see emergeBlock
256                 }
257
258                 {//envlock
259                 JMutexAutoLock envlock(m_server->m_env_mutex);
260                 
261                 if(got_block)
262                 {
263                         /*
264                                 Collect a list of blocks that have been modified in
265                                 addition to the fetched one.
266                         */
267
268 #if 0
269                         if(lighting_invalidated_blocks.size() > 0)
270                         {
271                                 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
272                                                 <<" blocks"<<std::endl;*/
273                         
274                                 // 50-100ms for single block generation
275                                 //TimeTaker timer("** EmergeThread updateLighting");
276                                 
277                                 // Update lighting without locking the environment mutex,
278                                 // add modified blocks to changed blocks
279                                 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
280                         }
281                                 
282                         // Add all from changed_blocks to modified_blocks
283                         for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
284                                         i.atEnd() == false; i++)
285                         {
286                                 MapBlock *block = i.getNode()->getValue();
287                                 modified_blocks.insert(block->getPos(), block);
288                         }
289 #endif
290                 }
291                 // If we got no block, there should be no invalidated blocks
292                 else
293                 {
294                         //assert(lighting_invalidated_blocks.size() == 0);
295                 }
296
297                 }//envlock
298
299                 /*
300                         Set sent status of modified blocks on clients
301                 */
302         
303                 // NOTE: Server's clients are also behind the connection mutex
304                 JMutexAutoLock lock(m_server->m_con_mutex);
305
306                 /*
307                         Add the originally fetched block to the modified list
308                 */
309                 if(got_block)
310                 {
311                         modified_blocks.insert(p, block);
312                 }
313                 
314                 /*
315                         Set the modified blocks unsent for all the clients
316                 */
317                 
318                 for(core::map<u16, RemoteClient*>::Iterator
319                                 i = m_server->m_clients.getIterator();
320                                 i.atEnd() == false; i++)
321                 {
322                         RemoteClient *client = i.getNode()->getValue();
323                         
324                         if(modified_blocks.size() > 0)
325                         {
326                                 // Remove block from sent history
327                                 client->SetBlocksNotSent(modified_blocks);
328                         }
329                 }
330                 
331         }
332
333         END_DEBUG_EXCEPTION_HANDLER
334
335         return NULL;
336 }
337
338 void RemoteClient::GetNextBlocks(Server *server, float dtime,
339                 core::array<PrioritySortedBlockTransfer> &dest)
340 {
341         DSTACK(__FUNCTION_NAME);
342         
343         /*u32 timer_result;
344         TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
345         
346         // Increment timers
347         m_nothing_to_send_pause_timer -= dtime;
348         
349         if(m_nothing_to_send_pause_timer >= 0)
350         {
351                 // Keep this reset
352                 m_nearest_unsent_reset_timer = 0;
353                 return;
354         }
355
356         // Won't send anything if already sending
357         if(m_blocks_sending.size() >= g_settings.getU16
358                         ("max_simultaneous_block_sends_per_client"))
359         {
360                 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
361                 return;
362         }
363
364         //TimeTaker timer("RemoteClient::GetNextBlocks");
365         
366         Player *player = server->m_env.getPlayer(peer_id);
367
368         assert(player != NULL);
369
370         v3f playerpos = player->getPosition();
371         v3f playerspeed = player->getSpeed();
372         v3f playerspeeddir(0,0,0);
373         if(playerspeed.getLength() > 1.0*BS)
374                 playerspeeddir = playerspeed / playerspeed.getLength();
375         // Predict to next block
376         v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
377
378         v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
379
380         v3s16 center = getNodeBlockPos(center_nodepos);
381         
382         // Camera position and direction
383         v3f camera_pos = player->getEyePosition();
384         v3f camera_dir = v3f(0,0,1);
385         camera_dir.rotateYZBy(player->getPitch());
386         camera_dir.rotateXZBy(player->getYaw());
387
388         /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
389                         <<camera_dir.Z<<")"<<std::endl;*/
390
391         /*
392                 Get the starting value of the block finder radius.
393         */
394                 
395         if(m_last_center != center)
396         {
397                 m_nearest_unsent_d = 0;
398                 m_last_center = center;
399         }
400
401         /*dstream<<"m_nearest_unsent_reset_timer="
402                         <<m_nearest_unsent_reset_timer<<std::endl;*/
403                         
404         // This has to be incremented only when the nothing to send pause
405         // is not active
406         m_nearest_unsent_reset_timer += dtime;
407         
408         // Reset periodically to avoid possible bugs or other mishaps
409         if(m_nearest_unsent_reset_timer > 10.0)
410         {
411                 m_nearest_unsent_reset_timer = 0;
412                 m_nearest_unsent_d = 0;
413                 /*dstream<<"Resetting m_nearest_unsent_d for "
414                                 <<server->getPlayerName(peer_id)<<std::endl;*/
415         }
416
417         //s16 last_nearest_unsent_d = m_nearest_unsent_d;
418         s16 d_start = m_nearest_unsent_d;
419
420         //dstream<<"d_start="<<d_start<<std::endl;
421
422         u16 max_simul_sends_setting = g_settings.getU16
423                         ("max_simultaneous_block_sends_per_client");
424         u16 max_simul_sends_usually = max_simul_sends_setting;
425
426         /*
427                 Check the time from last addNode/removeNode.
428                 
429                 Decrease send rate if player is building stuff.
430         */
431         m_time_from_building += dtime;
432         if(m_time_from_building < g_settings.getFloat(
433                                 "full_block_send_enable_min_time_from_building"))
434         {
435                 max_simul_sends_usually
436                         = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
437         }
438         
439         /*
440                 Number of blocks sending + number of blocks selected for sending
441         */
442         u32 num_blocks_selected = m_blocks_sending.size();
443         
444         /*
445                 next time d will be continued from the d from which the nearest
446                 unsent block was found this time.
447
448                 This is because not necessarily any of the blocks found this
449                 time are actually sent.
450         */
451         s32 new_nearest_unsent_d = -1;
452
453         s16 d_max = g_settings.getS16("max_block_send_distance");
454         s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
455         
456         // Don't loop very much at a time
457         if(d_max > d_start+1)
458                 d_max = d_start+1;
459         /*if(d_max_gen > d_start+2)
460                 d_max_gen = d_start+2;*/
461         
462         //dstream<<"Starting from "<<d_start<<std::endl;
463
464         bool sending_something = false;
465
466         bool no_blocks_found_for_sending = true;
467
468         bool queue_is_full = false;
469         
470         s16 d;
471         for(d = d_start; d <= d_max; d++)
472         {
473                 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
474                 
475                 /*
476                         If m_nearest_unsent_d was changed by the EmergeThread
477                         (it can change it to 0 through SetBlockNotSent),
478                         update our d to it.
479                         Else update m_nearest_unsent_d
480                 */
481                 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
482                 {
483                         d = m_nearest_unsent_d;
484                         last_nearest_unsent_d = m_nearest_unsent_d;
485                 }*/
486
487                 /*
488                         Get the border/face dot coordinates of a "d-radiused"
489                         box
490                 */
491                 core::list<v3s16> list;
492                 getFacePositions(list, d);
493                 
494                 core::list<v3s16>::Iterator li;
495                 for(li=list.begin(); li!=list.end(); li++)
496                 {
497                         v3s16 p = *li + center;
498                         
499                         /*
500                                 Send throttling
501                                 - Don't allow too many simultaneous transfers
502                                 - EXCEPT when the blocks are very close
503
504                                 Also, don't send blocks that are already flying.
505                         */
506                         
507                         // Start with the usual maximum
508                         u16 max_simul_dynamic = max_simul_sends_usually;
509                         
510                         // If block is very close, allow full maximum
511                         if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
512                                 max_simul_dynamic = max_simul_sends_setting;
513
514                         // Don't select too many blocks for sending
515                         if(num_blocks_selected >= max_simul_dynamic)
516                         {
517                                 queue_is_full = true;
518                                 goto queue_full_break;
519                         }
520                         
521                         // Don't send blocks that are currently being transferred
522                         if(m_blocks_sending.find(p) != NULL)
523                                 continue;
524                 
525                         /*
526                                 Do not go over-limit
527                         */
528                         if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
529                         || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
530                         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
531                         || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
532                         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
533                         || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
534                                 continue;
535                 
536                         // If this is true, inexistent block will be made from scratch
537                         bool generate = d <= d_max_gen;
538                         
539                         {
540                                 /*// Limit the generating area vertically to 2/3
541                                 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
542                                         generate = false;*/
543
544                                 // Limit the send area vertically to 2/3
545                                 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
546                                         continue;
547                         }
548
549 #if 1
550                         /*
551                                 If block is far away, don't generate it unless it is
552                                 near ground level.
553                         */
554                         if(d >= 4)
555                         {
556         #if 1
557                                 // Block center y in nodes
558                                 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
559                                 // Don't generate if it's very high or very low
560                                 if(y < -64 || y > 64)
561                                         generate = false;
562         #endif
563         #if 0
564                                 v2s16 p2d_nodes_center(
565                                         MAP_BLOCKSIZE*p.X,
566                                         MAP_BLOCKSIZE*p.Z);
567                                 
568                                 // Get ground height in nodes
569                                 s16 gh = server->m_env.getServerMap().findGroundLevel(
570                                                 p2d_nodes_center);
571
572                                 // If differs a lot, don't generate
573                                 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
574                                         generate = false;
575                                         // Actually, don't even send it
576                                         //continue;
577         #endif
578                         }
579 #endif
580
581                         //dstream<<"d="<<d<<std::endl;
582                         
583                         /*
584                                 Don't generate or send if not in sight
585                         */
586
587                         if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
588                         {
589                                 continue;
590                         }
591                         
592                         /*
593                                 Don't send already sent blocks
594                         */
595                         {
596                                 if(m_blocks_sent.find(p) != NULL)
597                                 {
598                                         continue;
599                                 }
600                         }
601
602                         /*
603                                 Check if map has this block
604                         */
605                         MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
606                         
607                         bool surely_not_found_on_disk = false;
608                         bool block_is_invalid = false;
609                         if(block != NULL)
610                         {
611                                 // Reset usage timer, this block will be of use in the future.
612                                 block->resetUsageTimer();
613
614                                 // Block is dummy if data doesn't exist.
615                                 // It means it has been not found from disk and not generated
616                                 if(block->isDummy())
617                                 {
618                                         surely_not_found_on_disk = true;
619                                 }
620                                 
621                                 // Block is valid if lighting is up-to-date and data exists
622                                 if(block->isValid() == false)
623                                 {
624                                         block_is_invalid = true;
625                                 }
626                                 
627                                 /*if(block->isFullyGenerated() == false)
628                                 {
629                                         block_is_invalid = true;
630                                 }*/
631
632 #if 0
633                                 v2s16 p2d(p.X, p.Z);
634                                 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
635                                 v2s16 chunkpos = map->sector_to_chunk(p2d);
636                                 if(map->chunkNonVolatile(chunkpos) == false)
637                                         block_is_invalid = true;
638 #endif
639                                 if(block->isGenerated() == false)
640                                         block_is_invalid = true;
641 #if 1
642                                 /*
643                                         If block is not close, don't send it unless it is near
644                                         ground level.
645
646                                         Block is near ground level if night-time mesh
647                                         differs from day-time mesh.
648                                 */
649                                 if(d > 3)
650                                 {
651                                         if(block->dayNightDiffed() == false)
652                                                 continue;
653                                 }
654 #endif
655                         }
656
657                         /*
658                                 If block has been marked to not exist on disk (dummy)
659                                 and generating new ones is not wanted, skip block.
660                         */
661                         if(generate == false && surely_not_found_on_disk == true)
662                         {
663                                 // get next one.
664                                 continue;
665                         }
666
667                         /*
668                                 Record the lowest d from which a block has been
669                                 found being not sent and possibly to exist
670                         */
671                         if(no_blocks_found_for_sending)
672                         {
673                                 if(generate == true)
674                                         new_nearest_unsent_d = d;
675                         }
676
677                         no_blocks_found_for_sending = false;
678                                         
679                         /*
680                                 Add inexistent block to emerge queue.
681                         */
682                         if(block == NULL || surely_not_found_on_disk || block_is_invalid)
683                         {
684                                 //TODO: Get value from somewhere
685                                 // Allow only one block in emerge queue
686                                 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
687                                 // Allow two blocks in queue per client
688                                 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
689                                 {
690                                         //dstream<<"Adding block to emerge queue"<<std::endl;
691                                         
692                                         // Add it to the emerge queue and trigger the thread
693                                         
694                                         u8 flags = 0;
695                                         if(generate == false)
696                                                 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
697                                         
698                                         server->m_emerge_queue.addBlock(peer_id, p, flags);
699                                         server->m_emergethread.trigger();
700                                 }
701                                 
702                                 // get next one.
703                                 continue;
704                         }
705
706                         /*
707                                 Add block to send queue
708                         */
709
710                         PrioritySortedBlockTransfer q((float)d, p, peer_id);
711
712                         dest.push_back(q);
713
714                         num_blocks_selected += 1;
715                         sending_something = true;
716                 }
717         }
718 queue_full_break:
719
720         //dstream<<"Stopped at "<<d<<std::endl;
721         
722         if(no_blocks_found_for_sending)
723         {
724                 if(queue_is_full == false)
725                         new_nearest_unsent_d = d;
726         }
727
728         if(new_nearest_unsent_d != -1)
729                 m_nearest_unsent_d = new_nearest_unsent_d;
730
731         if(sending_something == false)
732         {
733                 m_nothing_to_send_counter++;
734                 if((s16)m_nothing_to_send_counter >=
735                                 g_settings.getS16("max_block_send_distance"))
736                 {
737                         // Pause time in seconds
738                         m_nothing_to_send_pause_timer = 1.0;
739                         /*dstream<<"nothing to send to "
740                                         <<server->getPlayerName(peer_id)
741                                         <<" (d="<<d<<")"<<std::endl;*/
742                 }
743         }
744         else
745         {
746                 m_nothing_to_send_counter = 0;
747         }
748
749         /*timer_result = timer.stop(true);
750         if(timer_result != 0)
751                 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
752 }
753
754 void RemoteClient::SendObjectData(
755                 Server *server,
756                 float dtime,
757                 core::map<v3s16, bool> &stepped_blocks
758         )
759 {
760         DSTACK(__FUNCTION_NAME);
761
762         // Can't send anything without knowing version
763         if(serialization_version == SER_FMT_VER_INVALID)
764         {
765                 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
766                                 <<std::endl;
767                 return;
768         }
769
770         /*
771                 Send a TOCLIENT_OBJECTDATA packet.
772                 Sent as unreliable.
773
774                 u16 command
775                 u16 number of player positions
776                 for each player:
777                         v3s32 position*100
778                         v3s32 speed*100
779                         s32 pitch*100
780                         s32 yaw*100
781                 u16 count of blocks
782                 for each block:
783                         block objects
784         */
785
786         std::ostringstream os(std::ios_base::binary);
787         u8 buf[12];
788         
789         // Write command
790         writeU16(buf, TOCLIENT_OBJECTDATA);
791         os.write((char*)buf, 2);
792         
793         /*
794                 Get and write player data
795         */
796         
797         // Get connected players
798         core::list<Player*> players = server->m_env.getPlayers(true);
799
800         // Write player count
801         u16 playercount = players.size();
802         writeU16(buf, playercount);
803         os.write((char*)buf, 2);
804
805         core::list<Player*>::Iterator i;
806         for(i = players.begin();
807                         i != players.end(); i++)
808         {
809                 Player *player = *i;
810
811                 v3f pf = player->getPosition();
812                 v3f sf = player->getSpeed();
813
814                 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
815                 v3s32 speed_i   (sf.X*100, sf.Y*100, sf.Z*100);
816                 s32   pitch_i   (player->getPitch() * 100);
817                 s32   yaw_i     (player->getYaw() * 100);
818                 
819                 writeU16(buf, player->peer_id);
820                 os.write((char*)buf, 2);
821                 writeV3S32(buf, position_i);
822                 os.write((char*)buf, 12);
823                 writeV3S32(buf, speed_i);
824                 os.write((char*)buf, 12);
825                 writeS32(buf, pitch_i);
826                 os.write((char*)buf, 4);
827                 writeS32(buf, yaw_i);
828                 os.write((char*)buf, 4);
829         }
830         
831         /*
832                 Get and write object data
833         */
834
835         /*
836                 Get nearby blocks.
837                 
838                 For making players to be able to build to their nearby
839                 environment (building is not possible on blocks that are not
840                 in memory):
841                 - Set blocks changed
842                 - Add blocks to emerge queue if they are not found
843
844                 SUGGESTION: These could be ignored from the backside of the player
845         */
846
847         Player *player = server->m_env.getPlayer(peer_id);
848
849         assert(player);
850
851         v3f playerpos = player->getPosition();
852         v3f playerspeed = player->getSpeed();
853
854         v3s16 center_nodepos = floatToInt(playerpos, BS);
855         v3s16 center = getNodeBlockPos(center_nodepos);
856
857         s16 d_max = g_settings.getS16("active_object_range");
858         
859         // Number of blocks whose objects were written to bos
860         u16 blockcount = 0;
861
862         std::ostringstream bos(std::ios_base::binary);
863
864         for(s16 d = 0; d <= d_max; d++)
865         {
866                 core::list<v3s16> list;
867                 getFacePositions(list, d);
868                 
869                 core::list<v3s16>::Iterator li;
870                 for(li=list.begin(); li!=list.end(); li++)
871                 {
872                         v3s16 p = *li + center;
873
874                         /*
875                                 Ignore blocks that haven't been sent to the client
876                         */
877                         {
878                                 if(m_blocks_sent.find(p) == NULL)
879                                         continue;
880                         }
881                         
882                         // Try stepping block and add it to a send queue
883                         try
884                         {
885
886                         // Get block
887                         MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
888
889                         /*
890                                 Step block if not in stepped_blocks and add to stepped_blocks.
891                         */
892                         if(stepped_blocks.find(p) == NULL)
893                         {
894                                 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
895                                 stepped_blocks.insert(p, true);
896                                 block->setChangedFlag();
897                         }
898
899                         // Skip block if there are no objects
900                         if(block->getObjectCount() == 0)
901                                 continue;
902                         
903                         /*
904                                 Write objects
905                         */
906
907                         // Write blockpos
908                         writeV3S16(buf, p);
909                         bos.write((char*)buf, 6);
910
911                         // Write objects
912                         //block->serializeObjects(bos, serialization_version); // DEPRECATED
913                         // count=0
914                         writeU16(bos, 0);
915
916                         blockcount++;
917
918                         /*
919                                 Stop collecting objects if data is already too big
920                         */
921                         // Sum of player and object data sizes
922                         s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
923                         // break out if data too big
924                         if(sum > MAX_OBJECTDATA_SIZE)
925                         {
926                                 goto skip_subsequent;
927                         }
928                         
929                         } //try
930                         catch(InvalidPositionException &e)
931                         {
932                                 // Not in memory
933                                 // Add it to the emerge queue and trigger the thread.
934                                 // Fetch the block only if it is on disk.
935                                 
936                                 // Grab and increment counter
937                                 /*SharedPtr<JMutexAutoLock> lock
938                                                 (m_num_blocks_in_emerge_queue.getLock());
939                                 m_num_blocks_in_emerge_queue.m_value++;*/
940                                 
941                                 // Add to queue as an anonymous fetch from disk
942                                 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
943                                 server->m_emerge_queue.addBlock(0, p, flags);
944                                 server->m_emergethread.trigger();
945                         }
946                 }
947         }
948
949 skip_subsequent:
950
951         // Write block count
952         writeU16(buf, blockcount);
953         os.write((char*)buf, 2);
954
955         // Write block objects
956         os<<bos.str();
957
958         /*
959                 Send data
960         */
961         
962         //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
963
964         // Make data buffer
965         std::string s = os.str();
966         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
967         // Send as unreliable
968         server->m_con.Send(peer_id, 0, data, false);
969 }
970
971 void RemoteClient::GotBlock(v3s16 p)
972 {
973         if(m_blocks_sending.find(p) != NULL)
974                 m_blocks_sending.remove(p);
975         else
976         {
977                 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
978                                 " m_blocks_sending"<<std::endl;*/
979                 m_excess_gotblocks++;
980         }
981         m_blocks_sent.insert(p, true);
982 }
983
984 void RemoteClient::SentBlock(v3s16 p)
985 {
986         if(m_blocks_sending.find(p) == NULL)
987                 m_blocks_sending.insert(p, 0.0);
988         else
989                 dstream<<"RemoteClient::SentBlock(): Sent block"
990                                 " already in m_blocks_sending"<<std::endl;
991 }
992
993 void RemoteClient::SetBlockNotSent(v3s16 p)
994 {
995         m_nearest_unsent_d = 0;
996         
997         if(m_blocks_sending.find(p) != NULL)
998                 m_blocks_sending.remove(p);
999         if(m_blocks_sent.find(p) != NULL)
1000                 m_blocks_sent.remove(p);
1001 }
1002
1003 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
1004 {
1005         m_nearest_unsent_d = 0;
1006         
1007         for(core::map<v3s16, MapBlock*>::Iterator
1008                         i = blocks.getIterator();
1009                         i.atEnd()==false; i++)
1010         {
1011                 v3s16 p = i.getNode()->getKey();
1012
1013                 if(m_blocks_sending.find(p) != NULL)
1014                         m_blocks_sending.remove(p);
1015                 if(m_blocks_sent.find(p) != NULL)
1016                         m_blocks_sent.remove(p);
1017         }
1018 }
1019
1020 /*
1021         PlayerInfo
1022 */
1023
1024 PlayerInfo::PlayerInfo()
1025 {
1026         name[0] = 0;
1027         avg_rtt = 0;
1028 }
1029
1030 void PlayerInfo::PrintLine(std::ostream *s)
1031 {
1032         (*s)<<id<<": ";
1033         (*s)<<"\""<<name<<"\" ("
1034                         <<(position.X/10)<<","<<(position.Y/10)
1035                         <<","<<(position.Z/10)<<") ";
1036         address.print(s);
1037         (*s)<<" avg_rtt="<<avg_rtt;
1038         (*s)<<std::endl;
1039 }
1040
1041 u32 PIChecksum(core::list<PlayerInfo> &l)
1042 {
1043         core::list<PlayerInfo>::Iterator i;
1044         u32 checksum = 1;
1045         u32 a = 10;
1046         for(i=l.begin(); i!=l.end(); i++)
1047         {
1048                 checksum += a * (i->id+1);
1049                 checksum ^= 0x435aafcd;
1050                 a *= 10;
1051         }
1052         return checksum;
1053 }
1054
1055 /*
1056         Server
1057 */
1058
1059 Server::Server(
1060                 std::string mapsavedir,
1061                 std::string configpath
1062         ):
1063         m_env(new ServerMap(mapsavedir), this),
1064         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1065         m_authmanager(mapsavedir+"/auth.txt"),
1066         m_thread(this),
1067         m_emergethread(this),
1068         m_time_counter(0),
1069         m_time_of_day_send_timer(0),
1070         m_uptime(0),
1071         m_mapsavedir(mapsavedir),
1072         m_configpath(configpath),
1073         m_shutdown_requested(false),
1074         m_ignore_map_edit_events(false),
1075         m_ignore_map_edit_events_peer_id(0)
1076 {
1077         m_liquid_transform_timer = 0.0;
1078         m_print_info_timer = 0.0;
1079         m_objectdata_timer = 0.0;
1080         m_emergethread_trigger_timer = 0.0;
1081         m_savemap_timer = 0.0;
1082         
1083         m_env_mutex.Init();
1084         m_con_mutex.Init();
1085         m_step_dtime_mutex.Init();
1086         m_step_dtime = 0.0;
1087         
1088         // Register us to receive map edit events
1089         m_env.getMap().addEventReceiver(this);
1090
1091         // If file exists, load environment metadata
1092         if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1093         {
1094                 dstream<<"Server: Loading environment metadata"<<std::endl;
1095                 m_env.loadMeta(m_mapsavedir);
1096         }
1097
1098         // Load players
1099         dstream<<"Server: Loading players"<<std::endl;
1100         m_env.deSerializePlayers(m_mapsavedir);
1101 }
1102
1103 Server::~Server()
1104 {
1105         dstream<<"Server::~Server()"<<std::endl;
1106
1107         /*
1108                 Send shutdown message
1109         */
1110         {
1111                 JMutexAutoLock conlock(m_con_mutex);
1112                 
1113                 std::wstring line = L"*** Server shutting down";
1114
1115                 /*
1116                         Send the message to clients
1117                 */
1118                 for(core::map<u16, RemoteClient*>::Iterator
1119                         i = m_clients.getIterator();
1120                         i.atEnd() == false; i++)
1121                 {
1122                         // Get client and check that it is valid
1123                         RemoteClient *client = i.getNode()->getValue();
1124                         assert(client->peer_id == i.getNode()->getKey());
1125                         if(client->serialization_version == SER_FMT_VER_INVALID)
1126                                 continue;
1127
1128                         try{
1129                                 SendChatMessage(client->peer_id, line);
1130                         }
1131                         catch(con::PeerNotFoundException &e)
1132                         {}
1133                 }
1134         }
1135
1136         /*
1137                 Save players
1138         */
1139         dstream<<"Server: Saving players"<<std::endl;
1140         m_env.serializePlayers(m_mapsavedir);
1141
1142         /*
1143                 Save environment metadata
1144         */
1145         dstream<<"Server: Saving environment metadata"<<std::endl;
1146         m_env.saveMeta(m_mapsavedir);
1147         
1148         /*
1149                 Stop threads
1150         */
1151         stop();
1152         
1153         /*
1154                 Delete clients
1155         */
1156         {
1157                 JMutexAutoLock clientslock(m_con_mutex);
1158
1159                 for(core::map<u16, RemoteClient*>::Iterator
1160                         i = m_clients.getIterator();
1161                         i.atEnd() == false; i++)
1162                 {
1163                         /*// Delete player
1164                         // NOTE: These are removed by env destructor
1165                         {
1166                                 u16 peer_id = i.getNode()->getKey();
1167                                 JMutexAutoLock envlock(m_env_mutex);
1168                                 m_env.removePlayer(peer_id);
1169                         }*/
1170                         
1171                         // Delete client
1172                         delete i.getNode()->getValue();
1173                 }
1174         }
1175 }
1176
1177 void Server::start(unsigned short port)
1178 {
1179         DSTACK(__FUNCTION_NAME);
1180         // Stop thread if already running
1181         m_thread.stop();
1182         
1183         // Initialize connection
1184         m_con.setTimeoutMs(30);
1185         m_con.Serve(port);
1186
1187         // Start thread
1188         m_thread.setRun(true);
1189         m_thread.Start();
1190         
1191         dout_server<<"Server: Started on port "<<port<<std::endl;
1192 }
1193
1194 void Server::stop()
1195 {
1196         DSTACK(__FUNCTION_NAME);
1197
1198         // Stop threads (set run=false first so both start stopping)
1199         m_thread.setRun(false);
1200         m_emergethread.setRun(false);
1201         m_thread.stop();
1202         m_emergethread.stop();
1203         
1204         dout_server<<"Server: Threads stopped"<<std::endl;
1205 }
1206
1207 void Server::step(float dtime)
1208 {
1209         DSTACK(__FUNCTION_NAME);
1210         // Limit a bit
1211         if(dtime > 2.0)
1212                 dtime = 2.0;
1213         {
1214                 JMutexAutoLock lock(m_step_dtime_mutex);
1215                 m_step_dtime += dtime;
1216         }
1217 }
1218
1219 void Server::AsyncRunStep()
1220 {
1221         DSTACK(__FUNCTION_NAME);
1222         
1223         float dtime;
1224         {
1225                 JMutexAutoLock lock1(m_step_dtime_mutex);
1226                 dtime = m_step_dtime;
1227         }
1228         
1229         {
1230                 ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
1231                                 "blocks to clients");
1232                 // Send blocks to clients
1233                 SendBlocks(dtime);
1234         }
1235         
1236         if(dtime < 0.001)
1237                 return;
1238
1239         //dstream<<"Server steps "<<dtime<<std::endl;
1240         //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1241         
1242         {
1243                 JMutexAutoLock lock1(m_step_dtime_mutex);
1244                 m_step_dtime -= dtime;
1245         }
1246
1247         /*
1248                 Update uptime
1249         */
1250         {
1251                 m_uptime.set(m_uptime.get() + dtime);
1252         }
1253         
1254         /*
1255                 Update m_time_of_day and overall game time
1256         */
1257         {
1258                 JMutexAutoLock envlock(m_env_mutex);
1259
1260                 m_time_counter += dtime;
1261                 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1262                 u32 units = (u32)(m_time_counter*speed);
1263                 m_time_counter -= (f32)units / speed;
1264                 
1265                 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1266                 
1267                 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1268
1269                 /*
1270                         Send to clients at constant intervals
1271                 */
1272
1273                 m_time_of_day_send_timer -= dtime;
1274                 if(m_time_of_day_send_timer < 0.0)
1275                 {
1276                         m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1277
1278                         //JMutexAutoLock envlock(m_env_mutex);
1279                         JMutexAutoLock conlock(m_con_mutex);
1280
1281                         for(core::map<u16, RemoteClient*>::Iterator
1282                                 i = m_clients.getIterator();
1283                                 i.atEnd() == false; i++)
1284                         {
1285                                 RemoteClient *client = i.getNode()->getValue();
1286                                 //Player *player = m_env.getPlayer(client->peer_id);
1287                                 
1288                                 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1289                                                 m_env.getTimeOfDay());
1290                                 // Send as reliable
1291                                 m_con.Send(client->peer_id, 0, data, true);
1292                         }
1293                 }
1294         }
1295
1296         {
1297                 // Process connection's timeouts
1298                 JMutexAutoLock lock2(m_con_mutex);
1299                 ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
1300                 m_con.RunTimeouts(dtime);
1301         }
1302         
1303         {
1304                 // This has to be called so that the client list gets synced
1305                 // with the peer list of the connection
1306                 ScopeProfiler sp(&g_profiler, "Server: peer change handling");
1307                 handlePeerChanges();
1308         }
1309
1310         {
1311                 JMutexAutoLock lock(m_env_mutex);
1312                 // Step environment
1313                 ScopeProfiler sp(&g_profiler, "Server: environment step");
1314                 m_env.step(dtime);
1315         }
1316                 
1317         const float map_timer_and_unload_dtime = 5.15;
1318         if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1319         {
1320                 JMutexAutoLock lock(m_env_mutex);
1321                 // Run Map's timers and unload unused data
1322                 ScopeProfiler sp(&g_profiler, "Server: map timer and unload");
1323                 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
1324                                 g_settings.getFloat("server_unload_unused_data_timeout"));
1325         }
1326         
1327         /*
1328                 Do background stuff
1329         */
1330         
1331         /*
1332                 Transform liquids
1333         */
1334         m_liquid_transform_timer += dtime;
1335         if(m_liquid_transform_timer >= 1.00)
1336         {
1337                 m_liquid_transform_timer -= 1.00;
1338                 
1339                 JMutexAutoLock lock(m_env_mutex);
1340
1341                 ScopeProfiler sp(&g_profiler, "Server: liquid transform");
1342
1343                 core::map<v3s16, MapBlock*> modified_blocks;
1344                 m_env.getMap().transformLiquids(modified_blocks);
1345 #if 0           
1346                 /*
1347                         Update lighting
1348                 */
1349                 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1350                 ServerMap &map = ((ServerMap&)m_env.getMap());
1351                 map.updateLighting(modified_blocks, lighting_modified_blocks);
1352                 
1353                 // Add blocks modified by lighting to modified_blocks
1354                 for(core::map<v3s16, MapBlock*>::Iterator
1355                                 i = lighting_modified_blocks.getIterator();
1356                                 i.atEnd() == false; i++)
1357                 {
1358                         MapBlock *block = i.getNode()->getValue();
1359                         modified_blocks.insert(block->getPos(), block);
1360                 }
1361 #endif
1362                 /*
1363                         Set the modified blocks unsent for all the clients
1364                 */
1365                 
1366                 JMutexAutoLock lock2(m_con_mutex);
1367
1368                 for(core::map<u16, RemoteClient*>::Iterator
1369                                 i = m_clients.getIterator();
1370                                 i.atEnd() == false; i++)
1371                 {
1372                         RemoteClient *client = i.getNode()->getValue();
1373                         
1374                         if(modified_blocks.size() > 0)
1375                         {
1376                                 // Remove block from sent history
1377                                 client->SetBlocksNotSent(modified_blocks);
1378                         }
1379                 }
1380         }
1381
1382         // Periodically print some info
1383         {
1384                 float &counter = m_print_info_timer;
1385                 counter += dtime;
1386                 if(counter >= 30.0)
1387                 {
1388                         counter = 0.0;
1389
1390                         JMutexAutoLock lock2(m_con_mutex);
1391
1392                         for(core::map<u16, RemoteClient*>::Iterator
1393                                 i = m_clients.getIterator();
1394                                 i.atEnd() == false; i++)
1395                         {
1396                                 //u16 peer_id = i.getNode()->getKey();
1397                                 RemoteClient *client = i.getNode()->getValue();
1398                                 Player *player = m_env.getPlayer(client->peer_id);
1399                                 if(player==NULL)
1400                                         continue;
1401                                 std::cout<<player->getName()<<"\t";
1402                                 client->PrintInfo(std::cout);
1403                         }
1404                 }
1405         }
1406
1407         //if(g_settings.getBool("enable_experimental"))
1408         {
1409
1410         /*
1411                 Check added and deleted active objects
1412         */
1413         {
1414                 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1415                 JMutexAutoLock envlock(m_env_mutex);
1416                 JMutexAutoLock conlock(m_con_mutex);
1417
1418                 ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
1419
1420                 // Radius inside which objects are active
1421                 s16 radius = 32;
1422
1423                 for(core::map<u16, RemoteClient*>::Iterator
1424                         i = m_clients.getIterator();
1425                         i.atEnd() == false; i++)
1426                 {
1427                         RemoteClient *client = i.getNode()->getValue();
1428                         Player *player = m_env.getPlayer(client->peer_id);
1429                         if(player==NULL)
1430                         {
1431                                 // This can happen if the client timeouts somehow
1432                                 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1433                                                 <<client->peer_id
1434                                                 <<" has no associated player"<<std::endl;*/
1435                                 continue;
1436                         }
1437                         v3s16 pos = floatToInt(player->getPosition(), BS);
1438
1439                         core::map<u16, bool> removed_objects;
1440                         core::map<u16, bool> added_objects;
1441                         m_env.getRemovedActiveObjects(pos, radius,
1442                                         client->m_known_objects, removed_objects);
1443                         m_env.getAddedActiveObjects(pos, radius,
1444                                         client->m_known_objects, added_objects);
1445                         
1446                         // Ignore if nothing happened
1447                         if(removed_objects.size() == 0 && added_objects.size() == 0)
1448                         {
1449                                 //dstream<<"INFO: active objects: none changed"<<std::endl;
1450                                 continue;
1451                         }
1452                         
1453                         std::string data_buffer;
1454
1455                         char buf[4];
1456                         
1457                         // Handle removed objects
1458                         writeU16((u8*)buf, removed_objects.size());
1459                         data_buffer.append(buf, 2);
1460                         for(core::map<u16, bool>::Iterator
1461                                         i = removed_objects.getIterator();
1462                                         i.atEnd()==false; i++)
1463                         {
1464                                 // Get object
1465                                 u16 id = i.getNode()->getKey();
1466                                 ServerActiveObject* obj = m_env.getActiveObject(id);
1467
1468                                 // Add to data buffer for sending
1469                                 writeU16((u8*)buf, i.getNode()->getKey());
1470                                 data_buffer.append(buf, 2);
1471                                 
1472                                 // Remove from known objects
1473                                 client->m_known_objects.remove(i.getNode()->getKey());
1474
1475                                 if(obj && obj->m_known_by_count > 0)
1476                                         obj->m_known_by_count--;
1477                         }
1478
1479                         // Handle added objects
1480                         writeU16((u8*)buf, added_objects.size());
1481                         data_buffer.append(buf, 2);
1482                         for(core::map<u16, bool>::Iterator
1483                                         i = added_objects.getIterator();
1484                                         i.atEnd()==false; i++)
1485                         {
1486                                 // Get object
1487                                 u16 id = i.getNode()->getKey();
1488                                 ServerActiveObject* obj = m_env.getActiveObject(id);
1489                                 
1490                                 // Get object type
1491                                 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1492                                 if(obj == NULL)
1493                                         dstream<<"WARNING: "<<__FUNCTION_NAME
1494                                                         <<": NULL object"<<std::endl;
1495                                 else
1496                                         type = obj->getType();
1497
1498                                 // Add to data buffer for sending
1499                                 writeU16((u8*)buf, id);
1500                                 data_buffer.append(buf, 2);
1501                                 writeU8((u8*)buf, type);
1502                                 data_buffer.append(buf, 1);
1503                                 
1504                                 if(obj)
1505                                         data_buffer.append(serializeLongString(
1506                                                         obj->getClientInitializationData()));
1507                                 else
1508                                         data_buffer.append(serializeLongString(""));
1509
1510                                 // Add to known objects
1511                                 client->m_known_objects.insert(i.getNode()->getKey(), false);
1512
1513                                 if(obj)
1514                                         obj->m_known_by_count++;
1515                         }
1516
1517                         // Send packet
1518                         SharedBuffer<u8> reply(2 + data_buffer.size());
1519                         writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1520                         memcpy((char*)&reply[2], data_buffer.c_str(),
1521                                         data_buffer.size());
1522                         // Send as reliable
1523                         m_con.Send(client->peer_id, 0, reply, true);
1524
1525                         dstream<<"INFO: Server: Sent object remove/add: "
1526                                         <<removed_objects.size()<<" removed, "
1527                                         <<added_objects.size()<<" added, "
1528                                         <<"packet size is "<<reply.getSize()<<std::endl;
1529                 }
1530
1531 #if 0
1532                 /*
1533                         Collect a list of all the objects known by the clients
1534                         and report it back to the environment.
1535                 */
1536
1537                 core::map<u16, bool> all_known_objects;
1538
1539                 for(core::map<u16, RemoteClient*>::Iterator
1540                         i = m_clients.getIterator();
1541                         i.atEnd() == false; i++)
1542                 {
1543                         RemoteClient *client = i.getNode()->getValue();
1544                         // Go through all known objects of client
1545                         for(core::map<u16, bool>::Iterator
1546                                         i = client->m_known_objects.getIterator();
1547                                         i.atEnd()==false; i++)
1548                         {
1549                                 u16 id = i.getNode()->getKey();
1550                                 all_known_objects[id] = true;
1551                         }
1552                 }
1553                 
1554                 m_env.setKnownActiveObjects(whatever);
1555 #endif
1556
1557         }
1558
1559         /*
1560                 Send object messages
1561         */
1562         {
1563                 JMutexAutoLock envlock(m_env_mutex);
1564                 JMutexAutoLock conlock(m_con_mutex);
1565
1566                 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1567
1568                 // Key = object id
1569                 // Value = data sent by object
1570                 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1571
1572                 // Get active object messages from environment
1573                 for(;;)
1574                 {
1575                         ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1576                         if(aom.id == 0)
1577                                 break;
1578                         
1579                         core::list<ActiveObjectMessage>* message_list = NULL;
1580                         core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1581                         n = buffered_messages.find(aom.id);
1582                         if(n == NULL)
1583                         {
1584                                 message_list = new core::list<ActiveObjectMessage>;
1585                                 buffered_messages.insert(aom.id, message_list);
1586                         }
1587                         else
1588                         {
1589                                 message_list = n->getValue();
1590                         }
1591                         message_list->push_back(aom);
1592                 }
1593                 
1594                 // Route data to every client
1595                 for(core::map<u16, RemoteClient*>::Iterator
1596                         i = m_clients.getIterator();
1597                         i.atEnd()==false; i++)
1598                 {
1599                         RemoteClient *client = i.getNode()->getValue();
1600                         std::string reliable_data;
1601                         std::string unreliable_data;
1602                         // Go through all objects in message buffer
1603                         for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1604                                         j = buffered_messages.getIterator();
1605                                         j.atEnd()==false; j++)
1606                         {
1607                                 // If object is not known by client, skip it
1608                                 u16 id = j.getNode()->getKey();
1609                                 if(client->m_known_objects.find(id) == NULL)
1610                                         continue;
1611                                 // Get message list of object
1612                                 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1613                                 // Go through every message
1614                                 for(core::list<ActiveObjectMessage>::Iterator
1615                                                 k = list->begin(); k != list->end(); k++)
1616                                 {
1617                                         // Compose the full new data with header
1618                                         ActiveObjectMessage aom = *k;
1619                                         std::string new_data;
1620                                         // Add object id
1621                                         char buf[2];
1622                                         writeU16((u8*)&buf[0], aom.id);
1623                                         new_data.append(buf, 2);
1624                                         // Add data
1625                                         new_data += serializeString(aom.datastring);
1626                                         // Add data to buffer
1627                                         if(aom.reliable)
1628                                                 reliable_data += new_data;
1629                                         else
1630                                                 unreliable_data += new_data;
1631                                 }
1632                         }
1633                         /*
1634                                 reliable_data and unreliable_data are now ready.
1635                                 Send them.
1636                         */
1637                         if(reliable_data.size() > 0)
1638                         {
1639                                 SharedBuffer<u8> reply(2 + reliable_data.size());
1640                                 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1641                                 memcpy((char*)&reply[2], reliable_data.c_str(),
1642                                                 reliable_data.size());
1643                                 // Send as reliable
1644                                 m_con.Send(client->peer_id, 0, reply, true);
1645                         }
1646                         if(unreliable_data.size() > 0)
1647                         {
1648                                 SharedBuffer<u8> reply(2 + unreliable_data.size());
1649                                 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1650                                 memcpy((char*)&reply[2], unreliable_data.c_str(),
1651                                                 unreliable_data.size());
1652                                 // Send as unreliable
1653                                 m_con.Send(client->peer_id, 0, reply, false);
1654                         }
1655
1656                         /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1657                         {
1658                                 dstream<<"INFO: Server: Size of object message data: "
1659                                                 <<"reliable: "<<reliable_data.size()
1660                                                 <<", unreliable: "<<unreliable_data.size()
1661                                                 <<std::endl;
1662                         }*/
1663                 }
1664
1665                 // Clear buffered_messages
1666                 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1667                                 i = buffered_messages.getIterator();
1668                                 i.atEnd()==false; i++)
1669                 {
1670                         delete i.getNode()->getValue();
1671                 }
1672         }
1673
1674         } // enable_experimental
1675
1676         /*
1677                 Send queued-for-sending map edit events.
1678         */
1679         {
1680                 // Don't send too many at a time
1681                 //u32 count = 0;
1682
1683                 // Single change sending is disabled if queue size is not small
1684                 bool disable_single_change_sending = false;
1685                 if(m_unsent_map_edit_queue.size() >= 4)
1686                         disable_single_change_sending = true;
1687
1688                 bool got_any_events = false;
1689
1690                 // We'll log the amount of each
1691                 Profiler prof;
1692
1693                 while(m_unsent_map_edit_queue.size() != 0)
1694                 {
1695                         got_any_events = true;
1696
1697                         MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1698                         
1699                         // Players far away from the change are stored here.
1700                         // Instead of sending the changes, MapBlocks are set not sent
1701                         // for them.
1702                         core::list<u16> far_players;
1703
1704                         if(event->type == MEET_ADDNODE)
1705                         {
1706                                 //dstream<<"Server: MEET_ADDNODE"<<std::endl;
1707                                 prof.add("MEET_ADDNODE", 1);
1708                                 if(disable_single_change_sending)
1709                                         sendAddNode(event->p, event->n, event->already_known_by_peer,
1710                                                         &far_players, 5);
1711                                 else
1712                                         sendAddNode(event->p, event->n, event->already_known_by_peer,
1713                                                         &far_players, 30);
1714                         }
1715                         else if(event->type == MEET_REMOVENODE)
1716                         {
1717                                 //dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1718                                 prof.add("MEET_REMOVENODE", 1);
1719                                 if(disable_single_change_sending)
1720                                         sendRemoveNode(event->p, event->already_known_by_peer,
1721                                                         &far_players, 5);
1722                                 else
1723                                         sendRemoveNode(event->p, event->already_known_by_peer,
1724                                                         &far_players, 30);
1725                         }
1726                         else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1727                         {
1728                                 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1729                                 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1730                                 setBlockNotSent(event->p);
1731                         }
1732                         else if(event->type == MEET_OTHER)
1733                         {
1734                                 prof.add("MEET_OTHER", 1);
1735                                 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1736                                                 <<std::endl;
1737                         }
1738                         else
1739                         {
1740                                 prof.add("unknown", 1);
1741                                 dstream<<"WARNING: Server: Unknown MapEditEvent "
1742                                                 <<((u32)event->type)<<std::endl;
1743                         }
1744                         
1745                         /*
1746                                 Set blocks not sent to far players
1747                         */
1748                         if(far_players.size() > 0)
1749                         {
1750                                 // Convert list format to that wanted by SetBlocksNotSent
1751                                 core::map<v3s16, MapBlock*> modified_blocks2;
1752                                 for(core::map<v3s16, bool>::Iterator
1753                                                 i = event->modified_blocks.getIterator();
1754                                                 i.atEnd()==false; i++)
1755                                 {
1756                                         v3s16 p = i.getNode()->getKey();
1757                                         modified_blocks2.insert(p,
1758                                                         m_env.getMap().getBlockNoCreateNoEx(p));
1759                                 }
1760                                 // Set blocks not sent
1761                                 for(core::list<u16>::Iterator
1762                                                 i = far_players.begin();
1763                                                 i != far_players.end(); i++)
1764                                 {
1765                                         u16 peer_id = *i;
1766                                         RemoteClient *client = getClient(peer_id);
1767                                         if(client==NULL)
1768                                                 continue;
1769                                         client->SetBlocksNotSent(modified_blocks2);
1770                                 }
1771                         }
1772
1773                         delete event;
1774
1775                         /*// Don't send too many at a time
1776                         count++;
1777                         if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1778                                 break;*/
1779                 }
1780
1781                 if(got_any_events)
1782                 {
1783                         dstream<<"Server: MapEditEvents:"<<std::endl;
1784                         prof.print(dstream);
1785                 }
1786                 
1787         }
1788
1789         /*
1790                 Send object positions
1791                 TODO: Get rid of MapBlockObjects
1792         */
1793         {
1794                 float &counter = m_objectdata_timer;
1795                 counter += dtime;
1796                 if(counter >= g_settings.getFloat("objectdata_interval"))
1797                 {
1798                         JMutexAutoLock lock1(m_env_mutex);
1799                         JMutexAutoLock lock2(m_con_mutex);
1800
1801                         ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1802
1803                         SendObjectData(counter);
1804
1805                         counter = 0.0;
1806                 }
1807         }
1808         
1809         /*
1810                 Trigger emergethread (it somehow gets to a non-triggered but
1811                 bysy state sometimes)
1812         */
1813         {
1814                 float &counter = m_emergethread_trigger_timer;
1815                 counter += dtime;
1816                 if(counter >= 2.0)
1817                 {
1818                         counter = 0.0;
1819                         
1820                         m_emergethread.trigger();
1821                 }
1822         }
1823
1824         // Save map, players and auth stuff
1825         {
1826                 float &counter = m_savemap_timer;
1827                 counter += dtime;
1828                 if(counter >= g_settings.getFloat("server_map_save_interval"))
1829                 {
1830                         counter = 0.0;
1831
1832                         ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1833
1834                         // Auth stuff
1835                         if(m_authmanager.isModified())
1836                                 m_authmanager.save();
1837                         
1838                         // Map
1839                         JMutexAutoLock lock(m_env_mutex);
1840
1841                         /*// Unload unused data (delete from memory)
1842                         m_env.getMap().unloadUnusedData(
1843                                         g_settings.getFloat("server_unload_unused_sectors_timeout"));
1844                                         */
1845                         /*u32 deleted_count = m_env.getMap().unloadUnusedData(
1846                                         g_settings.getFloat("server_unload_unused_sectors_timeout"));
1847                                         */
1848
1849                         // Save only changed parts
1850                         m_env.getMap().save(true);
1851
1852                         /*if(deleted_count > 0)
1853                         {
1854                                 dout_server<<"Server: Unloaded "<<deleted_count
1855                                                 <<" blocks from memory"<<std::endl;
1856                         }*/
1857
1858                         // Save players
1859                         m_env.serializePlayers(m_mapsavedir);
1860                         
1861                         // Save environment metadata
1862                         m_env.saveMeta(m_mapsavedir);
1863                 }
1864         }
1865 }
1866
1867 void Server::Receive()
1868 {
1869         DSTACK(__FUNCTION_NAME);
1870         u32 data_maxsize = 10000;
1871         Buffer<u8> data(data_maxsize);
1872         u16 peer_id;
1873         u32 datasize;
1874         try{
1875                 {
1876                         JMutexAutoLock conlock(m_con_mutex);
1877                         datasize = m_con.Receive(peer_id, *data, data_maxsize);
1878                 }
1879
1880                 // This has to be called so that the client list gets synced
1881                 // with the peer list of the connection
1882                 handlePeerChanges();
1883
1884                 ProcessData(*data, datasize, peer_id);
1885         }
1886         catch(con::InvalidIncomingDataException &e)
1887         {
1888                 derr_server<<"Server::Receive(): "
1889                                 "InvalidIncomingDataException: what()="
1890                                 <<e.what()<<std::endl;
1891         }
1892         catch(con::PeerNotFoundException &e)
1893         {
1894                 //NOTE: This is not needed anymore
1895                 
1896                 // The peer has been disconnected.
1897                 // Find the associated player and remove it.
1898
1899                 /*JMutexAutoLock envlock(m_env_mutex);
1900
1901                 dout_server<<"ServerThread: peer_id="<<peer_id
1902                                 <<" has apparently closed connection. "
1903                                 <<"Removing player."<<std::endl;
1904
1905                 m_env.removePlayer(peer_id);*/
1906         }
1907 }
1908
1909 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1910 {
1911         DSTACK(__FUNCTION_NAME);
1912         // Environment is locked first.
1913         JMutexAutoLock envlock(m_env_mutex);
1914         JMutexAutoLock conlock(m_con_mutex);
1915         
1916         con::Peer *peer;
1917         try{
1918                 peer = m_con.GetPeer(peer_id);
1919         }
1920         catch(con::PeerNotFoundException &e)
1921         {
1922                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1923                                 <<peer_id<<" not found"<<std::endl;
1924                 return;
1925         }
1926         
1927         u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1928
1929         try
1930         {
1931
1932         if(datasize < 2)
1933                 return;
1934
1935         ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1936         
1937         if(command == TOSERVER_INIT)
1938         {
1939                 // [0] u16 TOSERVER_INIT
1940                 // [2] u8 SER_FMT_VER_HIGHEST
1941                 // [3] u8[20] player_name
1942                 // [23] u8[28] password <--- can be sent without this, from old versions
1943
1944                 if(datasize < 2+1+PLAYERNAME_SIZE)
1945                         return;
1946
1947                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1948                                 <<peer->id<<std::endl;
1949
1950                 // First byte after command is maximum supported
1951                 // serialization version
1952                 u8 client_max = data[2];
1953                 u8 our_max = SER_FMT_VER_HIGHEST;
1954                 // Use the highest version supported by both
1955                 u8 deployed = core::min_(client_max, our_max);
1956                 // If it's lower than the lowest supported, give up.
1957                 if(deployed < SER_FMT_VER_LOWEST)
1958                         deployed = SER_FMT_VER_INVALID;
1959
1960                 //peer->serialization_version = deployed;
1961                 getClient(peer->id)->pending_serialization_version = deployed;
1962
1963                 if(deployed == SER_FMT_VER_INVALID)
1964                 {
1965                         derr_server<<DTIME<<"Server: Cannot negotiate "
1966                                         "serialization version with peer "
1967                                         <<peer_id<<std::endl;
1968                         SendAccessDenied(m_con, peer_id,
1969                                         L"Your client is too old (map format)");
1970                         return;
1971                 }
1972                 
1973                 /*
1974                         Read and check network protocol version
1975                 */
1976
1977                 u16 net_proto_version = 0;
1978                 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1979                 {
1980                         net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1981                 }
1982
1983                 getClient(peer->id)->net_proto_version = net_proto_version;
1984
1985                 if(net_proto_version == 0)
1986                 {
1987                         SendAccessDenied(m_con, peer_id,
1988                                         L"Your client is too old. Please upgrade.");
1989                         return;
1990                 }
1991
1992                 /*
1993                         Set up player
1994                 */
1995                 
1996                 // Get player name
1997                 char playername[PLAYERNAME_SIZE];
1998                 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1999                 {
2000                         playername[i] = data[3+i];
2001                 }
2002                 playername[PLAYERNAME_SIZE-1] = 0;
2003                 
2004                 if(playername[0]=='\0')
2005                 {
2006                         derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
2007                         SendAccessDenied(m_con, peer_id,
2008                                         L"Empty name");
2009                         return;
2010                 }
2011
2012                 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2013                 {
2014                         derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
2015                         SendAccessDenied(m_con, peer_id,
2016                                         L"Name contains unallowed characters");
2017                         return;
2018                 }
2019
2020                 // Get password
2021                 char password[PASSWORD_SIZE];
2022                 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2023                 {
2024                         // old version - assume blank password
2025                         password[0] = 0;
2026                 }
2027                 else
2028                 {
2029                                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2030                                 {
2031                                         password[i] = data[23+i];
2032                                 }
2033                                 password[PASSWORD_SIZE-1] = 0;
2034                 }
2035                 
2036                 std::string checkpwd;
2037                 if(m_authmanager.exists(playername))
2038                 {
2039                         checkpwd = m_authmanager.getPassword(playername);
2040                 }
2041                 else
2042                 {
2043                         checkpwd = g_settings.get("default_password");
2044                 }
2045                 
2046                 /*dstream<<"Server: Client gave password '"<<password
2047                                 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2048                 
2049                 if(password != checkpwd && m_authmanager.exists(playername))
2050                 {
2051                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
2052                                         <<": supplied invalid password for "
2053                                         <<playername<<std::endl;
2054                         SendAccessDenied(m_con, peer_id, L"Invalid password");
2055                         return;
2056                 }
2057                 
2058                 // Add player to auth manager
2059                 if(m_authmanager.exists(playername) == false)
2060                 {
2061                         derr_server<<DTIME<<"Server: adding player "<<playername
2062                                         <<" to auth manager"<<std::endl;
2063                         m_authmanager.add(playername);
2064                         m_authmanager.setPassword(playername, checkpwd);
2065                         m_authmanager.setPrivs(playername,
2066                                         stringToPrivs(g_settings.get("default_privs")));
2067                         m_authmanager.save();
2068                 }
2069
2070                 // Get player
2071                 Player *player = emergePlayer(playername, password, peer_id);
2072
2073
2074                 /*{
2075                         // DEBUG: Test serialization
2076                         std::ostringstream test_os;
2077                         player->serialize(test_os);
2078                         dstream<<"Player serialization test: \""<<test_os.str()
2079                                         <<"\""<<std::endl;
2080                         std::istringstream test_is(test_os.str());
2081                         player->deSerialize(test_is);
2082                 }*/
2083
2084                 // If failed, cancel
2085                 if(player == NULL)
2086                 {
2087                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
2088                                         <<": failed to emerge player"<<std::endl;
2089                         return;
2090                 }
2091
2092                 /*
2093                 // If a client is already connected to the player, cancel
2094                 if(player->peer_id != 0)
2095                 {
2096                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
2097                                         <<" tried to connect to "
2098                                         "an already connected player (peer_id="
2099                                         <<player->peer_id<<")"<<std::endl;
2100                         return;
2101                 }
2102                 // Set client of player
2103                 player->peer_id = peer_id;
2104                 */
2105
2106                 // Check if player doesn't exist
2107                 if(player == NULL)
2108                         throw con::InvalidIncomingDataException
2109                                 ("Server::ProcessData(): INIT: Player doesn't exist");
2110
2111                 /*// update name if it was supplied
2112                 if(datasize >= 20+3)
2113                 {
2114                         data[20+3-1] = 0;
2115                         player->updateName((const char*)&data[3]);
2116                 }*/
2117                 
2118                 /*
2119                         Answer with a TOCLIENT_INIT
2120                 */
2121                 {
2122                         SharedBuffer<u8> reply(2+1+6+8);
2123                         writeU16(&reply[0], TOCLIENT_INIT);
2124                         writeU8(&reply[2], deployed);
2125                         writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2126                         writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2127                         
2128                         // Send as reliable
2129                         m_con.Send(peer_id, 0, reply, true);
2130                 }
2131
2132                 /*
2133                         Send complete position information
2134                 */
2135                 SendMovePlayer(player);
2136
2137                 return;
2138         }
2139
2140         if(command == TOSERVER_INIT2)
2141         {
2142                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2143                                 <<peer->id<<std::endl;
2144
2145
2146                 getClient(peer->id)->serialization_version
2147                                 = getClient(peer->id)->pending_serialization_version;
2148
2149                 /*
2150                         Send some initialization data
2151                 */
2152                 
2153                 // Send player info to all players
2154                 SendPlayerInfos();
2155
2156                 // Send inventory to player
2157                 UpdateCrafting(peer->id);
2158                 SendInventory(peer->id);
2159
2160                 // Send HP
2161                 {
2162                         Player *player = m_env.getPlayer(peer_id);
2163                         SendPlayerHP(player);
2164                 }
2165                 
2166                 // Send time of day
2167                 {
2168                         SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2169                                         m_env.getTimeOfDay());
2170                         m_con.Send(peer->id, 0, data, true);
2171                 }
2172                 
2173                 // Send information about server to player in chat
2174                 SendChatMessage(peer_id, getStatusString());
2175                 
2176                 // Send information about joining in chat
2177                 {
2178                         std::wstring name = L"unknown";
2179                         Player *player = m_env.getPlayer(peer_id);
2180                         if(player != NULL)
2181                                 name = narrow_to_wide(player->getName());
2182                         
2183                         std::wstring message;
2184                         message += L"*** ";
2185                         message += name;
2186                         message += L" joined game";
2187                         BroadcastChatMessage(message);
2188                 }
2189                 
2190                 // Warnings about protocol version can be issued here
2191                 /*if(getClient(peer->id)->net_proto_version == 0)
2192                 {
2193                         SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
2194                 }*/
2195
2196                 return;
2197         }
2198
2199         if(peer_ser_ver == SER_FMT_VER_INVALID)
2200         {
2201                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2202                                 " serialization format invalid or not initialized."
2203                                 " Skipping incoming command="<<command<<std::endl;
2204                 return;
2205         }
2206         
2207         Player *player = m_env.getPlayer(peer_id);
2208
2209         if(player == NULL){
2210                 derr_server<<"Server::ProcessData(): Cancelling: "
2211                                 "No player for peer_id="<<peer_id
2212                                 <<std::endl;
2213                 return;
2214         }
2215         if(command == TOSERVER_PLAYERPOS)
2216         {
2217                 if(datasize < 2+12+12+4+4)
2218                         return;
2219         
2220                 u32 start = 0;
2221                 v3s32 ps = readV3S32(&data[start+2]);
2222                 v3s32 ss = readV3S32(&data[start+2+12]);
2223                 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2224                 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2225                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2226                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2227                 pitch = wrapDegrees(pitch);
2228                 yaw = wrapDegrees(yaw);
2229                 player->setPosition(position);
2230                 player->setSpeed(speed);
2231                 player->setPitch(pitch);
2232                 player->setYaw(yaw);
2233                 
2234                 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2235                                 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2236                                 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2237         }
2238         else if(command == TOSERVER_GOTBLOCKS)
2239         {
2240                 if(datasize < 2+1)
2241                         return;
2242                 
2243                 /*
2244                         [0] u16 command
2245                         [2] u8 count
2246                         [3] v3s16 pos_0
2247                         [3+6] v3s16 pos_1
2248                         ...
2249                 */
2250
2251                 u16 count = data[2];
2252                 for(u16 i=0; i<count; i++)
2253                 {
2254                         if((s16)datasize < 2+1+(i+1)*6)
2255                                 throw con::InvalidIncomingDataException
2256                                         ("GOTBLOCKS length is too short");
2257                         v3s16 p = readV3S16(&data[2+1+i*6]);
2258                         /*dstream<<"Server: GOTBLOCKS ("
2259                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2260                         RemoteClient *client = getClient(peer_id);
2261                         client->GotBlock(p);
2262                 }
2263         }
2264         else if(command == TOSERVER_DELETEDBLOCKS)
2265         {
2266                 if(datasize < 2+1)
2267                         return;
2268                 
2269                 /*
2270                         [0] u16 command
2271                         [2] u8 count
2272                         [3] v3s16 pos_0
2273                         [3+6] v3s16 pos_1
2274                         ...
2275                 */
2276
2277                 u16 count = data[2];
2278                 for(u16 i=0; i<count; i++)
2279                 {
2280                         if((s16)datasize < 2+1+(i+1)*6)
2281                                 throw con::InvalidIncomingDataException
2282                                         ("DELETEDBLOCKS length is too short");
2283                         v3s16 p = readV3S16(&data[2+1+i*6]);
2284                         /*dstream<<"Server: DELETEDBLOCKS ("
2285                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2286                         RemoteClient *client = getClient(peer_id);
2287                         client->SetBlockNotSent(p);
2288                 }
2289         }
2290         else if(command == TOSERVER_CLICK_OBJECT)
2291         {
2292                 if(datasize < 13)
2293                         return;
2294
2295                 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2296                         return;
2297
2298                 /*
2299                         [0] u16 command
2300                         [2] u8 button (0=left, 1=right)
2301                         [3] v3s16 block
2302                         [9] s16 id
2303                         [11] u16 item
2304                 */
2305                 u8 button = readU8(&data[2]);
2306                 v3s16 p;
2307                 p.X = readS16(&data[3]);
2308                 p.Y = readS16(&data[5]);
2309                 p.Z = readS16(&data[7]);
2310                 s16 id = readS16(&data[9]);
2311                 //u16 item_i = readU16(&data[11]);
2312
2313                 MapBlock *block = NULL;
2314                 try
2315                 {
2316                         block = m_env.getMap().getBlockNoCreate(p);
2317                 }
2318                 catch(InvalidPositionException &e)
2319                 {
2320                         derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2321                         return;
2322                 }
2323
2324                 MapBlockObject *obj = block->getObject(id);
2325
2326                 if(obj == NULL)
2327                 {
2328                         derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2329                         return;
2330                 }
2331
2332                 //TODO: Check that object is reasonably close
2333                 
2334                 // Left click
2335                 if(button == 0)
2336                 {
2337                         InventoryList *ilist = player->inventory.getList("main");
2338                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2339                         {
2340                         
2341                                 // Skip if inventory has no free space
2342                                 if(ilist->getUsedSlots() == ilist->getSize())
2343                                 {
2344                                         dout_server<<"Player inventory has no free space"<<std::endl;
2345                                         return;
2346                                 }
2347                                 
2348                                 /*
2349                                         Create the inventory item
2350                                 */
2351                                 InventoryItem *item = NULL;
2352                                 // If it is an item-object, take the item from it
2353                                 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2354                                 {
2355                                         item = ((ItemObject*)obj)->createInventoryItem();
2356                                 }
2357                                 // Else create an item of the object
2358                                 else
2359                                 {
2360                                         item = new MapBlockObjectItem
2361                                                         (obj->getInventoryString());
2362                                 }
2363                                 
2364                                 // Add to inventory and send inventory
2365                                 ilist->addItem(item);
2366                                 UpdateCrafting(player->peer_id);
2367                                 SendInventory(player->peer_id);
2368                         }
2369
2370                         // Remove from block
2371                         block->removeObject(id);
2372                 }
2373         }
2374         else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2375         {
2376                 if(datasize < 7)
2377                         return;
2378
2379                 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2380                         return;
2381
2382                 /*
2383                         length: 7
2384                         [0] u16 command
2385                         [2] u8 button (0=left, 1=right)
2386                         [3] u16 id
2387                         [5] u16 item
2388                 */
2389                 u8 button = readU8(&data[2]);
2390                 u16 id = readS16(&data[3]);
2391                 u16 item_i = readU16(&data[11]);
2392         
2393                 ServerActiveObject *obj = m_env.getActiveObject(id);
2394
2395                 if(obj == NULL)
2396                 {
2397                         derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2398                                         <<std::endl;
2399                         return;
2400                 }
2401
2402                 // Skip if object has been removed
2403                 if(obj->m_removed)
2404                         return;
2405                 
2406                 //TODO: Check that object is reasonably close
2407                 
2408                 // Left click, pick object up (usually)
2409                 if(button == 0)
2410                 {
2411                         /*
2412                                 Try creating inventory item
2413                         */
2414                         InventoryItem *item = obj->createPickedUpItem();
2415                         
2416                         if(item)
2417                         {
2418                                 InventoryList *ilist = player->inventory.getList("main");
2419                                 if(ilist != NULL)
2420                                 {
2421                                         if(g_settings.getBool("creative_mode") == false)
2422                                         {
2423                                                 // Skip if inventory has no free space
2424                                                 if(ilist->getUsedSlots() == ilist->getSize())
2425                                                 {
2426                                                         dout_server<<"Player inventory has no free space"<<std::endl;
2427                                                         return;
2428                                                 }
2429
2430                                                 // Add to inventory and send inventory
2431                                                 ilist->addItem(item);
2432                                                 UpdateCrafting(player->peer_id);
2433                                                 SendInventory(player->peer_id);
2434                                         }
2435
2436                                         // Remove object from environment
2437                                         obj->m_removed = true;
2438                                 }
2439                         }
2440                         else
2441                         {
2442                                 /*
2443                                         Item cannot be picked up. Punch it instead.
2444                                 */
2445
2446                                 ToolItem *titem = NULL;
2447                                 std::string toolname = "";
2448
2449                                 InventoryList *mlist = player->inventory.getList("main");
2450                                 if(mlist != NULL)
2451                                 {
2452                                         InventoryItem *item = mlist->getItem(item_i);
2453                                         if(item && (std::string)item->getName() == "ToolItem")
2454                                         {
2455                                                 titem = (ToolItem*)item;
2456                                                 toolname = titem->getToolName();
2457                                         }
2458                                 }
2459
2460                                 v3f playerpos = player->getPosition();
2461                                 v3f objpos = obj->getBasePosition();
2462                                 v3f dir = (objpos - playerpos).normalize();
2463                                 
2464                                 u16 wear = obj->punch(toolname, dir);
2465                                 
2466                                 if(titem)
2467                                 {
2468                                         bool weared_out = titem->addWear(wear);
2469                                         if(weared_out)
2470                                                 mlist->deleteItem(item_i);
2471                                         SendInventory(player->peer_id);
2472                                 }
2473                         }
2474                 }
2475                 // Right click, do something with object
2476                 if(button == 1)
2477                 {
2478                         // Track hp changes super-crappily
2479                         u16 oldhp = player->hp;
2480                         
2481                         // Do stuff
2482                         obj->rightClick(player);
2483                         
2484                         // Send back stuff
2485                         if(player->hp != oldhp)
2486                         {
2487                                 SendPlayerHP(player);
2488                         }
2489                 }
2490         }
2491         else if(command == TOSERVER_GROUND_ACTION)
2492         {
2493                 if(datasize < 17)
2494                         return;
2495                 /*
2496                         length: 17
2497                         [0] u16 command
2498                         [2] u8 action
2499                         [3] v3s16 nodepos_undersurface
2500                         [9] v3s16 nodepos_abovesurface
2501                         [15] u16 item
2502                         actions:
2503                         0: start digging
2504                         1: place block
2505                         2: stop digging (all parameters ignored)
2506                         3: digging completed
2507                 */
2508                 u8 action = readU8(&data[2]);
2509                 v3s16 p_under;
2510                 p_under.X = readS16(&data[3]);
2511                 p_under.Y = readS16(&data[5]);
2512                 p_under.Z = readS16(&data[7]);
2513                 v3s16 p_over;
2514                 p_over.X = readS16(&data[9]);
2515                 p_over.Y = readS16(&data[11]);
2516                 p_over.Z = readS16(&data[13]);
2517                 u16 item_i = readU16(&data[15]);
2518
2519                 //TODO: Check that target is reasonably close
2520                 
2521                 /*
2522                         0: start digging
2523                 */
2524                 if(action == 0)
2525                 {
2526                         /*
2527                                 NOTE: This can be used in the future to check if
2528                                 somebody is cheating, by checking the timing.
2529                         */
2530                 } // action == 0
2531
2532                 /*
2533                         2: stop digging
2534                 */
2535                 else if(action == 2)
2536                 {
2537 #if 0
2538                         RemoteClient *client = getClient(peer->id);
2539                         JMutexAutoLock digmutex(client->m_dig_mutex);
2540                         client->m_dig_tool_item = -1;
2541 #endif
2542                 }
2543
2544                 /*
2545                         3: Digging completed
2546                 */
2547                 else if(action == 3)
2548                 {
2549                         // Mandatory parameter; actually used for nothing
2550                         core::map<v3s16, MapBlock*> modified_blocks;
2551
2552                         content_t material = CONTENT_IGNORE;
2553                         u8 mineral = MINERAL_NONE;
2554
2555                         bool cannot_remove_node = false;
2556
2557                         try
2558                         {
2559                                 MapNode n = m_env.getMap().getNode(p_under);
2560                                 // Get mineral
2561                                 mineral = n.getMineral();
2562                                 // Get material at position
2563                                 material = n.getContent();
2564                                 // If not yet cancelled
2565                                 if(cannot_remove_node == false)
2566                                 {
2567                                         // If it's not diggable, do nothing
2568                                         if(content_diggable(material) == false)
2569                                         {
2570                                                 derr_server<<"Server: Not finishing digging: "
2571                                                                 <<"Node not diggable"
2572                                                                 <<std::endl;
2573                                                 cannot_remove_node = true;
2574                                         }
2575                                 }
2576                                 // If not yet cancelled
2577                                 if(cannot_remove_node == false)
2578                                 {
2579                                         // Get node metadata
2580                                         NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2581                                         if(meta && meta->nodeRemovalDisabled() == true)
2582                                         {
2583                                                 derr_server<<"Server: Not finishing digging: "
2584                                                                 <<"Node metadata disables removal"
2585                                                                 <<std::endl;
2586                                                 cannot_remove_node = true;
2587                                         }
2588                                 }
2589                         }
2590                         catch(InvalidPositionException &e)
2591                         {
2592                                 derr_server<<"Server: Not finishing digging: Node not found."
2593                                                 <<" Adding block to emerge queue."
2594                                                 <<std::endl;
2595                                 m_emerge_queue.addBlock(peer_id,
2596                                                 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2597                                 cannot_remove_node = true;
2598                         }
2599
2600                         // Make sure the player is allowed to do it
2601                         if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2602                         {
2603                                 dstream<<"Player "<<player->getName()<<" cannot remove node"
2604                                                 <<" because privileges are "<<getPlayerPrivs(player)
2605                                                 <<std::endl;
2606                                 cannot_remove_node = true;
2607                         }
2608
2609                         /*
2610                                 If node can't be removed, set block to be re-sent to
2611                                 client and quit.
2612                         */
2613                         if(cannot_remove_node)
2614                         {
2615                                 derr_server<<"Server: Not finishing digging."<<std::endl;
2616
2617                                 // Client probably has wrong data.
2618                                 // Set block not sent, so that client will get
2619                                 // a valid one.
2620                                 dstream<<"Client "<<peer_id<<" tried to dig "
2621                                                 <<"node; but node cannot be removed."
2622                                                 <<" setting MapBlock not sent."<<std::endl;
2623                                 RemoteClient *client = getClient(peer_id);
2624                                 v3s16 blockpos = getNodeBlockPos(p_under);
2625                                 client->SetBlockNotSent(blockpos);
2626                                         
2627                                 return;
2628                         }
2629                         
2630                         /*
2631                                 Send the removal to all close-by players.
2632                                 - If other player is close, send REMOVENODE
2633                                 - Otherwise set blocks not sent
2634                         */
2635                         core::list<u16> far_players;
2636                         sendRemoveNode(p_under, peer_id, &far_players, 30);
2637                         
2638                         /*
2639                                 Update and send inventory
2640                         */
2641
2642                         if(g_settings.getBool("creative_mode") == false)
2643                         {
2644                                 /*
2645                                         Wear out tool
2646                                 */
2647                                 InventoryList *mlist = player->inventory.getList("main");
2648                                 if(mlist != NULL)
2649                                 {
2650                                         InventoryItem *item = mlist->getItem(item_i);
2651                                         if(item && (std::string)item->getName() == "ToolItem")
2652                                         {
2653                                                 ToolItem *titem = (ToolItem*)item;
2654                                                 std::string toolname = titem->getToolName();
2655
2656                                                 // Get digging properties for material and tool
2657                                                 DiggingProperties prop =
2658                                                                 getDiggingProperties(material, toolname);
2659
2660                                                 if(prop.diggable == false)
2661                                                 {
2662                                                         derr_server<<"Server: WARNING: Player digged"
2663                                                                         <<" with impossible material + tool"
2664                                                                         <<" combination"<<std::endl;
2665                                                 }
2666                                                 
2667                                                 bool weared_out = titem->addWear(prop.wear);
2668
2669                                                 if(weared_out)
2670                                                 {
2671                                                         mlist->deleteItem(item_i);
2672                                                 }
2673                                         }
2674                                 }
2675
2676                                 /*
2677                                         Add dug item to inventory
2678                                 */
2679
2680                                 InventoryItem *item = NULL;
2681
2682                                 if(mineral != MINERAL_NONE)
2683                                         item = getDiggedMineralItem(mineral);
2684                                 
2685                                 // If not mineral
2686                                 if(item == NULL)
2687                                 {
2688                                         std::string &dug_s = content_features(material).dug_item;
2689                                         if(dug_s != "")
2690                                         {
2691                                                 std::istringstream is(dug_s, std::ios::binary);
2692                                                 item = InventoryItem::deSerialize(is);
2693                                         }
2694                                 }
2695                                 
2696                                 if(item != NULL)
2697                                 {
2698                                         // Add a item to inventory
2699                                         player->inventory.addItem("main", item);
2700
2701                                         // Send inventory
2702                                         UpdateCrafting(player->peer_id);
2703                                         SendInventory(player->peer_id);
2704                                 }
2705                         }
2706
2707                         /*
2708                                 Remove the node
2709                                 (this takes some time so it is done after the quick stuff)
2710                         */
2711                         {
2712                                 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2713
2714                                 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2715                         }
2716                         /*
2717                                 Set blocks not sent to far players
2718                         */
2719                         for(core::list<u16>::Iterator
2720                                         i = far_players.begin();
2721                                         i != far_players.end(); i++)
2722                         {
2723                                 u16 peer_id = *i;
2724                                 RemoteClient *client = getClient(peer_id);
2725                                 if(client==NULL)
2726                                         continue;
2727                                 client->SetBlocksNotSent(modified_blocks);
2728                         }
2729                 }
2730                 
2731                 /*
2732                         1: place block
2733                 */
2734                 else if(action == 1)
2735                 {
2736
2737                         InventoryList *ilist = player->inventory.getList("main");
2738                         if(ilist == NULL)
2739                                 return;
2740
2741                         // Get item
2742                         InventoryItem *item = ilist->getItem(item_i);
2743                         
2744                         // If there is no item, it is not possible to add it anywhere
2745                         if(item == NULL)
2746                                 return;
2747                         
2748                         /*
2749                                 Handle material items
2750                         */
2751                         if(std::string("MaterialItem") == item->getName())
2752                         {
2753                                 try{
2754                                         // Don't add a node if this is not a free space
2755                                         MapNode n2 = m_env.getMap().getNode(p_over);
2756                                         bool no_enough_privs =
2757                                                         ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2758                                         if(no_enough_privs)
2759                                                 dstream<<"Player "<<player->getName()<<" cannot add node"
2760                                                         <<" because privileges are "<<getPlayerPrivs(player)
2761                                                         <<std::endl;
2762
2763                                         if(content_features(n2).buildable_to == false
2764                                                 || no_enough_privs)
2765                                         {
2766                                                 // Client probably has wrong data.
2767                                                 // Set block not sent, so that client will get
2768                                                 // a valid one.
2769                                                 dstream<<"Client "<<peer_id<<" tried to place"
2770                                                                 <<" node in invalid position; setting"
2771                                                                 <<" MapBlock not sent."<<std::endl;
2772                                                 RemoteClient *client = getClient(peer_id);
2773                                                 v3s16 blockpos = getNodeBlockPos(p_over);
2774                                                 client->SetBlockNotSent(blockpos);
2775                                                 return;
2776                                         }
2777                                 }
2778                                 catch(InvalidPositionException &e)
2779                                 {
2780                                         derr_server<<"Server: Ignoring ADDNODE: Node not found"
2781                                                         <<" Adding block to emerge queue."
2782                                                         <<std::endl;
2783                                         m_emerge_queue.addBlock(peer_id,
2784                                                         getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2785                                         return;
2786                                 }
2787
2788                                 // Reset build time counter
2789                                 getClient(peer->id)->m_time_from_building = 0.0;
2790                                 
2791                                 // Create node data
2792                                 MaterialItem *mitem = (MaterialItem*)item;
2793                                 MapNode n;
2794                                 n.setContent(mitem->getMaterial());
2795
2796                                 // Calculate direction for wall mounted stuff
2797                                 if(content_features(n).wall_mounted)
2798                                         n.param2 = packDir(p_under - p_over);
2799
2800                                 // Calculate the direction for furnaces and chests and stuff
2801                                 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2802                                 {
2803                                         v3f playerpos = player->getPosition();
2804                                         v3f blockpos = intToFloat(p_over, BS) - playerpos;
2805                                         blockpos = blockpos.normalize();
2806                                         n.param1 = 0;
2807                                         if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2808                                                 if (blockpos.X < 0)
2809                                                         n.param1 = 3;
2810                                                 else
2811                                                         n.param1 = 1;
2812                                         } else {
2813                                                 if (blockpos.Z < 0)
2814                                                         n.param1 = 2;
2815                                                 else
2816                                                         n.param1 = 0;
2817                                         }
2818                                 }
2819
2820                                 /*
2821                                         Send to all close-by players
2822                                 */
2823                                 core::list<u16> far_players;
2824                                 sendAddNode(p_over, n, 0, &far_players, 30);
2825                                 
2826                                 /*
2827                                         Handle inventory
2828                                 */
2829                                 InventoryList *ilist = player->inventory.getList("main");
2830                                 if(g_settings.getBool("creative_mode") == false && ilist)
2831                                 {
2832                                         // Remove from inventory and send inventory
2833                                         if(mitem->getCount() == 1)
2834                                                 ilist->deleteItem(item_i);
2835                                         else
2836                                                 mitem->remove(1);
2837                                         // Send inventory
2838                                         UpdateCrafting(peer_id);
2839                                         SendInventory(peer_id);
2840                                 }
2841                                 
2842                                 /*
2843                                         Add node.
2844
2845                                         This takes some time so it is done after the quick stuff
2846                                 */
2847                                 core::map<v3s16, MapBlock*> modified_blocks;
2848                                 {
2849                                         MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2850
2851                                         m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2852                                 }
2853                                 /*
2854                                         Set blocks not sent to far players
2855                                 */
2856                                 for(core::list<u16>::Iterator
2857                                                 i = far_players.begin();
2858                                                 i != far_players.end(); i++)
2859                                 {
2860                                         u16 peer_id = *i;
2861                                         RemoteClient *client = getClient(peer_id);
2862                                         if(client==NULL)
2863                                                 continue;
2864                                         client->SetBlocksNotSent(modified_blocks);
2865                                 }
2866
2867                                 /*
2868                                         Calculate special events
2869                                 */
2870                                 
2871                                 /*if(n.d == CONTENT_MESE)
2872                                 {
2873                                         u32 count = 0;
2874                                         for(s16 z=-1; z<=1; z++)
2875                                         for(s16 y=-1; y<=1; y++)
2876                                         for(s16 x=-1; x<=1; x++)
2877                                         {
2878                                                 
2879                                         }
2880                                 }*/
2881                         }
2882                         /*
2883                                 Place other item (not a block)
2884                         */
2885                         else
2886                         {
2887                                 v3s16 blockpos = getNodeBlockPos(p_over);
2888                                 
2889                                 /*
2890                                         Check that the block is loaded so that the item
2891                                         can properly be added to the static list too
2892                                 */
2893                                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2894                                 if(block==NULL)
2895                                 {
2896                                         derr_server<<"Error while placing object: "
2897                                                         "block not found"<<std::endl;
2898                                         return;
2899                                 }
2900
2901                                 dout_server<<"Placing a miscellaneous item on map"
2902                                                 <<std::endl;
2903                                 
2904                                 // Calculate a position for it
2905                                 v3f pos = intToFloat(p_over, BS);
2906                                 //pos.Y -= BS*0.45;
2907                                 pos.Y -= BS*0.25; // let it drop a bit
2908                                 // Randomize a bit
2909                                 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2910                                 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2911
2912                                 /*
2913                                         Create the object
2914                                 */
2915                                 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2916
2917                                 if(obj == NULL)
2918                                 {
2919                                         derr_server<<"WARNING: item resulted in NULL object, "
2920                                                         <<"not placing onto map"
2921                                                         <<std::endl;
2922                                 }
2923                                 else
2924                                 {
2925                                         // Add the object to the environment
2926                                         m_env.addActiveObject(obj);
2927                                         
2928                                         dout_server<<"Placed object"<<std::endl;
2929
2930                                         if(g_settings.getBool("creative_mode") == false)
2931                                         {
2932                                                 // Delete the right amount of items from the slot
2933                                                 u16 dropcount = item->getDropCount();
2934                                                 
2935                                                 // Delete item if all gone
2936                                                 if(item->getCount() <= dropcount)
2937                                                 {
2938                                                         if(item->getCount() < dropcount)
2939                                                                 dstream<<"WARNING: Server: dropped more items"
2940                                                                                 <<" than the slot contains"<<std::endl;
2941                                                         
2942                                                         InventoryList *ilist = player->inventory.getList("main");
2943                                                         if(ilist)
2944                                                                 // Remove from inventory and send inventory
2945                                                                 ilist->deleteItem(item_i);
2946                                                 }
2947                                                 // Else decrement it
2948                                                 else
2949                                                         item->remove(dropcount);
2950                                                 
2951                                                 // Send inventory
2952                                                 UpdateCrafting(peer_id);
2953                                                 SendInventory(peer_id);
2954                                         }
2955                                 }
2956                         }
2957
2958                 } // action == 1
2959
2960                 /*
2961                         Catch invalid actions
2962                 */
2963                 else
2964                 {
2965                         derr_server<<"WARNING: Server: Invalid action "
2966                                         <<action<<std::endl;
2967                 }
2968         }
2969 #if 0
2970         else if(command == TOSERVER_RELEASE)
2971         {
2972                 if(datasize < 3)
2973                         return;
2974                 /*
2975                         length: 3
2976                         [0] u16 command
2977                         [2] u8 button
2978                 */
2979                 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2980         }
2981 #endif
2982         else if(command == TOSERVER_SIGNTEXT)
2983         {
2984                 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2985                         return;
2986                 /*
2987                         u16 command
2988                         v3s16 blockpos
2989                         s16 id
2990                         u16 textlen
2991                         textdata
2992                 */
2993                 std::string datastring((char*)&data[2], datasize-2);
2994                 std::istringstream is(datastring, std::ios_base::binary);
2995                 u8 buf[6];
2996                 // Read stuff
2997                 is.read((char*)buf, 6);
2998                 v3s16 blockpos = readV3S16(buf);
2999                 is.read((char*)buf, 2);
3000                 s16 id = readS16(buf);
3001                 is.read((char*)buf, 2);
3002                 u16 textlen = readU16(buf);
3003                 std::string text;
3004                 for(u16 i=0; i<textlen; i++)
3005                 {
3006                         is.read((char*)buf, 1);
3007                         text += (char)buf[0];
3008                 }
3009
3010                 MapBlock *block = NULL;
3011                 try
3012                 {
3013                         block = m_env.getMap().getBlockNoCreate(blockpos);
3014                 }
3015                 catch(InvalidPositionException &e)
3016                 {
3017                         derr_server<<"Error while setting sign text: "
3018                                         "block not found"<<std::endl;
3019                         return;
3020                 }
3021
3022                 MapBlockObject *obj = block->getObject(id);
3023                 if(obj == NULL)
3024                 {
3025                         derr_server<<"Error while setting sign text: "
3026                                         "object not found"<<std::endl;
3027                         return;
3028                 }
3029                 
3030                 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
3031                 {
3032                         derr_server<<"Error while setting sign text: "
3033                                         "object is not a sign"<<std::endl;
3034                         return;
3035                 }
3036
3037                 ((SignObject*)obj)->setText(text);
3038
3039                 obj->getBlock()->setChangedFlag();
3040         }
3041         else if(command == TOSERVER_SIGNNODETEXT)
3042         {
3043                 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3044                         return;
3045                 /*
3046                         u16 command
3047                         v3s16 p
3048                         u16 textlen
3049                         textdata
3050                 */
3051                 std::string datastring((char*)&data[2], datasize-2);
3052                 std::istringstream is(datastring, std::ios_base::binary);
3053                 u8 buf[6];
3054                 // Read stuff
3055                 is.read((char*)buf, 6);
3056                 v3s16 p = readV3S16(buf);
3057                 is.read((char*)buf, 2);
3058                 u16 textlen = readU16(buf);
3059                 std::string text;
3060                 for(u16 i=0; i<textlen; i++)
3061                 {
3062                         is.read((char*)buf, 1);
3063                         text += (char)buf[0];
3064                 }
3065
3066                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3067                 if(!meta)
3068                         return;
3069                 if(meta->typeId() != CONTENT_SIGN_WALL)
3070                         return;
3071                 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3072                 signmeta->setText(text);
3073                 
3074                 v3s16 blockpos = getNodeBlockPos(p);
3075                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
3076                 if(block)
3077                 {
3078                         block->setChangedFlag();
3079                 }
3080
3081                 for(core::map<u16, RemoteClient*>::Iterator
3082                         i = m_clients.getIterator();
3083                         i.atEnd()==false; i++)
3084                 {
3085                         RemoteClient *client = i.getNode()->getValue();
3086                         client->SetBlockNotSent(blockpos);
3087                 }
3088         }
3089         else if(command == TOSERVER_INVENTORY_ACTION)
3090         {
3091                 /*// Ignore inventory changes if in creative mode
3092                 if(g_settings.getBool("creative_mode") == true)
3093                 {
3094                         dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3095                                         <<std::endl;
3096                         return;
3097                 }*/
3098                 // Strip command and create a stream
3099                 std::string datastring((char*)&data[2], datasize-2);
3100                 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3101                 std::istringstream is(datastring, std::ios_base::binary);
3102                 // Create an action
3103                 InventoryAction *a = InventoryAction::deSerialize(is);
3104                 if(a != NULL)
3105                 {
3106                         // Create context
3107                         InventoryContext c;
3108                         c.current_player = player;
3109
3110                         /*
3111                                 Handle craftresult specially if not in creative mode
3112                         */
3113                         bool disable_action = false;
3114                         if(a->getType() == IACTION_MOVE
3115                                         && g_settings.getBool("creative_mode") == false)
3116                         {
3117                                 IMoveAction *ma = (IMoveAction*)a;
3118                                 if(ma->to_inv == "current_player" &&
3119                                                 ma->from_inv == "current_player")
3120                                 {
3121                                         InventoryList *rlist = player->inventory.getList("craftresult");
3122                                         assert(rlist);
3123                                         InventoryList *clist = player->inventory.getList("craft");
3124                                         assert(clist);
3125                                         InventoryList *mlist = player->inventory.getList("main");
3126                                         assert(mlist);
3127                                         /*
3128                                                 Craftresult is no longer preview if something
3129                                                 is moved into it
3130                                         */
3131                                         if(ma->to_list == "craftresult"
3132                                                         && ma->from_list != "craftresult")
3133                                         {
3134                                                 // If it currently is a preview, remove
3135                                                 // its contents
3136                                                 if(player->craftresult_is_preview)
3137                                                 {
3138                                                         rlist->deleteItem(0);
3139                                                 }
3140                                                 player->craftresult_is_preview = false;
3141                                         }
3142                                         /*
3143                                                 Crafting takes place if this condition is true.
3144                                         */
3145                                         if(player->craftresult_is_preview &&
3146                                                         ma->from_list == "craftresult")
3147                                         {
3148                                                 player->craftresult_is_preview = false;
3149                                                 clist->decrementMaterials(1);
3150                                         }
3151                                         /*
3152                                                 If the craftresult is placed on itself, move it to
3153                                                 main inventory instead of doing the action
3154                                         */
3155                                         if(ma->to_list == "craftresult"
3156                                                         && ma->from_list == "craftresult")
3157                                         {
3158                                                 disable_action = true;
3159                                                 
3160                                                 InventoryItem *item1 = rlist->changeItem(0, NULL);
3161                                                 mlist->addItem(item1);
3162                                         }
3163                                 }
3164                         }
3165                         
3166                         if(disable_action == false)
3167                         {
3168                                 // Feed action to player inventory
3169                                 a->apply(&c, this);
3170                                 // Eat the action
3171                                 delete a;
3172                         }
3173                         else
3174                         {
3175                                 // Send inventory
3176                                 UpdateCrafting(player->peer_id);
3177                                 SendInventory(player->peer_id);
3178                         }
3179                 }
3180                 else
3181                 {
3182                         dstream<<"TOSERVER_INVENTORY_ACTION: "
3183                                         <<"InventoryAction::deSerialize() returned NULL"
3184                                         <<std::endl;
3185                 }
3186         }
3187         else if(command == TOSERVER_CHAT_MESSAGE)
3188         {
3189                 /*
3190                         u16 command
3191                         u16 length
3192                         wstring message
3193                 */
3194                 u8 buf[6];
3195                 std::string datastring((char*)&data[2], datasize-2);
3196                 std::istringstream is(datastring, std::ios_base::binary);
3197                 
3198                 // Read stuff
3199                 is.read((char*)buf, 2);
3200                 u16 len = readU16(buf);
3201                 
3202                 std::wstring message;
3203                 for(u16 i=0; i<len; i++)
3204                 {
3205                         is.read((char*)buf, 2);
3206                         message += (wchar_t)readU16(buf);
3207                 }
3208
3209                 // Get player name of this client
3210                 std::wstring name = narrow_to_wide(player->getName());
3211                 
3212                 // Line to send to players
3213                 std::wstring line;
3214                 // Whether to send to the player that sent the line
3215                 bool send_to_sender = false;
3216                 // Whether to send to other players
3217                 bool send_to_others = false;
3218                 
3219                 // Local player gets all privileges regardless of
3220                 // what's set on their account.
3221                 u64 privs = getPlayerPrivs(player);
3222
3223                 // Parse commands
3224                 std::wstring commandprefix = L"/#";
3225                 if(message.substr(0, commandprefix.size()) == commandprefix)
3226                 {
3227                         line += L"Server: ";
3228
3229                         message = message.substr(commandprefix.size());
3230                         
3231                         WStrfnd f1(message);
3232                         f1.next(L" "); // Skip over /#whatever
3233                         std::wstring paramstring = f1.next(L"");
3234
3235                         ServerCommandContext *ctx = new ServerCommandContext(
3236                                 str_split(message, L' '),
3237                                 paramstring,
3238                                 this,
3239                                 &m_env,
3240                                 player,
3241                                 privs);
3242
3243                         line += processServerCommand(ctx);
3244                         send_to_sender = ctx->flags & 1;
3245                         send_to_others = ctx->flags & 2;
3246                         delete ctx;
3247
3248                 }
3249                 else
3250                 {
3251                         if(privs & PRIV_SHOUT)
3252                         {
3253                                 line += L"<";
3254                                 line += name;
3255                                 line += L"> ";
3256                                 line += message;
3257                                 send_to_others = true;
3258                         }
3259                         else
3260                         {
3261                                 line += L"Server: You are not allowed to shout";
3262                                 send_to_sender = true;
3263                         }
3264                 }
3265                 
3266                 if(line != L"")
3267                 {
3268                         dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3269
3270                         /*
3271                                 Send the message to clients
3272                         */
3273                         for(core::map<u16, RemoteClient*>::Iterator
3274                                 i = m_clients.getIterator();
3275                                 i.atEnd() == false; i++)
3276                         {
3277                                 // Get client and check that it is valid
3278                                 RemoteClient *client = i.getNode()->getValue();
3279                                 assert(client->peer_id == i.getNode()->getKey());
3280                                 if(client->serialization_version == SER_FMT_VER_INVALID)
3281                                         continue;
3282
3283                                 // Filter recipient
3284                                 bool sender_selected = (peer_id == client->peer_id);
3285                                 if(sender_selected == true && send_to_sender == false)
3286                                         continue;
3287                                 if(sender_selected == false && send_to_others == false)
3288                                         continue;
3289
3290                                 SendChatMessage(client->peer_id, line);
3291                         }
3292                 }
3293         }
3294         else if(command == TOSERVER_DAMAGE)
3295         {
3296                 if(g_settings.getBool("enable_damage"))
3297                 {
3298                         std::string datastring((char*)&data[2], datasize-2);
3299                         std::istringstream is(datastring, std::ios_base::binary);
3300                         u8 damage = readU8(is);
3301                         if(player->hp > damage)
3302                         {
3303                                 player->hp -= damage;
3304                         }
3305                         else
3306                         {
3307                                 player->hp = 0;
3308
3309                                 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3310                                                 <<std::endl;
3311                                 
3312                                 v3f pos = findSpawnPos(m_env.getServerMap());
3313                                 player->setPosition(pos);
3314                                 player->hp = 20;
3315                                 SendMovePlayer(player);
3316                                 SendPlayerHP(player);
3317                                 
3318                                 //TODO: Throw items around
3319                         }
3320                 }
3321
3322                 SendPlayerHP(player);
3323         }
3324         else if(command == TOSERVER_PASSWORD)
3325         {
3326                 /*
3327                         [0] u16 TOSERVER_PASSWORD
3328                         [2] u8[28] old password
3329                         [30] u8[28] new password
3330                 */
3331
3332                 if(datasize != 2+PASSWORD_SIZE*2)
3333                         return;
3334                 /*char password[PASSWORD_SIZE];
3335                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3336                         password[i] = data[2+i];
3337                 password[PASSWORD_SIZE-1] = 0;*/
3338                 std::string oldpwd;
3339                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3340                 {
3341                         char c = data[2+i];
3342                         if(c == 0)
3343                                 break;
3344                         oldpwd += c;
3345                 }
3346                 std::string newpwd;
3347                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3348                 {
3349                         char c = data[2+PASSWORD_SIZE+i];
3350                         if(c == 0)
3351                                 break;
3352                         newpwd += c;
3353                 }
3354
3355                 dstream<<"Server: Client requests a password change from "
3356                                 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3357
3358                 std::string playername = player->getName();
3359
3360                 if(m_authmanager.exists(playername) == false)
3361                 {
3362                         dstream<<"Server: playername not found in authmanager"<<std::endl;
3363                         // Wrong old password supplied!!
3364                         SendChatMessage(peer_id, L"playername not found in authmanager");
3365                         return;
3366                 }
3367
3368                 std::string checkpwd = m_authmanager.getPassword(playername);
3369
3370                 if(oldpwd != checkpwd)
3371                 {
3372                         dstream<<"Server: invalid old password"<<std::endl;
3373                         // Wrong old password supplied!!
3374                         SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3375                         return;
3376                 }
3377
3378                 m_authmanager.setPassword(playername, newpwd);
3379                 
3380                 dstream<<"Server: password change successful for "<<playername
3381                                 <<std::endl;
3382                 SendChatMessage(peer_id, L"Password change successful");
3383         }
3384         else if (command == TOSERVER_PLAYERITEM)
3385         {
3386                 if (datasize < 2+2)
3387                         return;
3388
3389                 u16 item = readU16(&data[2]);
3390                 player->wieldItem(item);
3391         }
3392         else
3393         {
3394                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3395                                 "unknown command "<<command<<std::endl;
3396         }
3397         
3398         } //try
3399         catch(SendFailedException &e)
3400         {
3401                 derr_server<<"Server::ProcessData(): SendFailedException: "
3402                                 <<"what="<<e.what()
3403                                 <<std::endl;
3404         }
3405 }
3406
3407 void Server::onMapEditEvent(MapEditEvent *event)
3408 {
3409         //dstream<<"Server::onMapEditEvent()"<<std::endl;
3410         if(m_ignore_map_edit_events)
3411                 return;
3412         MapEditEvent *e = event->clone();
3413         m_unsent_map_edit_queue.push_back(e);
3414 }
3415
3416 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3417 {
3418         if(id == "current_player")
3419         {
3420                 assert(c->current_player);
3421                 return &(c->current_player->inventory);
3422         }
3423         
3424         Strfnd fn(id);
3425         std::string id0 = fn.next(":");
3426
3427         if(id0 == "nodemeta")
3428         {
3429                 v3s16 p;
3430                 p.X = stoi(fn.next(","));
3431                 p.Y = stoi(fn.next(","));
3432                 p.Z = stoi(fn.next(","));
3433                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3434                 if(meta)
3435                         return meta->getInventory();
3436                 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3437                                 <<"no metadata found"<<std::endl;
3438                 return NULL;
3439         }
3440
3441         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3442         return NULL;
3443 }
3444 void Server::inventoryModified(InventoryContext *c, std::string id)
3445 {
3446         if(id == "current_player")
3447         {
3448                 assert(c->current_player);
3449                 // Send inventory
3450                 UpdateCrafting(c->current_player->peer_id);
3451                 SendInventory(c->current_player->peer_id);
3452                 return;
3453         }
3454         
3455         Strfnd fn(id);
3456         std::string id0 = fn.next(":");
3457
3458         if(id0 == "nodemeta")
3459         {
3460                 v3s16 p;
3461                 p.X = stoi(fn.next(","));
3462                 p.Y = stoi(fn.next(","));
3463                 p.Z = stoi(fn.next(","));
3464                 v3s16 blockpos = getNodeBlockPos(p);
3465
3466                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3467                 if(meta)
3468                         meta->inventoryModified();
3469
3470                 for(core::map<u16, RemoteClient*>::Iterator
3471                         i = m_clients.getIterator();
3472                         i.atEnd()==false; i++)
3473                 {
3474                         RemoteClient *client = i.getNode()->getValue();
3475                         client->SetBlockNotSent(blockpos);
3476                 }
3477
3478                 return;
3479         }
3480
3481         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3482 }
3483
3484 core::list<PlayerInfo> Server::getPlayerInfo()
3485 {
3486         DSTACK(__FUNCTION_NAME);
3487         JMutexAutoLock envlock(m_env_mutex);
3488         JMutexAutoLock conlock(m_con_mutex);
3489         
3490         core::list<PlayerInfo> list;
3491
3492         core::list<Player*> players = m_env.getPlayers();
3493         
3494         core::list<Player*>::Iterator i;
3495         for(i = players.begin();
3496                         i != players.end(); i++)
3497         {
3498                 PlayerInfo info;
3499
3500                 Player *player = *i;
3501
3502                 try{
3503                         con::Peer *peer = m_con.GetPeer(player->peer_id);
3504                         // Copy info from peer to info struct
3505                         info.id = peer->id;
3506                         info.address = peer->address;
3507                         info.avg_rtt = peer->avg_rtt;
3508                 }
3509                 catch(con::PeerNotFoundException &e)
3510                 {
3511                         // Set dummy peer info
3512                         info.id = 0;
3513                         info.address = Address(0,0,0,0,0);
3514                         info.avg_rtt = 0.0;
3515                 }
3516
3517                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3518                 info.position = player->getPosition();
3519
3520                 list.push_back(info);
3521         }
3522
3523         return list;
3524 }
3525
3526
3527 void Server::peerAdded(con::Peer *peer)
3528 {
3529         DSTACK(__FUNCTION_NAME);
3530         dout_server<<"Server::peerAdded(): peer->id="
3531                         <<peer->id<<std::endl;
3532         
3533         PeerChange c;
3534         c.type = PEER_ADDED;
3535         c.peer_id = peer->id;
3536         c.timeout = false;
3537         m_peer_change_queue.push_back(c);
3538 }
3539
3540 void Server::deletingPeer(con::Peer *peer, bool timeout)
3541 {
3542         DSTACK(__FUNCTION_NAME);
3543         dout_server<<"Server::deletingPeer(): peer->id="
3544                         <<peer->id<<", timeout="<<timeout<<std::endl;
3545         
3546         PeerChange c;
3547         c.type = PEER_REMOVED;
3548         c.peer_id = peer->id;
3549         c.timeout = timeout;
3550         m_peer_change_queue.push_back(c);
3551 }
3552
3553 /*
3554         Static send methods
3555 */
3556
3557 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3558 {
3559         DSTACK(__FUNCTION_NAME);
3560         std::ostringstream os(std::ios_base::binary);
3561
3562         writeU16(os, TOCLIENT_HP);
3563         writeU8(os, hp);
3564
3565         // Make data buffer
3566         std::string s = os.str();
3567         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3568         // Send as reliable
3569         con.Send(peer_id, 0, data, true);
3570 }
3571
3572 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3573                 const std::wstring &reason)
3574 {
3575         DSTACK(__FUNCTION_NAME);
3576         std::ostringstream os(std::ios_base::binary);
3577
3578         writeU16(os, TOCLIENT_ACCESS_DENIED);
3579         os<<serializeWideString(reason);
3580
3581         // Make data buffer
3582         std::string s = os.str();
3583         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3584         // Send as reliable
3585         con.Send(peer_id, 0, data, true);
3586 }
3587
3588 /*
3589         Non-static send methods
3590 */
3591
3592 void Server::SendObjectData(float dtime)
3593 {
3594         DSTACK(__FUNCTION_NAME);
3595
3596         core::map<v3s16, bool> stepped_blocks;
3597         
3598         for(core::map<u16, RemoteClient*>::Iterator
3599                 i = m_clients.getIterator();
3600                 i.atEnd() == false; i++)
3601         {
3602                 u16 peer_id = i.getNode()->getKey();
3603                 RemoteClient *client = i.getNode()->getValue();
3604                 assert(client->peer_id == peer_id);
3605                 
3606                 if(client->serialization_version == SER_FMT_VER_INVALID)
3607                         continue;
3608                 
3609                 client->SendObjectData(this, dtime, stepped_blocks);
3610         }
3611 }
3612
3613 void Server::SendPlayerInfos()
3614 {
3615         DSTACK(__FUNCTION_NAME);
3616
3617         //JMutexAutoLock envlock(m_env_mutex);
3618         
3619         // Get connected players
3620         core::list<Player*> players = m_env.getPlayers(true);
3621         
3622         u32 player_count = players.getSize();
3623         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3624
3625         SharedBuffer<u8> data(datasize);
3626         writeU16(&data[0], TOCLIENT_PLAYERINFO);
3627         
3628         u32 start = 2;
3629         core::list<Player*>::Iterator i;
3630         for(i = players.begin();
3631                         i != players.end(); i++)
3632         {
3633                 Player *player = *i;
3634
3635                 /*dstream<<"Server sending player info for player with "
3636                                 "peer_id="<<player->peer_id<<std::endl;*/
3637                 
3638                 writeU16(&data[start], player->peer_id);
3639                 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3640                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3641                 start += 2+PLAYERNAME_SIZE;
3642         }
3643
3644         //JMutexAutoLock conlock(m_con_mutex);
3645
3646         // Send as reliable
3647         m_con.SendToAll(0, data, true);
3648 }
3649
3650 void Server::SendInventory(u16 peer_id)
3651 {
3652         DSTACK(__FUNCTION_NAME);
3653         
3654         Player* player = m_env.getPlayer(peer_id);
3655         assert(player);
3656
3657         /*
3658                 Serialize it
3659         */
3660
3661         std::ostringstream os;
3662         //os.imbue(std::locale("C"));
3663
3664         player->inventory.serialize(os);
3665
3666         std::string s = os.str();
3667         
3668         SharedBuffer<u8> data(s.size()+2);
3669         writeU16(&data[0], TOCLIENT_INVENTORY);
3670         memcpy(&data[2], s.c_str(), s.size());
3671         
3672         // Send as reliable
3673         m_con.Send(peer_id, 0, data, true);
3674 }
3675
3676 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3677 {
3678         DSTACK(__FUNCTION_NAME);
3679         
3680         std::ostringstream os(std::ios_base::binary);
3681         u8 buf[12];
3682         
3683         // Write command
3684         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3685         os.write((char*)buf, 2);
3686         
3687         // Write length
3688         writeU16(buf, message.size());
3689         os.write((char*)buf, 2);
3690         
3691         // Write string
3692         for(u32 i=0; i<message.size(); i++)
3693         {
3694                 u16 w = message[i];
3695                 writeU16(buf, w);
3696                 os.write((char*)buf, 2);
3697         }
3698         
3699         // Make data buffer
3700         std::string s = os.str();
3701         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3702         // Send as reliable
3703         m_con.Send(peer_id, 0, data, true);
3704 }
3705
3706 void Server::BroadcastChatMessage(const std::wstring &message)
3707 {
3708         for(core::map<u16, RemoteClient*>::Iterator
3709                 i = m_clients.getIterator();
3710                 i.atEnd() == false; i++)
3711         {
3712                 // Get client and check that it is valid
3713                 RemoteClient *client = i.getNode()->getValue();
3714                 assert(client->peer_id == i.getNode()->getKey());
3715                 if(client->serialization_version == SER_FMT_VER_INVALID)
3716                         continue;
3717
3718                 SendChatMessage(client->peer_id, message);
3719         }
3720 }
3721
3722 void Server::SendPlayerHP(Player *player)
3723 {
3724         SendHP(m_con, player->peer_id, player->hp);
3725 }
3726
3727 void Server::SendMovePlayer(Player *player)
3728 {
3729         DSTACK(__FUNCTION_NAME);
3730         std::ostringstream os(std::ios_base::binary);
3731
3732         writeU16(os, TOCLIENT_MOVE_PLAYER);
3733         writeV3F1000(os, player->getPosition());
3734         writeF1000(os, player->getPitch());
3735         writeF1000(os, player->getYaw());
3736         
3737         {
3738                 v3f pos = player->getPosition();
3739                 f32 pitch = player->getPitch();
3740                 f32 yaw = player->getYaw();
3741                 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3742                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3743                                 <<" pitch="<<pitch
3744                                 <<" yaw="<<yaw
3745                                 <<std::endl;
3746         }
3747
3748         // Make data buffer
3749         std::string s = os.str();
3750         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3751         // Send as reliable
3752         m_con.Send(player->peer_id, 0, data, true);
3753 }
3754
3755 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3756         core::list<u16> *far_players, float far_d_nodes)
3757 {
3758         float maxd = far_d_nodes*BS;
3759         v3f p_f = intToFloat(p, BS);
3760
3761         // Create packet
3762         u32 replysize = 8;
3763         SharedBuffer<u8> reply(replysize);
3764         writeU16(&reply[0], TOCLIENT_REMOVENODE);
3765         writeS16(&reply[2], p.X);
3766         writeS16(&reply[4], p.Y);
3767         writeS16(&reply[6], p.Z);
3768
3769         for(core::map<u16, RemoteClient*>::Iterator
3770                 i = m_clients.getIterator();
3771                 i.atEnd() == false; i++)
3772         {
3773                 // Get client and check that it is valid
3774                 RemoteClient *client = i.getNode()->getValue();
3775                 assert(client->peer_id == i.getNode()->getKey());
3776                 if(client->serialization_version == SER_FMT_VER_INVALID)
3777                         continue;
3778
3779                 // Don't send if it's the same one
3780                 if(client->peer_id == ignore_id)
3781                         continue;
3782                 
3783                 if(far_players)
3784                 {
3785                         // Get player
3786                         Player *player = m_env.getPlayer(client->peer_id);
3787                         if(player)
3788                         {
3789                                 // If player is far away, only set modified blocks not sent
3790                                 v3f player_pos = player->getPosition();
3791                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3792                                 {
3793                                         far_players->push_back(client->peer_id);
3794                                         continue;
3795                                 }
3796                         }
3797                 }
3798
3799                 // Send as reliable
3800                 m_con.Send(client->peer_id, 0, reply, true);
3801         }
3802 }
3803
3804 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3805                 core::list<u16> *far_players, float far_d_nodes)
3806 {
3807         float maxd = far_d_nodes*BS;
3808         v3f p_f = intToFloat(p, BS);
3809
3810         for(core::map<u16, RemoteClient*>::Iterator
3811                 i = m_clients.getIterator();
3812                 i.atEnd() == false; i++)
3813         {
3814                 // Get client and check that it is valid
3815                 RemoteClient *client = i.getNode()->getValue();
3816                 assert(client->peer_id == i.getNode()->getKey());
3817                 if(client->serialization_version == SER_FMT_VER_INVALID)
3818                         continue;
3819
3820                 // Don't send if it's the same one
3821                 if(client->peer_id == ignore_id)
3822                         continue;
3823
3824                 if(far_players)
3825                 {
3826                         // Get player
3827                         Player *player = m_env.getPlayer(client->peer_id);
3828                         if(player)
3829                         {
3830                                 // If player is far away, only set modified blocks not sent
3831                                 v3f player_pos = player->getPosition();
3832                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3833                                 {
3834                                         far_players->push_back(client->peer_id);
3835                                         continue;
3836                                 }
3837                         }
3838                 }
3839
3840                 // Create packet
3841                 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3842                 SharedBuffer<u8> reply(replysize);
3843                 writeU16(&reply[0], TOCLIENT_ADDNODE);
3844                 writeS16(&reply[2], p.X);
3845                 writeS16(&reply[4], p.Y);
3846                 writeS16(&reply[6], p.Z);
3847                 n.serialize(&reply[8], client->serialization_version);
3848
3849                 // Send as reliable
3850                 m_con.Send(client->peer_id, 0, reply, true);
3851         }
3852 }
3853
3854 void Server::setBlockNotSent(v3s16 p)
3855 {
3856         for(core::map<u16, RemoteClient*>::Iterator
3857                 i = m_clients.getIterator();
3858                 i.atEnd()==false; i++)
3859         {
3860                 RemoteClient *client = i.getNode()->getValue();
3861                 client->SetBlockNotSent(p);
3862         }
3863 }
3864
3865 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3866 {
3867         DSTACK(__FUNCTION_NAME);
3868
3869         v3s16 p = block->getPos();
3870         
3871 #if 0
3872         // Analyze it a bit
3873         bool completely_air = true;
3874         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3875         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3876         for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3877         {
3878                 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3879                 {
3880                         completely_air = false;
3881                         x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3882                 }
3883         }
3884
3885         // Print result
3886         dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3887         if(completely_air)
3888                 dstream<<"[completely air] ";
3889         dstream<<std::endl;
3890 #endif
3891
3892         /*
3893                 Create a packet with the block in the right format
3894         */
3895         
3896         std::ostringstream os(std::ios_base::binary);
3897         block->serialize(os, ver);
3898         std::string s = os.str();
3899         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3900
3901         u32 replysize = 8 + blockdata.getSize();
3902         SharedBuffer<u8> reply(replysize);
3903         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3904         writeS16(&reply[2], p.X);
3905         writeS16(&reply[4], p.Y);
3906         writeS16(&reply[6], p.Z);
3907         memcpy(&reply[8], *blockdata, blockdata.getSize());
3908
3909         /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3910                         <<":  \tpacket size: "<<replysize<<std::endl;*/
3911         
3912         /*
3913                 Send packet
3914         */
3915         m_con.Send(peer_id, 1, reply, true);
3916 }
3917
3918 void Server::SendBlocks(float dtime)
3919 {
3920         DSTACK(__FUNCTION_NAME);
3921
3922         JMutexAutoLock envlock(m_env_mutex);
3923         JMutexAutoLock conlock(m_con_mutex);
3924
3925         //TimeTaker timer("Server::SendBlocks");
3926
3927         core::array<PrioritySortedBlockTransfer> queue;
3928
3929         s32 total_sending = 0;
3930         
3931         {
3932                 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3933
3934                 for(core::map<u16, RemoteClient*>::Iterator
3935                         i = m_clients.getIterator();
3936                         i.atEnd() == false; i++)
3937                 {
3938                         RemoteClient *client = i.getNode()->getValue();
3939                         assert(client->peer_id == i.getNode()->getKey());
3940
3941                         total_sending += client->SendingCount();
3942                         
3943                         if(client->serialization_version == SER_FMT_VER_INVALID)
3944                                 continue;
3945                         
3946                         client->GetNextBlocks(this, dtime, queue);
3947                 }
3948         }
3949
3950         // Sort.
3951         // Lowest priority number comes first.
3952         // Lowest is most important.
3953         queue.sort();
3954
3955         for(u32 i=0; i<queue.size(); i++)
3956         {
3957                 //TODO: Calculate limit dynamically
3958                 if(total_sending >= g_settings.getS32
3959                                 ("max_simultaneous_block_sends_server_total"))
3960                         break;
3961                 
3962                 PrioritySortedBlockTransfer q = queue[i];
3963
3964                 MapBlock *block = NULL;
3965                 try
3966                 {
3967                         block = m_env.getMap().getBlockNoCreate(q.pos);
3968                 }
3969                 catch(InvalidPositionException &e)
3970                 {
3971                         continue;
3972                 }
3973
3974                 RemoteClient *client = getClient(q.peer_id);
3975
3976                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3977
3978                 client->SentBlock(q.pos);
3979
3980                 total_sending++;
3981         }
3982 }
3983
3984 /*
3985         Something random
3986 */
3987
3988 void Server::UpdateCrafting(u16 peer_id)
3989 {
3990         DSTACK(__FUNCTION_NAME);
3991         
3992         Player* player = m_env.getPlayer(peer_id);
3993         assert(player);
3994
3995         /*
3996                 Calculate crafting stuff
3997         */
3998         if(g_settings.getBool("creative_mode") == false)
3999         {
4000                 InventoryList *clist = player->inventory.getList("craft");
4001                 InventoryList *rlist = player->inventory.getList("craftresult");
4002
4003                 if(rlist->getUsedSlots() == 0)
4004                         player->craftresult_is_preview = true;
4005
4006                 if(rlist && player->craftresult_is_preview)
4007                 {
4008                         rlist->clearItems();
4009                 }
4010                 if(clist && rlist && player->craftresult_is_preview)
4011                 {
4012                         InventoryItem *items[9];
4013                         for(u16 i=0; i<9; i++)
4014                         {
4015                                 items[i] = clist->getItem(i);
4016                         }
4017                         
4018                         // Get result of crafting grid
4019                         InventoryItem *result = craft_get_result(items);
4020                         if(result)
4021                                 rlist->addItem(result);
4022                 }
4023         
4024         } // if creative_mode == false
4025 }
4026
4027 RemoteClient* Server::getClient(u16 peer_id)
4028 {
4029         DSTACK(__FUNCTION_NAME);
4030         //JMutexAutoLock lock(m_con_mutex);
4031         core::map<u16, RemoteClient*>::Node *n;
4032         n = m_clients.find(peer_id);
4033         // A client should exist for all peers
4034         assert(n != NULL);
4035         return n->getValue();
4036 }
4037
4038 std::wstring Server::getStatusString()
4039 {
4040         std::wostringstream os(std::ios_base::binary);
4041         os<<L"# Server: ";
4042         // Version
4043         os<<L"version="<<narrow_to_wide(VERSION_STRING);
4044         // Uptime
4045         os<<L", uptime="<<m_uptime.get();
4046         // Information about clients
4047         os<<L", clients={";
4048         for(core::map<u16, RemoteClient*>::Iterator
4049                 i = m_clients.getIterator();
4050                 i.atEnd() == false; i++)
4051         {
4052                 // Get client and check that it is valid
4053                 RemoteClient *client = i.getNode()->getValue();
4054                 assert(client->peer_id == i.getNode()->getKey());
4055                 if(client->serialization_version == SER_FMT_VER_INVALID)
4056                         continue;
4057                 // Get player
4058                 Player *player = m_env.getPlayer(client->peer_id);
4059                 // Get name of player
4060                 std::wstring name = L"unknown";
4061                 if(player != NULL)
4062                         name = narrow_to_wide(player->getName());
4063                 // Add name to information string
4064                 os<<name<<L",";
4065         }
4066         os<<L"}";
4067         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4068                 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4069         if(g_settings.get("motd") != "")
4070                 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings.get("motd"));
4071         return os.str();
4072 }
4073
4074 v3f findSpawnPos(ServerMap &map)
4075 {
4076         //return v3f(50,50,50)*BS;
4077
4078         v2s16 nodepos;
4079         s16 groundheight = 0;
4080         
4081 #if 0
4082         nodepos = v2s16(0,0);
4083         groundheight = 20;
4084 #endif
4085
4086 #if 1
4087         // Try to find a good place a few times
4088         for(s32 i=0; i<1000; i++)
4089         {
4090                 s32 range = 1 + i;
4091                 // We're going to try to throw the player to this position
4092                 nodepos = v2s16(-range + (myrand()%(range*2)),
4093                                 -range + (myrand()%(range*2)));
4094                 v2s16 sectorpos = getNodeSectorPos(nodepos);
4095                 // Get sector (NOTE: Don't get because it's slow)
4096                 //m_env.getMap().emergeSector(sectorpos);
4097                 // Get ground height at point (fallbacks to heightmap function)
4098                 groundheight = map.findGroundLevel(nodepos);
4099                 // Don't go underwater
4100                 if(groundheight < WATER_LEVEL)
4101                 {
4102                         //dstream<<"-> Underwater"<<std::endl;
4103                         continue;
4104                 }
4105                 // Don't go to high places
4106                 if(groundheight > WATER_LEVEL + 4)
4107                 {
4108                         //dstream<<"-> Underwater"<<std::endl;
4109                         continue;
4110                 }
4111
4112                 // Found a good place
4113                 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4114                 break;
4115         }
4116 #endif
4117         
4118         // If no suitable place was not found, go above water at least.
4119         if(groundheight < WATER_LEVEL)
4120                 groundheight = WATER_LEVEL;
4121
4122         return intToFloat(v3s16(
4123                         nodepos.X,
4124                         groundheight + 3,
4125                         nodepos.Y
4126                         ), BS);
4127 }
4128
4129 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4130 {
4131         /*
4132                 Try to get an existing player
4133         */
4134         Player *player = m_env.getPlayer(name);
4135         if(player != NULL)
4136         {
4137                 // If player is already connected, cancel
4138                 if(player->peer_id != 0)
4139                 {
4140                         dstream<<"emergePlayer(): Player already connected"<<std::endl;
4141                         return NULL;
4142                 }
4143
4144                 // Got one.
4145                 player->peer_id = peer_id;
4146                 
4147                 // Reset inventory to creative if in creative mode
4148                 if(g_settings.getBool("creative_mode"))
4149                 {
4150                         // Warning: double code below
4151                         // Backup actual inventory
4152                         player->inventory_backup = new Inventory();
4153                         *(player->inventory_backup) = player->inventory;
4154                         // Set creative inventory
4155                         craft_set_creative_inventory(player);
4156                 }
4157
4158                 return player;
4159         }
4160
4161         /*
4162                 If player with the wanted peer_id already exists, cancel.
4163         */
4164         if(m_env.getPlayer(peer_id) != NULL)
4165         {
4166                 dstream<<"emergePlayer(): Player with wrong name but same"
4167                                 " peer_id already exists"<<std::endl;
4168                 return NULL;
4169         }
4170         
4171         /*
4172                 Create a new player
4173         */
4174         {
4175                 player = new ServerRemotePlayer();
4176                 //player->peer_id = c.peer_id;
4177                 //player->peer_id = PEER_ID_INEXISTENT;
4178                 player->peer_id = peer_id;
4179                 player->updateName(name);
4180                 m_authmanager.add(name);
4181                 m_authmanager.setPassword(name, password);
4182                 m_authmanager.setPrivs(name,
4183                                 stringToPrivs(g_settings.get("default_privs")));
4184
4185                 /*
4186                         Set player position
4187                 */
4188                 
4189                 dstream<<"Server: Finding spawn place for player \""
4190                                 <<player->getName()<<"\""<<std::endl;
4191
4192                 v3f pos = findSpawnPos(m_env.getServerMap());
4193
4194                 player->setPosition(pos);
4195
4196                 /*
4197                         Add player to environment
4198                 */
4199
4200                 m_env.addPlayer(player);
4201
4202                 /*
4203                         Add stuff to inventory
4204                 */
4205                 
4206                 if(g_settings.getBool("creative_mode"))
4207                 {
4208                         // Warning: double code above
4209                         // Backup actual inventory
4210                         player->inventory_backup = new Inventory();
4211                         *(player->inventory_backup) = player->inventory;
4212                         // Set creative inventory
4213                         craft_set_creative_inventory(player);
4214                 }
4215                 else if(g_settings.getBool("give_initial_stuff"))
4216                 {
4217                         craft_give_initial_stuff(player);
4218                 }
4219
4220                 return player;
4221                 
4222         } // create new player
4223 }
4224
4225 void Server::handlePeerChange(PeerChange &c)
4226 {
4227         JMutexAutoLock envlock(m_env_mutex);
4228         JMutexAutoLock conlock(m_con_mutex);
4229         
4230         if(c.type == PEER_ADDED)
4231         {
4232                 /*
4233                         Add
4234                 */
4235
4236                 // Error check
4237                 core::map<u16, RemoteClient*>::Node *n;
4238                 n = m_clients.find(c.peer_id);
4239                 // The client shouldn't already exist
4240                 assert(n == NULL);
4241
4242                 // Create client
4243                 RemoteClient *client = new RemoteClient();
4244                 client->peer_id = c.peer_id;
4245                 m_clients.insert(client->peer_id, client);
4246
4247         } // PEER_ADDED
4248         else if(c.type == PEER_REMOVED)
4249         {
4250                 /*
4251                         Delete
4252                 */
4253
4254                 // Error check
4255                 core::map<u16, RemoteClient*>::Node *n;
4256                 n = m_clients.find(c.peer_id);
4257                 // The client should exist
4258                 assert(n != NULL);
4259                 
4260                 /*
4261                         Mark objects to be not known by the client
4262                 */
4263                 RemoteClient *client = n->getValue();
4264                 // Handle objects
4265                 for(core::map<u16, bool>::Iterator
4266                                 i = client->m_known_objects.getIterator();
4267                                 i.atEnd()==false; i++)
4268                 {
4269                         // Get object
4270                         u16 id = i.getNode()->getKey();
4271                         ServerActiveObject* obj = m_env.getActiveObject(id);
4272                         
4273                         if(obj && obj->m_known_by_count > 0)
4274                                 obj->m_known_by_count--;
4275                 }
4276
4277                 // Collect information about leaving in chat
4278                 std::wstring message;
4279                 {
4280                         std::wstring name = L"unknown";
4281                         Player *player = m_env.getPlayer(c.peer_id);
4282                         if(player != NULL)
4283                                 name = narrow_to_wide(player->getName());
4284                         
4285                         message += L"*** ";
4286                         message += name;
4287                         message += L" left game";
4288                         if(c.timeout)
4289                                 message += L" (timed out)";
4290                 }
4291
4292                 /*// Delete player
4293                 {
4294                         m_env.removePlayer(c.peer_id);
4295                 }*/
4296
4297                 // Set player client disconnected
4298                 {
4299                         Player *player = m_env.getPlayer(c.peer_id);
4300                         if(player != NULL)
4301                                 player->peer_id = 0;
4302                 }
4303                 
4304                 // Delete client
4305                 delete m_clients[c.peer_id];
4306                 m_clients.remove(c.peer_id);
4307
4308                 // Send player info to all remaining clients
4309                 SendPlayerInfos();
4310                 
4311                 // Send leave chat message to all remaining clients
4312                 BroadcastChatMessage(message);
4313                 
4314         } // PEER_REMOVED
4315         else
4316         {
4317                 assert(0);
4318         }
4319 }
4320
4321 void Server::handlePeerChanges()
4322 {
4323         while(m_peer_change_queue.size() > 0)
4324         {
4325                 PeerChange c = m_peer_change_queue.pop_front();
4326
4327                 dout_server<<"Server: Handling peer change: "
4328                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4329                                 <<std::endl;
4330
4331                 handlePeerChange(c);
4332         }
4333 }
4334
4335 u64 Server::getPlayerPrivs(Player *player)
4336 {
4337         if(player==NULL)
4338                 return 0;
4339         std::string playername = player->getName();
4340         // Local player gets all privileges regardless of
4341         // what's set on their account.
4342         if(g_settings.get("name") == playername)
4343         {
4344                 return PRIV_ALL;
4345         }
4346         else
4347         {
4348                 return getPlayerAuthPrivs(playername);
4349         }
4350 }
4351
4352 void dedicated_server_loop(Server &server, bool &kill)
4353 {
4354         DSTACK(__FUNCTION_NAME);
4355         
4356         dstream<<DTIME<<std::endl;
4357         dstream<<"========================"<<std::endl;
4358         dstream<<"Running dedicated server"<<std::endl;
4359         dstream<<"========================"<<std::endl;
4360         dstream<<std::endl;
4361
4362         IntervalLimiter m_profiler_interval;
4363
4364         for(;;)
4365         {
4366                 // This is kind of a hack but can be done like this
4367                 // because server.step() is very light
4368                 {
4369                         ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4370                         sleep_ms(30);
4371                 }
4372                 server.step(0.030);
4373
4374                 if(server.getShutdownRequested() || kill)
4375                 {
4376                         dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4377                         break;
4378                 }
4379
4380                 /*
4381                         Profiler
4382                 */
4383                 float profiler_print_interval =
4384                                 g_settings.getFloat("profiler_print_interval");
4385                 if(profiler_print_interval != 0)
4386                 {
4387                         if(m_profiler_interval.step(0.030, profiler_print_interval))
4388                         {
4389                                 dstream<<"Profiler:"<<std::endl;
4390                                 g_profiler.print(dstream);
4391                                 g_profiler.clear();
4392                         }
4393                 }
4394                 
4395                 /*
4396                         Player info
4397                 */
4398                 static int counter = 0;
4399                 counter--;
4400                 if(counter <= 0)
4401                 {
4402                         counter = 10;
4403
4404                         core::list<PlayerInfo> list = server.getPlayerInfo();
4405                         core::list<PlayerInfo>::Iterator i;
4406                         static u32 sum_old = 0;
4407                         u32 sum = PIChecksum(list);
4408                         if(sum != sum_old)
4409                         {
4410                                 dstream<<DTIME<<"Player info:"<<std::endl;
4411                                 for(i=list.begin(); i!=list.end(); i++)
4412                                 {
4413                                         i->PrintLine(&dstream);
4414                                 }
4415                         }
4416                         sum_old = sum;
4417                 }
4418         }
4419 }
4420
4421