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