]> git.lizzy.rs Git - minetest.git/blob - src/mapblockobject.cpp
some work-in-progress
[minetest.git] / src / mapblockobject.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 #include "mapblockobject.h"
21 #include "mapblock.h"
22 // Only for ::getNodeBox, TODO: Get rid of this
23 #include "map.h"
24
25 /*
26         MapBlockObject
27 */
28
29 // This is here because it uses the MapBlock
30 v3f MapBlockObject::getAbsolutePos()
31 {
32         if(m_block == NULL)
33                 return m_pos;
34         
35         // getPosRelative gets nodepos relative to map origin
36         v3f blockpos = intToFloat(m_block->getPosRelative());
37         return blockpos + m_pos;
38 }
39
40 void MapBlockObject::setBlockChanged()
41 {
42         if(m_block)
43                 m_block->setChangedFlag();
44 }
45
46 /*
47         MovingObject
48 */
49
50 v3f MovingObject::getAbsoluteShowPos()
51 {
52         if(m_block == NULL)
53                 return m_pos;
54         
55         // getPosRelative gets nodepos relative to map origin
56         v3f blockpos = intToFloat(m_block->getPosRelative());
57         return blockpos + m_showpos;
58 }
59
60 void MovingObject::move(float dtime, v3f acceleration)
61 {
62         DSTACK("%s: typeid=%i, pos=(%f,%f,%f), speed=(%f,%f,%f)"
63                         ", dtime=%f, acc=(%f,%f,%f)",
64                         __FUNCTION_NAME,
65                         getTypeId(),
66                         m_pos.X, m_pos.Y, m_pos.Z,
67                         m_speed.X, m_speed.Y, m_speed.Z,
68                         dtime,
69                         acceleration.X, acceleration.Y, acceleration.Z
70                         );
71         
72         v3s16 oldpos_i = floatToInt(m_pos);
73         
74         if(m_block->isValidPosition(oldpos_i) == false)
75         {
76                 // Should have wrapped, cancelling further movement.
77                 return;
78         }
79
80         // No collisions if there is no collision box
81         if(m_collision_box == NULL)
82         {
83                 m_speed += dtime * acceleration;
84                 m_pos += m_speed * dtime;
85                 return;
86         }
87         
88         // Set insane speed to zero
89         // Otherwise there will be divides by zero and other silly stuff
90         if(m_speed.getLength() > 1000.0*BS)
91                 m_speed = v3f(0,0,0);
92                 
93         // Limit speed to a reasonable value
94         float speed_limit = 20.0*BS;
95         if(m_speed.getLength() > speed_limit)
96                 m_speed = m_speed * (speed_limit / m_speed.getLength());
97
98         v3f position = m_pos;
99         v3f oldpos = position;
100
101         /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
102                         <<oldpos_i.Z<<")"<<std::endl;*/
103
104         // Maximum time increment (for collision detection etc)
105         // Allow 0.1 blocks per increment
106         // time = distance / speed
107         // NOTE: In the loop below collisions are detected at 0.15*BS radius
108         float speedlength = m_speed.getLength();
109         f32 dtime_max_increment;
110         if(fabs(speedlength) > 0.001)
111                 dtime_max_increment = 0.05*BS / speedlength;
112         else
113                 dtime_max_increment = 0.5;
114         
115         m_touching_ground = false;
116                 
117         u32 loopcount = 0;
118         do
119         {
120                 loopcount++;
121
122                 f32 dtime_part;
123                 if(dtime > dtime_max_increment)
124                         dtime_part = dtime_max_increment;
125                 else
126                         dtime_part = dtime;
127                 dtime -= dtime_part;
128
129                 // Begin of dtime limited code
130                 
131                 m_speed += acceleration * dtime_part;
132                 position += m_speed * dtime_part;
133
134                 /*
135                         Collision detection
136                 */
137                 
138                 v3s16 pos_i = floatToInt(position);
139                 
140                 // The loop length is limited to the object moving a distance
141                 f32 d = (float)BS * 0.15;
142
143                 core::aabbox3d<f32> objectbox(
144                                 m_collision_box->MinEdge + position,
145                                 m_collision_box->MaxEdge + position
146                 );
147                 
148                 core::aabbox3d<f32> objectbox_old(
149                                 m_collision_box->MinEdge + oldpos,
150                                 m_collision_box->MaxEdge + oldpos
151                 );
152                 
153                 //TODO: Get these ranges from somewhere
154                 for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
155                 for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
156                 for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
157                 {
158                         try{
159                                 if(content_walkable(m_block->getNodeParent(v3s16(x,y,z)).d)
160                                                 == false)
161                                         continue;
162                         }
163                         catch(InvalidPositionException &e)
164                         {
165                                 // Doing nothing here will block the object from
166                                 // walking over map borders
167                         }
168
169                         core::aabbox3d<f32> nodebox = Map::getNodeBox(
170                                         v3s16(x,y,z));
171                         
172                         // See if the object is touching ground
173                         if(
174                                         fabs(nodebox.MaxEdge.Y-objectbox.MinEdge.Y) < d
175                                         && nodebox.MaxEdge.X-d > objectbox.MinEdge.X
176                                         && nodebox.MinEdge.X+d < objectbox.MaxEdge.X
177                                         && nodebox.MaxEdge.Z-d > objectbox.MinEdge.Z
178                                         && nodebox.MinEdge.Z+d < objectbox.MaxEdge.Z
179                         ){
180                                 m_touching_ground = true;
181                         }
182                         
183                         if(objectbox.intersectsWithBox(nodebox))
184                         {
185                                         
186                 v3f dirs[3] = {
187                         v3f(0,0,1), // back
188                         v3f(0,1,0), // top
189                         v3f(1,0,0), // right
190                 };
191                 for(u16 i=0; i<3; i++)
192                 {
193                         f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
194                         f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
195                         f32 playermax = objectbox.MaxEdge.dotProduct(dirs[i]);
196                         f32 playermin = objectbox.MinEdge.dotProduct(dirs[i]);
197                         f32 playermax_old = objectbox_old.MaxEdge.dotProduct(dirs[i]);
198                         f32 playermin_old = objectbox_old.MinEdge.dotProduct(dirs[i]);
199
200                         bool main_edge_collides = 
201                                 ((nodemax > playermin && nodemax <= playermin_old + d
202                                         && m_speed.dotProduct(dirs[i]) < 0)
203                                 ||
204                                 (nodemin < playermax && nodemin >= playermax_old - d
205                                         && m_speed.dotProduct(dirs[i]) > 0));
206
207                         bool other_edges_collide = true;
208                         for(u16 j=0; j<3; j++)
209                         {
210                                 if(j == i)
211                                         continue;
212                                 f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
213                                 f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
214                                 f32 playermax = objectbox.MaxEdge.dotProduct(dirs[j]);
215                                 f32 playermin = objectbox.MinEdge.dotProduct(dirs[j]);
216                                 if(!(nodemax - d > playermin && nodemin + d < playermax))
217                                 {
218                                         other_edges_collide = false;
219                                         break;
220                                 }
221                         }
222                         
223                         if(main_edge_collides && other_edges_collide)
224                         {
225                                 m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
226                                 position -= position.dotProduct(dirs[i]) * dirs[i];
227                                 position += oldpos.dotProduct(dirs[i]) * dirs[i];
228                         }
229                 
230                 }
231                 
232                         } // if(objectbox.intersectsWithBox(nodebox))
233                 } // for y
234
235         } // End of dtime limited loop
236         while(dtime > 0.001);
237
238         m_pos = position;
239 }
240
241 void MovingObject::simpleMove(float dtime)
242 {
243         m_pos_animation_time_counter += dtime;
244         m_pos_animation_counter += dtime;
245         v3f movevector = m_pos - m_oldpos;
246         f32 moveratio;
247         if(m_pos_animation_time < 0.001)
248                 moveratio = 1.0;
249         else
250                 moveratio = m_pos_animation_counter / m_pos_animation_time;
251         if(moveratio > 1.5)
252                 moveratio = 1.5;
253         m_showpos = m_oldpos + movevector * moveratio;
254 }
255
256 #ifndef SERVER
257 /*
258         RatObject
259 */
260 void RatObject::addToScene(scene::ISceneManager *smgr)
261 {
262         if(m_node != NULL)
263                 return;
264         
265         video::IVideoDriver* driver = smgr->getVideoDriver();
266         
267         scene::SMesh *mesh = new scene::SMesh();
268         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
269         video::SColor c(255,255,255,255);
270         video::S3DVertex vertices[4] =
271         {
272                 video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
273                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
274                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
275                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
276         };
277         u16 indices[] = {0,1,2,2,3,0};
278         buf->append(vertices, 4, indices, 6);
279         // Set material
280         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
281         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
282         buf->getMaterial().setTexture
283                         (0, driver->getTexture("../data/rat.png"));
284         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
285         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
286         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
287         // Add to mesh
288         mesh->addMeshBuffer(buf);
289         buf->drop();
290         m_node = smgr->addMeshSceneNode(mesh, NULL);
291         mesh->drop();
292         updateNodePos();
293 }
294 #endif
295
296 #ifndef SERVER
297 /*
298         PlayerObject
299 */
300 void PlayerObject::addToScene(scene::ISceneManager *smgr)
301 {
302         if(m_node != NULL)
303                 return;
304         
305         video::IVideoDriver* driver = smgr->getVideoDriver();
306
307         // Attach a simple mesh to the player for showing an image
308         scene::SMesh *mesh = new scene::SMesh();
309         { // Front
310         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
311         video::SColor c(255,255,255,255);
312         video::S3DVertex vertices[4] =
313         {
314                 video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
315                 video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
316                 video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
317                 video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
318         };
319         u16 indices[] = {0,1,2,2,3,0};
320         buf->append(vertices, 4, indices, 6);
321         // Set material
322         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
323         //buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
324         buf->getMaterial().setTexture(0, driver->getTexture("../data/player.png"));
325         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
326         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
327         //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
328         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
329         // Add to mesh
330         mesh->addMeshBuffer(buf);
331         buf->drop();
332         }
333         { // Back
334         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
335         video::SColor c(255,255,255,255);
336         video::S3DVertex vertices[4] =
337         {
338                 video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
339                 video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
340                 video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
341                 video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
342         };
343         u16 indices[] = {0,1,2,2,3,0};
344         buf->append(vertices, 4, indices, 6);
345         // Set material
346         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
347         //buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
348         buf->getMaterial().setTexture(0, driver->getTexture("../data/player_back.png"));
349         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
350         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
351         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
352         // Add to mesh
353         mesh->addMeshBuffer(buf);
354         buf->drop();
355         }
356         
357         m_node = smgr->addMeshSceneNode(mesh, NULL);
358         mesh->drop();
359         updateNodePos();
360 }
361 #endif
362
363 /*
364         MapBlockObjectList
365 */
366
367 MapBlockObjectList::MapBlockObjectList(MapBlock *block):
368         m_block(block)
369 {
370         m_mutex.Init();
371 }
372
373 MapBlockObjectList::~MapBlockObjectList()
374 {
375         clear();
376 }
377
378 /*
379         The serialization format:
380         [0] u16 number of entries
381         [2] entries (id, typeId, parameters)
382 */
383
384 void MapBlockObjectList::serialize(std::ostream &os, u8 version)
385 {
386         JMutexAutoLock lock(m_mutex);
387
388         u8 buf[2];
389         writeU16(buf, m_objects.size());
390         os.write((char*)buf, 2);
391
392         for(core::map<s16, MapBlockObject*>::Iterator
393                         i = m_objects.getIterator();
394                         i.atEnd() == false; i++)
395         {
396                 i.getNode()->getValue()->serialize(os, version);
397         }
398 }
399
400 void MapBlockObjectList::update(std::istream &is, u8 version,
401                 scene::ISceneManager *smgr, u32 daynight_ratio)
402 {
403         JMutexAutoLock lock(m_mutex);
404
405         /*
406                 Collect all existing ids to a set.
407
408                 As things are updated, they are removed from this.
409
410                 All remaining ones are deleted.
411         */
412         core::map<s16, bool> ids_to_delete;
413         for(core::map<s16, MapBlockObject*>::Iterator
414                         i = m_objects.getIterator();
415                         i.atEnd() == false; i++)
416         {
417                 ids_to_delete.insert(i.getNode()->getKey(), true);
418         }
419         
420         u8 buf[6];
421         
422         is.read((char*)buf, 2);
423         u16 count = readU16(buf);
424
425         for(u16 i=0; i<count; i++)
426         {
427                 // Read id
428                 is.read((char*)buf, 2);
429                 s16 id = readS16(buf);
430                 
431                 // Read position
432                 // stored as x1000/BS v3s16
433                 is.read((char*)buf, 6);
434                 v3s16 pos_i = readV3S16(buf);
435                 v3f pos((f32)pos_i.X/1000*BS,
436                                 (f32)pos_i.Y/1000*BS,
437                                 (f32)pos_i.Z/1000*BS);
438
439                 // Read typeId
440                 is.read((char*)buf, 2);
441                 u16 type_id = readU16(buf);
442                 
443                 bool create_new = false;
444
445                 // Find an object with the id
446                 core::map<s16, MapBlockObject*>::Node *n;
447                 n = m_objects.find(id);
448                 // If no entry is found for id
449                 if(n == NULL)
450                 {
451                         // Insert dummy pointer node
452                         m_objects.insert(id, NULL);
453                         // Get node
454                         n = m_objects.find(id);
455                         // A new object will be created at this node
456                         create_new = true;
457                 }
458                 // If type_id differs
459                 else if(n->getValue()->getTypeId() != type_id)
460                 {
461                         // Delete old object
462                         delete n->getValue();
463                         // A new object will be created at this node
464                         create_new = true;
465                 }
466
467                 MapBlockObject *obj = NULL;
468
469                 if(create_new)
470                 {
471                         /*dstream<<"MapBlockObjectList adding new object"
472                                         " id="<<id
473                                         <<std::endl;*/
474
475                         if(type_id == MAPBLOCKOBJECT_TYPE_SIGN)
476                         {
477                                 obj = new SignObject(m_block, id, pos);
478                         }
479                         else if(type_id == MAPBLOCKOBJECT_TYPE_RAT)
480                         {
481                                 obj = new RatObject(m_block, id, pos);
482                         }
483                         else
484                         {
485                                 // This is fatal because we cannot know the length
486                                 // of the object's data
487                                 throw SerializationError
488                                 ("MapBlockObjectList::update(): Unknown MapBlockObject type");
489                         }
490
491                         if(smgr != NULL)
492                                 //obj->addToScene(smgr, daynight_ratio);
493                                 obj->addToScene(smgr);
494
495                         n->setValue(obj);
496                 }
497                 else
498                 {
499                         obj = n->getValue();
500                         obj->updatePos(pos);
501                         /*if(daynight_ratio != m_last_update_daynight_ratio)
502                         {
503                                 obj->removeFromScene();
504                                 obj->addToScene(smgr, daynight_ratio);
505                         }*/
506                 }
507
508                 // Now there is an object in obj.
509                 // Update it.
510                 
511                 obj->update(is, version);
512                 
513                 /*
514                         Update light on client
515                 */
516                 if(smgr != NULL)
517                 {
518                         u8 light = LIGHT_MAX;
519                         try{
520                                 v3s16 relpos_i = floatToInt(obj->m_pos);
521                                 MapNode n = m_block->getNodeParent(relpos_i);
522                                 light = n.getLightBlend(daynight_ratio);
523                         }
524                         catch(InvalidPositionException &e) {}
525                         obj->updateLight(light);
526                 }
527                 
528                 // Remove from deletion list
529                 if(ids_to_delete.find(id) != NULL)
530                         ids_to_delete.remove(id);
531         }
532
533         // Delete all objects whose ids_to_delete remain in ids_to_delete
534         for(core::map<s16, bool>::Iterator
535                         i = ids_to_delete.getIterator();
536                         i.atEnd() == false; i++)
537         {
538                 s16 id = i.getNode()->getKey();
539
540                 /*dstream<<"MapBlockObjectList deleting object"
541                                 " id="<<id
542                                 <<std::endl;*/
543
544                 MapBlockObject *obj = m_objects[id];
545                 obj->removeFromScene();
546                 delete obj;
547                 m_objects.remove(id);
548         }
549
550         m_last_update_daynight_ratio = daynight_ratio;
551 }
552
553 s16 MapBlockObjectList::getFreeId() throw(ContainerFullException)
554 {
555         s16 id = 0;
556         for(;;)
557         {
558                 if(m_objects.find(id) == NULL)
559                         return id;
560                 if(id == 32767)
561                         throw ContainerFullException
562                                         ("MapBlockObjectList doesn't fit more objects");
563                 id++;
564         }
565 }
566
567 void MapBlockObjectList::add(MapBlockObject *object)
568                 throw(ContainerFullException, AlreadyExistsException)
569 {
570         if(object == NULL)
571         {
572                 dstream<<"MapBlockObjectList::add(): NULL object"<<std::endl;
573                 return;
574         }
575
576         JMutexAutoLock lock(m_mutex);
577
578         // Create unique id if id==-1
579         if(object->m_id == -1)
580         {
581                 object->m_id = getFreeId();
582         }
583
584         if(m_objects.find(object->m_id) != NULL)
585         {
586                 dstream<<"MapBlockObjectList::add(): "
587                                 "object with same id already exists"<<std::endl;
588                 throw AlreadyExistsException
589                                 ("MapBlockObjectList already has given id");
590         }
591         
592         object->m_block = m_block;
593         
594         /*v3f p = object->m_pos;
595         dstream<<"MapBlockObjectList::add(): "
596                         <<"m_block->getPos()=("
597                         <<m_block->getPos().X<<","
598                         <<m_block->getPos().Y<<","
599                         <<m_block->getPos().Z<<")"
600                         <<" inserting object with id="<<object->m_id
601                         <<" pos="
602                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
603                         <<std::endl;*/
604         
605         m_objects.insert(object->m_id, object);
606 }
607
608 void MapBlockObjectList::clear()
609 {
610         JMutexAutoLock lock(m_mutex);
611
612         for(core::map<s16, MapBlockObject*>::Iterator
613                         i = m_objects.getIterator();
614                         i.atEnd() == false; i++)
615         {
616                 MapBlockObject *obj = i.getNode()->getValue();
617                 //FIXME: This really shouldn't be NULL at any time,
618                 //       but this condition was added because it was.
619                 if(obj != NULL)
620                 {
621                         obj->removeFromScene();
622                         delete obj;
623                 }
624         }
625
626         m_objects.clear();
627 }
628
629 void MapBlockObjectList::remove(s16 id)
630 {
631         JMutexAutoLock lock(m_mutex);
632
633         core::map<s16, MapBlockObject*>::Node *n;
634         n = m_objects.find(id);
635         if(n == NULL)
636                 return;
637         
638         n->getValue()->removeFromScene();
639         delete n->getValue();
640         m_objects.remove(id);
641 }
642
643 MapBlockObject * MapBlockObjectList::get(s16 id)
644 {
645         core::map<s16, MapBlockObject*>::Node *n;
646         n = m_objects.find(id);
647         if(n == NULL)
648                 return NULL;
649         else
650                 return n->getValue();
651 }
652
653 void MapBlockObjectList::step(float dtime, bool server, u32 daynight_ratio)
654 {
655         DSTACK(__FUNCTION_NAME);
656         
657         JMutexAutoLock lock(m_mutex);
658         
659         core::map<s16, bool> ids_to_delete;
660
661         {
662                 DSTACK("%s: stepping objects", __FUNCTION_NAME);
663
664                 for(core::map<s16, MapBlockObject*>::Iterator
665                                 i = m_objects.getIterator();
666                                 i.atEnd() == false; i++)
667                 {
668                         MapBlockObject *obj = i.getNode()->getValue();
669                         
670                         DSTACK("%s: stepping object type %i", __FUNCTION_NAME,
671                                         obj->getTypeId());
672
673                         if(server)
674                         {
675                                 // Update light
676                                 u8 light = LIGHT_MAX;
677                                 try{
678                                         v3s16 relpos_i = floatToInt(obj->m_pos);
679                                         MapNode n = m_block->getNodeParent(relpos_i);
680                                         light = n.getLightBlend(daynight_ratio);
681                                 }
682                                 catch(InvalidPositionException &e) {}
683                                 obj->updateLight(light);
684                                 
685                                 bool to_delete = obj->serverStep(dtime, daynight_ratio);
686
687                                 if(to_delete)
688                                         ids_to_delete.insert(obj->m_id, true);
689                         }
690                         else
691                         {
692                                 obj->clientStep(dtime);
693                         }
694                 }
695         }
696
697         {
698                 DSTACK("%s: deleting objects", __FUNCTION_NAME);
699
700                 // Delete objects in delete queue
701                 for(core::map<s16, bool>::Iterator
702                                 i = ids_to_delete.getIterator();
703                                 i.atEnd() == false; i++)
704                 {
705                         s16 id = i.getNode()->getKey();
706
707                         MapBlockObject *obj = m_objects[id];
708                         obj->removeFromScene();
709                         delete obj;
710                         m_objects.remove(id);
711                 }
712         }
713         
714         /*
715                 Wrap objects on server
716         */
717
718         if(server == false)
719                 return;
720         
721         {
722                 DSTACK("%s: object wrap loop", __FUNCTION_NAME);
723
724                 for(core::map<s16, MapBlockObject*>::Iterator
725                                 i = m_objects.getIterator();
726                                 i.atEnd() == false; i++)
727                 {
728                         MapBlockObject *obj = i.getNode()->getValue();
729
730                         v3s16 pos_i = floatToInt(obj->m_pos);
731
732                         if(m_block->isValidPosition(pos_i))
733                         {
734                                 // No wrap
735                                 continue;
736                         }
737
738                         bool impossible = wrapObject(obj);
739
740                         if(impossible)
741                         {
742                                 // No wrap
743                                 continue;
744                         }
745
746                         // Restart find
747                         i = m_objects.getIterator();
748                 }
749         }
750 }
751
752 bool MapBlockObjectList::wrapObject(MapBlockObject *object)
753 {
754         DSTACK(__FUNCTION_NAME);
755         
756         // No lock here; this is called so that the lock is already locked.
757         //JMutexAutoLock lock(m_mutex);
758
759         assert(object->m_block == m_block);
760         assert(m_objects.find(object->m_id) != NULL);
761         assert(m_objects[object->m_id] == object);
762
763         NodeContainer *parentcontainer = m_block->getParent();
764         // This will only work if the parent is the map
765         if(parentcontainer->nodeContainerId() != NODECONTAINER_ID_MAP)
766         {
767                 dstream<<"WARNING: Wrapping object not possible: "
768                                 "MapBlock's parent is not map"<<std::endl;
769                 return true;
770         }
771         // OK, we have the map!
772         Map *map = (Map*)parentcontainer;
773         
774         // Calculate blockpos on map
775         v3s16 oldblock_pos_i_on_map = m_block->getPosRelative();
776         v3f pos_f_on_oldblock = object->m_pos;
777         v3s16 pos_i_on_oldblock = floatToInt(pos_f_on_oldblock);
778         v3s16 pos_i_on_map = pos_i_on_oldblock + oldblock_pos_i_on_map;
779         v3s16 pos_blocks_on_map = getNodeBlockPos(pos_i_on_map);
780
781         // Get new block
782         MapBlock *newblock;
783         try{
784                 newblock = map->getBlockNoCreate(pos_blocks_on_map);
785         }
786         catch(InvalidPositionException &e)
787         {
788                 // Couldn't find block -> not wrapping
789                 /*dstream<<"WARNING: Wrapping object not possible: "
790                                 <<"could not find new block"
791                                 <<"("<<pos_blocks_on_map.X
792                                 <<","<<pos_blocks_on_map.Y
793                                 <<","<<pos_blocks_on_map.Z
794                                 <<")"<<std::endl;*/
795                 /*dstream<<"pos_f_on_oldblock=("
796                                 <<pos_f_on_oldblock.X<<","
797                                 <<pos_f_on_oldblock.Y<<","
798                                 <<pos_f_on_oldblock.Z<<")"
799                                 <<std::endl;*/
800                 return true;
801         }
802
803         if(newblock == m_block)
804         {
805                 dstream<<"WARNING: Wrapping object not possible: "
806                                 "newblock == oldblock"<<std::endl;
807                 return true;
808         }
809         
810         // Calculate position on new block
811         v3f oldblock_pos_f_on_map = intToFloat(oldblock_pos_i_on_map);
812         v3s16 newblock_pos_i_on_map = newblock->getPosRelative();
813         v3f newblock_pos_f_on_map = intToFloat(newblock_pos_i_on_map);
814         v3f pos_f_on_newblock = pos_f_on_oldblock
815                         - newblock_pos_f_on_map + oldblock_pos_f_on_map;
816
817         // Remove object from this block
818         m_objects.remove(object->m_id);
819         
820         // Add object to new block
821         object->m_pos = pos_f_on_newblock;
822         object->m_id = -1;
823         object->m_block = NULL;
824         newblock->addObject(object);
825
826         //dstream<<"NOTE: Wrapped object"<<std::endl;
827
828         return false;
829 }
830
831 void MapBlockObjectList::getObjects(v3f origin, f32 max_d,
832                 core::array<DistanceSortedObject> &dest)
833 {
834         for(core::map<s16, MapBlockObject*>::Iterator
835                         i = m_objects.getIterator();
836                         i.atEnd() == false; i++)
837         {
838                 MapBlockObject *obj = i.getNode()->getValue();
839
840                 f32 d = (obj->getRelativeShowPos() - origin).getLength();
841
842                 if(d > max_d)
843                         continue;
844
845                 DistanceSortedObject dso(obj, d);
846
847                 dest.push_back(dso);
848         }
849 }
850
851 //END