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