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