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