]> git.lizzy.rs Git - dragonfireclient.git/blob - src/server.cpp
716ff77c6bfa6f1311e78d10c23003599f235683
[dragonfireclient.git] / src / server.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /*
21 (c) 2010 Perttu Ahola <celeron55@gmail.com>
22 */
23
24 #include "server.h"
25 #include "utility.h"
26 #include <iostream>
27 #include "clientserver.h"
28 #include "map.h"
29 #include "jmutexautolock.h"
30 #include "main.h"
31 #include "constants.h"
32 #include "voxel.h"
33 #include "materials.h"
34
35 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
36
37 void * ServerThread::Thread()
38 {
39         ThreadStarted();
40
41         DSTACK(__FUNCTION_NAME);
42
43         BEGIN_DEBUG_EXCEPTION_HANDLER
44
45         while(getRun())
46         {
47                 try{
48                         m_server->AsyncRunStep();
49                 
50                         //dout_server<<"Running m_server->Receive()"<<std::endl;
51                         m_server->Receive();
52                 }
53                 catch(con::NoIncomingDataException &e)
54                 {
55                 }
56                 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                                                 <<std::endl;
1971                                 return;
1972                         }
1973                         
1974                         //TODO: Send to only other clients
1975                         
1976                         /*
1977                                 Send the removal to all other clients
1978                         */
1979
1980                         // Create packet
1981                         u32 replysize = 8;
1982                         SharedBuffer<u8> reply(replysize);
1983                         writeU16(&reply[0], TOCLIENT_REMOVENODE);
1984                         writeS16(&reply[2], p_under.X);
1985                         writeS16(&reply[4], p_under.Y);
1986                         writeS16(&reply[6], p_under.Z);
1987
1988                         for(core::map<u16, RemoteClient*>::Iterator
1989                                 i = m_clients.getIterator();
1990                                 i.atEnd() == false; i++)
1991                         {
1992                                 // Get client and check that it is valid
1993                                 RemoteClient *client = i.getNode()->getValue();
1994                                 assert(client->peer_id == i.getNode()->getKey());
1995                                 if(client->serialization_version == SER_FMT_VER_INVALID)
1996                                         continue;
1997
1998                                 // Don't send if it's the same one
1999                                 if(peer_id == client->peer_id)
2000                                         continue;
2001
2002                                 // Send as reliable
2003                                 m_con.Send(client->peer_id, 0, reply, true);
2004                         }
2005                         
2006                         /*
2007                                 Update and send inventory
2008                         */
2009
2010                         if(g_settings.getBool("creative_mode") == false)
2011                         {
2012                                 /*
2013                                         Wear out tool
2014                                 */
2015                                 InventoryList *mlist = player->inventory.getList("main");
2016                                 if(mlist != NULL)
2017                                 {
2018                                         InventoryItem *item = mlist->getItem(item_i);
2019                                         if(item && (std::string)item->getName() == "ToolItem")
2020                                         {
2021                                                 ToolItem *titem = (ToolItem*)item;
2022                                                 std::string toolname = titem->getToolName();
2023
2024                                                 // Get digging properties for material and tool
2025                                                 DiggingProperties prop =
2026                                                                 getDiggingProperties(material, toolname);
2027
2028                                                 if(prop.diggable == false)
2029                                                 {
2030                                                         derr_server<<"Server: WARNING: Player digged"
2031                                                                         <<" with impossible material + tool"
2032                                                                         <<" combination"<<std::endl;
2033                                                 }
2034                                                 
2035                                                 bool weared_out = titem->addWear(prop.wear);
2036
2037                                                 if(weared_out)
2038                                                 {
2039                                                         mlist->deleteItem(item_i);
2040                                                 }
2041                                         }
2042                                 }
2043
2044                                 /*
2045                                         Add digged item to inventory
2046                                 */
2047                                 InventoryItem *item = new MaterialItem(material, 1);
2048                                 player->inventory.addItem("main", item);
2049
2050                                 /*
2051                                         Send inventory
2052                                 */
2053                                 SendInventory(player->peer_id);
2054                         }
2055
2056                         /*
2057                                 Remove the node
2058                                 (this takes some time so it is done after the quick stuff)
2059                         */
2060                         m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2061
2062 #if 0
2063                         /*
2064                                 Update water
2065                         */
2066                         
2067                         // Update water pressure around modification
2068                         // This also adds it to m_flow_active_nodes if appropriate
2069
2070                         MapVoxelManipulator v(&m_env.getMap());
2071                         v.m_disable_water_climb =
2072                                         g_settings.getBool("disable_water_climb");
2073                         
2074                         VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2075
2076                         try
2077                         {
2078                                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2079                         }
2080                         catch(ProcessingLimitException &e)
2081                         {
2082                                 dstream<<"Processing limit reached (1)"<<std::endl;
2083                         }
2084                         
2085                         v.blitBack(modified_blocks);
2086 #endif
2087                 }
2088                 
2089                 /*
2090                         1: place block
2091                 */
2092                 else if(action == 1)
2093                 {
2094
2095                         InventoryList *ilist = player->inventory.getList("main");
2096                         if(ilist == NULL)
2097                                 return;
2098
2099                         // Get item
2100                         InventoryItem *item = ilist->getItem(item_i);
2101                         
2102                         // If there is no item, it is not possible to add it anywhere
2103                         if(item == NULL)
2104                                 return;
2105                         
2106                         /*
2107                                 Handle material items
2108                         */
2109                         if(std::string("MaterialItem") == item->getName())
2110                         {
2111                                 try{
2112                                         // Don't add a node if this is not a free space
2113                                         MapNode n2 = m_env.getMap().getNode(p_over);
2114                                         if(content_buildable_to(n2.d) == false)
2115                                                 return;
2116                                 }
2117                                 catch(InvalidPositionException &e)
2118                                 {
2119                                         derr_server<<"Server: Ignoring ADDNODE: Node not found"
2120                                                         <<std::endl;
2121                                         return;
2122                                 }
2123
2124                                 // Reset build time counter
2125                                 getClient(peer->id)->m_time_from_building.set(0.0);
2126                                 
2127                                 // Create node data
2128                                 MaterialItem *mitem = (MaterialItem*)item;
2129                                 MapNode n;
2130                                 n.d = mitem->getMaterial();
2131                                 if(content_directional(n.d))
2132                                         n.dir = packDir(p_under - p_over);
2133
2134 #if 1
2135                                 // Create packet
2136                                 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2137                                 SharedBuffer<u8> reply(replysize);
2138                                 writeU16(&reply[0], TOCLIENT_ADDNODE);
2139                                 writeS16(&reply[2], p_over.X);
2140                                 writeS16(&reply[4], p_over.Y);
2141                                 writeS16(&reply[6], p_over.Z);
2142                                 n.serialize(&reply[8], peer_ser_ver);
2143                                 // Send as reliable
2144                                 m_con.SendToAll(0, reply, true);
2145                                 
2146                                 /*
2147                                         Handle inventory
2148                                 */
2149                                 InventoryList *ilist = player->inventory.getList("main");
2150                                 if(g_settings.getBool("creative_mode") == false && ilist)
2151                                 {
2152                                         // Remove from inventory and send inventory
2153                                         if(mitem->getCount() == 1)
2154                                                 ilist->deleteItem(item_i);
2155                                         else
2156                                                 mitem->remove(1);
2157                                         // Send inventory
2158                                         SendInventory(peer_id);
2159                                 }
2160                                 
2161                                 /*
2162                                         Add node.
2163
2164                                         This takes some time so it is done after the quick stuff
2165                                 */
2166                                 core::map<v3s16, MapBlock*> modified_blocks;
2167                                 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2168 #endif
2169 #if 0
2170                                 /*
2171                                         Handle inventory
2172                                 */
2173                                 InventoryList *ilist = player->inventory.getList("main");
2174                                 if(g_settings.getBool("creative_mode") == false && ilist)
2175                                 {
2176                                         // Remove from inventory and send inventory
2177                                         if(mitem->getCount() == 1)
2178                                                 ilist->deleteItem(item_i);
2179                                         else
2180                                                 mitem->remove(1);
2181                                         // Send inventory
2182                                         SendInventory(peer_id);
2183                                 }
2184
2185                                 /*
2186                                         Add node.
2187
2188                                         This takes some time so it is done after the quick stuff
2189                                 */
2190                                 core::map<v3s16, MapBlock*> modified_blocks;
2191                                 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2192
2193                                 /*
2194                                         Set the modified blocks unsent for all the clients
2195                                 */
2196                                 
2197                                 //JMutexAutoLock lock2(m_con_mutex);
2198
2199                                 for(core::map<u16, RemoteClient*>::Iterator
2200                                                 i = m_clients.getIterator();
2201                                                 i.atEnd() == false; i++)
2202                                 {
2203                                         RemoteClient *client = i.getNode()->getValue();
2204                                         
2205                                         if(modified_blocks.size() > 0)
2206                                         {
2207                                                 // Remove block from sent history
2208                                                 client->SetBlocksNotSent(modified_blocks);
2209                                         }
2210                                 }
2211 #endif
2212
2213 #if 0
2214                                 /*
2215                                         Update water
2216                                 */
2217                                 
2218                                 // Update water pressure around modification
2219                                 // This also adds it to m_flow_active_nodes if appropriate
2220
2221                                 MapVoxelManipulator v(&m_env.getMap());
2222                                 v.m_disable_water_climb =
2223                                                 g_settings.getBool("disable_water_climb");
2224                                 
2225                                 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2226
2227                                 try
2228                                 {
2229                                         v.updateAreaWaterPressure(area, m_flow_active_nodes);
2230                                 }
2231                                 catch(ProcessingLimitException &e)
2232                                 {
2233                                         dstream<<"Processing limit reached (1)"<<std::endl;
2234                                 }
2235                                 
2236                                 v.blitBack(modified_blocks);
2237 #endif
2238                         }
2239                         /*
2240                                 Handle other items
2241                         */
2242                         else
2243                         {
2244                                 v3s16 blockpos = getNodeBlockPos(p_over);
2245
2246                                 MapBlock *block = NULL;
2247                                 try
2248                                 {
2249                                         block = m_env.getMap().getBlockNoCreate(blockpos);
2250                                 }
2251                                 catch(InvalidPositionException &e)
2252                                 {
2253                                         derr_server<<"Error while placing object: "
2254                                                         "block not found"<<std::endl;
2255                                         return;
2256                                 }
2257
2258                                 v3s16 block_pos_i_on_map = block->getPosRelative();
2259                                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2260
2261                                 v3f pos = intToFloat(p_over);
2262                                 pos -= block_pos_f_on_map;
2263                                 
2264                                 /*dout_server<<"pos="
2265                                                 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2266                                                 <<std::endl;*/
2267
2268                                 MapBlockObject *obj = NULL;
2269
2270                                 /*
2271                                         Handle block object items
2272                                 */
2273                                 if(std::string("MBOItem") == item->getName())
2274                                 {
2275                                         MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2276
2277                                         /*dout_server<<"Trying to place a MapBlockObjectItem: "
2278                                                         "inventorystring=\""
2279                                                         <<oitem->getInventoryString()
2280                                                         <<"\""<<std::endl;*/
2281                                                         
2282                                         obj = oitem->createObject
2283                                                         (pos, player->getYaw(), player->getPitch());
2284                                 }
2285                                 /*
2286                                         Handle other items
2287                                 */
2288                                 else
2289                                 {
2290                                         dout_server<<"Placing a miscellaneous item on map"
2291                                                         <<std::endl;
2292                                         /*
2293                                                 Create an ItemObject that contains the item.
2294                                         */
2295                                         ItemObject *iobj = new ItemObject(NULL, -1, pos);
2296                                         std::ostringstream os(std::ios_base::binary);
2297                                         item->serialize(os);
2298                                         dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2299                                         iobj->setItemString(os.str());
2300                                         obj = iobj;
2301                                 }
2302
2303                                 if(obj == NULL)
2304                                 {
2305                                         derr_server<<"WARNING: item resulted in NULL object, "
2306                                                         <<"not placing onto map"
2307                                                         <<std::endl;
2308                                 }
2309                                 else
2310                                 {
2311                                         block->addObject(obj);
2312
2313                                         dout_server<<"Placed object"<<std::endl;
2314
2315                                         InventoryList *ilist = player->inventory.getList("main");
2316                                         if(g_settings.getBool("creative_mode") == false && ilist)
2317                                         {
2318                                                 // Remove from inventory and send inventory
2319                                                 ilist->deleteItem(item_i);
2320                                                 // Send inventory
2321                                                 SendInventory(peer_id);
2322                                         }
2323                                 }
2324                         }
2325
2326                 } // action == 1
2327
2328                 /*
2329                         Catch invalid actions
2330                 */
2331                 else
2332                 {
2333                         derr_server<<"WARNING: Server: Invalid action "
2334                                         <<action<<std::endl;
2335                 }
2336         }
2337 #if 0
2338         else if(command == TOSERVER_RELEASE)
2339         {
2340                 if(datasize < 3)
2341                         return;
2342                 /*
2343                         length: 3
2344                         [0] u16 command
2345                         [2] u8 button
2346                 */
2347                 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2348         }
2349 #endif
2350         else if(command == TOSERVER_SIGNTEXT)
2351         {
2352                 /*
2353                         u16 command
2354                         v3s16 blockpos
2355                         s16 id
2356                         u16 textlen
2357                         textdata
2358                 */
2359                 std::string datastring((char*)&data[2], datasize-2);
2360                 std::istringstream is(datastring, std::ios_base::binary);
2361                 u8 buf[6];
2362                 // Read stuff
2363                 is.read((char*)buf, 6);
2364                 v3s16 blockpos = readV3S16(buf);
2365                 is.read((char*)buf, 2);
2366                 s16 id = readS16(buf);
2367                 is.read((char*)buf, 2);
2368                 u16 textlen = readU16(buf);
2369                 std::string text;
2370                 for(u16 i=0; i<textlen; i++)
2371                 {
2372                         is.read((char*)buf, 1);
2373                         text += (char)buf[0];
2374                 }
2375
2376                 MapBlock *block = NULL;
2377                 try
2378                 {
2379                         block = m_env.getMap().getBlockNoCreate(blockpos);
2380                 }
2381                 catch(InvalidPositionException &e)
2382                 {
2383                         derr_server<<"Error while setting sign text: "
2384                                         "block not found"<<std::endl;
2385                         return;
2386                 }
2387
2388                 MapBlockObject *obj = block->getObject(id);
2389                 if(obj == NULL)
2390                 {
2391                         derr_server<<"Error while setting sign text: "
2392                                         "object not found"<<std::endl;
2393                         return;
2394                 }
2395                 
2396                 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2397                 {
2398                         derr_server<<"Error while setting sign text: "
2399                                         "object is not a sign"<<std::endl;
2400                         return;
2401                 }
2402
2403                 ((SignObject*)obj)->setText(text);
2404
2405                 obj->getBlock()->setChangedFlag();
2406         }
2407         else if(command == TOSERVER_INVENTORY_ACTION)
2408         {
2409                 /*// Ignore inventory changes if in creative mode
2410                 if(g_settings.getBool("creative_mode") == true)
2411                 {
2412                         dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2413                                         <<std::endl;
2414                         return;
2415                 }*/
2416                 // Strip command and create a stream
2417                 std::string datastring((char*)&data[2], datasize-2);
2418                 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2419                 std::istringstream is(datastring, std::ios_base::binary);
2420                 // Create an action
2421                 InventoryAction *a = InventoryAction::deSerialize(is);
2422                 if(a != NULL)
2423                 {
2424                         /*
2425                                 Handle craftresult specially if not in creative mode
2426                         */
2427                         bool disable_action = false;
2428                         if(a->getType() == IACTION_MOVE
2429                                         && g_settings.getBool("creative_mode") == false)
2430                         {
2431                                 IMoveAction *ma = (IMoveAction*)a;
2432                                 // Don't allow moving anything to craftresult
2433                                 if(ma->to_name == "craftresult")
2434                                 {
2435                                         // Do nothing
2436                                         disable_action = true;
2437                                 }
2438                                 // When something is removed from craftresult
2439                                 if(ma->from_name == "craftresult")
2440                                 {
2441                                         disable_action = true;
2442                                         // Remove stuff from craft
2443                                         InventoryList *clist = player->inventory.getList("craft");
2444                                         if(clist)
2445                                         {
2446                                                 u16 count = ma->count;
2447                                                 if(count == 0)
2448                                                         count = 1;
2449                                                 clist->decrementMaterials(count);
2450                                         }
2451                                         // Do action
2452                                         // Feed action to player inventory
2453                                         a->apply(&player->inventory);
2454                                         // Eat it
2455                                         delete a;
2456                                         // If something appeared in craftresult, throw it
2457                                         // in the main list
2458                                         InventoryList *rlist = player->inventory.getList("craftresult");
2459                                         InventoryList *mlist = player->inventory.getList("main");
2460                                         if(rlist && mlist && rlist->getUsedSlots() == 1)
2461                                         {
2462                                                 InventoryItem *item1 = rlist->changeItem(0, NULL);
2463                                                 mlist->addItem(item1);
2464                                         }
2465                                 }
2466                         }
2467                         if(disable_action == false)
2468                         {
2469                                 // Feed action to player inventory
2470                                 a->apply(&player->inventory);
2471                                 // Eat it
2472                                 delete a;
2473                         }
2474                         // Send inventory
2475                         SendInventory(player->peer_id);
2476                 }
2477                 else
2478                 {
2479                         dstream<<"TOSERVER_INVENTORY_ACTION: "
2480                                         <<"InventoryAction::deSerialize() returned NULL"
2481                                         <<std::endl;
2482                 }
2483         }
2484         else if(command == TOSERVER_CHAT_MESSAGE)
2485         {
2486                 /*
2487                         u16 command
2488                         u16 length
2489                         wstring message
2490                 */
2491                 u8 buf[6];
2492                 std::string datastring((char*)&data[2], datasize-2);
2493                 std::istringstream is(datastring, std::ios_base::binary);
2494                 
2495                 // Read stuff
2496                 is.read((char*)buf, 2);
2497                 u16 len = readU16(buf);
2498                 
2499                 std::wstring message;
2500                 for(u16 i=0; i<len; i++)
2501                 {
2502                         is.read((char*)buf, 2);
2503                         message += (wchar_t)readU16(buf);
2504                 }
2505
2506                 // Get player name of this client
2507                 std::wstring name = narrow_to_wide(player->getName());
2508
2509                 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2510                 
2511                 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2512
2513                 /*
2514                         Send the message to all other clients
2515                 */
2516                 for(core::map<u16, RemoteClient*>::Iterator
2517                         i = m_clients.getIterator();
2518                         i.atEnd() == false; i++)
2519                 {
2520                         // Get client and check that it is valid
2521                         RemoteClient *client = i.getNode()->getValue();
2522                         assert(client->peer_id == i.getNode()->getKey());
2523                         if(client->serialization_version == SER_FMT_VER_INVALID)
2524                                 continue;
2525
2526                         // Don't send if it's the same one
2527                         if(peer_id == client->peer_id)
2528                                 continue;
2529
2530                         SendChatMessage(client->peer_id, line);
2531                 }
2532         }
2533         else
2534         {
2535                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2536                                 "unknown command "<<command<<std::endl;
2537         }
2538         
2539         } //try
2540         catch(SendFailedException &e)
2541         {
2542                 derr_server<<"Server::ProcessData(): SendFailedException: "
2543                                 <<"what="<<e.what()
2544                                 <<std::endl;
2545         }
2546 }
2547
2548 /*void Server::Send(u16 peer_id, u16 channelnum,
2549                 SharedBuffer<u8> data, bool reliable)
2550 {
2551         JMutexAutoLock lock(m_con_mutex);
2552         m_con.Send(peer_id, channelnum, data, reliable);
2553 }*/
2554
2555 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2556 {
2557         DSTACK(__FUNCTION_NAME);
2558         /*
2559                 Create a packet with the block in the right format
2560         */
2561         
2562         std::ostringstream os(std::ios_base::binary);
2563         block->serialize(os, ver);
2564         std::string s = os.str();
2565         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2566
2567         u32 replysize = 8 + blockdata.getSize();
2568         SharedBuffer<u8> reply(replysize);
2569         v3s16 p = block->getPos();
2570         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2571         writeS16(&reply[2], p.X);
2572         writeS16(&reply[4], p.Y);
2573         writeS16(&reply[6], p.Z);
2574         memcpy(&reply[8], *blockdata, blockdata.getSize());
2575
2576         /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2577                         <<":  \tpacket size: "<<replysize<<std::endl;*/
2578         
2579         /*
2580                 Send packet
2581         */
2582         m_con.Send(peer_id, 1, reply, true);
2583 }
2584
2585 core::list<PlayerInfo> Server::getPlayerInfo()
2586 {
2587         DSTACK(__FUNCTION_NAME);
2588         JMutexAutoLock envlock(m_env_mutex);
2589         JMutexAutoLock conlock(m_con_mutex);
2590         
2591         core::list<PlayerInfo> list;
2592
2593         core::list<Player*> players = m_env.getPlayers();
2594         
2595         core::list<Player*>::Iterator i;
2596         for(i = players.begin();
2597                         i != players.end(); i++)
2598         {
2599                 PlayerInfo info;
2600
2601                 Player *player = *i;
2602
2603                 try{
2604                         con::Peer *peer = m_con.GetPeer(player->peer_id);
2605                         // Copy info from peer to info struct
2606                         info.id = peer->id;
2607                         info.address = peer->address;
2608                         info.avg_rtt = peer->avg_rtt;
2609                 }
2610                 catch(con::PeerNotFoundException &e)
2611                 {
2612                         // Set dummy peer info
2613                         info.id = 0;
2614                         info.address = Address(0,0,0,0,0);
2615                         info.avg_rtt = 0.0;
2616                 }
2617
2618                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2619                 info.position = player->getPosition();
2620
2621                 list.push_back(info);
2622         }
2623
2624         return list;
2625 }
2626
2627 void Server::peerAdded(con::Peer *peer)
2628 {
2629         DSTACK(__FUNCTION_NAME);
2630         dout_server<<"Server::peerAdded(): peer->id="
2631                         <<peer->id<<std::endl;
2632         
2633         PeerChange c;
2634         c.type = PEER_ADDED;
2635         c.peer_id = peer->id;
2636         c.timeout = false;
2637         m_peer_change_queue.push_back(c);
2638 }
2639
2640 void Server::deletingPeer(con::Peer *peer, bool timeout)
2641 {
2642         DSTACK(__FUNCTION_NAME);
2643         dout_server<<"Server::deletingPeer(): peer->id="
2644                         <<peer->id<<", timeout="<<timeout<<std::endl;
2645         
2646         PeerChange c;
2647         c.type = PEER_REMOVED;
2648         c.peer_id = peer->id;
2649         c.timeout = timeout;
2650         m_peer_change_queue.push_back(c);
2651 }
2652
2653 void Server::SendObjectData(float dtime)
2654 {
2655         DSTACK(__FUNCTION_NAME);
2656
2657         core::map<v3s16, bool> stepped_blocks;
2658         
2659         for(core::map<u16, RemoteClient*>::Iterator
2660                 i = m_clients.getIterator();
2661                 i.atEnd() == false; i++)
2662         {
2663                 u16 peer_id = i.getNode()->getKey();
2664                 RemoteClient *client = i.getNode()->getValue();
2665                 assert(client->peer_id == peer_id);
2666                 
2667                 if(client->serialization_version == SER_FMT_VER_INVALID)
2668                         continue;
2669                 
2670                 client->SendObjectData(this, dtime, stepped_blocks);
2671         }
2672 }
2673
2674 void Server::SendPlayerInfos()
2675 {
2676         DSTACK(__FUNCTION_NAME);
2677
2678         //JMutexAutoLock envlock(m_env_mutex);
2679         
2680         // Get connected players
2681         core::list<Player*> players = m_env.getPlayers(true);
2682         
2683         u32 player_count = players.getSize();
2684         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2685
2686         SharedBuffer<u8> data(datasize);
2687         writeU16(&data[0], TOCLIENT_PLAYERINFO);
2688         
2689         u32 start = 2;
2690         core::list<Player*>::Iterator i;
2691         for(i = players.begin();
2692                         i != players.end(); i++)
2693         {
2694                 Player *player = *i;
2695
2696                 /*dstream<<"Server sending player info for player with "
2697                                 "peer_id="<<player->peer_id<<std::endl;*/
2698                 
2699                 writeU16(&data[start], player->peer_id);
2700                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2701                 start += 2+PLAYERNAME_SIZE;
2702         }
2703
2704         //JMutexAutoLock conlock(m_con_mutex);
2705
2706         // Send as reliable
2707         m_con.SendToAll(0, data, true);
2708 }
2709
2710 enum ItemSpecType
2711 {
2712         ITEM_NONE,
2713         ITEM_MATERIAL,
2714         ITEM_CRAFT,
2715         ITEM_TOOL,
2716         ITEM_MBO
2717 };
2718
2719 struct ItemSpec
2720 {
2721         ItemSpec():
2722                 type(ITEM_NONE)
2723         {
2724         }
2725         ItemSpec(enum ItemSpecType a_type, std::string a_name):
2726                 type(a_type),
2727                 name(a_name),
2728                 num(65535)
2729         {
2730         }
2731         ItemSpec(enum ItemSpecType a_type, u16 a_num):
2732                 type(a_type),
2733                 name(""),
2734                 num(a_num)
2735         {
2736         }
2737         enum ItemSpecType type;
2738         // Only other one of these is used
2739         std::string name;
2740         u16 num;
2741 };
2742
2743 /*
2744         items: a pointer to an array of 9 pointers to items
2745         specs: a pointer to an array of 9 ItemSpecs
2746 */
2747 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2748 {
2749         u16 items_min_x = 100;
2750         u16 items_max_x = 100;
2751         u16 items_min_y = 100;
2752         u16 items_max_y = 100;
2753         for(u16 y=0; y<3; y++)
2754         for(u16 x=0; x<3; x++)
2755         {
2756                 if(items[y*3 + x] == NULL)
2757                         continue;
2758                 if(items_min_x == 100 || x < items_min_x)
2759                         items_min_x = x;
2760                 if(items_min_y == 100 || y < items_min_y)
2761                         items_min_y = y;
2762                 if(items_max_x == 100 || x > items_max_x)
2763                         items_max_x = x;
2764                 if(items_max_y == 100 || y > items_max_y)
2765                         items_max_y = y;
2766         }
2767         // No items at all, just return false
2768         if(items_min_x == 100)
2769                 return false;
2770         
2771         u16 items_w = items_max_x - items_min_x + 1;
2772         u16 items_h = items_max_y - items_min_y + 1;
2773
2774         u16 specs_min_x = 100;
2775         u16 specs_max_x = 100;
2776         u16 specs_min_y = 100;
2777         u16 specs_max_y = 100;
2778         for(u16 y=0; y<3; y++)
2779         for(u16 x=0; x<3; x++)
2780         {
2781                 if(specs[y*3 + x].type == ITEM_NONE)
2782                         continue;
2783                 if(specs_min_x == 100 || x < specs_min_x)
2784                         specs_min_x = x;
2785                 if(specs_min_y == 100 || y < specs_min_y)
2786                         specs_min_y = y;
2787                 if(specs_max_x == 100 || x > specs_max_x)
2788                         specs_max_x = x;
2789                 if(specs_max_y == 100 || y > specs_max_y)
2790                         specs_max_y = y;
2791         }
2792         // No specs at all, just return false
2793         if(specs_min_x == 100)
2794                 return false;
2795
2796         u16 specs_w = specs_max_x - specs_min_x + 1;
2797         u16 specs_h = specs_max_y - specs_min_y + 1;
2798
2799         // Different sizes
2800         if(items_w != specs_w || items_h != specs_h)
2801                 return false;
2802
2803         for(u16 y=0; y<specs_h; y++)
2804         for(u16 x=0; x<specs_w; x++)
2805         {
2806                 u16 items_x = items_min_x + x;
2807                 u16 items_y = items_min_y + y;
2808                 u16 specs_x = specs_min_x + x;
2809                 u16 specs_y = specs_min_y + y;
2810                 InventoryItem *item = items[items_y * 3 + items_x];
2811                 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2812                 
2813                 if(spec.type == ITEM_NONE)
2814                 {
2815                         // Has to be no item
2816                         if(item != NULL)
2817                                 return false;
2818                         continue;
2819                 }
2820                 
2821                 // There should be an item
2822                 if(item == NULL)
2823                         return false;
2824
2825                 std::string itemname = item->getName();
2826
2827                 if(spec.type == ITEM_MATERIAL)
2828                 {
2829                         if(itemname != "MaterialItem")
2830                                 return false;
2831                         MaterialItem *mitem = (MaterialItem*)item;
2832                         if(mitem->getMaterial() != spec.num)
2833                                 return false;
2834                 }
2835                 else if(spec.type == ITEM_CRAFT)
2836                 {
2837                         if(itemname != "CraftItem")
2838                                 return false;
2839                         CraftItem *mitem = (CraftItem*)item;
2840                         if(mitem->getSubName() != spec.name)
2841                                 return false;
2842                 }
2843                 else if(spec.type == ITEM_TOOL)
2844                 {
2845                         // Not supported yet
2846                         assert(0);
2847                 }
2848                 else if(spec.type == ITEM_MBO)
2849                 {
2850                         // Not supported yet
2851                         assert(0);
2852                 }
2853                 else
2854                 {
2855                         // Not supported yet
2856                         assert(0);
2857                 }
2858         }
2859
2860         return true;
2861 }
2862
2863 void Server::SendInventory(u16 peer_id)
2864 {
2865         DSTACK(__FUNCTION_NAME);
2866         
2867         Player* player = m_env.getPlayer(peer_id);
2868
2869         /*
2870                 Calculate crafting stuff
2871         */
2872         if(g_settings.getBool("creative_mode") == false)
2873         {
2874                 InventoryList *clist = player->inventory.getList("craft");
2875                 InventoryList *rlist = player->inventory.getList("craftresult");
2876                 if(rlist)
2877                 {
2878                         rlist->clearItems();
2879                 }
2880                 if(clist && rlist)
2881                 {
2882                         InventoryItem *items[9];
2883                         for(u16 i=0; i<9; i++)
2884                         {
2885                                 items[i] = clist->getItem(i);
2886                         }
2887                         
2888                         bool found = false;
2889
2890                         // Wood
2891                         if(!found)
2892                         {
2893                                 ItemSpec specs[9];
2894                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2895                                 if(checkItemCombination(items, specs))
2896                                 {
2897                                         rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2898                                         found = true;
2899                                 }
2900                         }
2901
2902                         // Stick
2903                         if(!found)
2904                         {
2905                                 ItemSpec specs[9];
2906                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2907                                 if(checkItemCombination(items, specs))
2908                                 {
2909                                         rlist->addItem(new CraftItem("Stick", 4));
2910                                         found = true;
2911                                 }
2912                         }
2913
2914                         // Sign
2915                         if(!found)
2916                         {
2917                                 ItemSpec specs[9];
2918                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2919                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2920                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2921                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2922                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2923                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2924                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2925                                 if(checkItemCombination(items, specs))
2926                                 {
2927                                         rlist->addItem(new MapBlockObjectItem("Sign"));
2928                                         found = true;
2929                                 }
2930                         }
2931
2932                         // Torch
2933                         if(!found)
2934                         {
2935                                 ItemSpec specs[9];
2936                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COALSTONE);
2937                                 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2938                                 if(checkItemCombination(items, specs))
2939                                 {
2940                                         rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2941                                         found = true;
2942                                 }
2943                         }
2944
2945                         // Wooden pick
2946                         if(!found)
2947                         {
2948                                 ItemSpec specs[9];
2949                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2950                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2951                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2952                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2953                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2954                                 if(checkItemCombination(items, specs))
2955                                 {
2956                                         rlist->addItem(new ToolItem("WPick", 0));
2957                                         found = true;
2958                                 }
2959                         }
2960
2961                         // Stone pick
2962                         if(!found)
2963                         {
2964                                 ItemSpec specs[9];
2965                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2966                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2967                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2968                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2969                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2970                                 if(checkItemCombination(items, specs))
2971                                 {
2972                                         rlist->addItem(new ToolItem("STPick", 0));
2973                                         found = true;
2974                                 }
2975                         }
2976
2977                         // Mese pick
2978                         if(!found)
2979                         {
2980                                 ItemSpec specs[9];
2981                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2982                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2983                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2984                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2985                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2986                                 if(checkItemCombination(items, specs))
2987                                 {
2988                                         rlist->addItem(new ToolItem("MesePick", 0));
2989                                         found = true;
2990                                 }
2991                         }
2992                 }
2993         } // if creative_mode == false
2994
2995         /*
2996                 Serialize it
2997         */
2998
2999         std::ostringstream os;
3000         //os.imbue(std::locale("C"));
3001
3002         player->inventory.serialize(os);
3003
3004         std::string s = os.str();
3005         
3006         SharedBuffer<u8> data(s.size()+2);
3007         writeU16(&data[0], TOCLIENT_INVENTORY);
3008         memcpy(&data[2], s.c_str(), s.size());
3009         
3010         // Send as reliable
3011         m_con.Send(peer_id, 0, data, true);
3012 }
3013
3014 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3015 {
3016         DSTACK(__FUNCTION_NAME);
3017         
3018         std::ostringstream os(std::ios_base::binary);
3019         u8 buf[12];
3020         
3021         // Write command
3022         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3023         os.write((char*)buf, 2);
3024         
3025         // Write length
3026         writeU16(buf, message.size());
3027         os.write((char*)buf, 2);
3028         
3029         // Write string
3030         for(u32 i=0; i<message.size(); i++)
3031         {
3032                 u16 w = message[i];
3033                 writeU16(buf, w);
3034                 os.write((char*)buf, 2);
3035         }
3036         
3037         // Make data buffer
3038         std::string s = os.str();
3039         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3040         // Send as reliable
3041         m_con.Send(peer_id, 0, data, true);
3042 }
3043
3044 void Server::BroadcastChatMessage(const std::wstring &message)
3045 {
3046         for(core::map<u16, RemoteClient*>::Iterator
3047                 i = m_clients.getIterator();
3048                 i.atEnd() == false; i++)
3049         {
3050                 // Get client and check that it is valid
3051                 RemoteClient *client = i.getNode()->getValue();
3052                 assert(client->peer_id == i.getNode()->getKey());
3053                 if(client->serialization_version == SER_FMT_VER_INVALID)
3054                         continue;
3055
3056                 SendChatMessage(client->peer_id, message);
3057         }
3058 }
3059
3060 void Server::SendBlocks(float dtime)
3061 {
3062         DSTACK(__FUNCTION_NAME);
3063
3064         JMutexAutoLock envlock(m_env_mutex);
3065
3066         core::array<PrioritySortedBlockTransfer> queue;
3067
3068         s32 total_sending = 0;
3069
3070         for(core::map<u16, RemoteClient*>::Iterator
3071                 i = m_clients.getIterator();
3072                 i.atEnd() == false; i++)
3073         {
3074                 RemoteClient *client = i.getNode()->getValue();
3075                 assert(client->peer_id == i.getNode()->getKey());
3076
3077                 total_sending += client->SendingCount();
3078                 
3079                 if(client->serialization_version == SER_FMT_VER_INVALID)
3080                         continue;
3081                 
3082                 client->GetNextBlocks(this, dtime, queue);
3083         }
3084
3085         // Sort.
3086         // Lowest priority number comes first.
3087         // Lowest is most important.
3088         queue.sort();
3089
3090         JMutexAutoLock conlock(m_con_mutex);
3091
3092         for(u32 i=0; i<queue.size(); i++)
3093         {
3094                 //TODO: Calculate limit dynamically
3095                 if(total_sending >= g_settings.getS32
3096                                 ("max_simultaneous_block_sends_server_total"))
3097                         break;
3098                 
3099                 PrioritySortedBlockTransfer q = queue[i];
3100
3101                 MapBlock *block = NULL;
3102                 try
3103                 {
3104                         block = m_env.getMap().getBlockNoCreate(q.pos);
3105                 }
3106                 catch(InvalidPositionException &e)
3107                 {
3108                         continue;
3109                 }
3110
3111                 RemoteClient *client = getClient(q.peer_id);
3112
3113                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3114
3115                 client->SentBlock(q.pos);
3116
3117                 total_sending++;
3118         }
3119 }
3120
3121
3122 RemoteClient* Server::getClient(u16 peer_id)
3123 {
3124         DSTACK(__FUNCTION_NAME);
3125         //JMutexAutoLock lock(m_con_mutex);
3126         core::map<u16, RemoteClient*>::Node *n;
3127         n = m_clients.find(peer_id);
3128         // A client should exist for all peers
3129         assert(n != NULL);
3130         return n->getValue();
3131 }
3132
3133 Player *Server::emergePlayer(const char *name, const char *password,
3134                 u16 peer_id)
3135 {
3136         /*
3137                 Try to get an existing player
3138         */
3139         Player *player = m_env.getPlayer(name);
3140         if(player != NULL)
3141         {
3142                 // If player is already connected, cancel
3143                 if(player->peer_id != 0)
3144                 {
3145                         dstream<<"emergePlayer(): Player already connected"<<std::endl;
3146                         return NULL;
3147                 }
3148                 // Got one.
3149                 player->peer_id = peer_id;
3150                 return player;
3151         }
3152
3153         /*
3154                 If player with the wanted peer_id already exists, cancel.
3155         */
3156         if(m_env.getPlayer(peer_id) != NULL)
3157         {
3158                 dstream<<"emergePlayer(): Player with wrong name but same"
3159                                 " peer_id already exists"<<std::endl;
3160                 return NULL;
3161         }
3162         
3163         /*
3164                 Create a new player
3165         */
3166         {
3167                 player = new ServerRemotePlayer();
3168                 //player->peer_id = c.peer_id;
3169                 //player->peer_id = PEER_ID_INEXISTENT;
3170                 player->peer_id = peer_id;
3171                 player->updateName(name);
3172
3173                 /*
3174                         Set player position
3175                 */
3176                 
3177                 dstream<<"Server: Finding spawn place for player \""
3178                                 <<player->getName()<<"\""<<std::endl;
3179
3180 #if 1
3181                 v2s16 nodepos;
3182                 f32 groundheight = 0;
3183                 // Try to find a good place a few times
3184                 for(s32 i=0; i<500; i++)
3185                 {
3186                         s32 range = 1 + i;
3187                         // We're going to try to throw the player to this position
3188                         nodepos = v2s16(-range + (myrand()%(range*2)),
3189                                         -range + (myrand()%(range*2)));
3190                         v2s16 sectorpos = getNodeSectorPos(nodepos);
3191                         // Get sector
3192                         m_env.getMap().emergeSector(sectorpos);
3193                         // Get ground height at point
3194                         groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3195                         // The sector should have been generated -> groundheight exists
3196                         assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3197                         // Don't go underwater
3198                         if(groundheight < WATER_LEVEL)
3199                         {
3200                                 //dstream<<"-> Underwater"<<std::endl;
3201                                 continue;
3202                         }
3203 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3204                         // Get block at point
3205                         v3s16 nodepos3d;
3206                         nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3207                         v3s16 blockpos = getNodeBlockPos(nodepos3d);
3208                         ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3209                         // Don't go inside ground
3210                         try{
3211                                 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3212                                 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3213                                 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3214                                 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3215                                 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3216                                         || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3217                                 {
3218                                         dstream<<"-> Inside ground"<<std::endl;
3219                                         // In ground
3220                                         continue;
3221                                 }
3222                         }catch(InvalidPositionException &e)
3223                         {
3224                                 dstream<<"-> Invalid position"<<std::endl;
3225                                 // Ignore invalid position
3226                                 continue;
3227                         }
3228 #endif
3229                         // Found a good place
3230                         dstream<<"Searched through "<<i<<" places."<<std::endl;
3231                         break;
3232                 }
3233 #endif
3234                 
3235                 // If no suitable place was not found, go above water at least.
3236                 if(groundheight < WATER_LEVEL)
3237                         groundheight = WATER_LEVEL;
3238
3239                 player->setPosition(intToFloat(v3s16(
3240                                 nodepos.X,
3241                                 groundheight + 1,
3242                                 nodepos.Y
3243                 )));
3244
3245                 /*
3246                         Add player to environment
3247                 */
3248
3249                 m_env.addPlayer(player);
3250
3251                 /*
3252                         Add stuff to inventory
3253                 */
3254                 
3255                 if(g_settings.getBool("creative_mode"))
3256                 {
3257                         // Give some good picks
3258                         {
3259                                 InventoryItem *item = new ToolItem("STPick", 0);
3260                                 void* r = player->inventory.addItem("main", item);
3261                                 assert(r == NULL);
3262                         }
3263                         {
3264                                 InventoryItem *item = new ToolItem("MesePick", 0);
3265                                 void* r = player->inventory.addItem("main", item);
3266                                 assert(r == NULL);
3267                         }
3268
3269                         /*
3270                                 Give materials
3271                         */
3272                         assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3273                         
3274                         // add torch first
3275                         InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3276                         player->inventory.addItem("main", item);
3277                         
3278                         // Then others
3279                         for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3280                         {
3281                                 // Skip some materials
3282                                 if(i == CONTENT_WATER || i == CONTENT_TORCH)
3283                                         continue;
3284
3285                                 InventoryItem *item = new MaterialItem(i, 1);
3286                                 player->inventory.addItem("main", item);
3287                         }
3288                         // Sign
3289                         {
3290                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3291                                 void* r = player->inventory.addItem("main", item);
3292                                 assert(r == NULL);
3293                         }
3294                 }
3295                 else
3296                 {
3297                         /*{
3298                                 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3299                                 void* r = player->inventory.addItem("main", item);
3300                                 assert(r == NULL);
3301                         }
3302                         {
3303                                 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3304                                 void* r = player->inventory.addItem("main", item);
3305                                 assert(r == NULL);
3306                         }
3307                         {
3308                                 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3309                                 void* r = player->inventory.addItem("main", item);
3310                                 assert(r == NULL);
3311                         }
3312                         {
3313                                 InventoryItem *item = new CraftItem("Stick", 4);
3314                                 void* r = player->inventory.addItem("main", item);
3315                                 assert(r == NULL);
3316                         }
3317                         {
3318                                 InventoryItem *item = new ToolItem("WPick", 32000);
3319                                 void* r = player->inventory.addItem("main", item);
3320                                 assert(r == NULL);
3321                         }
3322                         {
3323                                 InventoryItem *item = new ToolItem("STPick", 32000);
3324                                 void* r = player->inventory.addItem("main", item);
3325                                 assert(r == NULL);
3326                         }*/
3327                         /*// Give some lights
3328                         {
3329                                 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3330                                 bool r = player->inventory.addItem("main", item);
3331                                 assert(r == true);
3332                         }
3333                         // and some signs
3334                         for(u16 i=0; i<4; i++)
3335                         {
3336                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3337                                 bool r = player->inventory.addItem("main", item);
3338                                 assert(r == true);
3339                         }*/
3340                         /*// Give some other stuff
3341                         {
3342                                 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3343                                 bool r = player->inventory.addItem("main", item);
3344                                 assert(r == true);
3345                         }*/
3346                 }
3347
3348                 return player;
3349                 
3350         } // create new player
3351 }
3352
3353 #if 0
3354 void Server::UpdateBlockWaterPressure(MapBlock *block,
3355                         core::map<v3s16, MapBlock*> &modified_blocks)
3356 {
3357         MapVoxelManipulator v(&m_env.getMap());
3358         v.m_disable_water_climb =
3359                         g_settings.getBool("disable_water_climb");
3360         
3361         VoxelArea area(block->getPosRelative(),
3362                         block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3363
3364         try
3365         {
3366                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3367         }
3368         catch(ProcessingLimitException &e)
3369         {
3370                 dstream<<"Processing limit reached (1)"<<std::endl;
3371         }
3372         
3373         v.blitBack(modified_blocks);
3374 }
3375 #endif
3376
3377 void Server::handlePeerChange(PeerChange &c)
3378 {
3379         JMutexAutoLock envlock(m_env_mutex);
3380         JMutexAutoLock conlock(m_con_mutex);
3381         
3382         if(c.type == PEER_ADDED)
3383         {
3384                 /*
3385                         Add
3386                 */
3387
3388                 // Error check
3389                 core::map<u16, RemoteClient*>::Node *n;
3390                 n = m_clients.find(c.peer_id);
3391                 // The client shouldn't already exist
3392                 assert(n == NULL);
3393
3394                 // Create client
3395                 RemoteClient *client = new RemoteClient();
3396                 client->peer_id = c.peer_id;
3397                 m_clients.insert(client->peer_id, client);
3398
3399         } // PEER_ADDED
3400         else if(c.type == PEER_REMOVED)
3401         {
3402                 /*
3403                         Delete
3404                 */
3405
3406                 // Error check
3407                 core::map<u16, RemoteClient*>::Node *n;
3408                 n = m_clients.find(c.peer_id);
3409                 // The client should exist
3410                 assert(n != NULL);
3411                 
3412                 // Collect information about leaving in chat
3413                 std::wstring message;
3414                 {
3415                         std::wstring name = L"unknown";
3416                         Player *player = m_env.getPlayer(c.peer_id);
3417                         if(player != NULL)
3418                                 name = narrow_to_wide(player->getName());
3419                         
3420                         message += L"*** ";
3421                         message += name;
3422                         message += L" left game";
3423                         if(c.timeout)
3424                                 message += L" (timed out)";
3425                 }
3426
3427                 /*// Delete player
3428                 {
3429                         m_env.removePlayer(c.peer_id);
3430                 }*/
3431
3432                 // Set player client disconnected
3433                 {
3434                         Player *player = m_env.getPlayer(c.peer_id);
3435                         if(player != NULL)
3436                                 player->peer_id = 0;
3437                 }
3438                 
3439                 // Delete client
3440                 delete m_clients[c.peer_id];
3441                 m_clients.remove(c.peer_id);
3442
3443                 // Send player info to all remaining clients
3444                 SendPlayerInfos();
3445                 
3446                 // Send leave chat message to all remaining clients
3447                 BroadcastChatMessage(message);
3448                 
3449         } // PEER_REMOVED
3450         else
3451         {
3452                 assert(0);
3453         }
3454 }
3455
3456 void Server::handlePeerChanges()
3457 {
3458         while(m_peer_change_queue.size() > 0)
3459         {
3460                 PeerChange c = m_peer_change_queue.pop_front();
3461
3462                 dout_server<<"Server: Handling peer change: "
3463                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3464                                 <<std::endl;
3465
3466                 handlePeerChange(c);
3467         }
3468 }
3469
3470 void dedicated_server_loop(Server &server)
3471 {
3472         DSTACK(__FUNCTION_NAME);
3473         
3474         std::cout<<std::endl;
3475         std::cout<<"========================"<<std::endl;
3476         std::cout<<"Running dedicated server"<<std::endl;
3477         std::cout<<"========================"<<std::endl;
3478         std::cout<<std::endl;
3479
3480         for(;;)
3481         {
3482                 // This is kind of a hack but can be done like this
3483                 // because server.step() is very light
3484                 sleep_ms(30);
3485                 server.step(0.030);
3486
3487                 static int counter = 0;
3488                 counter--;
3489                 if(counter <= 0)
3490                 {
3491                         counter = 10;
3492
3493                         core::list<PlayerInfo> list = server.getPlayerInfo();
3494                         core::list<PlayerInfo>::Iterator i;
3495                         static u32 sum_old = 0;
3496                         u32 sum = PIChecksum(list);
3497                         if(sum != sum_old)
3498                         {
3499                                 std::cout<<DTIME<<"Player info:"<<std::endl;
3500                                 for(i=list.begin(); i!=list.end(); i++)
3501                                 {
3502                                         i->PrintLine(&std::cout);
3503                                 }
3504                         }
3505                         sum_old = sum;
3506                 }
3507         }
3508 }
3509
3510