]> git.lizzy.rs Git - minetest.git/blob - src/server.cpp
Chests work now!
[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                         // Create context
2464                         InventoryContext c;
2465                         c.current_player = player;
2466
2467                         /*
2468                                 Handle craftresult specially if not in creative mode
2469                         */
2470                         bool disable_action = false;
2471                         if(a->getType() == IACTION_MOVE
2472                                         && g_settings.getBool("creative_mode") == false)
2473                         {
2474                                 IMoveAction *ma = (IMoveAction*)a;
2475                                 if(ma->to_inv == "current_player" &&
2476                                                 ma->from_inv == "current_player")
2477                                 {
2478                                         // Don't allow moving anything to craftresult
2479                                         if(ma->to_list == "craftresult")
2480                                         {
2481                                                 // Do nothing
2482                                                 disable_action = true;
2483                                         }
2484                                         // When something is removed from craftresult
2485                                         if(ma->from_list == "craftresult")
2486                                         {
2487                                                 disable_action = true;
2488                                                 // Remove stuff from craft
2489                                                 InventoryList *clist = player->inventory.getList("craft");
2490                                                 if(clist)
2491                                                 {
2492                                                         u16 count = ma->count;
2493                                                         if(count == 0)
2494                                                                 count = 1;
2495                                                         clist->decrementMaterials(count);
2496                                                 }
2497                                                 // Do action
2498                                                 // Feed action to player inventory
2499                                                 //a->apply(&player->inventory);
2500                                                 a->apply(&c, this);
2501                                                 // Eat it
2502                                                 delete a;
2503                                                 // If something appeared in craftresult, throw it
2504                                                 // in the main list
2505                                                 InventoryList *rlist = player->inventory.getList("craftresult");
2506                                                 InventoryList *mlist = player->inventory.getList("main");
2507                                                 if(rlist && mlist && rlist->getUsedSlots() == 1)
2508                                                 {
2509                                                         InventoryItem *item1 = rlist->changeItem(0, NULL);
2510                                                         mlist->addItem(item1);
2511                                                 }
2512                                         }
2513                                 }
2514                         }
2515                         
2516                         if(disable_action == false)
2517                         {
2518                                 // Feed action to player inventory
2519                                 //a->apply(&player->inventory);
2520                                 a->apply(&c, this);
2521                                 // Eat the action
2522                                 delete a;
2523                         }
2524                         else
2525                         {
2526                                 // Send inventory
2527                                 SendInventory(player->peer_id);
2528                         }
2529                 }
2530                 else
2531                 {
2532                         dstream<<"TOSERVER_INVENTORY_ACTION: "
2533                                         <<"InventoryAction::deSerialize() returned NULL"
2534                                         <<std::endl;
2535                 }
2536         }
2537         else if(command == TOSERVER_CHAT_MESSAGE)
2538         {
2539                 /*
2540                         u16 command
2541                         u16 length
2542                         wstring message
2543                 */
2544                 u8 buf[6];
2545                 std::string datastring((char*)&data[2], datasize-2);
2546                 std::istringstream is(datastring, std::ios_base::binary);
2547                 
2548                 // Read stuff
2549                 is.read((char*)buf, 2);
2550                 u16 len = readU16(buf);
2551                 
2552                 std::wstring message;
2553                 for(u16 i=0; i<len; i++)
2554                 {
2555                         is.read((char*)buf, 2);
2556                         message += (wchar_t)readU16(buf);
2557                 }
2558
2559                 // Get player name of this client
2560                 std::wstring name = narrow_to_wide(player->getName());
2561                 
2562                 // Line to send to players
2563                 std::wstring line;
2564                 // Whether to send to the player that sent the line
2565                 bool send_to_sender = false;
2566                 // Whether to send to other players
2567                 bool send_to_others = false;
2568                 
2569                 // Parse commands
2570                 std::wstring commandprefix = L"/#";
2571                 if(message.substr(0, commandprefix.size()) == commandprefix)
2572                 {
2573                         line += L"Server: ";
2574
2575                         message = message.substr(commandprefix.size());
2576                         // Get player name as narrow string
2577                         std::string name_s = player->getName();
2578                         // Convert message to narrow string
2579                         std::string message_s = wide_to_narrow(message);
2580                         // Operator is the single name defined in config.
2581                         std::string operator_name = g_settings.get("name");
2582                         bool is_operator = (operator_name != "" &&
2583                                         wide_to_narrow(name) == operator_name);
2584                         bool valid_command = false;
2585                         if(message_s == "help")
2586                         {
2587                                 line += L"-!- Available commands: ";
2588                                 line += L"status ";
2589                                 if(is_operator)
2590                                 {
2591                                         line += L"shutdown setting ";
2592                                 }
2593                                 else
2594                                 {
2595                                 }
2596                                 send_to_sender = true;
2597                                 valid_command = true;
2598                         }
2599                         else if(message_s == "status")
2600                         {
2601                                 line = getStatusString();
2602                                 send_to_sender = true;
2603                                 valid_command = true;
2604                         }
2605                         else if(is_operator)
2606                         {
2607                                 if(message_s == "shutdown")
2608                                 {
2609                                         dstream<<DTIME<<" Server: Operator requested shutdown."
2610                                                         <<std::endl;
2611                                         m_shutdown_requested.set(true);
2612                                         
2613                                         line += L"*** Server shutting down (operator request)";
2614                                         send_to_sender = true;
2615                                         valid_command = true;
2616                                 }
2617                                 else if(message_s.substr(0,8) == "setting ")
2618                                 {
2619                                         std::string confline = message_s.substr(8);
2620                                         g_settings.parseConfigLine(confline);
2621                                         line += L"-!- Setting changed.";
2622                                         send_to_sender = true;
2623                                         valid_command = true;
2624                                 }
2625                         }
2626                         
2627                         if(valid_command == false)
2628                         {
2629                                 line += L"-!- Invalid command: " + message;
2630                                 send_to_sender = true;
2631                         }
2632                 }
2633                 else
2634                 {
2635                         line += L"<";
2636                         /*if(is_operator)
2637                                 line += L"@";*/
2638                         line += name;
2639                         line += L"> ";
2640                         line += message;
2641                         send_to_others = true;
2642                 }
2643                 
2644                 if(line != L"")
2645                 {
2646                         dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2647
2648                         /*
2649                                 Send the message to clients
2650                         */
2651                         for(core::map<u16, RemoteClient*>::Iterator
2652                                 i = m_clients.getIterator();
2653                                 i.atEnd() == false; i++)
2654                         {
2655                                 // Get client and check that it is valid
2656                                 RemoteClient *client = i.getNode()->getValue();
2657                                 assert(client->peer_id == i.getNode()->getKey());
2658                                 if(client->serialization_version == SER_FMT_VER_INVALID)
2659                                         continue;
2660
2661                                 // Filter recipient
2662                                 bool sender_selected = (peer_id == client->peer_id);
2663                                 if(sender_selected == true && send_to_sender == false)
2664                                         continue;
2665                                 if(sender_selected == false && send_to_others == false)
2666                                         continue;
2667
2668                                 SendChatMessage(client->peer_id, line);
2669                         }
2670                 }
2671         }
2672         else
2673         {
2674                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2675                                 "unknown command "<<command<<std::endl;
2676         }
2677         
2678         } //try
2679         catch(SendFailedException &e)
2680         {
2681                 derr_server<<"Server::ProcessData(): SendFailedException: "
2682                                 <<"what="<<e.what()
2683                                 <<std::endl;
2684         }
2685 }
2686
2687 void Server::onMapEditEvent(MapEditEvent *event)
2688 {
2689         dstream<<"Server::onMapEditEvent()"<<std::endl;
2690         if(m_ignore_map_edit_events)
2691                 return;
2692         MapEditEvent *e = event->clone();
2693         m_unsent_map_edit_queue.push_back(e);
2694 }
2695
2696 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2697 {
2698         if(id == "current_player")
2699         {
2700                 assert(c->current_player);
2701                 return &(c->current_player->inventory);
2702         }
2703         
2704         Strfnd fn(id);
2705         std::string id0 = fn.next(":");
2706
2707         if(id0 == "nodemeta")
2708         {
2709                 v3s16 p;
2710                 p.X = stoi(fn.next(","));
2711                 p.Y = stoi(fn.next(","));
2712                 p.Z = stoi(fn.next(","));
2713                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2714                 if(meta)
2715                         return meta->getInventory();
2716                 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2717                                 <<"no metadata found"<<std::endl;
2718                 return NULL;
2719         }
2720
2721         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2722         return NULL;
2723 }
2724 void Server::inventoryModified(InventoryContext *c, std::string id)
2725 {
2726         if(id == "current_player")
2727         {
2728                 assert(c->current_player);
2729                 // Send inventory
2730                 SendInventory(c->current_player->peer_id);
2731                 return;
2732         }
2733         
2734         Strfnd fn(id);
2735         std::string id0 = fn.next(":");
2736
2737         if(id0 == "nodemeta")
2738         {
2739                 v3s16 p;
2740                 p.X = stoi(fn.next(","));
2741                 p.Y = stoi(fn.next(","));
2742                 p.Z = stoi(fn.next(","));
2743                 assert(c->current_player);
2744                 RemoteClient *client = getClient(c->current_player->peer_id);
2745                 v3s16 blockpos = getNodeBlockPos(p);
2746                 client->SetBlockNotSent(blockpos);
2747                 return;
2748         }
2749
2750         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2751 }
2752
2753 core::list<PlayerInfo> Server::getPlayerInfo()
2754 {
2755         DSTACK(__FUNCTION_NAME);
2756         JMutexAutoLock envlock(m_env_mutex);
2757         JMutexAutoLock conlock(m_con_mutex);
2758         
2759         core::list<PlayerInfo> list;
2760
2761         core::list<Player*> players = m_env.getPlayers();
2762         
2763         core::list<Player*>::Iterator i;
2764         for(i = players.begin();
2765                         i != players.end(); i++)
2766         {
2767                 PlayerInfo info;
2768
2769                 Player *player = *i;
2770
2771                 try{
2772                         con::Peer *peer = m_con.GetPeer(player->peer_id);
2773                         // Copy info from peer to info struct
2774                         info.id = peer->id;
2775                         info.address = peer->address;
2776                         info.avg_rtt = peer->avg_rtt;
2777                 }
2778                 catch(con::PeerNotFoundException &e)
2779                 {
2780                         // Set dummy peer info
2781                         info.id = 0;
2782                         info.address = Address(0,0,0,0,0);
2783                         info.avg_rtt = 0.0;
2784                 }
2785
2786                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2787                 info.position = player->getPosition();
2788
2789                 list.push_back(info);
2790         }
2791
2792         return list;
2793 }
2794
2795
2796 void Server::peerAdded(con::Peer *peer)
2797 {
2798         DSTACK(__FUNCTION_NAME);
2799         dout_server<<"Server::peerAdded(): peer->id="
2800                         <<peer->id<<std::endl;
2801         
2802         PeerChange c;
2803         c.type = PEER_ADDED;
2804         c.peer_id = peer->id;
2805         c.timeout = false;
2806         m_peer_change_queue.push_back(c);
2807 }
2808
2809 void Server::deletingPeer(con::Peer *peer, bool timeout)
2810 {
2811         DSTACK(__FUNCTION_NAME);
2812         dout_server<<"Server::deletingPeer(): peer->id="
2813                         <<peer->id<<", timeout="<<timeout<<std::endl;
2814         
2815         PeerChange c;
2816         c.type = PEER_REMOVED;
2817         c.peer_id = peer->id;
2818         c.timeout = timeout;
2819         m_peer_change_queue.push_back(c);
2820 }
2821
2822 void Server::SendObjectData(float dtime)
2823 {
2824         DSTACK(__FUNCTION_NAME);
2825
2826         core::map<v3s16, bool> stepped_blocks;
2827         
2828         for(core::map<u16, RemoteClient*>::Iterator
2829                 i = m_clients.getIterator();
2830                 i.atEnd() == false; i++)
2831         {
2832                 u16 peer_id = i.getNode()->getKey();
2833                 RemoteClient *client = i.getNode()->getValue();
2834                 assert(client->peer_id == peer_id);
2835                 
2836                 if(client->serialization_version == SER_FMT_VER_INVALID)
2837                         continue;
2838                 
2839                 client->SendObjectData(this, dtime, stepped_blocks);
2840         }
2841 }
2842
2843 void Server::SendPlayerInfos()
2844 {
2845         DSTACK(__FUNCTION_NAME);
2846
2847         //JMutexAutoLock envlock(m_env_mutex);
2848         
2849         // Get connected players
2850         core::list<Player*> players = m_env.getPlayers(true);
2851         
2852         u32 player_count = players.getSize();
2853         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2854
2855         SharedBuffer<u8> data(datasize);
2856         writeU16(&data[0], TOCLIENT_PLAYERINFO);
2857         
2858         u32 start = 2;
2859         core::list<Player*>::Iterator i;
2860         for(i = players.begin();
2861                         i != players.end(); i++)
2862         {
2863                 Player *player = *i;
2864
2865                 /*dstream<<"Server sending player info for player with "
2866                                 "peer_id="<<player->peer_id<<std::endl;*/
2867                 
2868                 writeU16(&data[start], player->peer_id);
2869                 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2870                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2871                 start += 2+PLAYERNAME_SIZE;
2872         }
2873
2874         //JMutexAutoLock conlock(m_con_mutex);
2875
2876         // Send as reliable
2877         m_con.SendToAll(0, data, true);
2878 }
2879
2880 /*
2881         Craft checking system
2882 */
2883
2884 enum ItemSpecType
2885 {
2886         ITEM_NONE,
2887         ITEM_MATERIAL,
2888         ITEM_CRAFT,
2889         ITEM_TOOL,
2890         ITEM_MBO
2891 };
2892
2893 struct ItemSpec
2894 {
2895         ItemSpec():
2896                 type(ITEM_NONE)
2897         {
2898         }
2899         ItemSpec(enum ItemSpecType a_type, std::string a_name):
2900                 type(a_type),
2901                 name(a_name),
2902                 num(65535)
2903         {
2904         }
2905         ItemSpec(enum ItemSpecType a_type, u16 a_num):
2906                 type(a_type),
2907                 name(""),
2908                 num(a_num)
2909         {
2910         }
2911         enum ItemSpecType type;
2912         // Only other one of these is used
2913         std::string name;
2914         u16 num;
2915 };
2916
2917 /*
2918         items: a pointer to an array of 9 pointers to items
2919         specs: a pointer to an array of 9 ItemSpecs
2920 */
2921 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2922 {
2923         u16 items_min_x = 100;
2924         u16 items_max_x = 100;
2925         u16 items_min_y = 100;
2926         u16 items_max_y = 100;
2927         for(u16 y=0; y<3; y++)
2928         for(u16 x=0; x<3; x++)
2929         {
2930                 if(items[y*3 + x] == NULL)
2931                         continue;
2932                 if(items_min_x == 100 || x < items_min_x)
2933                         items_min_x = x;
2934                 if(items_min_y == 100 || y < items_min_y)
2935                         items_min_y = y;
2936                 if(items_max_x == 100 || x > items_max_x)
2937                         items_max_x = x;
2938                 if(items_max_y == 100 || y > items_max_y)
2939                         items_max_y = y;
2940         }
2941         // No items at all, just return false
2942         if(items_min_x == 100)
2943                 return false;
2944         
2945         u16 items_w = items_max_x - items_min_x + 1;
2946         u16 items_h = items_max_y - items_min_y + 1;
2947
2948         u16 specs_min_x = 100;
2949         u16 specs_max_x = 100;
2950         u16 specs_min_y = 100;
2951         u16 specs_max_y = 100;
2952         for(u16 y=0; y<3; y++)
2953         for(u16 x=0; x<3; x++)
2954         {
2955                 if(specs[y*3 + x].type == ITEM_NONE)
2956                         continue;
2957                 if(specs_min_x == 100 || x < specs_min_x)
2958                         specs_min_x = x;
2959                 if(specs_min_y == 100 || y < specs_min_y)
2960                         specs_min_y = y;
2961                 if(specs_max_x == 100 || x > specs_max_x)
2962                         specs_max_x = x;
2963                 if(specs_max_y == 100 || y > specs_max_y)
2964                         specs_max_y = y;
2965         }
2966         // No specs at all, just return false
2967         if(specs_min_x == 100)
2968                 return false;
2969
2970         u16 specs_w = specs_max_x - specs_min_x + 1;
2971         u16 specs_h = specs_max_y - specs_min_y + 1;
2972
2973         // Different sizes
2974         if(items_w != specs_w || items_h != specs_h)
2975                 return false;
2976
2977         for(u16 y=0; y<specs_h; y++)
2978         for(u16 x=0; x<specs_w; x++)
2979         {
2980                 u16 items_x = items_min_x + x;
2981                 u16 items_y = items_min_y + y;
2982                 u16 specs_x = specs_min_x + x;
2983                 u16 specs_y = specs_min_y + y;
2984                 InventoryItem *item = items[items_y * 3 + items_x];
2985                 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2986                 
2987                 if(spec.type == ITEM_NONE)
2988                 {
2989                         // Has to be no item
2990                         if(item != NULL)
2991                                 return false;
2992                         continue;
2993                 }
2994                 
2995                 // There should be an item
2996                 if(item == NULL)
2997                         return false;
2998
2999                 std::string itemname = item->getName();
3000
3001                 if(spec.type == ITEM_MATERIAL)
3002                 {
3003                         if(itemname != "MaterialItem")
3004                                 return false;
3005                         MaterialItem *mitem = (MaterialItem*)item;
3006                         if(mitem->getMaterial() != spec.num)
3007                                 return false;
3008                 }
3009                 else if(spec.type == ITEM_CRAFT)
3010                 {
3011                         if(itemname != "CraftItem")
3012                                 return false;
3013                         CraftItem *mitem = (CraftItem*)item;
3014                         if(mitem->getSubName() != spec.name)
3015                                 return false;
3016                 }
3017                 else if(spec.type == ITEM_TOOL)
3018                 {
3019                         // Not supported yet
3020                         assert(0);
3021                 }
3022                 else if(spec.type == ITEM_MBO)
3023                 {
3024                         // Not supported yet
3025                         assert(0);
3026                 }
3027                 else
3028                 {
3029                         // Not supported yet
3030                         assert(0);
3031                 }
3032         }
3033
3034         return true;
3035 }
3036
3037 void Server::SendInventory(u16 peer_id)
3038 {
3039         DSTACK(__FUNCTION_NAME);
3040         
3041         Player* player = m_env.getPlayer(peer_id);
3042
3043         /*
3044                 Calculate crafting stuff
3045         */
3046         if(g_settings.getBool("creative_mode") == false)
3047         {
3048                 InventoryList *clist = player->inventory.getList("craft");
3049                 InventoryList *rlist = player->inventory.getList("craftresult");
3050                 if(rlist)
3051                 {
3052                         rlist->clearItems();
3053                 }
3054                 if(clist && rlist)
3055                 {
3056                         InventoryItem *items[9];
3057                         for(u16 i=0; i<9; i++)
3058                         {
3059                                 items[i] = clist->getItem(i);
3060                         }
3061                         
3062                         bool found = false;
3063
3064                         // Wood
3065                         if(!found)
3066                         {
3067                                 ItemSpec specs[9];
3068                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3069                                 if(checkItemCombination(items, specs))
3070                                 {
3071                                         rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3072                                         found = true;
3073                                 }
3074                         }
3075
3076                         // Stick
3077                         if(!found)
3078                         {
3079                                 ItemSpec specs[9];
3080                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3081                                 if(checkItemCombination(items, specs))
3082                                 {
3083                                         rlist->addItem(new CraftItem("Stick", 4));
3084                                         found = true;
3085                                 }
3086                         }
3087
3088                         // Sign
3089                         if(!found)
3090                         {
3091                                 ItemSpec specs[9];
3092                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3093                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3094                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3095                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3096                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3097                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3098                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3099                                 if(checkItemCombination(items, specs))
3100                                 {
3101                                         //rlist->addItem(new MapBlockObjectItem("Sign"));
3102                                         rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3103                                         found = true;
3104                                 }
3105                         }
3106
3107                         // Torch
3108                         if(!found)
3109                         {
3110                                 ItemSpec specs[9];
3111                                 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3112                                 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3113                                 if(checkItemCombination(items, specs))
3114                                 {
3115                                         rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3116                                         found = true;
3117                                 }
3118                         }
3119
3120                         // Wooden pick
3121                         if(!found)
3122                         {
3123                                 ItemSpec specs[9];
3124                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3125                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3126                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3127                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3128                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3129                                 if(checkItemCombination(items, specs))
3130                                 {
3131                                         rlist->addItem(new ToolItem("WPick", 0));
3132                                         found = true;
3133                                 }
3134                         }
3135
3136                         // Stone pick
3137                         if(!found)
3138                         {
3139                                 ItemSpec specs[9];
3140                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3141                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3142                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3143                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3144                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3145                                 if(checkItemCombination(items, specs))
3146                                 {
3147                                         rlist->addItem(new ToolItem("STPick", 0));
3148                                         found = true;
3149                                 }
3150                         }
3151
3152                         // Mese pick
3153                         if(!found)
3154                         {
3155                                 ItemSpec specs[9];
3156                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3157                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3158                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3159                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3160                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3161                                 if(checkItemCombination(items, specs))
3162                                 {
3163                                         rlist->addItem(new ToolItem("MesePick", 0));
3164                                         found = true;
3165                                 }
3166                         }
3167
3168                         // Chest1
3169                         if(!found)
3170                         {
3171                                 ItemSpec specs[9];
3172                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3173                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3174                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3175                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3176                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3177                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3178                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3179                                 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3180                                 if(checkItemCombination(items, specs))
3181                                 {
3182                                         rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3183                                         found = true;
3184                                 }
3185                         }
3186
3187                 }
3188         } // if creative_mode == false
3189
3190         /*
3191                 Serialize it
3192         */
3193
3194         std::ostringstream os;
3195         //os.imbue(std::locale("C"));
3196
3197         player->inventory.serialize(os);
3198
3199         std::string s = os.str();
3200         
3201         SharedBuffer<u8> data(s.size()+2);
3202         writeU16(&data[0], TOCLIENT_INVENTORY);
3203         memcpy(&data[2], s.c_str(), s.size());
3204         
3205         // Send as reliable
3206         m_con.Send(peer_id, 0, data, true);
3207 }
3208
3209 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3210 {
3211         DSTACK(__FUNCTION_NAME);
3212         
3213         std::ostringstream os(std::ios_base::binary);
3214         u8 buf[12];
3215         
3216         // Write command
3217         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3218         os.write((char*)buf, 2);
3219         
3220         // Write length
3221         writeU16(buf, message.size());
3222         os.write((char*)buf, 2);
3223         
3224         // Write string
3225         for(u32 i=0; i<message.size(); i++)
3226         {
3227                 u16 w = message[i];
3228                 writeU16(buf, w);
3229                 os.write((char*)buf, 2);
3230         }
3231         
3232         // Make data buffer
3233         std::string s = os.str();
3234         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3235         // Send as reliable
3236         m_con.Send(peer_id, 0, data, true);
3237 }
3238
3239 void Server::BroadcastChatMessage(const std::wstring &message)
3240 {
3241         for(core::map<u16, RemoteClient*>::Iterator
3242                 i = m_clients.getIterator();
3243                 i.atEnd() == false; i++)
3244         {
3245                 // Get client and check that it is valid
3246                 RemoteClient *client = i.getNode()->getValue();
3247                 assert(client->peer_id == i.getNode()->getKey());
3248                 if(client->serialization_version == SER_FMT_VER_INVALID)
3249                         continue;
3250
3251                 SendChatMessage(client->peer_id, message);
3252         }
3253 }
3254
3255 void Server::sendRemoveNode(v3s16 p, u16 ignore_id)
3256 {
3257         // Create packet
3258         u32 replysize = 8;
3259         SharedBuffer<u8> reply(replysize);
3260         writeU16(&reply[0], TOCLIENT_REMOVENODE);
3261         writeS16(&reply[2], p.X);
3262         writeS16(&reply[4], p.Y);
3263         writeS16(&reply[6], p.Z);
3264
3265         for(core::map<u16, RemoteClient*>::Iterator
3266                 i = m_clients.getIterator();
3267                 i.atEnd() == false; i++)
3268         {
3269                 // Get client and check that it is valid
3270                 RemoteClient *client = i.getNode()->getValue();
3271                 assert(client->peer_id == i.getNode()->getKey());
3272                 if(client->serialization_version == SER_FMT_VER_INVALID)
3273                         continue;
3274
3275                 // Don't send if it's the same one
3276                 if(client->peer_id == ignore_id)
3277                         continue;
3278
3279                 // Send as reliable
3280                 m_con.Send(client->peer_id, 0, reply, true);
3281         }
3282 }
3283
3284 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id)
3285 {
3286         for(core::map<u16, RemoteClient*>::Iterator
3287                 i = m_clients.getIterator();
3288                 i.atEnd() == false; i++)
3289         {
3290                 // Get client and check that it is valid
3291                 RemoteClient *client = i.getNode()->getValue();
3292                 assert(client->peer_id == i.getNode()->getKey());
3293                 if(client->serialization_version == SER_FMT_VER_INVALID)
3294                         continue;
3295
3296                 // Don't send if it's the same one
3297                 if(client->peer_id == ignore_id)
3298                         continue;
3299
3300                 // Create packet
3301                 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3302                 SharedBuffer<u8> reply(replysize);
3303                 writeU16(&reply[0], TOCLIENT_ADDNODE);
3304                 writeS16(&reply[2], p.X);
3305                 writeS16(&reply[4], p.Y);
3306                 writeS16(&reply[6], p.Z);
3307                 n.serialize(&reply[8], client->serialization_version);
3308
3309                 // Send as reliable
3310                 m_con.Send(client->peer_id, 0, reply, true);
3311         }
3312 }
3313
3314 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3315 {
3316         DSTACK(__FUNCTION_NAME);
3317         /*
3318                 Create a packet with the block in the right format
3319         */
3320         
3321         std::ostringstream os(std::ios_base::binary);
3322         block->serialize(os, ver);
3323         std::string s = os.str();
3324         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3325
3326         u32 replysize = 8 + blockdata.getSize();
3327         SharedBuffer<u8> reply(replysize);
3328         v3s16 p = block->getPos();
3329         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3330         writeS16(&reply[2], p.X);
3331         writeS16(&reply[4], p.Y);
3332         writeS16(&reply[6], p.Z);
3333         memcpy(&reply[8], *blockdata, blockdata.getSize());
3334
3335         /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3336                         <<":  \tpacket size: "<<replysize<<std::endl;*/
3337         
3338         /*
3339                 Send packet
3340         */
3341         m_con.Send(peer_id, 1, reply, true);
3342 }
3343
3344 void Server::SendBlocks(float dtime)
3345 {
3346         DSTACK(__FUNCTION_NAME);
3347
3348         JMutexAutoLock envlock(m_env_mutex);
3349         JMutexAutoLock conlock(m_con_mutex);
3350
3351         //TimeTaker timer("Server::SendBlocks");
3352
3353         core::array<PrioritySortedBlockTransfer> queue;
3354
3355         s32 total_sending = 0;
3356
3357         for(core::map<u16, RemoteClient*>::Iterator
3358                 i = m_clients.getIterator();
3359                 i.atEnd() == false; i++)
3360         {
3361                 RemoteClient *client = i.getNode()->getValue();
3362                 assert(client->peer_id == i.getNode()->getKey());
3363
3364                 total_sending += client->SendingCount();
3365                 
3366                 if(client->serialization_version == SER_FMT_VER_INVALID)
3367                         continue;
3368                 
3369                 client->GetNextBlocks(this, dtime, queue);
3370         }
3371
3372         // Sort.
3373         // Lowest priority number comes first.
3374         // Lowest is most important.
3375         queue.sort();
3376
3377         for(u32 i=0; i<queue.size(); i++)
3378         {
3379                 //TODO: Calculate limit dynamically
3380                 if(total_sending >= g_settings.getS32
3381                                 ("max_simultaneous_block_sends_server_total"))
3382                         break;
3383                 
3384                 PrioritySortedBlockTransfer q = queue[i];
3385
3386                 MapBlock *block = NULL;
3387                 try
3388                 {
3389                         block = m_env.getMap().getBlockNoCreate(q.pos);
3390                 }
3391                 catch(InvalidPositionException &e)
3392                 {
3393                         continue;
3394                 }
3395
3396                 RemoteClient *client = getClient(q.peer_id);
3397
3398                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3399
3400                 client->SentBlock(q.pos);
3401
3402                 total_sending++;
3403         }
3404 }
3405
3406
3407 RemoteClient* Server::getClient(u16 peer_id)
3408 {
3409         DSTACK(__FUNCTION_NAME);
3410         //JMutexAutoLock lock(m_con_mutex);
3411         core::map<u16, RemoteClient*>::Node *n;
3412         n = m_clients.find(peer_id);
3413         // A client should exist for all peers
3414         assert(n != NULL);
3415         return n->getValue();
3416 }
3417
3418 std::wstring Server::getStatusString()
3419 {
3420         std::wostringstream os(std::ios_base::binary);
3421         os<<L"# Server: ";
3422         // Uptime
3423         os<<L"uptime="<<m_uptime.get();
3424         // Information about clients
3425         os<<L", clients={";
3426         for(core::map<u16, RemoteClient*>::Iterator
3427                 i = m_clients.getIterator();
3428                 i.atEnd() == false; i++)
3429         {
3430                 // Get client and check that it is valid
3431                 RemoteClient *client = i.getNode()->getValue();
3432                 assert(client->peer_id == i.getNode()->getKey());
3433                 if(client->serialization_version == SER_FMT_VER_INVALID)
3434                         continue;
3435                 // Get player
3436                 Player *player = m_env.getPlayer(client->peer_id);
3437                 // Get name of player
3438                 std::wstring name = L"unknown";
3439                 if(player != NULL)
3440                         name = narrow_to_wide(player->getName());
3441                 // Add name to information string
3442                 os<<name<<L",";
3443         }
3444         os<<L"}";
3445         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3446                 os<<" WARNING: Map saving is disabled."<<std::endl;
3447         return os.str();
3448 }
3449
3450
3451 void setCreativeInventory(Player *player)
3452 {
3453         player->resetInventory();
3454         
3455         // Give some good picks
3456         {
3457                 InventoryItem *item = new ToolItem("STPick", 0);
3458                 void* r = player->inventory.addItem("main", item);
3459                 assert(r == NULL);
3460         }
3461         {
3462                 InventoryItem *item = new ToolItem("MesePick", 0);
3463                 void* r = player->inventory.addItem("main", item);
3464                 assert(r == NULL);
3465         }
3466
3467         /*
3468                 Give materials
3469         */
3470         
3471         // CONTENT_IGNORE-terminated list
3472         u8 material_items[] = {
3473                 CONTENT_TORCH,
3474                 CONTENT_MUD,
3475                 CONTENT_STONE,
3476                 CONTENT_SAND,
3477                 CONTENT_TREE,
3478                 CONTENT_LEAVES,
3479                 CONTENT_MESE,
3480                 CONTENT_WATERSOURCE,
3481                 CONTENT_CLOUD,
3482                 CONTENT_CHEST,
3483                 CONTENT_FURNACE,
3484                 CONTENT_SIGN_WALL,
3485                 CONTENT_IGNORE
3486         };
3487         
3488         u8 *mip = material_items;
3489         for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3490         {
3491                 if(*mip == CONTENT_IGNORE)
3492                         break;
3493
3494                 InventoryItem *item = new MaterialItem(*mip, 1);
3495                 player->inventory.addItem("main", item);
3496
3497                 mip++;
3498         }
3499
3500 #if 0
3501         assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3502         
3503         // add torch first
3504         InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3505         player->inventory.addItem("main", item);
3506         
3507         // Then others
3508         for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3509         {
3510                 // Skip some materials
3511                 if(i == CONTENT_WATER || i == CONTENT_TORCH
3512                         || i == CONTENT_COALSTONE)
3513                         continue;
3514
3515                 InventoryItem *item = new MaterialItem(i, 1);
3516                 player->inventory.addItem("main", item);
3517         }
3518 #endif
3519
3520         /*// Sign
3521         {
3522                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3523                 void* r = player->inventory.addItem("main", item);
3524                 assert(r == NULL);
3525         }*/
3526 }
3527
3528 Player *Server::emergePlayer(const char *name, const char *password,
3529                 u16 peer_id)
3530 {
3531         /*
3532                 Try to get an existing player
3533         */
3534         Player *player = m_env.getPlayer(name);
3535         if(player != NULL)
3536         {
3537                 // If player is already connected, cancel
3538                 if(player->peer_id != 0)
3539                 {
3540                         dstream<<"emergePlayer(): Player already connected"<<std::endl;
3541                         return NULL;
3542                 }
3543
3544                 // Got one.
3545                 player->peer_id = peer_id;
3546                 
3547                 // Reset inventory to creative if in creative mode
3548                 if(g_settings.getBool("creative_mode"))
3549                 {
3550                         setCreativeInventory(player);
3551                 }
3552
3553                 return player;
3554         }
3555
3556         /*
3557                 If player with the wanted peer_id already exists, cancel.
3558         */
3559         if(m_env.getPlayer(peer_id) != NULL)
3560         {
3561                 dstream<<"emergePlayer(): Player with wrong name but same"
3562                                 " peer_id already exists"<<std::endl;
3563                 return NULL;
3564         }
3565         
3566         /*
3567                 Create a new player
3568         */
3569         {
3570                 player = new ServerRemotePlayer();
3571                 //player->peer_id = c.peer_id;
3572                 //player->peer_id = PEER_ID_INEXISTENT;
3573                 player->peer_id = peer_id;
3574                 player->updateName(name);
3575
3576                 /*
3577                         Set player position
3578                 */
3579                 
3580                 dstream<<"Server: Finding spawn place for player \""
3581                                 <<player->getName()<<"\""<<std::endl;
3582
3583                 v2s16 nodepos;
3584 #if 0
3585                 player->setPosition(intToFloat(v3s16(
3586                                 0,
3587                                 45, //64,
3588                                 0
3589                 ), BS));
3590 #endif
3591 #if 1
3592                 s16 groundheight = 0;
3593 #if 1
3594                 // Try to find a good place a few times
3595                 for(s32 i=0; i<1000; i++)
3596                 {
3597                         s32 range = 1 + i;
3598                         // We're going to try to throw the player to this position
3599                         nodepos = v2s16(-range + (myrand()%(range*2)),
3600                                         -range + (myrand()%(range*2)));
3601                         v2s16 sectorpos = getNodeSectorPos(nodepos);
3602                         // Get sector (NOTE: Don't get because it's slow)
3603                         //m_env.getMap().emergeSector(sectorpos);
3604                         // Get ground height at point (fallbacks to heightmap function)
3605                         groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3606                         // Don't go underwater
3607                         if(groundheight < WATER_LEVEL)
3608                         {
3609                                 //dstream<<"-> Underwater"<<std::endl;
3610                                 continue;
3611                         }
3612                         // Don't go to high places
3613                         if(groundheight > WATER_LEVEL + 4)
3614                         {
3615                                 //dstream<<"-> Underwater"<<std::endl;
3616                                 continue;
3617                         }
3618
3619 #if 0
3620 // Doesn't work, generating blocks is a bit too complicated for doing here
3621                         // Get block at point
3622                         v3s16 nodepos3d;
3623                         nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3624                         v3s16 blockpos = getNodeBlockPos(nodepos3d);
3625                         ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3626                         // Don't go inside ground
3627                         try{
3628                                 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3629                                 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3630                                 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3631                                 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3632                                 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3633                                         || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3634                                 {
3635                                         dstream<<"-> Inside ground"<<std::endl;
3636                                         // In ground
3637                                         continue;
3638                                 }
3639                         }catch(InvalidPositionException &e)
3640                         {
3641                                 dstream<<"-> Invalid position"<<std::endl;
3642                                 // Ignore invalid position
3643                                 continue;
3644                         }
3645 #endif
3646
3647                         // Found a good place
3648                         dstream<<"Searched through "<<i<<" places."<<std::endl;
3649                         break;
3650                 }
3651 #endif
3652                 
3653                 // If no suitable place was not found, go above water at least.
3654                 if(groundheight < WATER_LEVEL)
3655                         groundheight = WATER_LEVEL;
3656
3657                 player->setPosition(intToFloat(v3s16(
3658                                 nodepos.X,
3659                                 groundheight + 5, // Accomodate mud
3660                                 nodepos.Y
3661                 ), BS));
3662 #endif
3663
3664                 /*
3665                         Add player to environment
3666                 */
3667
3668                 m_env.addPlayer(player);
3669
3670                 /*
3671                         Add stuff to inventory
3672                 */
3673                 
3674                 if(g_settings.getBool("creative_mode"))
3675                 {
3676                         setCreativeInventory(player);
3677                 }
3678                 else
3679                 {
3680                         /*{
3681                                 InventoryItem *item = new ToolItem("WPick", 32000);
3682                                 void* r = player->inventory.addItem("main", item);
3683                                 assert(r == NULL);
3684                         }*/
3685                         /*{
3686                                 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3687                                 void* r = player->inventory.addItem("main", item);
3688                                 assert(r == NULL);
3689                         }
3690                         {
3691                                 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3692                                 void* r = player->inventory.addItem("main", item);
3693                                 assert(r == NULL);
3694                         }
3695                         {
3696                                 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3697                                 void* r = player->inventory.addItem("main", item);
3698                                 assert(r == NULL);
3699                         }
3700                         {
3701                                 InventoryItem *item = new CraftItem("Stick", 4);
3702                                 void* r = player->inventory.addItem("main", item);
3703                                 assert(r == NULL);
3704                         }
3705                         {
3706                                 InventoryItem *item = new ToolItem("WPick", 32000);
3707                                 void* r = player->inventory.addItem("main", item);
3708                                 assert(r == NULL);
3709                         }
3710                         {
3711                                 InventoryItem *item = new ToolItem("STPick", 32000);
3712                                 void* r = player->inventory.addItem("main", item);
3713                                 assert(r == NULL);
3714                         }*/
3715                         /*// Give some lights
3716                         {
3717                                 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3718                                 bool r = player->inventory.addItem("main", item);
3719                                 assert(r == true);
3720                         }
3721                         // and some signs
3722                         for(u16 i=0; i<4; i++)
3723                         {
3724                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3725                                 bool r = player->inventory.addItem("main", item);
3726                                 assert(r == true);
3727                         }*/
3728                         /*// Give some other stuff
3729                         {
3730                                 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3731                                 bool r = player->inventory.addItem("main", item);
3732                                 assert(r == true);
3733                         }*/
3734                 }
3735
3736                 return player;
3737                 
3738         } // create new player
3739 }
3740
3741 #if 0
3742 void Server::UpdateBlockWaterPressure(MapBlock *block,
3743                         core::map<v3s16, MapBlock*> &modified_blocks)
3744 {
3745         MapVoxelManipulator v(&m_env.getMap());
3746         v.m_disable_water_climb =
3747                         g_settings.getBool("disable_water_climb");
3748         
3749         VoxelArea area(block->getPosRelative(),
3750                         block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3751
3752         try
3753         {
3754                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3755         }
3756         catch(ProcessingLimitException &e)
3757         {
3758                 dstream<<"Processing limit reached (1)"<<std::endl;
3759         }
3760         
3761         v.blitBack(modified_blocks);
3762 }
3763 #endif
3764
3765 void Server::handlePeerChange(PeerChange &c)
3766 {
3767         JMutexAutoLock envlock(m_env_mutex);
3768         JMutexAutoLock conlock(m_con_mutex);
3769         
3770         if(c.type == PEER_ADDED)
3771         {
3772                 /*
3773                         Add
3774                 */
3775
3776                 // Error check
3777                 core::map<u16, RemoteClient*>::Node *n;
3778                 n = m_clients.find(c.peer_id);
3779                 // The client shouldn't already exist
3780                 assert(n == NULL);
3781
3782                 // Create client
3783                 RemoteClient *client = new RemoteClient();
3784                 client->peer_id = c.peer_id;
3785                 m_clients.insert(client->peer_id, client);
3786
3787         } // PEER_ADDED
3788         else if(c.type == PEER_REMOVED)
3789         {
3790                 /*
3791                         Delete
3792                 */
3793
3794                 // Error check
3795                 core::map<u16, RemoteClient*>::Node *n;
3796                 n = m_clients.find(c.peer_id);
3797                 // The client should exist
3798                 assert(n != NULL);
3799                 
3800                 // Collect information about leaving in chat
3801                 std::wstring message;
3802                 {
3803                         std::wstring name = L"unknown";
3804                         Player *player = m_env.getPlayer(c.peer_id);
3805                         if(player != NULL)
3806                                 name = narrow_to_wide(player->getName());
3807                         
3808                         message += L"*** ";
3809                         message += name;
3810                         message += L" left game";
3811                         if(c.timeout)
3812                                 message += L" (timed out)";
3813                 }
3814
3815                 /*// Delete player
3816                 {
3817                         m_env.removePlayer(c.peer_id);
3818                 }*/
3819
3820                 // Set player client disconnected
3821                 {
3822                         Player *player = m_env.getPlayer(c.peer_id);
3823                         if(player != NULL)
3824                                 player->peer_id = 0;
3825                 }
3826                 
3827                 // Delete client
3828                 delete m_clients[c.peer_id];
3829                 m_clients.remove(c.peer_id);
3830
3831                 // Send player info to all remaining clients
3832                 SendPlayerInfos();
3833                 
3834                 // Send leave chat message to all remaining clients
3835                 BroadcastChatMessage(message);
3836                 
3837         } // PEER_REMOVED
3838         else
3839         {
3840                 assert(0);
3841         }
3842 }
3843
3844 void Server::handlePeerChanges()
3845 {
3846         while(m_peer_change_queue.size() > 0)
3847         {
3848                 PeerChange c = m_peer_change_queue.pop_front();
3849
3850                 dout_server<<"Server: Handling peer change: "
3851                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3852                                 <<std::endl;
3853
3854                 handlePeerChange(c);
3855         }
3856 }
3857
3858 void dedicated_server_loop(Server &server, bool &kill)
3859 {
3860         DSTACK(__FUNCTION_NAME);
3861         
3862         std::cout<<DTIME<<std::endl;
3863         std::cout<<"========================"<<std::endl;
3864         std::cout<<"Running dedicated server"<<std::endl;
3865         std::cout<<"========================"<<std::endl;
3866         std::cout<<std::endl;
3867
3868         for(;;)
3869         {
3870                 // This is kind of a hack but can be done like this
3871                 // because server.step() is very light
3872                 sleep_ms(30);
3873                 server.step(0.030);
3874
3875                 if(server.getShutdownRequested() || kill)
3876                 {
3877                         std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
3878                         break;
3879                 }
3880
3881                 static int counter = 0;
3882                 counter--;
3883                 if(counter <= 0)
3884                 {
3885                         counter = 10;
3886
3887                         core::list<PlayerInfo> list = server.getPlayerInfo();
3888                         core::list<PlayerInfo>::Iterator i;
3889                         static u32 sum_old = 0;
3890                         u32 sum = PIChecksum(list);
3891                         if(sum != sum_old)
3892                         {
3893                                 std::cout<<DTIME<<"Player info:"<<std::endl;
3894                                 for(i=list.begin(); i!=list.end(); i++)
3895                                 {
3896                                         i->PrintLine(&std::cout);
3897                                 }
3898                         }
3899                         sum_old = sum;
3900                 }
3901         }
3902 }
3903
3904