]> git.lizzy.rs Git - dragonfireclient.git/blob - src/server.cpp
.
[dragonfireclient.git] / src / server.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /*
21 (c) 2010 Perttu Ahola <celeron55@gmail.com>
22 */
23
24 #include "server.h"
25 #include "utility.h"
26 #include <iostream>
27 #include "clientserver.h"
28 #include "map.h"
29 #include "jmutexautolock.h"
30 #include "main.h"
31 #include "constants.h"
32 #include "voxel.h"
33 #include "materials.h"
34
35 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
36
37 void * ServerThread::Thread()
38 {
39         ThreadStarted();
40
41         DSTACK(__FUNCTION_NAME);
42
43         BEGIN_DEBUG_EXCEPTION_HANDLER
44
45         while(getRun())
46         {
47                 try{
48                         m_server->AsyncRunStep();
49                 
50                         //dout_server<<"Running m_server->Receive()"<<std::endl;
51                         m_server->Receive();
52                 }
53                 catch(con::NoIncomingDataException &e)
54                 {
55                 }
56         }
57         
58         END_DEBUG_EXCEPTION_HANDLER
59
60         return NULL;
61 }
62
63 void * EmergeThread::Thread()
64 {
65         ThreadStarted();
66
67         DSTACK(__FUNCTION_NAME);
68
69         bool debug=false;
70         
71         BEGIN_DEBUG_EXCEPTION_HANDLER
72
73         /*
74                 Get block info from queue, emerge them and send them
75                 to clients.
76
77                 After queue is empty, exit.
78         */
79         while(getRun())
80         {
81                 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
82                 if(qptr == NULL)
83                         break;
84                 
85                 SharedPtr<QueuedBlockEmerge> q(qptr);
86
87                 v3s16 &p = q->pos;
88                 
89                 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
90
91                 //TimeTaker timer("block emerge");
92                 
93                 /*
94                         Try to emerge it from somewhere.
95
96                         If it is only wanted as optional, only loading from disk
97                         will be allowed.
98                 */
99                 
100                 /*
101                         Check if any peer wants it as non-optional. In that case it
102                         will be generated.
103
104                         Also decrement the emerge queue count in clients.
105                 */
106
107                 bool optional = true;
108
109                 {
110                         core::map<u16, u8>::Iterator i;
111                         for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
112                         {
113                                 //u16 peer_id = i.getNode()->getKey();
114
115                                 // Check flags
116                                 u8 flags = i.getNode()->getValue();
117                                 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
118                                         optional = false;
119                                 
120                         }
121                 }
122
123                 /*dstream<<"EmergeThread: p="
124                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
125                                 <<"optional="<<optional<<std::endl;*/
126                 
127                 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
128                         
129                 core::map<v3s16, MapBlock*> changed_blocks;
130                 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
131
132                 MapBlock *block = NULL;
133                 bool got_block = true;
134                 core::map<v3s16, MapBlock*> modified_blocks;
135                 
136                 {//envlock
137
138                 //TimeTaker envlockwaittimer("block emerge envlock wait time");
139                 
140                 // 0-50ms
141                 JMutexAutoLock envlock(m_server->m_env_mutex);
142
143                 //envlockwaittimer.stop();
144
145                 //TimeTaker timer("block emerge (while env locked)");
146                         
147                 try{
148                         bool only_from_disk = false;
149                         
150                         if(optional)
151                                 only_from_disk = true;
152                         
153                         // First check if the block already exists
154                         if(only_from_disk)
155                         {
156                                 block = map.getBlockNoCreate(p);
157                         }
158
159                         if(block == NULL)
160                         {
161                                 block = map.emergeBlock(
162                                                 p,
163                                                 only_from_disk,
164                                                 changed_blocks,
165                                                 lighting_invalidated_blocks);
166
167 #if 0
168                                 /*
169                                         EXPERIMENTAL: Create a few other blocks too
170                                 */
171                                 
172                                 map.emergeBlock(
173                                                 p + v3s16(0,1,0),
174                                                 only_from_disk,
175                                                 changed_blocks,
176                                                 lighting_invalidated_blocks);
177
178                                 map.emergeBlock(
179                                                 p + v3s16(0,-1,0),
180                                                 only_from_disk,
181                                                 changed_blocks,
182                                                 lighting_invalidated_blocks);
183 #endif
184                         }
185
186                         // If it is a dummy, block was not found on disk
187                         if(block->isDummy())
188                         {
189                                 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
190                                 got_block = false;
191                         }
192                 }
193                 catch(InvalidPositionException &e)
194                 {
195                         // Block not found.
196                         // This happens when position is over limit.
197                         got_block = false;
198                 }
199                 
200                 if(got_block)
201                 {
202                         if(debug && changed_blocks.size() > 0)
203                         {
204                                 dout_server<<DTIME<<"Got changed_blocks: ";
205                                 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
206                                                 i.atEnd() == false; i++)
207                                 {
208                                         MapBlock *block = i.getNode()->getValue();
209                                         v3s16 p = block->getPos();
210                                         dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
211                                 }
212                                 dout_server<<std::endl;
213                         }
214
215 #if 0
216                         /*
217                                 Update water pressure
218                         */
219
220                         m_server->UpdateBlockWaterPressure(block, modified_blocks);
221
222                         for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
223                                         i.atEnd() == false; i++)
224                         {
225                                 MapBlock *block = i.getNode()->getValue();
226                                 m_server->UpdateBlockWaterPressure(block, modified_blocks);
227                                 //v3s16 p = i.getNode()->getKey();
228                                 //m_server->UpdateBlockWaterPressure(p, modified_blocks);
229                         }
230 #endif
231
232                         /*
233                                 Collect a list of blocks that have been modified in
234                                 addition to the fetched one.
235                         */
236
237                         // Add all the "changed blocks" to modified_blocks
238                         for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
239                                         i.atEnd() == false; i++)
240                         {
241                                 MapBlock *block = i.getNode()->getValue();
242                                 modified_blocks.insert(block->getPos(), block);
243                         }
244                         
245                         /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
246                                         <<" blocks"<<std::endl;*/
247                         
248                         //TimeTaker timer("** updateLighting", g_device);
249                         
250                         // Update lighting without locking the environment mutex,
251                         // add modified blocks to changed blocks
252                         map.updateLighting(lighting_invalidated_blocks, modified_blocks);
253                 }
254                 // If we got no block, there should be no invalidated blocks
255                 else
256                 {
257                         assert(lighting_invalidated_blocks.size() == 0);
258                 }
259
260                 }//envlock
261
262                 /*
263                         Set sent status of modified blocks on clients
264                 */
265         
266                 // NOTE: Server's clients are also behind the connection mutex
267                 JMutexAutoLock lock(m_server->m_con_mutex);
268
269                 /*
270                         Add the originally fetched block to the modified list
271                 */
272                 if(got_block)
273                 {
274                         modified_blocks.insert(p, block);
275                 }
276                 
277                 /*
278                         Set the modified blocks unsent for all the clients
279                 */
280                 
281                 for(core::map<u16, RemoteClient*>::Iterator
282                                 i = m_server->m_clients.getIterator();
283                                 i.atEnd() == false; i++)
284                 {
285                         RemoteClient *client = i.getNode()->getValue();
286                         
287                         if(modified_blocks.size() > 0)
288                         {
289                                 // Remove block from sent history
290                                 client->SetBlocksNotSent(modified_blocks);
291                         }
292                 }
293                 
294         }
295
296         END_DEBUG_EXCEPTION_HANDLER
297
298         return NULL;
299 }
300
301 void RemoteClient::GetNextBlocks(Server *server, float dtime,
302                 core::array<PrioritySortedBlockTransfer> &dest)
303 {
304         DSTACK(__FUNCTION_NAME);
305         
306         // Increment timers
307         {
308                 JMutexAutoLock lock(m_blocks_sent_mutex);
309                 m_nearest_unsent_reset_timer += dtime;
310         }
311
312         // Won't send anything if already sending
313         {
314                 JMutexAutoLock lock(m_blocks_sending_mutex);
315                 
316                 if(m_blocks_sending.size() >= g_settings.getU16
317                                 ("max_simultaneous_block_sends_per_client"))
318                 {
319                         //dstream<<"Not sending any blocks, Queue full."<<std::endl;
320                         return;
321                 }
322         }
323
324         bool haxmode = g_settings.getBool("haxmode");
325         
326         Player *player = server->m_env.getPlayer(peer_id);
327
328         assert(player != NULL);
329
330         v3f playerpos = player->getPosition();
331         v3f playerspeed = player->getSpeed();
332
333         v3s16 center_nodepos = floatToInt(playerpos);
334
335         v3s16 center = getNodeBlockPos(center_nodepos);
336         
337         // Camera position and direction
338         v3f camera_pos =
339                         playerpos + v3f(0, BS+BS/2, 0);
340         v3f camera_dir = v3f(0,0,1);
341         camera_dir.rotateYZBy(player->getPitch());
342         camera_dir.rotateXZBy(player->getYaw());
343
344         /*
345                 Get the starting value of the block finder radius.
346         */
347         s16 last_nearest_unsent_d;
348         s16 d_start;
349         {
350                 JMutexAutoLock lock(m_blocks_sent_mutex);
351                 
352                 if(m_last_center != center)
353                 {
354                         m_nearest_unsent_d = 0;
355                         m_last_center = center;
356                 }
357
358                 /*dstream<<"m_nearest_unsent_reset_timer="
359                                 <<m_nearest_unsent_reset_timer<<std::endl;*/
360                 if(m_nearest_unsent_reset_timer > 5.0)
361                 {
362                         m_nearest_unsent_reset_timer = 0;
363                         m_nearest_unsent_d = 0;
364                         //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
365                 }
366
367                 last_nearest_unsent_d = m_nearest_unsent_d;
368                 
369                 d_start = m_nearest_unsent_d;
370         }
371
372         u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
373                         ("max_simultaneous_block_sends_per_client");
374         u16 maximum_simultaneous_block_sends = 
375                         maximum_simultaneous_block_sends_setting;
376
377         /*
378                 Check the time from last addNode/removeNode.
379                 
380                 Decrease send rate if player is building stuff.
381         */
382         {
383                 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
384                 m_time_from_building.m_value += dtime;
385                 /*if(m_time_from_building.m_value
386                                 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
387                 if(m_time_from_building.m_value < g_settings.getFloat(
388                                         "full_block_send_enable_min_time_from_building"))
389                 {
390                         maximum_simultaneous_block_sends
391                                 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
392                 }
393         }
394         
395         u32 num_blocks_selected;
396         {
397                 JMutexAutoLock lock(m_blocks_sending_mutex);
398                 num_blocks_selected = m_blocks_sending.size();
399         }
400         
401         /*
402                 next time d will be continued from the d from which the nearest
403                 unsent block was found this time.
404
405                 This is because not necessarily any of the blocks found this
406                 time are actually sent.
407         */
408         s32 new_nearest_unsent_d = -1;
409
410         // Serialization version used
411         //u8 ser_version = serialization_version;
412
413         //bool has_incomplete_blocks = false;
414         
415         s16 d_max = g_settings.getS16("max_block_send_distance");
416         s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
417         
418         //dstream<<"Starting from "<<d_start<<std::endl;
419
420         for(s16 d = d_start; d <= d_max; d++)
421         {
422                 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
423                 
424                 //if(has_incomplete_blocks == false)
425                 {
426                         JMutexAutoLock lock(m_blocks_sent_mutex);
427                         /*
428                                 If m_nearest_unsent_d was changed by the EmergeThread
429                                 (it can change it to 0 through SetBlockNotSent),
430                                 update our d to it.
431                                 Else update m_nearest_unsent_d
432                         */
433                         if(m_nearest_unsent_d != last_nearest_unsent_d)
434                         {
435                                 d = m_nearest_unsent_d;
436                                 last_nearest_unsent_d = m_nearest_unsent_d;
437                         }
438                 }
439
440                 /*
441                         Get the border/face dot coordinates of a "d-radiused"
442                         box
443                 */
444                 core::list<v3s16> list;
445                 getFacePositions(list, d);
446                 
447                 core::list<v3s16>::Iterator li;
448                 for(li=list.begin(); li!=list.end(); li++)
449                 {
450                         v3s16 p = *li + center;
451                         
452                         /*
453                                 Send throttling
454                                 - Don't allow too many simultaneous transfers
455                                 - EXCEPT when the blocks are very close
456
457                                 Also, don't send blocks that are already flying.
458                         */
459                         
460                         u16 maximum_simultaneous_block_sends_now =
461                                         maximum_simultaneous_block_sends;
462                         
463                         if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
464                         {
465                                 maximum_simultaneous_block_sends_now =
466                                                 maximum_simultaneous_block_sends_setting;
467                         }
468
469                         {
470                                 JMutexAutoLock lock(m_blocks_sending_mutex);
471                                 
472                                 // Limit is dynamically lowered when building
473                                 if(num_blocks_selected
474                                                 >= maximum_simultaneous_block_sends_now)
475                                 {
476                                         /*dstream<<"Not sending more blocks. Queue full. "
477                                                         <<m_blocks_sending.size()
478                                                         <<std::endl;*/
479                                         goto queue_full;
480                                 }
481
482                                 if(m_blocks_sending.find(p) != NULL)
483                                         continue;
484                         }
485                         
486                         /*
487                                 Do not go over-limit
488                         */
489                         if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
490                         || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
491                         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
492                         || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
493                         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
494                         || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
495                                 continue;
496
497                         bool generate = d <= d_max_gen;
498                         
499                         if(haxmode)
500                         {
501                                 // Don't generate above player
502                                 if(p.Y > center.Y)
503                                         generate = false;
504                         }
505                         else
506                         {
507                                 // Limit the generating area vertically to 2/3
508                                 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
509                                         generate = false;
510                         }
511
512                         /*
513                                 Don't draw if not in sight
514                         */
515
516                         if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
517                         {
518                                 continue;
519                         }
520                         
521                         /*
522                                 Don't send already sent blocks
523                         */
524                         {
525                                 JMutexAutoLock lock(m_blocks_sent_mutex);
526                                 
527                                 if(m_blocks_sent.find(p) != NULL)
528                                         continue;
529                         }
530
531                         if(haxmode)
532                         {
533                                 /*
534                                         Ignore block if it is not at ground surface
535                                         but don't ignore water surface blocks
536                                 */
537                                 v2s16 p2d(p.X*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2,
538                                                 p.Z*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
539                                 f32 y = server->m_env.getMap().getGroundHeight(p2d);
540                                 // The sector might not exist yet, thus no heightmap
541                                 if(y > GROUNDHEIGHT_VALID_MINVALUE)
542                                 {
543                                         f32 by = p.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;
544                                         if(fabs(by - y) > MAP_BLOCKSIZE + MAP_BLOCKSIZE/3
545                                                         && fabs(by - WATER_LEVEL) >= MAP_BLOCKSIZE)
546                                                 continue;
547                                 }
548                         }
549
550                         /*
551                                 Check if map has this block
552                         */
553                         MapBlock *block = NULL;
554                         try
555                         {
556                                 block = server->m_env.getMap().getBlockNoCreate(p);
557                         }
558                         catch(InvalidPositionException &e)
559                         {
560                         }
561                         
562                         bool surely_not_found_on_disk = false;
563                         if(block != NULL)
564                         {
565                                 /*if(block->isIncomplete())
566                                 {
567                                         has_incomplete_blocks = true;
568                                         continue;
569                                 }*/
570
571                                 if(block->isDummy())
572                                 {
573                                         surely_not_found_on_disk = true;
574                                 }
575                         }
576
577                         /*
578                                 If block has been marked to not exist on disk (dummy)
579                                 and generating new ones is not wanted, skip block.
580                         */
581                         if(generate == false && surely_not_found_on_disk == true)
582                         {
583                                 // get next one.
584                                 continue;
585                         }
586
587                         /*
588                                 Record the lowest d from which a a block has been
589                                 found being not sent and possibly to exist
590                         */
591                         if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
592                         {
593                                 new_nearest_unsent_d = d;
594                         }
595                                         
596                         /*
597                                 Add inexistent block to emerge queue.
598                         */
599                         if(block == NULL || surely_not_found_on_disk)
600                         {
601                                 /*SharedPtr<JMutexAutoLock> lock
602                                                 (m_num_blocks_in_emerge_queue.getLock());*/
603                                 
604                                 //TODO: Get value from somewhere
605                                 // Allow only one block in emerge queue
606                                 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
607                                 {
608                                         // Add it to the emerge queue and trigger the thread
609                                         
610                                         u8 flags = 0;
611                                         if(generate == false)
612                                                 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
613                                         
614                                         server->m_emerge_queue.addBlock(peer_id, p, flags);
615                                         server->m_emergethread.trigger();
616                                 }
617                                 
618                                 // get next one.
619                                 continue;
620                         }
621
622                         /*
623                                 Add block to queue
624                         */
625
626                         PrioritySortedBlockTransfer q((float)d, p, peer_id);
627
628                         dest.push_back(q);
629
630                         num_blocks_selected += 1;
631                 }
632         }
633 queue_full:
634
635         if(new_nearest_unsent_d != -1)
636         {
637                 JMutexAutoLock lock(m_blocks_sent_mutex);
638                 m_nearest_unsent_d = new_nearest_unsent_d;
639         }
640 }
641
642 void RemoteClient::SendObjectData(
643                 Server *server,
644                 float dtime,
645                 core::map<v3s16, bool> &stepped_blocks
646         )
647 {
648         DSTACK(__FUNCTION_NAME);
649
650         // Can't send anything without knowing version
651         if(serialization_version == SER_FMT_VER_INVALID)
652         {
653                 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
654                                 <<std::endl;
655                 return;
656         }
657
658         /*
659                 Send a TOCLIENT_OBJECTDATA packet.
660                 Sent as unreliable.
661
662                 u16 command
663                 u16 number of player positions
664                 for each player:
665                         v3s32 position*100
666                         v3s32 speed*100
667                         s32 pitch*100
668                         s32 yaw*100
669                 u16 count of blocks
670                 for each block:
671                         block objects
672         */
673
674         std::ostringstream os(std::ios_base::binary);
675         u8 buf[12];
676         
677         // Write command
678         writeU16(buf, TOCLIENT_OBJECTDATA);
679         os.write((char*)buf, 2);
680         
681         /*
682                 Get and write player data
683         */
684         
685         // Get connected players
686         core::list<Player*> players = server->m_env.getPlayers(true);
687
688         // Write player count
689         u16 playercount = players.size();
690         writeU16(buf, playercount);
691         os.write((char*)buf, 2);
692
693         core::list<Player*>::Iterator i;
694         for(i = players.begin();
695                         i != players.end(); i++)
696         {
697                 Player *player = *i;
698
699                 v3f pf = player->getPosition();
700                 v3f sf = player->getSpeed();
701
702                 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
703                 v3s32 speed_i   (sf.X*100, sf.Y*100, sf.Z*100);
704                 s32   pitch_i   (player->getPitch() * 100);
705                 s32   yaw_i     (player->getYaw() * 100);
706                 
707                 writeU16(buf, player->peer_id);
708                 os.write((char*)buf, 2);
709                 writeV3S32(buf, position_i);
710                 os.write((char*)buf, 12);
711                 writeV3S32(buf, speed_i);
712                 os.write((char*)buf, 12);
713                 writeS32(buf, pitch_i);
714                 os.write((char*)buf, 4);
715                 writeS32(buf, yaw_i);
716                 os.write((char*)buf, 4);
717         }
718         
719         /*
720                 Get and write object data
721         */
722
723         /*
724                 Get nearby blocks.
725                 
726                 For making players to be able to build to their nearby
727                 environment (building is not possible on blocks that are not
728                 in memory):
729                 - Set blocks changed
730                 - Add blocks to emerge queue if they are not found
731
732                 SUGGESTION: These could be ignored from the backside of the player
733         */
734
735         Player *player = server->m_env.getPlayer(peer_id);
736
737         assert(player);
738
739         v3f playerpos = player->getPosition();
740         v3f playerspeed = player->getSpeed();
741
742         v3s16 center_nodepos = floatToInt(playerpos);
743         v3s16 center = getNodeBlockPos(center_nodepos);
744
745         s16 d_max = g_settings.getS16("active_object_range");
746         
747         // Number of blocks whose objects were written to bos
748         u16 blockcount = 0;
749
750         std::ostringstream bos(std::ios_base::binary);
751
752         for(s16 d = 0; d <= d_max; d++)
753         {
754                 core::list<v3s16> list;
755                 getFacePositions(list, d);
756                 
757                 core::list<v3s16>::Iterator li;
758                 for(li=list.begin(); li!=list.end(); li++)
759                 {
760                         v3s16 p = *li + center;
761
762                         /*
763                                 Ignore blocks that haven't been sent to the client
764                         */
765                         {
766                                 JMutexAutoLock sentlock(m_blocks_sent_mutex);
767                                 if(m_blocks_sent.find(p) == NULL)
768                                         continue;
769                         }
770                         
771                         // Try stepping block and add it to a send queue
772                         try
773                         {
774
775                         // Get block
776                         MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
777
778                         /*
779                                 Step block if not in stepped_blocks and add to stepped_blocks.
780                         */
781                         if(stepped_blocks.find(p) == NULL)
782                         {
783                                 block->stepObjects(dtime, true, server->getDayNightRatio());
784                                 stepped_blocks.insert(p, true);
785                                 block->setChangedFlag();
786                         }
787
788                         // Skip block if there are no objects
789                         if(block->getObjectCount() == 0)
790                                 continue;
791                         
792                         /*
793                                 Write objects
794                         */
795
796                         // Write blockpos
797                         writeV3S16(buf, p);
798                         bos.write((char*)buf, 6);
799
800                         // Write objects
801                         block->serializeObjects(bos, serialization_version);
802
803                         blockcount++;
804
805                         /*
806                                 Stop collecting objects if data is already too big
807                         */
808                         // Sum of player and object data sizes
809                         s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
810                         // break out if data too big
811                         if(sum > MAX_OBJECTDATA_SIZE)
812                         {
813                                 goto skip_subsequent;
814                         }
815                         
816                         } //try
817                         catch(InvalidPositionException &e)
818                         {
819                                 // Not in memory
820                                 // Add it to the emerge queue and trigger the thread.
821                                 // Fetch the block only if it is on disk.
822                                 
823                                 // Grab and increment counter
824                                 /*SharedPtr<JMutexAutoLock> lock
825                                                 (m_num_blocks_in_emerge_queue.getLock());
826                                 m_num_blocks_in_emerge_queue.m_value++;*/
827                                 
828                                 // Add to queue as an anonymous fetch from disk
829                                 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
830                                 server->m_emerge_queue.addBlock(0, p, flags);
831                                 server->m_emergethread.trigger();
832                         }
833                 }
834         }
835
836 skip_subsequent:
837
838         // Write block count
839         writeU16(buf, blockcount);
840         os.write((char*)buf, 2);
841
842         // Write block objects
843         os<<bos.str();
844
845         /*
846                 Send data
847         */
848         
849         //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
850
851         // Make data buffer
852         std::string s = os.str();
853         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
854         // Send as unreliable
855         server->m_con.Send(peer_id, 0, data, false);
856 }
857
858 void RemoteClient::GotBlock(v3s16 p)
859 {
860         JMutexAutoLock lock(m_blocks_sending_mutex);
861         JMutexAutoLock lock2(m_blocks_sent_mutex);
862         if(m_blocks_sending.find(p) != NULL)
863                 m_blocks_sending.remove(p);
864         else
865         {
866                 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
867                                 " m_blocks_sending"<<std::endl;*/
868                 m_excess_gotblocks++;
869         }
870         m_blocks_sent.insert(p, true);
871 }
872
873 void RemoteClient::SentBlock(v3s16 p)
874 {
875         JMutexAutoLock lock(m_blocks_sending_mutex);
876         /*if(m_blocks_sending.size() > 15)
877         {
878                 dstream<<"RemoteClient::SentBlock(): "
879                                 <<"m_blocks_sending.size()="
880                                 <<m_blocks_sending.size()<<std::endl;
881         }*/
882         if(m_blocks_sending.find(p) == NULL)
883                 m_blocks_sending.insert(p, 0.0);
884         else
885                 dstream<<"RemoteClient::SentBlock(): Sent block"
886                                 " already in m_blocks_sending"<<std::endl;
887 }
888
889 void RemoteClient::SetBlockNotSent(v3s16 p)
890 {
891         JMutexAutoLock sendinglock(m_blocks_sending_mutex);
892         JMutexAutoLock sentlock(m_blocks_sent_mutex);
893
894         m_nearest_unsent_d = 0;
895         
896         if(m_blocks_sending.find(p) != NULL)
897                 m_blocks_sending.remove(p);
898         if(m_blocks_sent.find(p) != NULL)
899                 m_blocks_sent.remove(p);
900 }
901
902 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
903 {
904         JMutexAutoLock sendinglock(m_blocks_sending_mutex);
905         JMutexAutoLock sentlock(m_blocks_sent_mutex);
906
907         m_nearest_unsent_d = 0;
908         
909         for(core::map<v3s16, MapBlock*>::Iterator
910                         i = blocks.getIterator();
911                         i.atEnd()==false; i++)
912         {
913                 v3s16 p = i.getNode()->getKey();
914
915                 if(m_blocks_sending.find(p) != NULL)
916                         m_blocks_sending.remove(p);
917                 if(m_blocks_sent.find(p) != NULL)
918                         m_blocks_sent.remove(p);
919         }
920 }
921
922 /*
923         PlayerInfo
924 */
925
926 PlayerInfo::PlayerInfo()
927 {
928         name[0] = 0;
929 }
930
931 void PlayerInfo::PrintLine(std::ostream *s)
932 {
933         (*s)<<id<<": ";
934         (*s)<<"\""<<name<<"\" ("
935                         <<position.X<<","<<position.Y
936                         <<","<<position.Z<<") ";
937         address.print(s);
938         (*s)<<" avg_rtt="<<avg_rtt;
939         (*s)<<std::endl;
940 }
941
942 u32 PIChecksum(core::list<PlayerInfo> &l)
943 {
944         core::list<PlayerInfo>::Iterator i;
945         u32 checksum = 1;
946         u32 a = 10;
947         for(i=l.begin(); i!=l.end(); i++)
948         {
949                 checksum += a * (i->id+1);
950                 checksum ^= 0x435aafcd;
951                 a *= 10;
952         }
953         return checksum;
954 }
955
956 /*
957         Server
958 */
959
960 Server::Server(
961                 std::string mapsavedir,
962                 HMParams hm_params,
963                 MapParams map_params
964         ):
965         m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
966         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
967         m_thread(this),
968         m_emergethread(this),
969         m_time_of_day(9000),
970         m_time_counter(0),
971         m_time_of_day_send_timer(0),
972         m_uptime(0)
973 {
974         //m_flowwater_timer = 0.0;
975         m_liquid_transform_timer = 0.0;
976         m_print_info_timer = 0.0;
977         m_objectdata_timer = 0.0;
978         m_emergethread_trigger_timer = 0.0;
979         m_savemap_timer = 0.0;
980         
981         m_env_mutex.Init();
982         m_con_mutex.Init();
983         m_step_dtime_mutex.Init();
984         m_step_dtime = 0.0;
985 }
986
987 Server::~Server()
988 {
989         // Stop threads
990         stop();
991
992         JMutexAutoLock clientslock(m_con_mutex);
993
994         for(core::map<u16, RemoteClient*>::Iterator
995                 i = m_clients.getIterator();
996                 i.atEnd() == false; i++)
997         {
998                 /*// Delete player
999                 // NOTE: These are removed by env destructor
1000                 {
1001                         u16 peer_id = i.getNode()->getKey();
1002                         JMutexAutoLock envlock(m_env_mutex);
1003                         m_env.removePlayer(peer_id);
1004                 }*/
1005                 
1006                 // Delete client
1007                 delete i.getNode()->getValue();
1008         }
1009 }
1010
1011 void Server::start(unsigned short port)
1012 {
1013         DSTACK(__FUNCTION_NAME);
1014         // Stop thread if already running
1015         m_thread.stop();
1016         
1017         // Initialize connection
1018         m_con.setTimeoutMs(30);
1019         m_con.Serve(port);
1020
1021         // Start thread
1022         m_thread.setRun(true);
1023         m_thread.Start();
1024         
1025         dout_server<<"Server started on port "<<port<<std::endl;
1026 }
1027
1028 void Server::stop()
1029 {
1030         DSTACK(__FUNCTION_NAME);
1031         // Stop threads (set run=false first so both start stopping)
1032         m_thread.setRun(false);
1033         m_emergethread.setRun(false);
1034         m_thread.stop();
1035         m_emergethread.stop();
1036         
1037         dout_server<<"Server threads stopped"<<std::endl;
1038 }
1039
1040 void Server::step(float dtime)
1041 {
1042         DSTACK(__FUNCTION_NAME);
1043         // Limit a bit
1044         if(dtime > 2.0)
1045                 dtime = 2.0;
1046         {
1047                 JMutexAutoLock lock(m_step_dtime_mutex);
1048                 m_step_dtime += dtime;
1049         }
1050 }
1051
1052 void Server::AsyncRunStep()
1053 {
1054         DSTACK(__FUNCTION_NAME);
1055         
1056         float dtime;
1057         {
1058                 JMutexAutoLock lock1(m_step_dtime_mutex);
1059                 dtime = m_step_dtime;
1060         }
1061         
1062         // Send blocks to clients
1063         SendBlocks(dtime);
1064         
1065         if(dtime < 0.001)
1066                 return;
1067         
1068         //dstream<<"Server steps "<<dtime<<std::endl;
1069         //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1070         
1071         {
1072                 JMutexAutoLock lock1(m_step_dtime_mutex);
1073                 m_step_dtime -= dtime;
1074         }
1075
1076         /*
1077                 Update uptime
1078         */
1079         {
1080                 m_uptime.set(m_uptime.get() + dtime);
1081         }
1082         
1083         /*
1084                 Update m_time_of_day
1085         */
1086         {
1087                 m_time_counter += dtime;
1088                 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1089                 u32 units = (u32)(m_time_counter*speed);
1090                 m_time_counter -= (f32)units / speed;
1091                 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1092                 
1093                 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1094
1095                 /*
1096                         Send to clients at constant intervals
1097                 */
1098
1099                 m_time_of_day_send_timer -= dtime;
1100                 if(m_time_of_day_send_timer < 0.0)
1101                 {
1102                         m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1103
1104                         //JMutexAutoLock envlock(m_env_mutex);
1105                         JMutexAutoLock conlock(m_con_mutex);
1106
1107                         for(core::map<u16, RemoteClient*>::Iterator
1108                                 i = m_clients.getIterator();
1109                                 i.atEnd() == false; i++)
1110                         {
1111                                 RemoteClient *client = i.getNode()->getValue();
1112                                 //Player *player = m_env.getPlayer(client->peer_id);
1113                                 
1114                                 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1115                                                 m_time_of_day.get());
1116                                 // Send as reliable
1117                                 m_con.Send(client->peer_id, 0, data, true);
1118                         }
1119                 }
1120         }
1121
1122         {
1123                 // Process connection's timeouts
1124                 JMutexAutoLock lock2(m_con_mutex);
1125                 m_con.RunTimeouts(dtime);
1126         }
1127         
1128         {
1129                 // This has to be called so that the client list gets synced
1130                 // with the peer list of the connection
1131                 handlePeerChanges();
1132         }
1133
1134         {
1135                 // Step environment
1136                 // This also runs Map's timers
1137                 JMutexAutoLock lock(m_env_mutex);
1138                 m_env.step(dtime);
1139         }
1140         
1141         /*
1142                 Do background stuff
1143         */
1144         
1145         /*
1146                 Transform liquids
1147         */
1148         m_liquid_transform_timer += dtime;
1149         if(m_liquid_transform_timer >= 1.00)
1150         {
1151                 m_liquid_transform_timer -= 1.00;
1152                 
1153                 JMutexAutoLock lock(m_env_mutex);
1154                 
1155                 core::map<v3s16, MapBlock*> modified_blocks;
1156                 m_env.getMap().transformLiquids(modified_blocks);
1157 #if 0           
1158                 /*
1159                         Update lighting
1160                 */
1161                 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1162                 ServerMap &map = ((ServerMap&)m_env.getMap());
1163                 map.updateLighting(modified_blocks, lighting_modified_blocks);
1164                 
1165                 // Add blocks modified by lighting to modified_blocks
1166                 for(core::map<v3s16, MapBlock*>::Iterator
1167                                 i = lighting_modified_blocks.getIterator();
1168                                 i.atEnd() == false; i++)
1169                 {
1170                         MapBlock *block = i.getNode()->getValue();
1171                         modified_blocks.insert(block->getPos(), block);
1172                 }
1173 #endif
1174                 /*
1175                         Set the modified blocks unsent for all the clients
1176                 */
1177                 
1178                 JMutexAutoLock lock2(m_con_mutex);
1179
1180                 for(core::map<u16, RemoteClient*>::Iterator
1181                                 i = m_clients.getIterator();
1182                                 i.atEnd() == false; i++)
1183                 {
1184                         RemoteClient *client = i.getNode()->getValue();
1185                         
1186                         if(modified_blocks.size() > 0)
1187                         {
1188                                 // Remove block from sent history
1189                                 client->SetBlocksNotSent(modified_blocks);
1190                         }
1191                 }
1192         }
1193
1194 #if 0
1195         /*
1196                 Update water
1197         */
1198         if(g_settings.getBool("water_moves") == true)
1199         {
1200                 float interval;
1201                 
1202                 if(g_settings.getBool("endless_water") == false)
1203                         interval = 1.0;
1204                 else
1205                         interval = 0.25;
1206
1207                 float &counter = m_flowwater_timer;
1208                 counter += dtime;
1209                 if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
1210                 {
1211                 
1212                 counter = 0.0;
1213
1214                 core::map<v3s16, MapBlock*> modified_blocks;
1215
1216                 {
1217
1218                         JMutexAutoLock envlock(m_env_mutex);
1219                         
1220                         MapVoxelManipulator v(&m_env.getMap());
1221                         v.m_disable_water_climb =
1222                                         g_settings.getBool("disable_water_climb");
1223                         
1224                         if(g_settings.getBool("endless_water") == false)
1225                                 v.flowWater(m_flow_active_nodes, 0, false, 250);
1226                         else
1227                                 v.flowWater(m_flow_active_nodes, 0, false, 50);
1228
1229                         v.blitBack(modified_blocks);
1230
1231                         ServerMap &map = ((ServerMap&)m_env.getMap());
1232                         
1233                         // Update lighting
1234                         core::map<v3s16, MapBlock*> lighting_modified_blocks;
1235                         map.updateLighting(modified_blocks, lighting_modified_blocks);
1236                         
1237                         // Add blocks modified by lighting to modified_blocks
1238                         for(core::map<v3s16, MapBlock*>::Iterator
1239                                         i = lighting_modified_blocks.getIterator();
1240                                         i.atEnd() == false; i++)
1241                         {
1242                                 MapBlock *block = i.getNode()->getValue();
1243                                 modified_blocks.insert(block->getPos(), block);
1244                         }
1245                 } // envlock
1246
1247                 /*
1248                         Set the modified blocks unsent for all the clients
1249                 */
1250                 
1251                 JMutexAutoLock lock2(m_con_mutex);
1252
1253                 for(core::map<u16, RemoteClient*>::Iterator
1254                                 i = m_clients.getIterator();
1255                                 i.atEnd() == false; i++)
1256                 {
1257                         RemoteClient *client = i.getNode()->getValue();
1258                         
1259                         if(modified_blocks.size() > 0)
1260                         {
1261                                 // Remove block from sent history
1262                                 client->SetBlocksNotSent(modified_blocks);
1263                         }
1264                 }
1265
1266                 } // interval counter
1267         }
1268 #endif
1269         
1270         // Periodically print some info
1271         {
1272                 float &counter = m_print_info_timer;
1273                 counter += dtime;
1274                 if(counter >= 30.0)
1275                 {
1276                         counter = 0.0;
1277
1278                         JMutexAutoLock lock2(m_con_mutex);
1279
1280                         for(core::map<u16, RemoteClient*>::Iterator
1281                                 i = m_clients.getIterator();
1282                                 i.atEnd() == false; i++)
1283                         {
1284                                 //u16 peer_id = i.getNode()->getKey();
1285                                 RemoteClient *client = i.getNode()->getValue();
1286                                 client->PrintInfo(std::cout);
1287                         }
1288                 }
1289         }
1290
1291         /*
1292                 Update digging
1293
1294                 NOTE: Some of this could be moved to RemoteClient
1295         */
1296 #if 0
1297         {
1298                 JMutexAutoLock envlock(m_env_mutex);
1299                 JMutexAutoLock conlock(m_con_mutex);
1300
1301                 for(core::map<u16, RemoteClient*>::Iterator
1302                         i = m_clients.getIterator();
1303                         i.atEnd() == false; i++)
1304                 {
1305                         RemoteClient *client = i.getNode()->getValue();
1306                         Player *player = m_env.getPlayer(client->peer_id);
1307
1308                         JMutexAutoLock digmutex(client->m_dig_mutex);
1309
1310                         if(client->m_dig_tool_item == -1)
1311                                 continue;
1312
1313                         client->m_dig_time_remaining -= dtime;
1314
1315                         if(client->m_dig_time_remaining > 0)
1316                         {
1317                                 client->m_time_from_building.set(0.0);
1318                                 continue;
1319                         }
1320
1321                         v3s16 p_under = client->m_dig_position;
1322                         
1323                         // Mandatory parameter; actually used for nothing
1324                         core::map<v3s16, MapBlock*> modified_blocks;
1325
1326                         u8 material;
1327
1328                         try
1329                         {
1330                                 // Get material at position
1331                                 material = m_env.getMap().getNode(p_under).d;
1332                                 // If it's not diggable, do nothing
1333                                 if(content_diggable(material) == false)
1334                                 {
1335                                         derr_server<<"Server: Not finishing digging: Node not diggable"
1336                                                         <<std::endl;
1337                                         client->m_dig_tool_item = -1;
1338                                         break;
1339                                 }
1340                         }
1341                         catch(InvalidPositionException &e)
1342                         {
1343                                 derr_server<<"Server: Not finishing digging: Node not found"
1344                                                 <<std::endl;
1345                                 client->m_dig_tool_item = -1;
1346                                 break;
1347                         }
1348                         
1349                         // Create packet
1350                         u32 replysize = 8;
1351                         SharedBuffer<u8> reply(replysize);
1352                         writeU16(&reply[0], TOCLIENT_REMOVENODE);
1353                         writeS16(&reply[2], p_under.X);
1354                         writeS16(&reply[4], p_under.Y);
1355                         writeS16(&reply[6], p_under.Z);
1356                         // Send as reliable
1357                         m_con.SendToAll(0, reply, true);
1358                         
1359                         if(g_settings.getBool("creative_mode") == false)
1360                         {
1361                                 // Add to inventory and send inventory
1362                                 InventoryItem *item = new MaterialItem(material, 1);
1363                                 player->inventory.addItem("main", item);
1364                                 SendInventory(player->peer_id);
1365                         }
1366
1367                         /*
1368                                 Remove the node
1369                                 (this takes some time so it is done after the quick stuff)
1370                         */
1371                         m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1372                         
1373                         /*
1374                                 Update water
1375                         */
1376                         
1377                         // Update water pressure around modification
1378                         // This also adds it to m_flow_active_nodes if appropriate
1379
1380                         MapVoxelManipulator v(&m_env.getMap());
1381                         v.m_disable_water_climb =
1382                                         g_settings.getBool("disable_water_climb");
1383                         
1384                         VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1385
1386                         try
1387                         {
1388                                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1389                         }
1390                         catch(ProcessingLimitException &e)
1391                         {
1392                                 dstream<<"Processing limit reached (1)"<<std::endl;
1393                         }
1394                         
1395                         v.blitBack(modified_blocks);
1396                 }
1397         }
1398 #endif
1399
1400         // Send object positions
1401         {
1402                 float &counter = m_objectdata_timer;
1403                 counter += dtime;
1404                 if(counter >= g_settings.getFloat("objectdata_interval"))
1405                 {
1406                         JMutexAutoLock lock1(m_env_mutex);
1407                         JMutexAutoLock lock2(m_con_mutex);
1408                         SendObjectData(counter);
1409
1410                         counter = 0.0;
1411                 }
1412         }
1413         
1414         // Trigger emergethread (it gets somehow gets to a
1415         // non-triggered but bysy state sometimes)
1416         {
1417                 float &counter = m_emergethread_trigger_timer;
1418                 counter += dtime;
1419                 if(counter >= 2.0)
1420                 {
1421                         counter = 0.0;
1422                         
1423                         m_emergethread.trigger();
1424                 }
1425         }
1426
1427         // Save map
1428         {
1429                 float &counter = m_savemap_timer;
1430                 counter += dtime;
1431                 if(counter >= g_settings.getFloat("server_map_save_interval"))
1432                 {
1433                         counter = 0.0;
1434
1435                         JMutexAutoLock lock(m_env_mutex);
1436
1437                         // Save only changed parts
1438                         m_env.getMap().save(true);
1439
1440                         // Delete unused sectors
1441                         u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1442                                         g_settings.getFloat("server_unload_unused_sectors_timeout"));
1443                         if(deleted_count > 0)
1444                         {
1445                                 dout_server<<"Server: Unloaded "<<deleted_count
1446                                                 <<" sectors from memory"<<std::endl;
1447                         }
1448                 }
1449         }
1450 }
1451
1452 void Server::Receive()
1453 {
1454         DSTACK(__FUNCTION_NAME);
1455         u32 data_maxsize = 10000;
1456         Buffer<u8> data(data_maxsize);
1457         u16 peer_id;
1458         u32 datasize;
1459         try{
1460                 {
1461                         JMutexAutoLock conlock(m_con_mutex);
1462                         datasize = m_con.Receive(peer_id, *data, data_maxsize);
1463                 }
1464
1465                 // This has to be called so that the client list gets synced
1466                 // with the peer list of the connection
1467                 handlePeerChanges();
1468
1469                 ProcessData(*data, datasize, peer_id);
1470         }
1471         catch(con::InvalidIncomingDataException &e)
1472         {
1473                 derr_server<<"Server::Receive(): "
1474                                 "InvalidIncomingDataException: what()="
1475                                 <<e.what()<<std::endl;
1476         }
1477         catch(con::PeerNotFoundException &e)
1478         {
1479                 //NOTE: This is not needed anymore
1480                 
1481                 // The peer has been disconnected.
1482                 // Find the associated player and remove it.
1483
1484                 /*JMutexAutoLock envlock(m_env_mutex);
1485
1486                 dout_server<<"ServerThread: peer_id="<<peer_id
1487                                 <<" has apparently closed connection. "
1488                                 <<"Removing player."<<std::endl;
1489
1490                 m_env.removePlayer(peer_id);*/
1491         }
1492 }
1493
1494 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1495 {
1496         DSTACK(__FUNCTION_NAME);
1497         // Environment is locked first.
1498         JMutexAutoLock envlock(m_env_mutex);
1499         JMutexAutoLock conlock(m_con_mutex);
1500         
1501         con::Peer *peer;
1502         try{
1503                 peer = m_con.GetPeer(peer_id);
1504         }
1505         catch(con::PeerNotFoundException &e)
1506         {
1507                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1508                                 <<peer_id<<" not found"<<std::endl;
1509                 return;
1510         }
1511         
1512         //u8 peer_ser_ver = peer->serialization_version;
1513         u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1514
1515         try
1516         {
1517
1518         if(datasize < 2)
1519                 return;
1520
1521         ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1522         
1523         if(command == TOSERVER_INIT)
1524         {
1525                 // [0] u16 TOSERVER_INIT
1526                 // [2] u8 SER_FMT_VER_HIGHEST
1527                 // [3] u8[20] player_name
1528
1529                 if(datasize < 3)
1530                         return;
1531
1532                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1533                                 <<peer->id<<std::endl;
1534
1535                 // First byte after command is maximum supported
1536                 // serialization version
1537                 u8 client_max = data[2];
1538                 u8 our_max = SER_FMT_VER_HIGHEST;
1539                 // Use the highest version supported by both
1540                 u8 deployed = core::min_(client_max, our_max);
1541                 // If it's lower than the lowest supported, give up.
1542                 if(deployed < SER_FMT_VER_LOWEST)
1543                         deployed = SER_FMT_VER_INVALID;
1544
1545                 //peer->serialization_version = deployed;
1546                 getClient(peer->id)->pending_serialization_version = deployed;
1547
1548                 if(deployed == SER_FMT_VER_INVALID)
1549                 {
1550                         derr_server<<DTIME<<"Server: Cannot negotiate "
1551                                         "serialization version with peer "
1552                                         <<peer_id<<std::endl;
1553                         return;
1554                 }
1555
1556                 /*
1557                         Set up player
1558                 */
1559                 
1560                 // Get player name
1561                 const u32 playername_size = 20;
1562                 char playername[playername_size];
1563                 for(u32 i=0; i<playername_size-1; i++)
1564                 {
1565                         playername[i] = data[3+i];
1566                 }
1567                 playername[playername_size-1] = 0;
1568                 
1569                 // Get player
1570                 Player *player = emergePlayer(playername, "");
1571                 //Player *player = m_env.getPlayer(peer_id);
1572
1573                 // If a client is already connected to the player, cancel
1574                 if(player->peer_id != 0)
1575                 {
1576                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1577                                         <<" tried to connect to "
1578                                         "an already connected player (peer_id="
1579                                         <<player->peer_id<<")"<<std::endl;
1580                         return;
1581                 }
1582
1583                 // Set client of player
1584                 player->peer_id = peer_id;
1585
1586                 // Check if player doesn't exist
1587                 if(player == NULL)
1588                         throw con::InvalidIncomingDataException
1589                                 ("Server::ProcessData(): INIT: Player doesn't exist");
1590
1591                 /*// update name if it was supplied
1592                 if(datasize >= 20+3)
1593                 {
1594                         data[20+3-1] = 0;
1595                         player->updateName((const char*)&data[3]);
1596                 }*/
1597
1598                 // Now answer with a TOCLIENT_INIT
1599                 
1600                 SharedBuffer<u8> reply(2+1+6);
1601                 writeU16(&reply[0], TOCLIENT_INIT);
1602                 writeU8(&reply[2], deployed);
1603                 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1604                 // Send as reliable
1605                 m_con.Send(peer_id, 0, reply, true);
1606
1607                 return;
1608         }
1609         if(command == TOSERVER_INIT2)
1610         {
1611                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1612                                 <<peer->id<<std::endl;
1613
1614
1615                 getClient(peer->id)->serialization_version
1616                                 = getClient(peer->id)->pending_serialization_version;
1617
1618                 /*
1619                         Send some initialization data
1620                 */
1621                 
1622                 // Send player info to all players
1623                 SendPlayerInfos();
1624
1625                 // Send inventory to player
1626                 SendInventory(peer->id);
1627                 
1628                 // Send time of day
1629                 {
1630                         SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1631                                         m_time_of_day.get());
1632                         m_con.Send(peer->id, 0, data, true);
1633                 }
1634
1635                 // Send information about server to player in chat
1636                 {
1637                         std::wostringstream os(std::ios_base::binary);
1638                         os<<L"# Server: ";
1639                         // Uptime
1640                         os<<L"uptime="<<m_uptime.get();
1641                         // Information about clients
1642                         os<<L", clients={";
1643                         for(core::map<u16, RemoteClient*>::Iterator
1644                                 i = m_clients.getIterator();
1645                                 i.atEnd() == false; i++)
1646                         {
1647                                 // Get client and check that it is valid
1648                                 RemoteClient *client = i.getNode()->getValue();
1649                                 assert(client->peer_id == i.getNode()->getKey());
1650                                 if(client->serialization_version == SER_FMT_VER_INVALID)
1651                                         continue;
1652                                 // Get player
1653                                 Player *player = m_env.getPlayer(client->peer_id);
1654                                 // Get name of player
1655                                 std::wstring name = L"unknown";
1656                                 if(player != NULL)
1657                                         name = narrow_to_wide(player->getName());
1658                                 // Add name to information string
1659                                 os<<name<<L",";
1660                         }
1661                         os<<L"}";
1662                         // Send message
1663                         SendChatMessage(peer_id, os.str());
1664                 }
1665                 
1666                 // Send information about joining in chat
1667                 {
1668                         std::wstring name = L"unknown";
1669                         Player *player = m_env.getPlayer(peer_id);
1670                         if(player != NULL)
1671                                 name = narrow_to_wide(player->getName());
1672                         
1673                         std::wstring message;
1674                         message += L"*** ";
1675                         message += name;
1676                         message += L" joined game";
1677                         BroadcastChatMessage(message);
1678                 }
1679
1680                 return;
1681         }
1682
1683         if(peer_ser_ver == SER_FMT_VER_INVALID)
1684         {
1685                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1686                                 " serialization format invalid or not initialized."
1687                                 " Skipping incoming command="<<command<<std::endl;
1688                 return;
1689         }
1690         
1691         Player *player = m_env.getPlayer(peer_id);
1692
1693         if(player == NULL){
1694                 derr_server<<"Server::ProcessData(): Cancelling: "
1695                                 "No player for peer_id="<<peer_id
1696                                 <<std::endl;
1697                 return;
1698         }
1699         if(command == TOSERVER_PLAYERPOS)
1700         {
1701                 if(datasize < 2+12+12+4+4)
1702                         return;
1703         
1704                 u32 start = 0;
1705                 v3s32 ps = readV3S32(&data[start+2]);
1706                 v3s32 ss = readV3S32(&data[start+2+12]);
1707                 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1708                 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1709                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1710                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1711                 pitch = wrapDegrees(pitch);
1712                 yaw = wrapDegrees(yaw);
1713                 player->setPosition(position);
1714                 player->setSpeed(speed);
1715                 player->setPitch(pitch);
1716                 player->setYaw(yaw);
1717                 
1718                 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1719                                 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1720                                 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1721         }
1722         else if(command == TOSERVER_GOTBLOCKS)
1723         {
1724                 if(datasize < 2+1)
1725                         return;
1726                 
1727                 /*
1728                         [0] u16 command
1729                         [2] u8 count
1730                         [3] v3s16 pos_0
1731                         [3+6] v3s16 pos_1
1732                         ...
1733                 */
1734
1735                 u16 count = data[2];
1736                 for(u16 i=0; i<count; i++)
1737                 {
1738                         if((s16)datasize < 2+1+(i+1)*6)
1739                                 throw con::InvalidIncomingDataException
1740                                         ("GOTBLOCKS length is too short");
1741                         v3s16 p = readV3S16(&data[2+1+i*6]);
1742                         /*dstream<<"Server: GOTBLOCKS ("
1743                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1744                         RemoteClient *client = getClient(peer_id);
1745                         client->GotBlock(p);
1746                 }
1747         }
1748         else if(command == TOSERVER_DELETEDBLOCKS)
1749         {
1750                 if(datasize < 2+1)
1751                         return;
1752                 
1753                 /*
1754                         [0] u16 command
1755                         [2] u8 count
1756                         [3] v3s16 pos_0
1757                         [3+6] v3s16 pos_1
1758                         ...
1759                 */
1760
1761                 u16 count = data[2];
1762                 for(u16 i=0; i<count; i++)
1763                 {
1764                         if((s16)datasize < 2+1+(i+1)*6)
1765                                 throw con::InvalidIncomingDataException
1766                                         ("DELETEDBLOCKS length is too short");
1767                         v3s16 p = readV3S16(&data[2+1+i*6]);
1768                         /*dstream<<"Server: DELETEDBLOCKS ("
1769                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1770                         RemoteClient *client = getClient(peer_id);
1771                         client->SetBlockNotSent(p);
1772                 }
1773         }
1774         else if(command == TOSERVER_CLICK_OBJECT)
1775         {
1776                 if(datasize < 13)
1777                         return;
1778
1779                 /*
1780                         [0] u16 command
1781                         [2] u8 button (0=left, 1=right)
1782                         [3] v3s16 block
1783                         [9] s16 id
1784                         [11] u16 item
1785                 */
1786                 u8 button = readU8(&data[2]);
1787                 v3s16 p;
1788                 p.X = readS16(&data[3]);
1789                 p.Y = readS16(&data[5]);
1790                 p.Z = readS16(&data[7]);
1791                 s16 id = readS16(&data[9]);
1792                 //u16 item_i = readU16(&data[11]);
1793
1794                 MapBlock *block = NULL;
1795                 try
1796                 {
1797                         block = m_env.getMap().getBlockNoCreate(p);
1798                 }
1799                 catch(InvalidPositionException &e)
1800                 {
1801                         derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1802                         return;
1803                 }
1804
1805                 MapBlockObject *obj = block->getObject(id);
1806
1807                 if(obj == NULL)
1808                 {
1809                         derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1810                         return;
1811                 }
1812
1813                 //TODO: Check that object is reasonably close
1814                 
1815                 // Left click
1816                 if(button == 0)
1817                 {
1818                         InventoryList *ilist = player->inventory.getList("main");
1819                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1820                         {
1821                         
1822                                 // Skip if inventory has no free space
1823                                 if(ilist->getUsedSlots() == ilist->getSize())
1824                                 {
1825                                         dout_server<<"Player inventory has no free space"<<std::endl;
1826                                         return;
1827                                 }
1828                                 
1829                                 /*
1830                                         Create the inventory item
1831                                 */
1832                                 InventoryItem *item = NULL;
1833                                 // If it is an item-object, take the item from it
1834                                 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1835                                 {
1836                                         item = ((ItemObject*)obj)->createInventoryItem();
1837                                 }
1838                                 // Else create an item of the object
1839                                 else
1840                                 {
1841                                         item = new MapBlockObjectItem
1842                                                         (obj->getInventoryString());
1843                                 }
1844                                 
1845                                 // Add to inventory and send inventory
1846                                 ilist->addItem(item);
1847                                 SendInventory(player->peer_id);
1848                         }
1849
1850                         // Remove from block
1851                         block->removeObject(id);
1852                 }
1853         }
1854         else if(command == TOSERVER_GROUND_ACTION)
1855         {
1856                 if(datasize < 17)
1857                         return;
1858                 /*
1859                         length: 17
1860                         [0] u16 command
1861                         [2] u8 action
1862                         [3] v3s16 nodepos_undersurface
1863                         [9] v3s16 nodepos_abovesurface
1864                         [15] u16 item
1865                         actions:
1866                         0: start digging
1867                         1: place block
1868                         2: stop digging (all parameters ignored)
1869                 */
1870                 u8 action = readU8(&data[2]);
1871                 v3s16 p_under;
1872                 p_under.X = readS16(&data[3]);
1873                 p_under.Y = readS16(&data[5]);
1874                 p_under.Z = readS16(&data[7]);
1875                 v3s16 p_over;
1876                 p_over.X = readS16(&data[9]);
1877                 p_over.Y = readS16(&data[11]);
1878                 p_over.Z = readS16(&data[13]);
1879                 u16 item_i = readU16(&data[15]);
1880
1881                 //TODO: Check that target is reasonably close
1882                 
1883                 /*
1884                         0: start digging
1885                 */
1886                 if(action == 0)
1887                 {
1888                         /*
1889                                 NOTE: This can be used in the future to check if
1890                                 somebody is cheating, by checking the timing.
1891                         */
1892                 } // action == 0
1893
1894                 /*
1895                         2: stop digging
1896                 */
1897                 else if(action == 2)
1898                 {
1899 #if 0
1900                         RemoteClient *client = getClient(peer->id);
1901                         JMutexAutoLock digmutex(client->m_dig_mutex);
1902                         client->m_dig_tool_item = -1;
1903 #endif
1904                 }
1905
1906                 /*
1907                         3: Digging completed
1908                 */
1909                 else if(action == 3)
1910                 {
1911                         // Mandatory parameter; actually used for nothing
1912                         core::map<v3s16, MapBlock*> modified_blocks;
1913
1914                         u8 material;
1915
1916                         try
1917                         {
1918                                 // Get material at position
1919                                 material = m_env.getMap().getNode(p_under).d;
1920                                 // If it's not diggable, do nothing
1921                                 if(content_diggable(material) == false)
1922                                 {
1923                                         derr_server<<"Server: Not finishing digging: Node not diggable"
1924                                                         <<std::endl;
1925                                         return;
1926                                 }
1927                         }
1928                         catch(InvalidPositionException &e)
1929                         {
1930                                 derr_server<<"Server: Not finishing digging: Node not found"
1931                                                 <<std::endl;
1932                                 return;
1933                         }
1934                         
1935                         //TODO: Send to only other clients
1936                         
1937                         /*
1938                                 Send the removal to all other clients
1939                         */
1940
1941                         // Create packet
1942                         u32 replysize = 8;
1943                         SharedBuffer<u8> reply(replysize);
1944                         writeU16(&reply[0], TOCLIENT_REMOVENODE);
1945                         writeS16(&reply[2], p_under.X);
1946                         writeS16(&reply[4], p_under.Y);
1947                         writeS16(&reply[6], p_under.Z);
1948
1949                         for(core::map<u16, RemoteClient*>::Iterator
1950                                 i = m_clients.getIterator();
1951                                 i.atEnd() == false; i++)
1952                         {
1953                                 // Get client and check that it is valid
1954                                 RemoteClient *client = i.getNode()->getValue();
1955                                 assert(client->peer_id == i.getNode()->getKey());
1956                                 if(client->serialization_version == SER_FMT_VER_INVALID)
1957                                         continue;
1958
1959                                 // Don't send if it's the same one
1960                                 if(peer_id == client->peer_id)
1961                                         continue;
1962
1963                                 // Send as reliable
1964                                 m_con.Send(client->peer_id, 0, reply, true);
1965                         }
1966                         
1967                         /*
1968                                 Update and send inventory
1969                         */
1970
1971                         if(g_settings.getBool("creative_mode") == false)
1972                         {
1973                                 /*
1974                                         Wear out tool
1975                                 */
1976                                 InventoryList *mlist = player->inventory.getList("main");
1977                                 if(mlist != NULL)
1978                                 {
1979                                         InventoryItem *item = mlist->getItem(item_i);
1980                                         if(item && (std::string)item->getName() == "ToolItem")
1981                                         {
1982                                                 ToolItem *titem = (ToolItem*)item;
1983                                                 std::string toolname = titem->getToolName();
1984
1985                                                 // Get digging properties for material and tool
1986                                                 DiggingProperties prop =
1987                                                                 getDiggingProperties(material, toolname);
1988
1989                                                 if(prop.diggable == false)
1990                                                 {
1991                                                         derr_server<<"Server: WARNING: Player digged"
1992                                                                         <<" with impossible material + tool"
1993                                                                         <<" combination"<<std::endl;
1994                                                 }
1995                                                 
1996                                                 bool weared_out = titem->addWear(prop.wear);
1997
1998                                                 if(weared_out)
1999                                                 {
2000                                                         mlist->deleteItem(item_i);
2001                                                 }
2002                                         }
2003                                 }
2004
2005                                 /*
2006                                         Add digged item to inventory
2007                                 */
2008                                 InventoryItem *item = new MaterialItem(material, 1);
2009                                 player->inventory.addItem("main", item);
2010
2011                                 /*
2012                                         Send inventory
2013                                 */
2014                                 SendInventory(player->peer_id);
2015                         }
2016
2017                         /*
2018                                 Remove the node
2019                                 (this takes some time so it is done after the quick stuff)
2020                         */
2021                         m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2022
2023 #if 0
2024                         /*
2025                                 Update water
2026                         */
2027                         
2028                         // Update water pressure around modification
2029                         // This also adds it to m_flow_active_nodes if appropriate
2030
2031                         MapVoxelManipulator v(&m_env.getMap());
2032                         v.m_disable_water_climb =
2033                                         g_settings.getBool("disable_water_climb");
2034                         
2035                         VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2036
2037                         try
2038                         {
2039                                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2040                         }
2041                         catch(ProcessingLimitException &e)
2042                         {
2043                                 dstream<<"Processing limit reached (1)"<<std::endl;
2044                         }
2045                         
2046                         v.blitBack(modified_blocks);
2047 #endif
2048                 }
2049                 
2050                 /*
2051                         1: place block
2052                 */
2053                 else if(action == 1)
2054                 {
2055
2056                         InventoryList *ilist = player->inventory.getList("main");
2057                         if(ilist == NULL)
2058                                 return;
2059
2060                         // Get item
2061                         InventoryItem *item = ilist->getItem(item_i);
2062                         
2063                         // If there is no item, it is not possible to add it anywhere
2064                         if(item == NULL)
2065                                 return;
2066                         
2067                         /*
2068                                 Handle material items
2069                         */
2070                         if(std::string("MaterialItem") == item->getName())
2071                         {
2072                                 try{
2073                                         // Don't add a node if this is not a free space
2074                                         MapNode n2 = m_env.getMap().getNode(p_over);
2075                                         if(content_buildable_to(n2.d) == false)
2076                                                 return;
2077                                 }
2078                                 catch(InvalidPositionException &e)
2079                                 {
2080                                         derr_server<<"Server: Ignoring ADDNODE: Node not found"
2081                                                         <<std::endl;
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_directional(n.d))
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_MATERIAL, CONTENT_COALSTONE);
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 Player *Server::emergePlayer(const char *name, const char *password)
3095 {
3096         /*
3097                 Try to get an existing player
3098         */
3099         Player *player = m_env.getPlayer(name);
3100         if(player != NULL)
3101         {
3102                 // Got one.
3103                 return player;
3104         }
3105         
3106         /*
3107                 Create a new player
3108         */
3109         {
3110                 player = new ServerRemotePlayer();
3111                 //player->peer_id = c.peer_id;
3112                 player->peer_id = PEER_ID_INEXISTENT;
3113                 player->updateName(name);
3114
3115                 /*
3116                         Set player position
3117                 */
3118                 
3119                 dstream<<"Server: Finding spawn place for player \""
3120                                 <<player->getName()<<"\""<<std::endl;
3121
3122 #if 1
3123                 v2s16 nodepos;
3124                 f32 groundheight = 0;
3125                 // Try to find a good place a few times
3126                 for(s32 i=0; i<500; i++)
3127                 {
3128                         s32 range = 1 + i;
3129                         // We're going to try to throw the player to this position
3130                         nodepos = v2s16(-range + (myrand()%(range*2)),
3131                                         -range + (myrand()%(range*2)));
3132                         v2s16 sectorpos = getNodeSectorPos(nodepos);
3133                         // Get sector
3134                         m_env.getMap().emergeSector(sectorpos);
3135                         // Get ground height at point
3136                         groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3137                         // The sector should have been generated -> groundheight exists
3138                         assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3139                         // Don't go underwater
3140                         if(groundheight < WATER_LEVEL)
3141                         {
3142                                 //dstream<<"-> Underwater"<<std::endl;
3143                                 continue;
3144                         }
3145 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3146                         // Get block at point
3147                         v3s16 nodepos3d;
3148                         nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3149                         v3s16 blockpos = getNodeBlockPos(nodepos3d);
3150                         ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3151                         // Don't go inside ground
3152                         try{
3153                                 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3154                                 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3155                                 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3156                                 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3157                                 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3158                                         || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3159                                 {
3160                                         dstream<<"-> Inside ground"<<std::endl;
3161                                         // In ground
3162                                         continue;
3163                                 }
3164                         }catch(InvalidPositionException &e)
3165                         {
3166                                 dstream<<"-> Invalid position"<<std::endl;
3167                                 // Ignore invalid position
3168                                 continue;
3169                         }
3170 #endif
3171                         // Found a good place
3172                         dstream<<"Searched through "<<i<<" places."<<std::endl;
3173                         break;
3174                 }
3175 #endif
3176                 
3177                 // If no suitable place was not found, go above water at least.
3178                 if(groundheight < WATER_LEVEL)
3179                         groundheight = WATER_LEVEL;
3180
3181                 player->setPosition(intToFloat(v3s16(
3182                                 nodepos.X,
3183                                 groundheight + 1,
3184                                 nodepos.Y
3185                 )));
3186
3187                 /*
3188                         Add player to environment
3189                 */
3190
3191                 m_env.addPlayer(player);
3192
3193                 /*
3194                         Add stuff to inventory
3195                 */
3196                 
3197                 if(g_settings.getBool("creative_mode"))
3198                 {
3199                         // Give some good picks
3200                         {
3201                                 InventoryItem *item = new ToolItem("STPick", 0);
3202                                 void* r = player->inventory.addItem("main", item);
3203                                 assert(r == NULL);
3204                         }
3205                         {
3206                                 InventoryItem *item = new ToolItem("MesePick", 0);
3207                                 void* r = player->inventory.addItem("main", item);
3208                                 assert(r == NULL);
3209                         }
3210
3211                         /*
3212                                 Give materials
3213                         */
3214                         assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3215                         
3216                         // add torch first
3217                         InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3218                         player->inventory.addItem("main", item);
3219                         
3220                         // Then others
3221                         for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3222                         {
3223                                 // Skip some materials
3224                                 if(i == CONTENT_WATER || i == CONTENT_TORCH)
3225                                         continue;
3226
3227                                 InventoryItem *item = new MaterialItem(i, 1);
3228                                 player->inventory.addItem("main", item);
3229                         }
3230                         // Sign
3231                         {
3232                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3233                                 void* r = player->inventory.addItem("main", item);
3234                                 assert(r == NULL);
3235                         }
3236                 }
3237                 else
3238                 {
3239                         /*{
3240                                 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3241                                 void* r = player->inventory.addItem("main", item);
3242                                 assert(r == NULL);
3243                         }
3244                         {
3245                                 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3246                                 void* r = player->inventory.addItem("main", item);
3247                                 assert(r == NULL);
3248                         }
3249                         {
3250                                 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3251                                 void* r = player->inventory.addItem("main", item);
3252                                 assert(r == NULL);
3253                         }
3254                         {
3255                                 InventoryItem *item = new CraftItem("Stick", 4);
3256                                 void* r = player->inventory.addItem("main", item);
3257                                 assert(r == NULL);
3258                         }
3259                         {
3260                                 InventoryItem *item = new ToolItem("WPick", 32000);
3261                                 void* r = player->inventory.addItem("main", item);
3262                                 assert(r == NULL);
3263                         }
3264                         {
3265                                 InventoryItem *item = new ToolItem("STPick", 32000);
3266                                 void* r = player->inventory.addItem("main", item);
3267                                 assert(r == NULL);
3268                         }*/
3269                         /*// Give some lights
3270                         {
3271                                 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3272                                 bool r = player->inventory.addItem("main", item);
3273                                 assert(r == true);
3274                         }
3275                         // and some signs
3276                         for(u16 i=0; i<4; i++)
3277                         {
3278                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3279                                 bool r = player->inventory.addItem("main", item);
3280                                 assert(r == true);
3281                         }*/
3282                         /*// Give some other stuff
3283                         {
3284                                 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3285                                 bool r = player->inventory.addItem("main", item);
3286                                 assert(r == true);
3287                         }*/
3288                 }
3289
3290                 return player;
3291         }
3292 }
3293
3294 #if 0
3295 void Server::UpdateBlockWaterPressure(MapBlock *block,
3296                         core::map<v3s16, MapBlock*> &modified_blocks)
3297 {
3298         MapVoxelManipulator v(&m_env.getMap());
3299         v.m_disable_water_climb =
3300                         g_settings.getBool("disable_water_climb");
3301         
3302         VoxelArea area(block->getPosRelative(),
3303                         block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3304
3305         try
3306         {
3307                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3308         }
3309         catch(ProcessingLimitException &e)
3310         {
3311                 dstream<<"Processing limit reached (1)"<<std::endl;
3312         }
3313         
3314         v.blitBack(modified_blocks);
3315 }
3316 #endif
3317
3318 void Server::handlePeerChange(PeerChange &c)
3319 {
3320         JMutexAutoLock envlock(m_env_mutex);
3321         JMutexAutoLock conlock(m_con_mutex);
3322         
3323         if(c.type == PEER_ADDED)
3324         {
3325                 /*
3326                         Add
3327                 */
3328
3329                 // Error check
3330                 core::map<u16, RemoteClient*>::Node *n;
3331                 n = m_clients.find(c.peer_id);
3332                 // The client shouldn't already exist
3333                 assert(n == NULL);
3334
3335                 // Create client
3336                 RemoteClient *client = new RemoteClient();
3337                 client->peer_id = c.peer_id;
3338                 m_clients.insert(client->peer_id, client);
3339
3340         } // PEER_ADDED
3341         else if(c.type == PEER_REMOVED)
3342         {
3343                 /*
3344                         Delete
3345                 */
3346
3347                 // Error check
3348                 core::map<u16, RemoteClient*>::Node *n;
3349                 n = m_clients.find(c.peer_id);
3350                 // The client should exist
3351                 assert(n != NULL);
3352                 
3353                 // Collect information about leaving in chat
3354                 std::wstring message;
3355                 {
3356                         std::wstring name = L"unknown";
3357                         Player *player = m_env.getPlayer(c.peer_id);
3358                         if(player != NULL)
3359                                 name = narrow_to_wide(player->getName());
3360                         
3361                         message += L"*** ";
3362                         message += name;
3363                         message += L" left game";
3364                         if(c.timeout)
3365                                 message += L" (timed out)";
3366                 }
3367
3368                 /*// Delete player
3369                 {
3370                         m_env.removePlayer(c.peer_id);
3371                 }*/
3372
3373                 // Set player client disconnected
3374                 {
3375                         Player *player = m_env.getPlayer(c.peer_id);
3376                         player->peer_id = 0;
3377                 }
3378                 
3379                 // Delete client
3380                 delete m_clients[c.peer_id];
3381                 m_clients.remove(c.peer_id);
3382
3383                 // Send player info to all remaining clients
3384                 SendPlayerInfos();
3385                 
3386                 // Send leave chat message to all remaining clients
3387                 BroadcastChatMessage(message);
3388                 
3389         } // PEER_REMOVED
3390         else
3391         {
3392                 assert(0);
3393         }
3394 }
3395
3396 void Server::handlePeerChanges()
3397 {
3398         while(m_peer_change_queue.size() > 0)
3399         {
3400                 PeerChange c = m_peer_change_queue.pop_front();
3401
3402                 dout_server<<"Server: Handling peer change: "
3403                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3404                                 <<std::endl;
3405
3406                 handlePeerChange(c);
3407         }
3408 }
3409
3410