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