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