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