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