3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "mapblockobject.h"
22 // Only for ::getNodeBox, TODO: Get rid of this
29 // This is here because it uses the MapBlock
30 v3f MapBlockObject::getAbsolutePos()
35 // getPosRelative gets nodepos relative to map origin
36 v3f blockpos = intToFloat(m_block->getPosRelative());
37 return blockpos + m_pos;
40 void MapBlockObject::setBlockChanged()
43 m_block->setChangedFlag();
49 void MovingObject::move(float dtime, v3f acceleration)
51 DSTACK("%s: typeid=%i, pos=(%f,%f,%f), speed=(%f,%f,%f)"
52 ", dtime=%f, acc=(%f,%f,%f)",
55 m_pos.X, m_pos.Y, m_pos.Z,
56 m_speed.X, m_speed.Y, m_speed.Z,
58 acceleration.X, acceleration.Y, acceleration.Z
61 v3s16 oldpos_i = floatToInt(m_pos);
63 if(m_block->isValidPosition(oldpos_i) == false)
65 // Should have wrapped, cancelling further movement.
69 // No collisions if there is no collision box
70 if(m_collision_box == NULL)
72 m_speed += dtime * acceleration;
73 m_pos += m_speed * dtime;
77 // Set insane speed to zero
78 // Otherwise there will be divides by zero and other silly stuff
79 if(m_speed.getLength() > 1000.0*BS)
82 // Limit speed to a reasonable value
83 float speed_limit = 20.0*BS;
84 if(m_speed.getLength() > speed_limit)
85 m_speed = m_speed * (speed_limit / m_speed.getLength());
88 v3f oldpos = position;
90 /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
91 <<oldpos_i.Z<<")"<<std::endl;*/
93 // Maximum time increment (for collision detection etc)
94 // Allow 0.1 blocks per increment
95 // time = distance / speed
96 // NOTE: In the loop below collisions are detected at 0.15*BS radius
97 float speedlength = m_speed.getLength();
98 f32 dtime_max_increment;
99 if(fabs(speedlength) > 0.001)
100 dtime_max_increment = 0.1*BS / speedlength;
102 dtime_max_increment = 0.5;
104 m_touching_ground = false;
112 if(dtime > dtime_max_increment)
113 dtime_part = dtime_max_increment;
118 // Begin of dtime limited code
120 m_speed += acceleration * dtime_part;
121 position += m_speed * dtime_part;
127 v3s16 pos_i = floatToInt(position);
129 // The loop length is limited to the object moving a distance
130 f32 d = (float)BS * 0.15;
132 core::aabbox3d<f32> objectbox(
133 m_collision_box->MinEdge + position,
134 m_collision_box->MaxEdge + position
137 core::aabbox3d<f32> objectbox_old(
138 m_collision_box->MinEdge + oldpos,
139 m_collision_box->MaxEdge + oldpos
142 //TODO: Get these ranges from somewhere
143 for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
144 for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
145 for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
148 if(content_walkable(m_block->getNodeParent(v3s16(x,y,z)).d)
152 catch(InvalidPositionException &e)
154 // Doing nothing here will block the player from
155 // walking over map borders
158 core::aabbox3d<f32> nodebox = Map::getNodeBox(
161 // See if the player is touching ground
163 fabs(nodebox.MaxEdge.Y-objectbox.MinEdge.Y) < d
164 && nodebox.MaxEdge.X-d > objectbox.MinEdge.X
165 && nodebox.MinEdge.X+d < objectbox.MaxEdge.X
166 && nodebox.MaxEdge.Z-d > objectbox.MinEdge.Z
167 && nodebox.MinEdge.Z+d < objectbox.MaxEdge.Z
169 m_touching_ground = true;
172 if(objectbox.intersectsWithBox(nodebox))
180 for(u16 i=0; i<3; i++)
182 f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
183 f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
184 f32 playermax = objectbox.MaxEdge.dotProduct(dirs[i]);
185 f32 playermin = objectbox.MinEdge.dotProduct(dirs[i]);
186 f32 playermax_old = objectbox_old.MaxEdge.dotProduct(dirs[i]);
187 f32 playermin_old = objectbox_old.MinEdge.dotProduct(dirs[i]);
189 bool main_edge_collides =
190 ((nodemax > playermin && nodemax <= playermin_old + d
191 && m_speed.dotProduct(dirs[i]) < 0)
193 (nodemin < playermax && nodemin >= playermax_old - d
194 && m_speed.dotProduct(dirs[i]) > 0));
196 bool other_edges_collide = true;
197 for(u16 j=0; j<3; j++)
201 f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
202 f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
203 f32 playermax = objectbox.MaxEdge.dotProduct(dirs[j]);
204 f32 playermin = objectbox.MinEdge.dotProduct(dirs[j]);
205 if(!(nodemax - d > playermin && nodemin + d < playermax))
207 other_edges_collide = false;
212 if(main_edge_collides && other_edges_collide)
214 m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
215 position -= position.dotProduct(dirs[i]) * dirs[i];
216 position += oldpos.dotProduct(dirs[i]) * dirs[i];
221 } // if(objectbox.intersectsWithBox(nodebox))
224 } // End of dtime limited loop
225 while(dtime > 0.001);
234 MapBlockObjectList::MapBlockObjectList(MapBlock *block):
240 MapBlockObjectList::~MapBlockObjectList()
246 The serialization format:
247 [0] u16 number of entries
248 [2] entries (id, typeId, parameters)
251 void MapBlockObjectList::serialize(std::ostream &os, u8 version)
253 JMutexAutoLock lock(m_mutex);
256 writeU16(buf, m_objects.size());
257 os.write((char*)buf, 2);
259 for(core::map<s16, MapBlockObject*>::Iterator
260 i = m_objects.getIterator();
261 i.atEnd() == false; i++)
263 i.getNode()->getValue()->serialize(os, version);
267 void MapBlockObjectList::update(std::istream &is, u8 version,
268 scene::ISceneManager *smgr)
270 JMutexAutoLock lock(m_mutex);
273 Collect all existing ids to a set.
275 As things are updated, they are removed from this.
277 All remaining ones are deleted.
279 core::map<s16, bool> ids_to_delete;
280 for(core::map<s16, MapBlockObject*>::Iterator
281 i = m_objects.getIterator();
282 i.atEnd() == false; i++)
284 ids_to_delete.insert(i.getNode()->getKey(), true);
289 is.read((char*)buf, 2);
290 u16 count = readU16(buf);
292 for(u16 i=0; i<count; i++)
295 is.read((char*)buf, 2);
296 s16 id = readS16(buf);
299 // stored as x1000/BS v3s16
300 is.read((char*)buf, 6);
301 v3s16 pos_i = readV3S16(buf);
302 v3f pos((f32)pos_i.X/1000*BS,
303 (f32)pos_i.Y/1000*BS,
304 (f32)pos_i.Z/1000*BS);
307 is.read((char*)buf, 2);
308 u16 type_id = readU16(buf);
310 bool create_new = false;
312 // Find an object with the id
313 core::map<s16, MapBlockObject*>::Node *n;
314 n = m_objects.find(id);
315 // If no entry is found for id
318 // Insert dummy pointer node
319 m_objects.insert(id, NULL);
321 n = m_objects.find(id);
322 // A new object will be created at this node
325 // If type_id differs
326 else if(n->getValue()->getTypeId() != type_id)
329 delete n->getValue();
330 // A new object will be created at this node
334 MapBlockObject *obj = NULL;
338 /*dstream<<"MapBlockObjectList adding new object"
342 if(type_id == MAPBLOCKOBJECT_TYPE_TEST)
344 // The constructors of objects shouldn't need
345 // any more parameters than this.
346 obj = new TestObject(m_block, id, pos);
348 else if(type_id == MAPBLOCKOBJECT_TYPE_TEST2)
350 obj = new Test2Object(m_block, id, pos);
352 else if(type_id == MAPBLOCKOBJECT_TYPE_SIGN)
354 obj = new SignObject(m_block, id, pos);
356 else if(type_id == MAPBLOCKOBJECT_TYPE_RAT)
358 obj = new RatObject(m_block, id, pos);
362 throw SerializationError
363 ("MapBlockObjectList::update(): Unknown MapBlockObject type");
367 obj->addToScene(smgr);
377 // Now there is an object in obj.
380 obj->update(is, version);
382 // Remove from deletion list
383 if(ids_to_delete.find(id) != NULL)
384 ids_to_delete.remove(id);
387 // Delete all objects whose ids_to_delete remain in ids_to_delete
388 for(core::map<s16, bool>::Iterator
389 i = ids_to_delete.getIterator();
390 i.atEnd() == false; i++)
392 s16 id = i.getNode()->getKey();
394 /*dstream<<"MapBlockObjectList deleting object"
398 MapBlockObject *obj = m_objects[id];
399 obj->removeFromScene();
401 m_objects.remove(id);
405 s16 MapBlockObjectList::getFreeId() throw(ContainerFullException)
410 if(m_objects.find(id) == NULL)
413 throw ContainerFullException
414 ("MapBlockObjectList doesn't fit more objects");
419 void MapBlockObjectList::add(MapBlockObject *object)
420 throw(ContainerFullException, AlreadyExistsException)
424 dstream<<"MapBlockObjectList::add(): NULL object"<<std::endl;
428 JMutexAutoLock lock(m_mutex);
430 // Create unique id if id==-1
431 if(object->m_id == -1)
433 object->m_id = getFreeId();
436 if(m_objects.find(object->m_id) != NULL)
438 dstream<<"MapBlockObjectList::add(): "
439 "object with same id already exists"<<std::endl;
440 throw AlreadyExistsException
441 ("MapBlockObjectList already has given id");
444 object->m_block = m_block;
446 /*v3f p = object->m_pos;
447 dstream<<"MapBlockObjectList::add(): "
448 <<"m_block->getPos()=("
449 <<m_block->getPos().X<<","
450 <<m_block->getPos().Y<<","
451 <<m_block->getPos().Z<<")"
452 <<" inserting object with id="<<object->m_id
454 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
457 m_objects.insert(object->m_id, object);
460 void MapBlockObjectList::clear()
462 JMutexAutoLock lock(m_mutex);
464 for(core::map<s16, MapBlockObject*>::Iterator
465 i = m_objects.getIterator();
466 i.atEnd() == false; i++)
468 MapBlockObject *obj = i.getNode()->getValue();
469 //FIXME: This really shouldn't be NULL at any time,
470 // but this condition was added because it was.
473 obj->removeFromScene();
481 void MapBlockObjectList::remove(s16 id)
483 JMutexAutoLock lock(m_mutex);
485 core::map<s16, MapBlockObject*>::Node *n;
486 n = m_objects.find(id);
490 n->getValue()->removeFromScene();
491 delete n->getValue();
492 m_objects.remove(id);
495 MapBlockObject * MapBlockObjectList::get(s16 id)
497 core::map<s16, MapBlockObject*>::Node *n;
498 n = m_objects.find(id);
502 return n->getValue();
505 void MapBlockObjectList::step(float dtime, bool server)
507 DSTACK(__FUNCTION_NAME);
509 JMutexAutoLock lock(m_mutex);
511 core::map<s16, bool> ids_to_delete;
514 DSTACK("%s: stepping objects", __FUNCTION_NAME);
516 for(core::map<s16, MapBlockObject*>::Iterator
517 i = m_objects.getIterator();
518 i.atEnd() == false; i++)
520 MapBlockObject *obj = i.getNode()->getValue();
522 DSTACK("%s: stepping object type %i", __FUNCTION_NAME,
527 bool to_delete = obj->serverStep(dtime);
530 ids_to_delete.insert(obj->m_id, true);
534 obj->clientStep(dtime);
540 DSTACK("%s: deleting objects", __FUNCTION_NAME);
542 // Delete objects in delete queue
543 for(core::map<s16, bool>::Iterator
544 i = ids_to_delete.getIterator();
545 i.atEnd() == false; i++)
547 s16 id = i.getNode()->getKey();
549 MapBlockObject *obj = m_objects[id];
550 obj->removeFromScene();
552 m_objects.remove(id);
557 Wrap objects on server
564 DSTACK("%s: object wrap loop", __FUNCTION_NAME);
566 for(core::map<s16, MapBlockObject*>::Iterator
567 i = m_objects.getIterator();
568 i.atEnd() == false; i++)
570 MapBlockObject *obj = i.getNode()->getValue();
572 v3s16 pos_i = floatToInt(obj->m_pos);
574 if(m_block->isValidPosition(pos_i))
580 bool impossible = wrapObject(obj);
589 i = m_objects.getIterator();
594 bool MapBlockObjectList::wrapObject(MapBlockObject *object)
596 DSTACK(__FUNCTION_NAME);
598 // No lock here; this is called so that the lock is already locked.
599 //JMutexAutoLock lock(m_mutex);
601 assert(object->m_block == m_block);
602 assert(m_objects.find(object->m_id) != NULL);
603 assert(m_objects[object->m_id] == object);
605 NodeContainer *parentcontainer = m_block->getParent();
606 // This will only work if the parent is the map
607 if(parentcontainer->nodeContainerId() != NODECONTAINER_ID_MAP)
609 dstream<<"WARNING: Wrapping object not possible: "
610 "MapBlock's parent is not map"<<std::endl;
613 // OK, we have the map!
614 Map *map = (Map*)parentcontainer;
616 // Calculate blockpos on map
617 v3s16 oldblock_pos_i_on_map = m_block->getPosRelative();
618 v3f pos_f_on_oldblock = object->m_pos;
619 v3s16 pos_i_on_oldblock = floatToInt(pos_f_on_oldblock);
620 v3s16 pos_i_on_map = pos_i_on_oldblock + oldblock_pos_i_on_map;
621 v3s16 pos_blocks_on_map = getNodeBlockPos(pos_i_on_map);
626 newblock = map->getBlockNoCreate(pos_blocks_on_map);
628 catch(InvalidPositionException &e)
630 // Couldn't find block -> not wrapping
631 /*dstream<<"WARNING: Wrapping object not possible: "
632 <<"could not find new block"
633 <<"("<<pos_blocks_on_map.X
634 <<","<<pos_blocks_on_map.Y
635 <<","<<pos_blocks_on_map.Z
637 /*dstream<<"pos_f_on_oldblock=("
638 <<pos_f_on_oldblock.X<<","
639 <<pos_f_on_oldblock.Y<<","
640 <<pos_f_on_oldblock.Z<<")"
645 if(newblock == m_block)
647 dstream<<"WARNING: Wrapping object not possible: "
648 "newblock == oldblock"<<std::endl;
652 // Calculate position on new block
653 v3f oldblock_pos_f_on_map = intToFloat(oldblock_pos_i_on_map);
654 v3s16 newblock_pos_i_on_map = newblock->getPosRelative();
655 v3f newblock_pos_f_on_map = intToFloat(newblock_pos_i_on_map);
656 v3f pos_f_on_newblock = pos_f_on_oldblock
657 - newblock_pos_f_on_map + oldblock_pos_f_on_map;
659 // Remove object from this block
660 m_objects.remove(object->m_id);
662 // Add object to new block
663 object->m_pos = pos_f_on_newblock;
665 object->m_block = NULL;
666 newblock->addObject(object);
668 //dstream<<"NOTE: Wrapped object"<<std::endl;
673 void MapBlockObjectList::getObjects(v3f origin, f32 max_d,
674 core::array<DistanceSortedObject> &dest)
676 for(core::map<s16, MapBlockObject*>::Iterator
677 i = m_objects.getIterator();
678 i.atEnd() == false; i++)
680 MapBlockObject *obj = i.getNode()->getValue();
682 f32 d = (obj->m_pos - origin).getLength();
687 DistanceSortedObject dso(obj, d);