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