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