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