]> git.lizzy.rs Git - minetest.git/blob - src/server.cpp
Server should use the same eye position as the client
[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
3385         {
3386                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3387                                 "unknown command "<<command<<std::endl;
3388         }
3389         
3390         } //try
3391         catch(SendFailedException &e)
3392         {
3393                 derr_server<<"Server::ProcessData(): SendFailedException: "
3394                                 <<"what="<<e.what()
3395                                 <<std::endl;
3396         }
3397 }
3398
3399 void Server::onMapEditEvent(MapEditEvent *event)
3400 {
3401         //dstream<<"Server::onMapEditEvent()"<<std::endl;
3402         if(m_ignore_map_edit_events)
3403                 return;
3404         MapEditEvent *e = event->clone();
3405         m_unsent_map_edit_queue.push_back(e);
3406 }
3407
3408 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3409 {
3410         if(id == "current_player")
3411         {
3412                 assert(c->current_player);
3413                 return &(c->current_player->inventory);
3414         }
3415         
3416         Strfnd fn(id);
3417         std::string id0 = fn.next(":");
3418
3419         if(id0 == "nodemeta")
3420         {
3421                 v3s16 p;
3422                 p.X = stoi(fn.next(","));
3423                 p.Y = stoi(fn.next(","));
3424                 p.Z = stoi(fn.next(","));
3425                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3426                 if(meta)
3427                         return meta->getInventory();
3428                 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3429                                 <<"no metadata found"<<std::endl;
3430                 return NULL;
3431         }
3432
3433         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3434         return NULL;
3435 }
3436 void Server::inventoryModified(InventoryContext *c, std::string id)
3437 {
3438         if(id == "current_player")
3439         {
3440                 assert(c->current_player);
3441                 // Send inventory
3442                 UpdateCrafting(c->current_player->peer_id);
3443                 SendInventory(c->current_player->peer_id);
3444                 return;
3445         }
3446         
3447         Strfnd fn(id);
3448         std::string id0 = fn.next(":");
3449
3450         if(id0 == "nodemeta")
3451         {
3452                 v3s16 p;
3453                 p.X = stoi(fn.next(","));
3454                 p.Y = stoi(fn.next(","));
3455                 p.Z = stoi(fn.next(","));
3456                 v3s16 blockpos = getNodeBlockPos(p);
3457
3458                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3459                 if(meta)
3460                         meta->inventoryModified();
3461
3462                 for(core::map<u16, RemoteClient*>::Iterator
3463                         i = m_clients.getIterator();
3464                         i.atEnd()==false; i++)
3465                 {
3466                         RemoteClient *client = i.getNode()->getValue();
3467                         client->SetBlockNotSent(blockpos);
3468                 }
3469
3470                 return;
3471         }
3472
3473         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3474 }
3475
3476 core::list<PlayerInfo> Server::getPlayerInfo()
3477 {
3478         DSTACK(__FUNCTION_NAME);
3479         JMutexAutoLock envlock(m_env_mutex);
3480         JMutexAutoLock conlock(m_con_mutex);
3481         
3482         core::list<PlayerInfo> list;
3483
3484         core::list<Player*> players = m_env.getPlayers();
3485         
3486         core::list<Player*>::Iterator i;
3487         for(i = players.begin();
3488                         i != players.end(); i++)
3489         {
3490                 PlayerInfo info;
3491
3492                 Player *player = *i;
3493
3494                 try{
3495                         con::Peer *peer = m_con.GetPeer(player->peer_id);
3496                         // Copy info from peer to info struct
3497                         info.id = peer->id;
3498                         info.address = peer->address;
3499                         info.avg_rtt = peer->avg_rtt;
3500                 }
3501                 catch(con::PeerNotFoundException &e)
3502                 {
3503                         // Set dummy peer info
3504                         info.id = 0;
3505                         info.address = Address(0,0,0,0,0);
3506                         info.avg_rtt = 0.0;
3507                 }
3508
3509                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3510                 info.position = player->getPosition();
3511
3512                 list.push_back(info);
3513         }
3514
3515         return list;
3516 }
3517
3518
3519 void Server::peerAdded(con::Peer *peer)
3520 {
3521         DSTACK(__FUNCTION_NAME);
3522         dout_server<<"Server::peerAdded(): peer->id="
3523                         <<peer->id<<std::endl;
3524         
3525         PeerChange c;
3526         c.type = PEER_ADDED;
3527         c.peer_id = peer->id;
3528         c.timeout = false;
3529         m_peer_change_queue.push_back(c);
3530 }
3531
3532 void Server::deletingPeer(con::Peer *peer, bool timeout)
3533 {
3534         DSTACK(__FUNCTION_NAME);
3535         dout_server<<"Server::deletingPeer(): peer->id="
3536                         <<peer->id<<", timeout="<<timeout<<std::endl;
3537         
3538         PeerChange c;
3539         c.type = PEER_REMOVED;
3540         c.peer_id = peer->id;
3541         c.timeout = timeout;
3542         m_peer_change_queue.push_back(c);
3543 }
3544
3545 /*
3546         Static send methods
3547 */
3548
3549 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3550 {
3551         DSTACK(__FUNCTION_NAME);
3552         std::ostringstream os(std::ios_base::binary);
3553
3554         writeU16(os, TOCLIENT_HP);
3555         writeU8(os, hp);
3556
3557         // Make data buffer
3558         std::string s = os.str();
3559         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3560         // Send as reliable
3561         con.Send(peer_id, 0, data, true);
3562 }
3563
3564 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3565                 const std::wstring &reason)
3566 {
3567         DSTACK(__FUNCTION_NAME);
3568         std::ostringstream os(std::ios_base::binary);
3569
3570         writeU16(os, TOCLIENT_ACCESS_DENIED);
3571         os<<serializeWideString(reason);
3572
3573         // Make data buffer
3574         std::string s = os.str();
3575         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3576         // Send as reliable
3577         con.Send(peer_id, 0, data, true);
3578 }
3579
3580 /*
3581         Non-static send methods
3582 */
3583
3584 void Server::SendObjectData(float dtime)
3585 {
3586         DSTACK(__FUNCTION_NAME);
3587
3588         core::map<v3s16, bool> stepped_blocks;
3589         
3590         for(core::map<u16, RemoteClient*>::Iterator
3591                 i = m_clients.getIterator();
3592                 i.atEnd() == false; i++)
3593         {
3594                 u16 peer_id = i.getNode()->getKey();
3595                 RemoteClient *client = i.getNode()->getValue();
3596                 assert(client->peer_id == peer_id);
3597                 
3598                 if(client->serialization_version == SER_FMT_VER_INVALID)
3599                         continue;
3600                 
3601                 client->SendObjectData(this, dtime, stepped_blocks);
3602         }
3603 }
3604
3605 void Server::SendPlayerInfos()
3606 {
3607         DSTACK(__FUNCTION_NAME);
3608
3609         //JMutexAutoLock envlock(m_env_mutex);
3610         
3611         // Get connected players
3612         core::list<Player*> players = m_env.getPlayers(true);
3613         
3614         u32 player_count = players.getSize();
3615         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3616
3617         SharedBuffer<u8> data(datasize);
3618         writeU16(&data[0], TOCLIENT_PLAYERINFO);
3619         
3620         u32 start = 2;
3621         core::list<Player*>::Iterator i;
3622         for(i = players.begin();
3623                         i != players.end(); i++)
3624         {
3625                 Player *player = *i;
3626
3627                 /*dstream<<"Server sending player info for player with "
3628                                 "peer_id="<<player->peer_id<<std::endl;*/
3629                 
3630                 writeU16(&data[start], player->peer_id);
3631                 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3632                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3633                 start += 2+PLAYERNAME_SIZE;
3634         }
3635
3636         //JMutexAutoLock conlock(m_con_mutex);
3637
3638         // Send as reliable
3639         m_con.SendToAll(0, data, true);
3640 }
3641
3642 void Server::SendInventory(u16 peer_id)
3643 {
3644         DSTACK(__FUNCTION_NAME);
3645         
3646         Player* player = m_env.getPlayer(peer_id);
3647         assert(player);
3648
3649         /*
3650                 Serialize it
3651         */
3652
3653         std::ostringstream os;
3654         //os.imbue(std::locale("C"));
3655
3656         player->inventory.serialize(os);
3657
3658         std::string s = os.str();
3659         
3660         SharedBuffer<u8> data(s.size()+2);
3661         writeU16(&data[0], TOCLIENT_INVENTORY);
3662         memcpy(&data[2], s.c_str(), s.size());
3663         
3664         // Send as reliable
3665         m_con.Send(peer_id, 0, data, true);
3666 }
3667
3668 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3669 {
3670         DSTACK(__FUNCTION_NAME);
3671         
3672         std::ostringstream os(std::ios_base::binary);
3673         u8 buf[12];
3674         
3675         // Write command
3676         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3677         os.write((char*)buf, 2);
3678         
3679         // Write length
3680         writeU16(buf, message.size());
3681         os.write((char*)buf, 2);
3682         
3683         // Write string
3684         for(u32 i=0; i<message.size(); i++)
3685         {
3686                 u16 w = message[i];
3687                 writeU16(buf, w);
3688                 os.write((char*)buf, 2);
3689         }
3690         
3691         // Make data buffer
3692         std::string s = os.str();
3693         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3694         // Send as reliable
3695         m_con.Send(peer_id, 0, data, true);
3696 }
3697
3698 void Server::BroadcastChatMessage(const std::wstring &message)
3699 {
3700         for(core::map<u16, RemoteClient*>::Iterator
3701                 i = m_clients.getIterator();
3702                 i.atEnd() == false; i++)
3703         {
3704                 // Get client and check that it is valid
3705                 RemoteClient *client = i.getNode()->getValue();
3706                 assert(client->peer_id == i.getNode()->getKey());
3707                 if(client->serialization_version == SER_FMT_VER_INVALID)
3708                         continue;
3709
3710                 SendChatMessage(client->peer_id, message);
3711         }
3712 }
3713
3714 void Server::SendPlayerHP(Player *player)
3715 {
3716         SendHP(m_con, player->peer_id, player->hp);
3717 }
3718
3719 void Server::SendMovePlayer(Player *player)
3720 {
3721         DSTACK(__FUNCTION_NAME);
3722         std::ostringstream os(std::ios_base::binary);
3723
3724         writeU16(os, TOCLIENT_MOVE_PLAYER);
3725         writeV3F1000(os, player->getPosition());
3726         writeF1000(os, player->getPitch());
3727         writeF1000(os, player->getYaw());
3728         
3729         {
3730                 v3f pos = player->getPosition();
3731                 f32 pitch = player->getPitch();
3732                 f32 yaw = player->getYaw();
3733                 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3734                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3735                                 <<" pitch="<<pitch
3736                                 <<" yaw="<<yaw
3737                                 <<std::endl;
3738         }
3739
3740         // Make data buffer
3741         std::string s = os.str();
3742         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3743         // Send as reliable
3744         m_con.Send(player->peer_id, 0, data, true);
3745 }
3746
3747 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3748         core::list<u16> *far_players, float far_d_nodes)
3749 {
3750         float maxd = far_d_nodes*BS;
3751         v3f p_f = intToFloat(p, BS);
3752
3753         // Create packet
3754         u32 replysize = 8;
3755         SharedBuffer<u8> reply(replysize);
3756         writeU16(&reply[0], TOCLIENT_REMOVENODE);
3757         writeS16(&reply[2], p.X);
3758         writeS16(&reply[4], p.Y);
3759         writeS16(&reply[6], p.Z);
3760
3761         for(core::map<u16, RemoteClient*>::Iterator
3762                 i = m_clients.getIterator();
3763                 i.atEnd() == false; i++)
3764         {
3765                 // Get client and check that it is valid
3766                 RemoteClient *client = i.getNode()->getValue();
3767                 assert(client->peer_id == i.getNode()->getKey());
3768                 if(client->serialization_version == SER_FMT_VER_INVALID)
3769                         continue;
3770
3771                 // Don't send if it's the same one
3772                 if(client->peer_id == ignore_id)
3773                         continue;
3774                 
3775                 if(far_players)
3776                 {
3777                         // Get player
3778                         Player *player = m_env.getPlayer(client->peer_id);
3779                         if(player)
3780                         {
3781                                 // If player is far away, only set modified blocks not sent
3782                                 v3f player_pos = player->getPosition();
3783                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3784                                 {
3785                                         far_players->push_back(client->peer_id);
3786                                         continue;
3787                                 }
3788                         }
3789                 }
3790
3791                 // Send as reliable
3792                 m_con.Send(client->peer_id, 0, reply, true);
3793         }
3794 }
3795
3796 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3797                 core::list<u16> *far_players, float far_d_nodes)
3798 {
3799         float maxd = far_d_nodes*BS;
3800         v3f p_f = intToFloat(p, BS);
3801
3802         for(core::map<u16, RemoteClient*>::Iterator
3803                 i = m_clients.getIterator();
3804                 i.atEnd() == false; i++)
3805         {
3806                 // Get client and check that it is valid
3807                 RemoteClient *client = i.getNode()->getValue();
3808                 assert(client->peer_id == i.getNode()->getKey());
3809                 if(client->serialization_version == SER_FMT_VER_INVALID)
3810                         continue;
3811
3812                 // Don't send if it's the same one
3813                 if(client->peer_id == ignore_id)
3814                         continue;
3815
3816                 if(far_players)
3817                 {
3818                         // Get player
3819                         Player *player = m_env.getPlayer(client->peer_id);
3820                         if(player)
3821                         {
3822                                 // If player is far away, only set modified blocks not sent
3823                                 v3f player_pos = player->getPosition();
3824                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3825                                 {
3826                                         far_players->push_back(client->peer_id);
3827                                         continue;
3828                                 }
3829                         }
3830                 }
3831
3832                 // Create packet
3833                 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3834                 SharedBuffer<u8> reply(replysize);
3835                 writeU16(&reply[0], TOCLIENT_ADDNODE);
3836                 writeS16(&reply[2], p.X);
3837                 writeS16(&reply[4], p.Y);
3838                 writeS16(&reply[6], p.Z);
3839                 n.serialize(&reply[8], client->serialization_version);
3840
3841                 // Send as reliable
3842                 m_con.Send(client->peer_id, 0, reply, true);
3843         }
3844 }
3845
3846 void Server::setBlockNotSent(v3s16 p)
3847 {
3848         for(core::map<u16, RemoteClient*>::Iterator
3849                 i = m_clients.getIterator();
3850                 i.atEnd()==false; i++)
3851         {
3852                 RemoteClient *client = i.getNode()->getValue();
3853                 client->SetBlockNotSent(p);
3854         }
3855 }
3856
3857 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3858 {
3859         DSTACK(__FUNCTION_NAME);
3860
3861         v3s16 p = block->getPos();
3862         
3863 #if 0
3864         // Analyze it a bit
3865         bool completely_air = true;
3866         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3867         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3868         for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3869         {
3870                 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3871                 {
3872                         completely_air = false;
3873                         x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3874                 }
3875         }
3876
3877         // Print result
3878         dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3879         if(completely_air)
3880                 dstream<<"[completely air] ";
3881         dstream<<std::endl;
3882 #endif
3883
3884         /*
3885                 Create a packet with the block in the right format
3886         */
3887         
3888         std::ostringstream os(std::ios_base::binary);
3889         block->serialize(os, ver);
3890         std::string s = os.str();
3891         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3892
3893         u32 replysize = 8 + blockdata.getSize();
3894         SharedBuffer<u8> reply(replysize);
3895         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3896         writeS16(&reply[2], p.X);
3897         writeS16(&reply[4], p.Y);
3898         writeS16(&reply[6], p.Z);
3899         memcpy(&reply[8], *blockdata, blockdata.getSize());
3900
3901         /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3902                         <<":  \tpacket size: "<<replysize<<std::endl;*/
3903         
3904         /*
3905                 Send packet
3906         */
3907         m_con.Send(peer_id, 1, reply, true);
3908 }
3909
3910 void Server::SendBlocks(float dtime)
3911 {
3912         DSTACK(__FUNCTION_NAME);
3913
3914         JMutexAutoLock envlock(m_env_mutex);
3915         JMutexAutoLock conlock(m_con_mutex);
3916
3917         //TimeTaker timer("Server::SendBlocks");
3918
3919         core::array<PrioritySortedBlockTransfer> queue;
3920
3921         s32 total_sending = 0;
3922         
3923         {
3924                 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3925
3926                 for(core::map<u16, RemoteClient*>::Iterator
3927                         i = m_clients.getIterator();
3928                         i.atEnd() == false; i++)
3929                 {
3930                         RemoteClient *client = i.getNode()->getValue();
3931                         assert(client->peer_id == i.getNode()->getKey());
3932
3933                         total_sending += client->SendingCount();
3934                         
3935                         if(client->serialization_version == SER_FMT_VER_INVALID)
3936                                 continue;
3937                         
3938                         client->GetNextBlocks(this, dtime, queue);
3939                 }
3940         }
3941
3942         // Sort.
3943         // Lowest priority number comes first.
3944         // Lowest is most important.
3945         queue.sort();
3946
3947         for(u32 i=0; i<queue.size(); i++)
3948         {
3949                 //TODO: Calculate limit dynamically
3950                 if(total_sending >= g_settings.getS32
3951                                 ("max_simultaneous_block_sends_server_total"))
3952                         break;
3953                 
3954                 PrioritySortedBlockTransfer q = queue[i];
3955
3956                 MapBlock *block = NULL;
3957                 try
3958                 {
3959                         block = m_env.getMap().getBlockNoCreate(q.pos);
3960                 }
3961                 catch(InvalidPositionException &e)
3962                 {
3963                         continue;
3964                 }
3965
3966                 RemoteClient *client = getClient(q.peer_id);
3967
3968                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3969
3970                 client->SentBlock(q.pos);
3971
3972                 total_sending++;
3973         }
3974 }
3975
3976 /*
3977         Something random
3978 */
3979
3980 void Server::UpdateCrafting(u16 peer_id)
3981 {
3982         DSTACK(__FUNCTION_NAME);
3983         
3984         Player* player = m_env.getPlayer(peer_id);
3985         assert(player);
3986
3987         /*
3988                 Calculate crafting stuff
3989         */
3990         if(g_settings.getBool("creative_mode") == false)
3991         {
3992                 InventoryList *clist = player->inventory.getList("craft");
3993                 InventoryList *rlist = player->inventory.getList("craftresult");
3994
3995                 if(rlist->getUsedSlots() == 0)
3996                         player->craftresult_is_preview = true;
3997
3998                 if(rlist && player->craftresult_is_preview)
3999                 {
4000                         rlist->clearItems();
4001                 }
4002                 if(clist && rlist && player->craftresult_is_preview)
4003                 {
4004                         InventoryItem *items[9];
4005                         for(u16 i=0; i<9; i++)
4006                         {
4007                                 items[i] = clist->getItem(i);
4008                         }
4009                         
4010                         // Get result of crafting grid
4011                         InventoryItem *result = craft_get_result(items);
4012                         if(result)
4013                                 rlist->addItem(result);
4014                 }
4015         
4016         } // if creative_mode == false
4017 }
4018
4019 RemoteClient* Server::getClient(u16 peer_id)
4020 {
4021         DSTACK(__FUNCTION_NAME);
4022         //JMutexAutoLock lock(m_con_mutex);
4023         core::map<u16, RemoteClient*>::Node *n;
4024         n = m_clients.find(peer_id);
4025         // A client should exist for all peers
4026         assert(n != NULL);
4027         return n->getValue();
4028 }
4029
4030 std::wstring Server::getStatusString()
4031 {
4032         std::wostringstream os(std::ios_base::binary);
4033         os<<L"# Server: ";
4034         // Version
4035         os<<L"version="<<narrow_to_wide(VERSION_STRING);
4036         // Uptime
4037         os<<L", uptime="<<m_uptime.get();
4038         // Information about clients
4039         os<<L", clients={";
4040         for(core::map<u16, RemoteClient*>::Iterator
4041                 i = m_clients.getIterator();
4042                 i.atEnd() == false; i++)
4043         {
4044                 // Get client and check that it is valid
4045                 RemoteClient *client = i.getNode()->getValue();
4046                 assert(client->peer_id == i.getNode()->getKey());
4047                 if(client->serialization_version == SER_FMT_VER_INVALID)
4048                         continue;
4049                 // Get player
4050                 Player *player = m_env.getPlayer(client->peer_id);
4051                 // Get name of player
4052                 std::wstring name = L"unknown";
4053                 if(player != NULL)
4054                         name = narrow_to_wide(player->getName());
4055                 // Add name to information string
4056                 os<<name<<L",";
4057         }
4058         os<<L"}";
4059         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4060                 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4061         if(g_settings.get("motd") != "")
4062                 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings.get("motd"));
4063         return os.str();
4064 }
4065
4066 v3f findSpawnPos(ServerMap &map)
4067 {
4068         //return v3f(50,50,50)*BS;
4069
4070         v2s16 nodepos;
4071         s16 groundheight = 0;
4072         
4073 #if 0
4074         nodepos = v2s16(0,0);
4075         groundheight = 20;
4076 #endif
4077
4078 #if 1
4079         // Try to find a good place a few times
4080         for(s32 i=0; i<1000; i++)
4081         {
4082                 s32 range = 1 + i;
4083                 // We're going to try to throw the player to this position
4084                 nodepos = v2s16(-range + (myrand()%(range*2)),
4085                                 -range + (myrand()%(range*2)));
4086                 v2s16 sectorpos = getNodeSectorPos(nodepos);
4087                 // Get sector (NOTE: Don't get because it's slow)
4088                 //m_env.getMap().emergeSector(sectorpos);
4089                 // Get ground height at point (fallbacks to heightmap function)
4090                 groundheight = map.findGroundLevel(nodepos);
4091                 // Don't go underwater
4092                 if(groundheight < WATER_LEVEL)
4093                 {
4094                         //dstream<<"-> Underwater"<<std::endl;
4095                         continue;
4096                 }
4097                 // Don't go to high places
4098                 if(groundheight > WATER_LEVEL + 4)
4099                 {
4100                         //dstream<<"-> Underwater"<<std::endl;
4101                         continue;
4102                 }
4103
4104                 // Found a good place
4105                 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4106                 break;
4107         }
4108 #endif
4109         
4110         // If no suitable place was not found, go above water at least.
4111         if(groundheight < WATER_LEVEL)
4112                 groundheight = WATER_LEVEL;
4113
4114         return intToFloat(v3s16(
4115                         nodepos.X,
4116                         groundheight + 3,
4117                         nodepos.Y
4118                         ), BS);
4119 }
4120
4121 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4122 {
4123         /*
4124                 Try to get an existing player
4125         */
4126         Player *player = m_env.getPlayer(name);
4127         if(player != NULL)
4128         {
4129                 // If player is already connected, cancel
4130                 if(player->peer_id != 0)
4131                 {
4132                         dstream<<"emergePlayer(): Player already connected"<<std::endl;
4133                         return NULL;
4134                 }
4135
4136                 // Got one.
4137                 player->peer_id = peer_id;
4138                 
4139                 // Reset inventory to creative if in creative mode
4140                 if(g_settings.getBool("creative_mode"))
4141                 {
4142                         // Warning: double code below
4143                         // Backup actual inventory
4144                         player->inventory_backup = new Inventory();
4145                         *(player->inventory_backup) = player->inventory;
4146                         // Set creative inventory
4147                         craft_set_creative_inventory(player);
4148                 }
4149
4150                 return player;
4151         }
4152
4153         /*
4154                 If player with the wanted peer_id already exists, cancel.
4155         */
4156         if(m_env.getPlayer(peer_id) != NULL)
4157         {
4158                 dstream<<"emergePlayer(): Player with wrong name but same"
4159                                 " peer_id already exists"<<std::endl;
4160                 return NULL;
4161         }
4162         
4163         /*
4164                 Create a new player
4165         */
4166         {
4167                 player = new ServerRemotePlayer();
4168                 //player->peer_id = c.peer_id;
4169                 //player->peer_id = PEER_ID_INEXISTENT;
4170                 player->peer_id = peer_id;
4171                 player->updateName(name);
4172                 m_authmanager.add(name);
4173                 m_authmanager.setPassword(name, password);
4174                 m_authmanager.setPrivs(name,
4175                                 stringToPrivs(g_settings.get("default_privs")));
4176
4177                 /*
4178                         Set player position
4179                 */
4180                 
4181                 dstream<<"Server: Finding spawn place for player \""
4182                                 <<player->getName()<<"\""<<std::endl;
4183
4184                 v3f pos = findSpawnPos(m_env.getServerMap());
4185
4186                 player->setPosition(pos);
4187
4188                 /*
4189                         Add player to environment
4190                 */
4191
4192                 m_env.addPlayer(player);
4193
4194                 /*
4195                         Add stuff to inventory
4196                 */
4197                 
4198                 if(g_settings.getBool("creative_mode"))
4199                 {
4200                         // Warning: double code above
4201                         // Backup actual inventory
4202                         player->inventory_backup = new Inventory();
4203                         *(player->inventory_backup) = player->inventory;
4204                         // Set creative inventory
4205                         craft_set_creative_inventory(player);
4206                 }
4207                 else if(g_settings.getBool("give_initial_stuff"))
4208                 {
4209                         craft_give_initial_stuff(player);
4210                 }
4211
4212                 return player;
4213                 
4214         } // create new player
4215 }
4216
4217 void Server::handlePeerChange(PeerChange &c)
4218 {
4219         JMutexAutoLock envlock(m_env_mutex);
4220         JMutexAutoLock conlock(m_con_mutex);
4221         
4222         if(c.type == PEER_ADDED)
4223         {
4224                 /*
4225                         Add
4226                 */
4227
4228                 // Error check
4229                 core::map<u16, RemoteClient*>::Node *n;
4230                 n = m_clients.find(c.peer_id);
4231                 // The client shouldn't already exist
4232                 assert(n == NULL);
4233
4234                 // Create client
4235                 RemoteClient *client = new RemoteClient();
4236                 client->peer_id = c.peer_id;
4237                 m_clients.insert(client->peer_id, client);
4238
4239         } // PEER_ADDED
4240         else if(c.type == PEER_REMOVED)
4241         {
4242                 /*
4243                         Delete
4244                 */
4245
4246                 // Error check
4247                 core::map<u16, RemoteClient*>::Node *n;
4248                 n = m_clients.find(c.peer_id);
4249                 // The client should exist
4250                 assert(n != NULL);
4251                 
4252                 /*
4253                         Mark objects to be not known by the client
4254                 */
4255                 RemoteClient *client = n->getValue();
4256                 // Handle objects
4257                 for(core::map<u16, bool>::Iterator
4258                                 i = client->m_known_objects.getIterator();
4259                                 i.atEnd()==false; i++)
4260                 {
4261                         // Get object
4262                         u16 id = i.getNode()->getKey();
4263                         ServerActiveObject* obj = m_env.getActiveObject(id);
4264                         
4265                         if(obj && obj->m_known_by_count > 0)
4266                                 obj->m_known_by_count--;
4267                 }
4268
4269                 // Collect information about leaving in chat
4270                 std::wstring message;
4271                 {
4272                         std::wstring name = L"unknown";
4273                         Player *player = m_env.getPlayer(c.peer_id);
4274                         if(player != NULL)
4275                                 name = narrow_to_wide(player->getName());
4276                         
4277                         message += L"*** ";
4278                         message += name;
4279                         message += L" left game";
4280                         if(c.timeout)
4281                                 message += L" (timed out)";
4282                 }
4283
4284                 /*// Delete player
4285                 {
4286                         m_env.removePlayer(c.peer_id);
4287                 }*/
4288
4289                 // Set player client disconnected
4290                 {
4291                         Player *player = m_env.getPlayer(c.peer_id);
4292                         if(player != NULL)
4293                                 player->peer_id = 0;
4294                 }
4295                 
4296                 // Delete client
4297                 delete m_clients[c.peer_id];
4298                 m_clients.remove(c.peer_id);
4299
4300                 // Send player info to all remaining clients
4301                 SendPlayerInfos();
4302                 
4303                 // Send leave chat message to all remaining clients
4304                 BroadcastChatMessage(message);
4305                 
4306         } // PEER_REMOVED
4307         else
4308         {
4309                 assert(0);
4310         }
4311 }
4312
4313 void Server::handlePeerChanges()
4314 {
4315         while(m_peer_change_queue.size() > 0)
4316         {
4317                 PeerChange c = m_peer_change_queue.pop_front();
4318
4319                 dout_server<<"Server: Handling peer change: "
4320                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4321                                 <<std::endl;
4322
4323                 handlePeerChange(c);
4324         }
4325 }
4326
4327 u64 Server::getPlayerPrivs(Player *player)
4328 {
4329         if(player==NULL)
4330                 return 0;
4331         std::string playername = player->getName();
4332         // Local player gets all privileges regardless of
4333         // what's set on their account.
4334         if(g_settings.get("name") == playername)
4335         {
4336                 return PRIV_ALL;
4337         }
4338         else
4339         {
4340                 return getPlayerAuthPrivs(playername);
4341         }
4342 }
4343
4344 void dedicated_server_loop(Server &server, bool &kill)
4345 {
4346         DSTACK(__FUNCTION_NAME);
4347         
4348         dstream<<DTIME<<std::endl;
4349         dstream<<"========================"<<std::endl;
4350         dstream<<"Running dedicated server"<<std::endl;
4351         dstream<<"========================"<<std::endl;
4352         dstream<<std::endl;
4353
4354         IntervalLimiter m_profiler_interval;
4355
4356         for(;;)
4357         {
4358                 // This is kind of a hack but can be done like this
4359                 // because server.step() is very light
4360                 {
4361                         ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4362                         sleep_ms(30);
4363                 }
4364                 server.step(0.030);
4365
4366                 if(server.getShutdownRequested() || kill)
4367                 {
4368                         dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4369                         break;
4370                 }
4371
4372                 /*
4373                         Profiler
4374                 */
4375                 float profiler_print_interval =
4376                                 g_settings.getFloat("profiler_print_interval");
4377                 if(profiler_print_interval != 0)
4378                 {
4379                         if(m_profiler_interval.step(0.030, profiler_print_interval))
4380                         {
4381                                 dstream<<"Profiler:"<<std::endl;
4382                                 g_profiler.print(dstream);
4383                                 g_profiler.clear();
4384                         }
4385                 }
4386                 
4387                 /*
4388                         Player info
4389                 */
4390                 static int counter = 0;
4391                 counter--;
4392                 if(counter <= 0)
4393                 {
4394                         counter = 10;
4395
4396                         core::list<PlayerInfo> list = server.getPlayerInfo();
4397                         core::list<PlayerInfo>::Iterator i;
4398                         static u32 sum_old = 0;
4399                         u32 sum = PIChecksum(list);
4400                         if(sum != sum_old)
4401                         {
4402                                 dstream<<DTIME<<"Player info:"<<std::endl;
4403                                 for(i=list.begin(); i!=list.end(); i++)
4404                                 {
4405                                         i->PrintLine(&dstream);
4406                                 }
4407                         }
4408                         sum_old = sum;
4409                 }
4410         }
4411 }
4412
4413