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