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