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