]> git.lizzy.rs Git - minetest.git/blob - src/server.cpp
added sneaking/crouching and changelog
[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                         // Save only changed parts
1399                         m_env.getMap().save(true);
1400
1401                         // Delete unused sectors
1402                         u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1403                                         g_settings.getFloat("server_unload_unused_sectors_timeout"));
1404                         if(deleted_count > 0)
1405                         {
1406                                 dout_server<<"Server: Unloaded "<<deleted_count
1407                                                 <<" sectors from memory"<<std::endl;
1408                         }
1409
1410                         // Save players
1411                         m_env.serializePlayers(m_mapsavedir);
1412                 }
1413         }
1414 }
1415
1416 void Server::Receive()
1417 {
1418         DSTACK(__FUNCTION_NAME);
1419         u32 data_maxsize = 10000;
1420         Buffer<u8> data(data_maxsize);
1421         u16 peer_id;
1422         u32 datasize;
1423         try{
1424                 {
1425                         JMutexAutoLock conlock(m_con_mutex);
1426                         datasize = m_con.Receive(peer_id, *data, data_maxsize);
1427                 }
1428
1429                 // This has to be called so that the client list gets synced
1430                 // with the peer list of the connection
1431                 handlePeerChanges();
1432
1433                 ProcessData(*data, datasize, peer_id);
1434         }
1435         catch(con::InvalidIncomingDataException &e)
1436         {
1437                 derr_server<<"Server::Receive(): "
1438                                 "InvalidIncomingDataException: what()="
1439                                 <<e.what()<<std::endl;
1440         }
1441         catch(con::PeerNotFoundException &e)
1442         {
1443                 //NOTE: This is not needed anymore
1444                 
1445                 // The peer has been disconnected.
1446                 // Find the associated player and remove it.
1447
1448                 /*JMutexAutoLock envlock(m_env_mutex);
1449
1450                 dout_server<<"ServerThread: peer_id="<<peer_id
1451                                 <<" has apparently closed connection. "
1452                                 <<"Removing player."<<std::endl;
1453
1454                 m_env.removePlayer(peer_id);*/
1455         }
1456 }
1457
1458 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1459 {
1460         DSTACK(__FUNCTION_NAME);
1461         // Environment is locked first.
1462         JMutexAutoLock envlock(m_env_mutex);
1463         JMutexAutoLock conlock(m_con_mutex);
1464         
1465         con::Peer *peer;
1466         try{
1467                 peer = m_con.GetPeer(peer_id);
1468         }
1469         catch(con::PeerNotFoundException &e)
1470         {
1471                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1472                                 <<peer_id<<" not found"<<std::endl;
1473                 return;
1474         }
1475         
1476         //u8 peer_ser_ver = peer->serialization_version;
1477         u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1478
1479         try
1480         {
1481
1482         if(datasize < 2)
1483                 return;
1484
1485         ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1486         
1487         if(command == TOSERVER_INIT)
1488         {
1489                 // [0] u16 TOSERVER_INIT
1490                 // [2] u8 SER_FMT_VER_HIGHEST
1491                 // [3] u8[20] player_name
1492
1493                 if(datasize < 3)
1494                         return;
1495
1496                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1497                                 <<peer->id<<std::endl;
1498
1499                 // First byte after command is maximum supported
1500                 // serialization version
1501                 u8 client_max = data[2];
1502                 u8 our_max = SER_FMT_VER_HIGHEST;
1503                 // Use the highest version supported by both
1504                 u8 deployed = core::min_(client_max, our_max);
1505                 // If it's lower than the lowest supported, give up.
1506                 if(deployed < SER_FMT_VER_LOWEST)
1507                         deployed = SER_FMT_VER_INVALID;
1508
1509                 //peer->serialization_version = deployed;
1510                 getClient(peer->id)->pending_serialization_version = deployed;
1511
1512                 if(deployed == SER_FMT_VER_INVALID)
1513                 {
1514                         derr_server<<DTIME<<"Server: Cannot negotiate "
1515                                         "serialization version with peer "
1516                                         <<peer_id<<std::endl;
1517                         return;
1518                 }
1519
1520                 /*
1521                         Set up player
1522                 */
1523                 
1524                 // Get player name
1525                 const u32 playername_size = 20;
1526                 char playername[playername_size];
1527                 for(u32 i=0; i<playername_size-1; i++)
1528                 {
1529                         playername[i] = data[3+i];
1530                 }
1531                 playername[playername_size-1] = 0;
1532                 
1533                 // Get player
1534                 Player *player = emergePlayer(playername, "", peer_id);
1535                 //Player *player = m_env.getPlayer(peer_id);
1536
1537                 /*{
1538                         // DEBUG: Test serialization
1539                         std::ostringstream test_os;
1540                         player->serialize(test_os);
1541                         dstream<<"Player serialization test: \""<<test_os.str()
1542                                         <<"\""<<std::endl;
1543                         std::istringstream test_is(test_os.str());
1544                         player->deSerialize(test_is);
1545                 }*/
1546
1547                 // If failed, cancel
1548                 if(player == NULL)
1549                 {
1550                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1551                                         <<": failed to emerge player"<<std::endl;
1552                         return;
1553                 }
1554
1555                 /*
1556                 // If a client is already connected to the player, cancel
1557                 if(player->peer_id != 0)
1558                 {
1559                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1560                                         <<" tried to connect to "
1561                                         "an already connected player (peer_id="
1562                                         <<player->peer_id<<")"<<std::endl;
1563                         return;
1564                 }
1565                 // Set client of player
1566                 player->peer_id = peer_id;
1567                 */
1568
1569                 // Check if player doesn't exist
1570                 if(player == NULL)
1571                         throw con::InvalidIncomingDataException
1572                                 ("Server::ProcessData(): INIT: Player doesn't exist");
1573
1574                 /*// update name if it was supplied
1575                 if(datasize >= 20+3)
1576                 {
1577                         data[20+3-1] = 0;
1578                         player->updateName((const char*)&data[3]);
1579                 }*/
1580
1581                 // Now answer with a TOCLIENT_INIT
1582                 
1583                 SharedBuffer<u8> reply(2+1+6);
1584                 writeU16(&reply[0], TOCLIENT_INIT);
1585                 writeU8(&reply[2], deployed);
1586                 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1587                 // Send as reliable
1588                 m_con.Send(peer_id, 0, reply, true);
1589
1590                 return;
1591         }
1592         if(command == TOSERVER_INIT2)
1593         {
1594                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1595                                 <<peer->id<<std::endl;
1596
1597
1598                 getClient(peer->id)->serialization_version
1599                                 = getClient(peer->id)->pending_serialization_version;
1600
1601                 /*
1602                         Send some initialization data
1603                 */
1604                 
1605                 // Send player info to all players
1606                 SendPlayerInfos();
1607
1608                 // Send inventory to player
1609                 SendInventory(peer->id);
1610                 
1611                 // Send time of day
1612                 {
1613                         SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1614                                         m_time_of_day.get());
1615                         m_con.Send(peer->id, 0, data, true);
1616                 }
1617
1618                 // Send information about server to player in chat
1619                 {
1620                         std::wostringstream os(std::ios_base::binary);
1621                         os<<L"# Server: ";
1622                         // Uptime
1623                         os<<L"uptime="<<m_uptime.get();
1624                         // Information about clients
1625                         os<<L", clients={";
1626                         for(core::map<u16, RemoteClient*>::Iterator
1627                                 i = m_clients.getIterator();
1628                                 i.atEnd() == false; i++)
1629                         {
1630                                 // Get client and check that it is valid
1631                                 RemoteClient *client = i.getNode()->getValue();
1632                                 assert(client->peer_id == i.getNode()->getKey());
1633                                 if(client->serialization_version == SER_FMT_VER_INVALID)
1634                                         continue;
1635                                 // Get player
1636                                 Player *player = m_env.getPlayer(client->peer_id);
1637                                 // Get name of player
1638                                 std::wstring name = L"unknown";
1639                                 if(player != NULL)
1640                                         name = narrow_to_wide(player->getName());
1641                                 // Add name to information string
1642                                 os<<name<<L",";
1643                         }
1644                         os<<L"}";
1645                         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
1646                                 os<<" WARNING: Map saving is disabled."<<std::endl;
1647                         // Send message
1648                         SendChatMessage(peer_id, os.str());
1649                 }
1650                 
1651                 // Send information about joining in chat
1652                 {
1653                         std::wstring name = L"unknown";
1654                         Player *player = m_env.getPlayer(peer_id);
1655                         if(player != NULL)
1656                                 name = narrow_to_wide(player->getName());
1657                         
1658                         std::wstring message;
1659                         message += L"*** ";
1660                         message += name;
1661                         message += L" joined game";
1662                         BroadcastChatMessage(message);
1663                 }
1664
1665                 return;
1666         }
1667
1668         if(peer_ser_ver == SER_FMT_VER_INVALID)
1669         {
1670                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1671                                 " serialization format invalid or not initialized."
1672                                 " Skipping incoming command="<<command<<std::endl;
1673                 return;
1674         }
1675         
1676         Player *player = m_env.getPlayer(peer_id);
1677
1678         if(player == NULL){
1679                 derr_server<<"Server::ProcessData(): Cancelling: "
1680                                 "No player for peer_id="<<peer_id
1681                                 <<std::endl;
1682                 return;
1683         }
1684         if(command == TOSERVER_PLAYERPOS)
1685         {
1686                 if(datasize < 2+12+12+4+4)
1687                         return;
1688         
1689                 u32 start = 0;
1690                 v3s32 ps = readV3S32(&data[start+2]);
1691                 v3s32 ss = readV3S32(&data[start+2+12]);
1692                 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1693                 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1694                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1695                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1696                 pitch = wrapDegrees(pitch);
1697                 yaw = wrapDegrees(yaw);
1698                 player->setPosition(position);
1699                 player->setSpeed(speed);
1700                 player->setPitch(pitch);
1701                 player->setYaw(yaw);
1702                 
1703                 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1704                                 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1705                                 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1706         }
1707         else if(command == TOSERVER_GOTBLOCKS)
1708         {
1709                 if(datasize < 2+1)
1710                         return;
1711                 
1712                 /*
1713                         [0] u16 command
1714                         [2] u8 count
1715                         [3] v3s16 pos_0
1716                         [3+6] v3s16 pos_1
1717                         ...
1718                 */
1719
1720                 u16 count = data[2];
1721                 for(u16 i=0; i<count; i++)
1722                 {
1723                         if((s16)datasize < 2+1+(i+1)*6)
1724                                 throw con::InvalidIncomingDataException
1725                                         ("GOTBLOCKS length is too short");
1726                         v3s16 p = readV3S16(&data[2+1+i*6]);
1727                         /*dstream<<"Server: GOTBLOCKS ("
1728                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1729                         RemoteClient *client = getClient(peer_id);
1730                         client->GotBlock(p);
1731                 }
1732         }
1733         else if(command == TOSERVER_DELETEDBLOCKS)
1734         {
1735                 if(datasize < 2+1)
1736                         return;
1737                 
1738                 /*
1739                         [0] u16 command
1740                         [2] u8 count
1741                         [3] v3s16 pos_0
1742                         [3+6] v3s16 pos_1
1743                         ...
1744                 */
1745
1746                 u16 count = data[2];
1747                 for(u16 i=0; i<count; i++)
1748                 {
1749                         if((s16)datasize < 2+1+(i+1)*6)
1750                                 throw con::InvalidIncomingDataException
1751                                         ("DELETEDBLOCKS length is too short");
1752                         v3s16 p = readV3S16(&data[2+1+i*6]);
1753                         /*dstream<<"Server: DELETEDBLOCKS ("
1754                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1755                         RemoteClient *client = getClient(peer_id);
1756                         client->SetBlockNotSent(p);
1757                 }
1758         }
1759         else if(command == TOSERVER_CLICK_OBJECT)
1760         {
1761                 if(datasize < 13)
1762                         return;
1763
1764                 /*
1765                         [0] u16 command
1766                         [2] u8 button (0=left, 1=right)
1767                         [3] v3s16 block
1768                         [9] s16 id
1769                         [11] u16 item
1770                 */
1771                 u8 button = readU8(&data[2]);
1772                 v3s16 p;
1773                 p.X = readS16(&data[3]);
1774                 p.Y = readS16(&data[5]);
1775                 p.Z = readS16(&data[7]);
1776                 s16 id = readS16(&data[9]);
1777                 //u16 item_i = readU16(&data[11]);
1778
1779                 MapBlock *block = NULL;
1780                 try
1781                 {
1782                         block = m_env.getMap().getBlockNoCreate(p);
1783                 }
1784                 catch(InvalidPositionException &e)
1785                 {
1786                         derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1787                         return;
1788                 }
1789
1790                 MapBlockObject *obj = block->getObject(id);
1791
1792                 if(obj == NULL)
1793                 {
1794                         derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1795                         return;
1796                 }
1797
1798                 //TODO: Check that object is reasonably close
1799                 
1800                 // Left click
1801                 if(button == 0)
1802                 {
1803                         InventoryList *ilist = player->inventory.getList("main");
1804                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1805                         {
1806                         
1807                                 // Skip if inventory has no free space
1808                                 if(ilist->getUsedSlots() == ilist->getSize())
1809                                 {
1810                                         dout_server<<"Player inventory has no free space"<<std::endl;
1811                                         return;
1812                                 }
1813                                 
1814                                 /*
1815                                         Create the inventory item
1816                                 */
1817                                 InventoryItem *item = NULL;
1818                                 // If it is an item-object, take the item from it
1819                                 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1820                                 {
1821                                         item = ((ItemObject*)obj)->createInventoryItem();
1822                                 }
1823                                 // Else create an item of the object
1824                                 else
1825                                 {
1826                                         item = new MapBlockObjectItem
1827                                                         (obj->getInventoryString());
1828                                 }
1829                                 
1830                                 // Add to inventory and send inventory
1831                                 ilist->addItem(item);
1832                                 SendInventory(player->peer_id);
1833                         }
1834
1835                         // Remove from block
1836                         block->removeObject(id);
1837                 }
1838         }
1839         else if(command == TOSERVER_GROUND_ACTION)
1840         {
1841                 if(datasize < 17)
1842                         return;
1843                 /*
1844                         length: 17
1845                         [0] u16 command
1846                         [2] u8 action
1847                         [3] v3s16 nodepos_undersurface
1848                         [9] v3s16 nodepos_abovesurface
1849                         [15] u16 item
1850                         actions:
1851                         0: start digging
1852                         1: place block
1853                         2: stop digging (all parameters ignored)
1854                 */
1855                 u8 action = readU8(&data[2]);
1856                 v3s16 p_under;
1857                 p_under.X = readS16(&data[3]);
1858                 p_under.Y = readS16(&data[5]);
1859                 p_under.Z = readS16(&data[7]);
1860                 v3s16 p_over;
1861                 p_over.X = readS16(&data[9]);
1862                 p_over.Y = readS16(&data[11]);
1863                 p_over.Z = readS16(&data[13]);
1864                 u16 item_i = readU16(&data[15]);
1865
1866                 //TODO: Check that target is reasonably close
1867                 
1868                 /*
1869                         0: start digging
1870                 */
1871                 if(action == 0)
1872                 {
1873                         /*
1874                                 NOTE: This can be used in the future to check if
1875                                 somebody is cheating, by checking the timing.
1876                         */
1877                 } // action == 0
1878
1879                 /*
1880                         2: stop digging
1881                 */
1882                 else if(action == 2)
1883                 {
1884 #if 0
1885                         RemoteClient *client = getClient(peer->id);
1886                         JMutexAutoLock digmutex(client->m_dig_mutex);
1887                         client->m_dig_tool_item = -1;
1888 #endif
1889                 }
1890
1891                 /*
1892                         3: Digging completed
1893                 */
1894                 else if(action == 3)
1895                 {
1896                         // Mandatory parameter; actually used for nothing
1897                         core::map<v3s16, MapBlock*> modified_blocks;
1898
1899                         u8 material;
1900                         u8 mineral = MINERAL_NONE;
1901
1902                         try
1903                         {
1904                                 MapNode n = m_env.getMap().getNode(p_under);
1905                                 // Get material at position
1906                                 material = n.d;
1907                                 // If it's not diggable, do nothing
1908                                 if(content_diggable(material) == false)
1909                                 {
1910                                         derr_server<<"Server: Not finishing digging: Node not diggable"
1911                                                         <<std::endl;
1912                                         return;
1913                                 }
1914                                 // Get mineral
1915                                 mineral = n.getMineral();
1916                         }
1917                         catch(InvalidPositionException &e)
1918                         {
1919                                 derr_server<<"Server: Not finishing digging: Node not found."
1920                                                 <<" Adding block to emerge queue."
1921                                                 <<std::endl;
1922                                 m_emerge_queue.addBlock(peer_id,
1923                                                 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1924                                 return;
1925                         }
1926                         
1927                         /*
1928                                 Send the removal to all other clients
1929                         */
1930
1931                         // Create packet
1932                         u32 replysize = 8;
1933                         SharedBuffer<u8> reply(replysize);
1934                         writeU16(&reply[0], TOCLIENT_REMOVENODE);
1935                         writeS16(&reply[2], p_under.X);
1936                         writeS16(&reply[4], p_under.Y);
1937                         writeS16(&reply[6], p_under.Z);
1938
1939                         for(core::map<u16, RemoteClient*>::Iterator
1940                                 i = m_clients.getIterator();
1941                                 i.atEnd() == false; i++)
1942                         {
1943                                 // Get client and check that it is valid
1944                                 RemoteClient *client = i.getNode()->getValue();
1945                                 assert(client->peer_id == i.getNode()->getKey());
1946                                 if(client->serialization_version == SER_FMT_VER_INVALID)
1947                                         continue;
1948
1949                                 // Don't send if it's the same one
1950                                 if(peer_id == client->peer_id)
1951                                         continue;
1952
1953                                 // Send as reliable
1954                                 m_con.Send(client->peer_id, 0, reply, true);
1955                         }
1956                         
1957                         /*
1958                                 Update and send inventory
1959                         */
1960
1961                         if(g_settings.getBool("creative_mode") == false)
1962                         {
1963                                 /*
1964                                         Wear out tool
1965                                 */
1966                                 InventoryList *mlist = player->inventory.getList("main");
1967                                 if(mlist != NULL)
1968                                 {
1969                                         InventoryItem *item = mlist->getItem(item_i);
1970                                         if(item && (std::string)item->getName() == "ToolItem")
1971                                         {
1972                                                 ToolItem *titem = (ToolItem*)item;
1973                                                 std::string toolname = titem->getToolName();
1974
1975                                                 // Get digging properties for material and tool
1976                                                 DiggingProperties prop =
1977                                                                 getDiggingProperties(material, toolname);
1978
1979                                                 if(prop.diggable == false)
1980                                                 {
1981                                                         derr_server<<"Server: WARNING: Player digged"
1982                                                                         <<" with impossible material + tool"
1983                                                                         <<" combination"<<std::endl;
1984                                                 }
1985                                                 
1986                                                 bool weared_out = titem->addWear(prop.wear);
1987
1988                                                 if(weared_out)
1989                                                 {
1990                                                         mlist->deleteItem(item_i);
1991                                                 }
1992                                         }
1993                                 }
1994
1995                                 /*
1996                                         Add digged item to inventory
1997                                 */
1998
1999                                 InventoryItem *item = NULL;
2000
2001                                 if(mineral != MINERAL_NONE)
2002                                         item = getDiggedMineralItem(mineral);
2003
2004                                 if(item == NULL)
2005                                         item = new MaterialItem(material, 1);
2006
2007                                 player->inventory.addItem("main", item);
2008
2009                                 /*
2010                                         Send inventory
2011                                 */
2012                                 SendInventory(player->peer_id);
2013                         }
2014
2015                         /*
2016                                 Remove the node
2017                                 (this takes some time so it is done after the quick stuff)
2018                         */
2019                         m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2020
2021 #if 0
2022                         /*
2023                                 Update water
2024                         */
2025                         
2026                         // Update water pressure around modification
2027                         // This also adds it to m_flow_active_nodes if appropriate
2028
2029                         MapVoxelManipulator v(&m_env.getMap());
2030                         v.m_disable_water_climb =
2031                                         g_settings.getBool("disable_water_climb");
2032                         
2033                         VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2034
2035                         try
2036                         {
2037                                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2038                         }
2039                         catch(ProcessingLimitException &e)
2040                         {
2041                                 dstream<<"Processing limit reached (1)"<<std::endl;
2042                         }
2043                         
2044                         v.blitBack(modified_blocks);
2045 #endif
2046                 }
2047                 
2048                 /*
2049                         1: place block
2050                 */
2051                 else if(action == 1)
2052                 {
2053
2054                         InventoryList *ilist = player->inventory.getList("main");
2055                         if(ilist == NULL)
2056                                 return;
2057
2058                         // Get item
2059                         InventoryItem *item = ilist->getItem(item_i);
2060                         
2061                         // If there is no item, it is not possible to add it anywhere
2062                         if(item == NULL)
2063                                 return;
2064                         
2065                         /*
2066                                 Handle material items
2067                         */
2068                         if(std::string("MaterialItem") == item->getName())
2069                         {
2070                                 try{
2071                                         // Don't add a node if this is not a free space
2072                                         MapNode n2 = m_env.getMap().getNode(p_over);
2073                                         if(content_buildable_to(n2.d) == false)
2074                                         {
2075                                                 // Client probably has wrong data.
2076                                                 // Set block not sent, so that client will get
2077                                                 // a valid one.
2078                                                 dstream<<"Client "<<peer_id<<" tried to place"
2079                                                                 <<" node in invalid position; setting"
2080                                                                 <<" MapBlock not sent."<<std::endl;
2081                                                 RemoteClient *client = getClient(peer_id);
2082                                                 v3s16 blockpos = getNodeBlockPos(p_over);
2083                                                 client->SetBlockNotSent(blockpos);
2084                                                 return;
2085                                         }
2086                                 }
2087                                 catch(InvalidPositionException &e)
2088                                 {
2089                                         derr_server<<"Server: Ignoring ADDNODE: Node not found"
2090                                                         <<" Adding block to emerge queue."
2091                                                         <<std::endl;
2092                                         m_emerge_queue.addBlock(peer_id,
2093                                                         getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2094                                         return;
2095                                 }
2096
2097                                 // Reset build time counter
2098                                 getClient(peer->id)->m_time_from_building.set(0.0);
2099                                 
2100                                 // Create node data
2101                                 MaterialItem *mitem = (MaterialItem*)item;
2102                                 MapNode n;
2103                                 n.d = mitem->getMaterial();
2104                                 if(content_features(n.d).wall_mounted)
2105                                         n.dir = packDir(p_under - p_over);
2106
2107 #if 1
2108                                 // Create packet
2109                                 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2110                                 SharedBuffer<u8> reply(replysize);
2111                                 writeU16(&reply[0], TOCLIENT_ADDNODE);
2112                                 writeS16(&reply[2], p_over.X);
2113                                 writeS16(&reply[4], p_over.Y);
2114                                 writeS16(&reply[6], p_over.Z);
2115                                 n.serialize(&reply[8], peer_ser_ver);
2116                                 // Send as reliable
2117                                 m_con.SendToAll(0, reply, true);
2118                                 
2119                                 /*
2120                                         Handle inventory
2121                                 */
2122                                 InventoryList *ilist = player->inventory.getList("main");
2123                                 if(g_settings.getBool("creative_mode") == false && ilist)
2124                                 {
2125                                         // Remove from inventory and send inventory
2126                                         if(mitem->getCount() == 1)
2127                                                 ilist->deleteItem(item_i);
2128                                         else
2129                                                 mitem->remove(1);
2130                                         // Send inventory
2131                                         SendInventory(peer_id);
2132                                 }
2133                                 
2134                                 /*
2135                                         Add node.
2136
2137                                         This takes some time so it is done after the quick stuff
2138                                 */
2139                                 core::map<v3s16, MapBlock*> modified_blocks;
2140                                 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2141 #endif
2142 #if 0
2143                                 /*
2144                                         Handle inventory
2145                                 */
2146                                 InventoryList *ilist = player->inventory.getList("main");
2147                                 if(g_settings.getBool("creative_mode") == false && ilist)
2148                                 {
2149                                         // Remove from inventory and send inventory
2150                                         if(mitem->getCount() == 1)
2151                                                 ilist->deleteItem(item_i);
2152                                         else
2153                                                 mitem->remove(1);
2154                                         // Send inventory
2155                                         SendInventory(peer_id);
2156                                 }
2157
2158                                 /*
2159                                         Add node.
2160
2161                                         This takes some time so it is done after the quick stuff
2162                                 */
2163                                 core::map<v3s16, MapBlock*> modified_blocks;
2164                                 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2165
2166                                 /*
2167                                         Set the modified blocks unsent for all the clients
2168                                 */
2169                                 
2170                                 //JMutexAutoLock lock2(m_con_mutex);
2171
2172                                 for(core::map<u16, RemoteClient*>::Iterator
2173                                                 i = m_clients.getIterator();
2174                                                 i.atEnd() == false; i++)
2175                                 {
2176                                         RemoteClient *client = i.getNode()->getValue();
2177                                         
2178                                         if(modified_blocks.size() > 0)
2179                                         {
2180                                                 // Remove block from sent history
2181                                                 client->SetBlocksNotSent(modified_blocks);
2182                                         }
2183                                 }
2184 #endif
2185
2186 #if 0
2187                                 /*
2188                                         Update water
2189                                 */
2190                                 
2191                                 // Update water pressure around modification
2192                                 // This also adds it to m_flow_active_nodes if appropriate
2193
2194                                 MapVoxelManipulator v(&m_env.getMap());
2195                                 v.m_disable_water_climb =
2196                                                 g_settings.getBool("disable_water_climb");
2197                                 
2198                                 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2199
2200                                 try
2201                                 {
2202                                         v.updateAreaWaterPressure(area, m_flow_active_nodes);
2203                                 }
2204                                 catch(ProcessingLimitException &e)
2205                                 {
2206                                         dstream<<"Processing limit reached (1)"<<std::endl;
2207                                 }
2208                                 
2209                                 v.blitBack(modified_blocks);
2210 #endif
2211                         }
2212                         /*
2213                                 Handle other items
2214                         */
2215                         else
2216                         {
2217                                 v3s16 blockpos = getNodeBlockPos(p_over);
2218
2219                                 MapBlock *block = NULL;
2220                                 try
2221                                 {
2222                                         block = m_env.getMap().getBlockNoCreate(blockpos);
2223                                 }
2224                                 catch(InvalidPositionException &e)
2225                                 {
2226                                         derr_server<<"Error while placing object: "
2227                                                         "block not found"<<std::endl;
2228                                         return;
2229                                 }
2230
2231                                 v3s16 block_pos_i_on_map = block->getPosRelative();
2232                                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2233
2234                                 v3f pos = intToFloat(p_over);
2235                                 pos -= block_pos_f_on_map;
2236                                 
2237                                 /*dout_server<<"pos="
2238                                                 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2239                                                 <<std::endl;*/
2240
2241                                 MapBlockObject *obj = NULL;
2242
2243                                 /*
2244                                         Handle block object items
2245                                 */
2246                                 if(std::string("MBOItem") == item->getName())
2247                                 {
2248                                         MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2249
2250                                         /*dout_server<<"Trying to place a MapBlockObjectItem: "
2251                                                         "inventorystring=\""
2252                                                         <<oitem->getInventoryString()
2253                                                         <<"\""<<std::endl;*/
2254                                                         
2255                                         obj = oitem->createObject
2256                                                         (pos, player->getYaw(), player->getPitch());
2257                                 }
2258                                 /*
2259                                         Handle other items
2260                                 */
2261                                 else
2262                                 {
2263                                         dout_server<<"Placing a miscellaneous item on map"
2264                                                         <<std::endl;
2265                                         /*
2266                                                 Create an ItemObject that contains the item.
2267                                         */
2268                                         ItemObject *iobj = new ItemObject(NULL, -1, pos);
2269                                         std::ostringstream os(std::ios_base::binary);
2270                                         item->serialize(os);
2271                                         dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2272                                         iobj->setItemString(os.str());
2273                                         obj = iobj;
2274                                 }
2275
2276                                 if(obj == NULL)
2277                                 {
2278                                         derr_server<<"WARNING: item resulted in NULL object, "
2279                                                         <<"not placing onto map"
2280                                                         <<std::endl;
2281                                 }
2282                                 else
2283                                 {
2284                                         block->addObject(obj);
2285
2286                                         dout_server<<"Placed object"<<std::endl;
2287
2288                                         InventoryList *ilist = player->inventory.getList("main");
2289                                         if(g_settings.getBool("creative_mode") == false && ilist)
2290                                         {
2291                                                 // Remove from inventory and send inventory
2292                                                 ilist->deleteItem(item_i);
2293                                                 // Send inventory
2294                                                 SendInventory(peer_id);
2295                                         }
2296                                 }
2297                         }
2298
2299                 } // action == 1
2300
2301                 /*
2302                         Catch invalid actions
2303                 */
2304                 else
2305                 {
2306                         derr_server<<"WARNING: Server: Invalid action "
2307                                         <<action<<std::endl;
2308                 }
2309         }
2310 #if 0
2311         else if(command == TOSERVER_RELEASE)
2312         {
2313                 if(datasize < 3)
2314                         return;
2315                 /*
2316                         length: 3
2317                         [0] u16 command
2318                         [2] u8 button
2319                 */
2320                 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2321         }
2322 #endif
2323         else if(command == TOSERVER_SIGNTEXT)
2324         {
2325                 /*
2326                         u16 command
2327                         v3s16 blockpos
2328                         s16 id
2329                         u16 textlen
2330                         textdata
2331                 */
2332                 std::string datastring((char*)&data[2], datasize-2);
2333                 std::istringstream is(datastring, std::ios_base::binary);
2334                 u8 buf[6];
2335                 // Read stuff
2336                 is.read((char*)buf, 6);
2337                 v3s16 blockpos = readV3S16(buf);
2338                 is.read((char*)buf, 2);
2339                 s16 id = readS16(buf);
2340                 is.read((char*)buf, 2);
2341                 u16 textlen = readU16(buf);
2342                 std::string text;
2343                 for(u16 i=0; i<textlen; i++)
2344                 {
2345                         is.read((char*)buf, 1);
2346                         text += (char)buf[0];
2347                 }
2348
2349                 MapBlock *block = NULL;
2350                 try
2351                 {
2352                         block = m_env.getMap().getBlockNoCreate(blockpos);
2353                 }
2354                 catch(InvalidPositionException &e)
2355                 {
2356                         derr_server<<"Error while setting sign text: "
2357                                         "block not found"<<std::endl;
2358                         return;
2359                 }
2360
2361                 MapBlockObject *obj = block->getObject(id);
2362                 if(obj == NULL)
2363                 {
2364                         derr_server<<"Error while setting sign text: "
2365                                         "object not found"<<std::endl;
2366                         return;
2367                 }
2368                 
2369                 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2370                 {
2371                         derr_server<<"Error while setting sign text: "
2372                                         "object is not a sign"<<std::endl;
2373                         return;
2374                 }
2375
2376                 ((SignObject*)obj)->setText(text);
2377
2378                 obj->getBlock()->setChangedFlag();
2379         }
2380         else if(command == TOSERVER_INVENTORY_ACTION)
2381         {
2382                 /*// Ignore inventory changes if in creative mode
2383                 if(g_settings.getBool("creative_mode") == true)
2384                 {
2385                         dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2386                                         <<std::endl;
2387                         return;
2388                 }*/
2389                 // Strip command and create a stream
2390                 std::string datastring((char*)&data[2], datasize-2);
2391                 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2392                 std::istringstream is(datastring, std::ios_base::binary);
2393                 // Create an action
2394                 InventoryAction *a = InventoryAction::deSerialize(is);
2395                 if(a != NULL)
2396                 {
2397                         /*
2398                                 Handle craftresult specially if not in creative mode
2399                         */
2400                         bool disable_action = false;
2401                         if(a->getType() == IACTION_MOVE
2402                                         && g_settings.getBool("creative_mode") == false)
2403                         {
2404                                 IMoveAction *ma = (IMoveAction*)a;
2405                                 // Don't allow moving anything to craftresult
2406                                 if(ma->to_name == "craftresult")
2407                                 {
2408                                         // Do nothing
2409                                         disable_action = true;
2410                                 }
2411                                 // When something is removed from craftresult
2412                                 if(ma->from_name == "craftresult")
2413                                 {
2414                                         disable_action = true;
2415                                         // Remove stuff from craft
2416                                         InventoryList *clist = player->inventory.getList("craft");
2417                                         if(clist)
2418                                         {
2419                                                 u16 count = ma->count;
2420                                                 if(count == 0)
2421                                                         count = 1;
2422                                                 clist->decrementMaterials(count);
2423                                         }
2424                                         // Do action
2425                                         // Feed action to player inventory
2426                                         a->apply(&player->inventory);
2427                                         // Eat it
2428                                         delete a;
2429                                         // If something appeared in craftresult, throw it
2430                                         // in the main list
2431                                         InventoryList *rlist = player->inventory.getList("craftresult");
2432                                         InventoryList *mlist = player->inventory.getList("main");
2433                                         if(rlist && mlist && rlist->getUsedSlots() == 1)
2434                                         {
2435                                                 InventoryItem *item1 = rlist->changeItem(0, NULL);
2436                                                 mlist->addItem(item1);
2437                                         }
2438                                 }
2439                         }
2440                         if(disable_action == false)
2441                         {
2442                                 // Feed action to player inventory
2443                                 a->apply(&player->inventory);
2444                                 // Eat it
2445                                 delete a;
2446                         }
2447                         // Send inventory
2448                         SendInventory(player->peer_id);
2449                 }
2450                 else
2451                 {
2452                         dstream<<"TOSERVER_INVENTORY_ACTION: "
2453                                         <<"InventoryAction::deSerialize() returned NULL"
2454                                         <<std::endl;
2455                 }
2456         }
2457         else if(command == TOSERVER_CHAT_MESSAGE)
2458         {
2459                 /*
2460                         u16 command
2461                         u16 length
2462                         wstring message
2463                 */
2464                 u8 buf[6];
2465                 std::string datastring((char*)&data[2], datasize-2);
2466                 std::istringstream is(datastring, std::ios_base::binary);
2467                 
2468                 // Read stuff
2469                 is.read((char*)buf, 2);
2470                 u16 len = readU16(buf);
2471                 
2472                 std::wstring message;
2473                 for(u16 i=0; i<len; i++)
2474                 {
2475                         is.read((char*)buf, 2);
2476                         message += (wchar_t)readU16(buf);
2477                 }
2478
2479                 // Get player name of this client
2480                 std::wstring name = narrow_to_wide(player->getName());
2481
2482                 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2483                 
2484                 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2485
2486                 /*
2487                         Send the message to all other clients
2488                 */
2489                 for(core::map<u16, RemoteClient*>::Iterator
2490                         i = m_clients.getIterator();
2491                         i.atEnd() == false; i++)
2492                 {
2493                         // Get client and check that it is valid
2494                         RemoteClient *client = i.getNode()->getValue();
2495                         assert(client->peer_id == i.getNode()->getKey());
2496                         if(client->serialization_version == SER_FMT_VER_INVALID)
2497                                 continue;
2498
2499                         // Don't send if it's the same one
2500                         if(peer_id == client->peer_id)
2501                                 continue;
2502
2503                         SendChatMessage(client->peer_id, line);
2504                 }
2505         }
2506         else
2507         {
2508                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2509                                 "unknown command "<<command<<std::endl;
2510         }
2511         
2512         } //try
2513         catch(SendFailedException &e)
2514         {
2515                 derr_server<<"Server::ProcessData(): SendFailedException: "
2516                                 <<"what="<<e.what()
2517                                 <<std::endl;
2518         }
2519 }
2520
2521 /*void Server::Send(u16 peer_id, u16 channelnum,
2522                 SharedBuffer<u8> data, bool reliable)
2523 {
2524         JMutexAutoLock lock(m_con_mutex);
2525         m_con.Send(peer_id, channelnum, data, reliable);
2526 }*/
2527
2528 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2529 {
2530         DSTACK(__FUNCTION_NAME);
2531         /*
2532                 Create a packet with the block in the right format
2533         */
2534         
2535         std::ostringstream os(std::ios_base::binary);
2536         block->serialize(os, ver);
2537         std::string s = os.str();
2538         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2539
2540         u32 replysize = 8 + blockdata.getSize();
2541         SharedBuffer<u8> reply(replysize);
2542         v3s16 p = block->getPos();
2543         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2544         writeS16(&reply[2], p.X);
2545         writeS16(&reply[4], p.Y);
2546         writeS16(&reply[6], p.Z);
2547         memcpy(&reply[8], *blockdata, blockdata.getSize());
2548
2549         /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2550                         <<":  \tpacket size: "<<replysize<<std::endl;*/
2551         
2552         /*
2553                 Send packet
2554         */
2555         m_con.Send(peer_id, 1, reply, true);
2556 }
2557
2558 core::list<PlayerInfo> Server::getPlayerInfo()
2559 {
2560         DSTACK(__FUNCTION_NAME);
2561         JMutexAutoLock envlock(m_env_mutex);
2562         JMutexAutoLock conlock(m_con_mutex);
2563         
2564         core::list<PlayerInfo> list;
2565
2566         core::list<Player*> players = m_env.getPlayers();
2567         
2568         core::list<Player*>::Iterator i;
2569         for(i = players.begin();
2570                         i != players.end(); i++)
2571         {
2572                 PlayerInfo info;
2573
2574                 Player *player = *i;
2575
2576                 try{
2577                         con::Peer *peer = m_con.GetPeer(player->peer_id);
2578                         // Copy info from peer to info struct
2579                         info.id = peer->id;
2580                         info.address = peer->address;
2581                         info.avg_rtt = peer->avg_rtt;
2582                 }
2583                 catch(con::PeerNotFoundException &e)
2584                 {
2585                         // Set dummy peer info
2586                         info.id = 0;
2587                         info.address = Address(0,0,0,0,0);
2588                         info.avg_rtt = 0.0;
2589                 }
2590
2591                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2592                 info.position = player->getPosition();
2593
2594                 list.push_back(info);
2595         }
2596
2597         return list;
2598 }
2599
2600 void Server::peerAdded(con::Peer *peer)
2601 {
2602         DSTACK(__FUNCTION_NAME);
2603         dout_server<<"Server::peerAdded(): peer->id="
2604                         <<peer->id<<std::endl;
2605         
2606         PeerChange c;
2607         c.type = PEER_ADDED;
2608         c.peer_id = peer->id;
2609         c.timeout = false;
2610         m_peer_change_queue.push_back(c);
2611 }
2612
2613 void Server::deletingPeer(con::Peer *peer, bool timeout)
2614 {
2615         DSTACK(__FUNCTION_NAME);
2616         dout_server<<"Server::deletingPeer(): peer->id="
2617                         <<peer->id<<", timeout="<<timeout<<std::endl;
2618         
2619         PeerChange c;
2620         c.type = PEER_REMOVED;
2621         c.peer_id = peer->id;
2622         c.timeout = timeout;
2623         m_peer_change_queue.push_back(c);
2624 }
2625
2626 void Server::SendObjectData(float dtime)
2627 {
2628         DSTACK(__FUNCTION_NAME);
2629
2630         core::map<v3s16, bool> stepped_blocks;
2631         
2632         for(core::map<u16, RemoteClient*>::Iterator
2633                 i = m_clients.getIterator();
2634                 i.atEnd() == false; i++)
2635         {
2636                 u16 peer_id = i.getNode()->getKey();
2637                 RemoteClient *client = i.getNode()->getValue();
2638                 assert(client->peer_id == peer_id);
2639                 
2640                 if(client->serialization_version == SER_FMT_VER_INVALID)
2641                         continue;
2642                 
2643                 client->SendObjectData(this, dtime, stepped_blocks);
2644         }
2645 }
2646
2647 void Server::SendPlayerInfos()
2648 {
2649         DSTACK(__FUNCTION_NAME);
2650
2651         //JMutexAutoLock envlock(m_env_mutex);
2652         
2653         // Get connected players
2654         core::list<Player*> players = m_env.getPlayers(true);
2655         
2656         u32 player_count = players.getSize();
2657         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2658
2659         SharedBuffer<u8> data(datasize);
2660         writeU16(&data[0], TOCLIENT_PLAYERINFO);
2661         
2662         u32 start = 2;
2663         core::list<Player*>::Iterator i;
2664         for(i = players.begin();
2665                         i != players.end(); i++)
2666         {
2667                 Player *player = *i;
2668
2669                 /*dstream<<"Server sending player info for player with "
2670                                 "peer_id="<<player->peer_id<<std::endl;*/
2671                 
2672                 writeU16(&data[start], player->peer_id);
2673                 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2674                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2675                 start += 2+PLAYERNAME_SIZE;
2676         }
2677
2678         //JMutexAutoLock conlock(m_con_mutex);
2679
2680         // Send as reliable
2681         m_con.SendToAll(0, data, true);
2682 }
2683
2684 enum ItemSpecType
2685 {
2686         ITEM_NONE,
2687         ITEM_MATERIAL,
2688         ITEM_CRAFT,
2689         ITEM_TOOL,
2690         ITEM_MBO
2691 };
2692
2693 struct ItemSpec
2694 {
2695         ItemSpec():
2696                 type(ITEM_NONE)
2697         {
2698         }
2699         ItemSpec(enum ItemSpecType a_type, std::string a_name):
2700                 type(a_type),
2701                 name(a_name),
2702                 num(65535)
2703         {
2704         }
2705         ItemSpec(enum ItemSpecType a_type, u16 a_num):
2706                 type(a_type),
2707                 name(""),
2708                 num(a_num)
2709         {
2710         }
2711         enum ItemSpecType type;
2712         // Only other one of these is used
2713         std::string name;
2714         u16 num;
2715 };
2716
2717 /*
2718         items: a pointer to an array of 9 pointers to items
2719         specs: a pointer to an array of 9 ItemSpecs
2720 */
2721 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2722 {
2723         u16 items_min_x = 100;
2724         u16 items_max_x = 100;
2725         u16 items_min_y = 100;
2726         u16 items_max_y = 100;
2727         for(u16 y=0; y<3; y++)
2728         for(u16 x=0; x<3; x++)
2729         {
2730                 if(items[y*3 + x] == NULL)
2731                         continue;
2732                 if(items_min_x == 100 || x < items_min_x)
2733                         items_min_x = x;
2734                 if(items_min_y == 100 || y < items_min_y)
2735                         items_min_y = y;
2736                 if(items_max_x == 100 || x > items_max_x)
2737                         items_max_x = x;
2738                 if(items_max_y == 100 || y > items_max_y)
2739                         items_max_y = y;
2740         }
2741         // No items at all, just return false
2742         if(items_min_x == 100)
2743                 return false;
2744         
2745         u16 items_w = items_max_x - items_min_x + 1;
2746         u16 items_h = items_max_y - items_min_y + 1;
2747
2748         u16 specs_min_x = 100;
2749         u16 specs_max_x = 100;
2750         u16 specs_min_y = 100;
2751         u16 specs_max_y = 100;
2752         for(u16 y=0; y<3; y++)
2753         for(u16 x=0; x<3; x++)
2754         {
2755                 if(specs[y*3 + x].type == ITEM_NONE)
2756                         continue;
2757                 if(specs_min_x == 100 || x < specs_min_x)
2758                         specs_min_x = x;
2759                 if(specs_min_y == 100 || y < specs_min_y)
2760                         specs_min_y = y;
2761                 if(specs_max_x == 100 || x > specs_max_x)
2762                         specs_max_x = x;
2763                 if(specs_max_y == 100 || y > specs_max_y)
2764                         specs_max_y = y;
2765         }
2766         // No specs at all, just return false
2767         if(specs_min_x == 100)
2768                 return false;
2769
2770         u16 specs_w = specs_max_x - specs_min_x + 1;
2771         u16 specs_h = specs_max_y - specs_min_y + 1;
2772
2773         // Different sizes
2774         if(items_w != specs_w || items_h != specs_h)
2775                 return false;
2776
2777         for(u16 y=0; y<specs_h; y++)
2778         for(u16 x=0; x<specs_w; x++)
2779         {
2780                 u16 items_x = items_min_x + x;
2781                 u16 items_y = items_min_y + y;
2782                 u16 specs_x = specs_min_x + x;
2783                 u16 specs_y = specs_min_y + y;
2784                 InventoryItem *item = items[items_y * 3 + items_x];
2785                 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2786                 
2787                 if(spec.type == ITEM_NONE)
2788                 {
2789                         // Has to be no item
2790                         if(item != NULL)
2791                                 return false;
2792                         continue;
2793                 }
2794                 
2795                 // There should be an item
2796                 if(item == NULL)
2797                         return false;
2798
2799                 std::string itemname = item->getName();
2800
2801                 if(spec.type == ITEM_MATERIAL)
2802                 {
2803                         if(itemname != "MaterialItem")
2804                                 return false;
2805                         MaterialItem *mitem = (MaterialItem*)item;
2806                         if(mitem->getMaterial() != spec.num)
2807                                 return false;
2808                 }
2809                 else if(spec.type == ITEM_CRAFT)
2810                 {
2811                         if(itemname != "CraftItem")
2812                                 return false;
2813                         CraftItem *mitem = (CraftItem*)item;
2814                         if(mitem->getSubName() != spec.name)
2815                                 return false;
2816                 }
2817                 else if(spec.type == ITEM_TOOL)
2818                 {
2819                         // Not supported yet
2820                         assert(0);
2821                 }
2822                 else if(spec.type == ITEM_MBO)
2823                 {
2824                         // Not supported yet
2825                         assert(0);
2826                 }
2827                 else
2828                 {
2829                         // Not supported yet
2830                         assert(0);
2831                 }
2832         }
2833
2834         return true;
2835 }
2836
2837 void Server::SendInventory(u16 peer_id)
2838 {
2839         DSTACK(__FUNCTION_NAME);
2840         
2841         Player* player = m_env.getPlayer(peer_id);
2842
2843         /*
2844                 Calculate crafting stuff
2845         */
2846         if(g_settings.getBool("creative_mode") == false)
2847         {
2848                 InventoryList *clist = player->inventory.getList("craft");
2849                 InventoryList *rlist = player->inventory.getList("craftresult");
2850                 if(rlist)
2851                 {
2852                         rlist->clearItems();
2853                 }
2854                 if(clist && rlist)
2855                 {
2856                         InventoryItem *items[9];
2857                         for(u16 i=0; i<9; i++)
2858                         {
2859                                 items[i] = clist->getItem(i);
2860                         }
2861                         
2862                         bool found = false;
2863
2864                         // Wood
2865                         if(!found)
2866                         {
2867                                 ItemSpec specs[9];
2868                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2869                                 if(checkItemCombination(items, specs))
2870                                 {
2871                                         rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2872                                         found = true;
2873                                 }
2874                         }
2875
2876                         // Stick
2877                         if(!found)
2878                         {
2879                                 ItemSpec specs[9];
2880                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2881                                 if(checkItemCombination(items, specs))
2882                                 {
2883                                         rlist->addItem(new CraftItem("Stick", 4));
2884                                         found = true;
2885                                 }
2886                         }
2887
2888                         // Sign
2889                         if(!found)
2890                         {
2891                                 ItemSpec specs[9];
2892                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2893                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2894                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2895                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2896                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2897                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2898                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2899                                 if(checkItemCombination(items, specs))
2900                                 {
2901                                         rlist->addItem(new MapBlockObjectItem("Sign"));
2902                                         found = true;
2903                                 }
2904                         }
2905
2906                         // Torch
2907                         if(!found)
2908                         {
2909                                 ItemSpec specs[9];
2910                                 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2911                                 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2912                                 if(checkItemCombination(items, specs))
2913                                 {
2914                                         rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2915                                         found = true;
2916                                 }
2917                         }
2918
2919                         // Wooden pick
2920                         if(!found)
2921                         {
2922                                 ItemSpec specs[9];
2923                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2924                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2925                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2926                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2927                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2928                                 if(checkItemCombination(items, specs))
2929                                 {
2930                                         rlist->addItem(new ToolItem("WPick", 0));
2931                                         found = true;
2932                                 }
2933                         }
2934
2935                         // Stone pick
2936                         if(!found)
2937                         {
2938                                 ItemSpec specs[9];
2939                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2940                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2941                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2942                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2943                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2944                                 if(checkItemCombination(items, specs))
2945                                 {
2946                                         rlist->addItem(new ToolItem("STPick", 0));
2947                                         found = true;
2948                                 }
2949                         }
2950
2951                         // Mese pick
2952                         if(!found)
2953                         {
2954                                 ItemSpec specs[9];
2955                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2956                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2957                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2958                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2959                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2960                                 if(checkItemCombination(items, specs))
2961                                 {
2962                                         rlist->addItem(new ToolItem("MesePick", 0));
2963                                         found = true;
2964                                 }
2965                         }
2966                 }
2967         } // if creative_mode == false
2968
2969         /*
2970                 Serialize it
2971         */
2972
2973         std::ostringstream os;
2974         //os.imbue(std::locale("C"));
2975
2976         player->inventory.serialize(os);
2977
2978         std::string s = os.str();
2979         
2980         SharedBuffer<u8> data(s.size()+2);
2981         writeU16(&data[0], TOCLIENT_INVENTORY);
2982         memcpy(&data[2], s.c_str(), s.size());
2983         
2984         // Send as reliable
2985         m_con.Send(peer_id, 0, data, true);
2986 }
2987
2988 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2989 {
2990         DSTACK(__FUNCTION_NAME);
2991         
2992         std::ostringstream os(std::ios_base::binary);
2993         u8 buf[12];
2994         
2995         // Write command
2996         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2997         os.write((char*)buf, 2);
2998         
2999         // Write length
3000         writeU16(buf, message.size());
3001         os.write((char*)buf, 2);
3002         
3003         // Write string
3004         for(u32 i=0; i<message.size(); i++)
3005         {
3006                 u16 w = message[i];
3007                 writeU16(buf, w);
3008                 os.write((char*)buf, 2);
3009         }
3010         
3011         // Make data buffer
3012         std::string s = os.str();
3013         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3014         // Send as reliable
3015         m_con.Send(peer_id, 0, data, true);
3016 }
3017
3018 void Server::BroadcastChatMessage(const std::wstring &message)
3019 {
3020         for(core::map<u16, RemoteClient*>::Iterator
3021                 i = m_clients.getIterator();
3022                 i.atEnd() == false; i++)
3023         {
3024                 // Get client and check that it is valid
3025                 RemoteClient *client = i.getNode()->getValue();
3026                 assert(client->peer_id == i.getNode()->getKey());
3027                 if(client->serialization_version == SER_FMT_VER_INVALID)
3028                         continue;
3029
3030                 SendChatMessage(client->peer_id, message);
3031         }
3032 }
3033
3034 void Server::SendBlocks(float dtime)
3035 {
3036         DSTACK(__FUNCTION_NAME);
3037
3038         JMutexAutoLock envlock(m_env_mutex);
3039
3040         core::array<PrioritySortedBlockTransfer> queue;
3041
3042         s32 total_sending = 0;
3043
3044         for(core::map<u16, RemoteClient*>::Iterator
3045                 i = m_clients.getIterator();
3046                 i.atEnd() == false; i++)
3047         {
3048                 RemoteClient *client = i.getNode()->getValue();
3049                 assert(client->peer_id == i.getNode()->getKey());
3050
3051                 total_sending += client->SendingCount();
3052                 
3053                 if(client->serialization_version == SER_FMT_VER_INVALID)
3054                         continue;
3055                 
3056                 client->GetNextBlocks(this, dtime, queue);
3057         }
3058
3059         // Sort.
3060         // Lowest priority number comes first.
3061         // Lowest is most important.
3062         queue.sort();
3063
3064         JMutexAutoLock conlock(m_con_mutex);
3065
3066         for(u32 i=0; i<queue.size(); i++)
3067         {
3068                 //TODO: Calculate limit dynamically
3069                 if(total_sending >= g_settings.getS32
3070                                 ("max_simultaneous_block_sends_server_total"))
3071                         break;
3072                 
3073                 PrioritySortedBlockTransfer q = queue[i];
3074
3075                 MapBlock *block = NULL;
3076                 try
3077                 {
3078                         block = m_env.getMap().getBlockNoCreate(q.pos);
3079                 }
3080                 catch(InvalidPositionException &e)
3081                 {
3082                         continue;
3083                 }
3084
3085                 RemoteClient *client = getClient(q.peer_id);
3086
3087                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3088
3089                 client->SentBlock(q.pos);
3090
3091                 total_sending++;
3092         }
3093 }
3094
3095
3096 RemoteClient* Server::getClient(u16 peer_id)
3097 {
3098         DSTACK(__FUNCTION_NAME);
3099         //JMutexAutoLock lock(m_con_mutex);
3100         core::map<u16, RemoteClient*>::Node *n;
3101         n = m_clients.find(peer_id);
3102         // A client should exist for all peers
3103         assert(n != NULL);
3104         return n->getValue();
3105 }
3106
3107 void setCreativeInventory(Player *player)
3108 {
3109         player->resetInventory();
3110         
3111         // Give some good picks
3112         {
3113                 InventoryItem *item = new ToolItem("STPick", 0);
3114                 void* r = player->inventory.addItem("main", item);
3115                 assert(r == NULL);
3116         }
3117         {
3118                 InventoryItem *item = new ToolItem("MesePick", 0);
3119                 void* r = player->inventory.addItem("main", item);
3120                 assert(r == NULL);
3121         }
3122
3123         /*
3124                 Give materials
3125         */
3126         assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3127         
3128         // add torch first
3129         InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3130         player->inventory.addItem("main", item);
3131         
3132         // Then others
3133         for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3134         {
3135                 // Skip some materials
3136                 if(i == CONTENT_WATER || i == CONTENT_TORCH
3137                         || i == CONTENT_COALSTONE)
3138                         continue;
3139
3140                 InventoryItem *item = new MaterialItem(i, 1);
3141                 player->inventory.addItem("main", item);
3142         }
3143         // Sign
3144         {
3145                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3146                 void* r = player->inventory.addItem("main", item);
3147                 assert(r == NULL);
3148         }
3149 }
3150
3151 Player *Server::emergePlayer(const char *name, const char *password,
3152                 u16 peer_id)
3153 {
3154         /*
3155                 Try to get an existing player
3156         */
3157         Player *player = m_env.getPlayer(name);
3158         if(player != NULL)
3159         {
3160                 // If player is already connected, cancel
3161                 if(player->peer_id != 0)
3162                 {
3163                         dstream<<"emergePlayer(): Player already connected"<<std::endl;
3164                         return NULL;
3165                 }
3166
3167                 // Got one.
3168                 player->peer_id = peer_id;
3169                 
3170                 // Reset inventory to creative if in creative mode
3171                 if(g_settings.getBool("creative_mode"))
3172                 {
3173                         setCreativeInventory(player);
3174                 }
3175
3176                 return player;
3177         }
3178
3179         /*
3180                 If player with the wanted peer_id already exists, cancel.
3181         */
3182         if(m_env.getPlayer(peer_id) != NULL)
3183         {
3184                 dstream<<"emergePlayer(): Player with wrong name but same"
3185                                 " peer_id already exists"<<std::endl;
3186                 return NULL;
3187         }
3188         
3189         /*
3190                 Create a new player
3191         */
3192         {
3193                 player = new ServerRemotePlayer();
3194                 //player->peer_id = c.peer_id;
3195                 //player->peer_id = PEER_ID_INEXISTENT;
3196                 player->peer_id = peer_id;
3197                 player->updateName(name);
3198
3199                 /*
3200                         Set player position
3201                 */
3202                 
3203                 dstream<<"Server: Finding spawn place for player \""
3204                                 <<player->getName()<<"\""<<std::endl;
3205
3206                 v2s16 nodepos;
3207 #if 1
3208                 player->setPosition(intToFloat(v3s16(
3209                                 0,
3210                                 45, //64,
3211                                 0
3212                 )));
3213 #endif
3214 #if 0
3215                 f32 groundheight = 0;
3216 #if 0
3217                 // Try to find a good place a few times
3218                 for(s32 i=0; i<500; i++)
3219                 {
3220                         s32 range = 1 + i;
3221                         // We're going to try to throw the player to this position
3222                         nodepos = v2s16(-range + (myrand()%(range*2)),
3223                                         -range + (myrand()%(range*2)));
3224                         v2s16 sectorpos = getNodeSectorPos(nodepos);
3225                         // Get sector
3226                         m_env.getMap().emergeSector(sectorpos);
3227                         // Get ground height at point
3228                         groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3229                         // The sector should have been generated -> groundheight exists
3230                         assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3231                         // Don't go underwater
3232                         if(groundheight < WATER_LEVEL)
3233                         {
3234                                 //dstream<<"-> Underwater"<<std::endl;
3235                                 continue;
3236                         }
3237 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3238                         // Get block at point
3239                         v3s16 nodepos3d;
3240                         nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3241                         v3s16 blockpos = getNodeBlockPos(nodepos3d);
3242                         ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3243                         // Don't go inside ground
3244                         try{
3245                                 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3246                                 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3247                                 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3248                                 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3249                                 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3250                                         || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3251                                 {
3252                                         dstream<<"-> Inside ground"<<std::endl;
3253                                         // In ground
3254                                         continue;
3255                                 }
3256                         }catch(InvalidPositionException &e)
3257                         {
3258                                 dstream<<"-> Invalid position"<<std::endl;
3259                                 // Ignore invalid position
3260                                 continue;
3261                         }
3262 #endif
3263                         // Found a good place
3264                         dstream<<"Searched through "<<i<<" places."<<std::endl;
3265                         break;
3266                 }
3267 #endif
3268                 
3269                 // If no suitable place was not found, go above water at least.
3270                 if(groundheight < WATER_LEVEL)
3271                         groundheight = WATER_LEVEL;
3272
3273                 player->setPosition(intToFloat(v3s16(
3274                                 nodepos.X,
3275                                 //groundheight + 1,
3276                                 groundheight + 15,
3277                                 nodepos.Y
3278                 )));
3279 #endif
3280
3281                 /*
3282                         Add player to environment
3283                 */
3284
3285                 m_env.addPlayer(player);
3286
3287                 /*
3288                         Add stuff to inventory
3289                 */
3290                 
3291                 if(g_settings.getBool("creative_mode"))
3292                 {
3293                         setCreativeInventory(player);
3294                 }
3295                 else
3296                 {
3297                         /*{
3298                                 InventoryItem *item = new ToolItem("WPick", 32000);
3299                                 void* r = player->inventory.addItem("main", item);
3300                                 assert(r == NULL);
3301                         }*/
3302                         /*{
3303                                 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3304                                 void* r = player->inventory.addItem("main", item);
3305                                 assert(r == NULL);
3306                         }
3307                         {
3308                                 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3309                                 void* r = player->inventory.addItem("main", item);
3310                                 assert(r == NULL);
3311                         }
3312                         {
3313                                 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3314                                 void* r = player->inventory.addItem("main", item);
3315                                 assert(r == NULL);
3316                         }
3317                         {
3318                                 InventoryItem *item = new CraftItem("Stick", 4);
3319                                 void* r = player->inventory.addItem("main", item);
3320                                 assert(r == NULL);
3321                         }
3322                         {
3323                                 InventoryItem *item = new ToolItem("WPick", 32000);
3324                                 void* r = player->inventory.addItem("main", item);
3325                                 assert(r == NULL);
3326                         }
3327                         {
3328                                 InventoryItem *item = new ToolItem("STPick", 32000);
3329                                 void* r = player->inventory.addItem("main", item);
3330                                 assert(r == NULL);
3331                         }*/
3332                         /*// Give some lights
3333                         {
3334                                 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3335                                 bool r = player->inventory.addItem("main", item);
3336                                 assert(r == true);
3337                         }
3338                         // and some signs
3339                         for(u16 i=0; i<4; i++)
3340                         {
3341                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3342                                 bool r = player->inventory.addItem("main", item);
3343                                 assert(r == true);
3344                         }*/
3345                         /*// Give some other stuff
3346                         {
3347                                 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3348                                 bool r = player->inventory.addItem("main", item);
3349                                 assert(r == true);
3350                         }*/
3351                 }
3352
3353                 return player;
3354                 
3355         } // create new player
3356 }
3357
3358 #if 0
3359 void Server::UpdateBlockWaterPressure(MapBlock *block,
3360                         core::map<v3s16, MapBlock*> &modified_blocks)
3361 {
3362         MapVoxelManipulator v(&m_env.getMap());
3363         v.m_disable_water_climb =
3364                         g_settings.getBool("disable_water_climb");
3365         
3366         VoxelArea area(block->getPosRelative(),
3367                         block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3368
3369         try
3370         {
3371                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3372         }
3373         catch(ProcessingLimitException &e)
3374         {
3375                 dstream<<"Processing limit reached (1)"<<std::endl;
3376         }
3377         
3378         v.blitBack(modified_blocks);
3379 }
3380 #endif
3381
3382 void Server::handlePeerChange(PeerChange &c)
3383 {
3384         JMutexAutoLock envlock(m_env_mutex);
3385         JMutexAutoLock conlock(m_con_mutex);
3386         
3387         if(c.type == PEER_ADDED)
3388         {
3389                 /*
3390                         Add
3391                 */
3392
3393                 // Error check
3394                 core::map<u16, RemoteClient*>::Node *n;
3395                 n = m_clients.find(c.peer_id);
3396                 // The client shouldn't already exist
3397                 assert(n == NULL);
3398
3399                 // Create client
3400                 RemoteClient *client = new RemoteClient();
3401                 client->peer_id = c.peer_id;
3402                 m_clients.insert(client->peer_id, client);
3403
3404         } // PEER_ADDED
3405         else if(c.type == PEER_REMOVED)
3406         {
3407                 /*
3408                         Delete
3409                 */
3410
3411                 // Error check
3412                 core::map<u16, RemoteClient*>::Node *n;
3413                 n = m_clients.find(c.peer_id);
3414                 // The client should exist
3415                 assert(n != NULL);
3416                 
3417                 // Collect information about leaving in chat
3418                 std::wstring message;
3419                 {
3420                         std::wstring name = L"unknown";
3421                         Player *player = m_env.getPlayer(c.peer_id);
3422                         if(player != NULL)
3423                                 name = narrow_to_wide(player->getName());
3424                         
3425                         message += L"*** ";
3426                         message += name;
3427                         message += L" left game";
3428                         if(c.timeout)
3429                                 message += L" (timed out)";
3430                 }
3431
3432                 /*// Delete player
3433                 {
3434                         m_env.removePlayer(c.peer_id);
3435                 }*/
3436
3437                 // Set player client disconnected
3438                 {
3439                         Player *player = m_env.getPlayer(c.peer_id);
3440                         if(player != NULL)
3441                                 player->peer_id = 0;
3442                 }
3443                 
3444                 // Delete client
3445                 delete m_clients[c.peer_id];
3446                 m_clients.remove(c.peer_id);
3447
3448                 // Send player info to all remaining clients
3449                 SendPlayerInfos();
3450                 
3451                 // Send leave chat message to all remaining clients
3452                 BroadcastChatMessage(message);
3453                 
3454         } // PEER_REMOVED
3455         else
3456         {
3457                 assert(0);
3458         }
3459 }
3460
3461 void Server::handlePeerChanges()
3462 {
3463         while(m_peer_change_queue.size() > 0)
3464         {
3465                 PeerChange c = m_peer_change_queue.pop_front();
3466
3467                 dout_server<<"Server: Handling peer change: "
3468                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3469                                 <<std::endl;
3470
3471                 handlePeerChange(c);
3472         }
3473 }
3474
3475 void dedicated_server_loop(Server &server)
3476 {
3477         DSTACK(__FUNCTION_NAME);
3478         
3479         std::cout<<std::endl;
3480         std::cout<<"========================"<<std::endl;
3481         std::cout<<"Running dedicated server"<<std::endl;
3482         std::cout<<"========================"<<std::endl;
3483         std::cout<<std::endl;
3484
3485         for(;;)
3486         {
3487                 // This is kind of a hack but can be done like this
3488                 // because server.step() is very light
3489                 sleep_ms(30);
3490                 server.step(0.030);
3491
3492                 static int counter = 0;
3493                 counter--;
3494                 if(counter <= 0)
3495                 {
3496                         counter = 10;
3497
3498                         core::list<PlayerInfo> list = server.getPlayerInfo();
3499                         core::list<PlayerInfo>::Iterator i;
3500                         static u32 sum_old = 0;
3501                         u32 sum = PIChecksum(list);
3502                         if(sum != sum_old)
3503                         {
3504                                 std::cout<<DTIME<<"Player info:"<<std::endl;
3505                                 for(i=list.begin(); i!=list.end(); i++)
3506                                 {
3507                                         i->PrintLine(&std::cout);
3508                                 }
3509                         }
3510                         sum_old = sum;
3511                 }
3512         }
3513 }
3514
3515