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