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