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