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