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