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