]> git.lizzy.rs Git - minetest.git/blob - src/mapblockobject.cpp
added dedicated server build without irrlicht
[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 void MovingObject::move(float dtime, v3f acceleration)
50 {
51         DSTACK("%s: typeid=%i, pos=(%f,%f,%f), speed=(%f,%f,%f)"
52                         ", dtime=%f, acc=(%f,%f,%f)",
53                         __FUNCTION_NAME,
54                         getTypeId(),
55                         m_pos.X, m_pos.Y, m_pos.Z,
56                         m_speed.X, m_speed.Y, m_speed.Z,
57                         dtime,
58                         acceleration.X, acceleration.Y, acceleration.Z
59                         );
60         
61         v3s16 oldpos_i = floatToInt(m_pos);
62         
63         if(m_block->isValidPosition(oldpos_i) == false)
64         {
65                 // Should have wrapped, cancelling further movement.
66                 return;
67         }
68
69         // No collisions if there is no collision box
70         if(m_collision_box == NULL)
71         {
72                 m_speed += dtime * acceleration;
73                 m_pos += m_speed * dtime;
74                 return;
75         }
76         
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)
80                 m_speed = v3f(0,0,0);
81                 
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());
86
87         v3f position = m_pos;
88         v3f oldpos = position;
89
90         /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
91                         <<oldpos_i.Z<<")"<<std::endl;*/
92
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;
101         else
102                 dtime_max_increment = 0.5;
103         
104         m_touching_ground = false;
105                 
106         u32 loopcount = 0;
107         do
108         {
109                 loopcount++;
110
111                 f32 dtime_part;
112                 if(dtime > dtime_max_increment)
113                         dtime_part = dtime_max_increment;
114                 else
115                         dtime_part = dtime;
116                 dtime -= dtime_part;
117
118                 // Begin of dtime limited code
119                 
120                 m_speed += acceleration * dtime_part;
121                 position += m_speed * dtime_part;
122
123                 /*
124                         Collision detection
125                 */
126                 
127                 v3s16 pos_i = floatToInt(position);
128                 
129                 // The loop length is limited to the object moving a distance
130                 f32 d = (float)BS * 0.15;
131
132                 core::aabbox3d<f32> objectbox(
133                                 m_collision_box->MinEdge + position,
134                                 m_collision_box->MaxEdge + position
135                 );
136                 
137                 core::aabbox3d<f32> objectbox_old(
138                                 m_collision_box->MinEdge + oldpos,
139                                 m_collision_box->MaxEdge + oldpos
140                 );
141                 
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++)
146                 {
147                         try{
148                                 if(content_walkable(m_block->getNodeParent(v3s16(x,y,z)).d)
149                                                 == false)
150                                         continue;
151                         }
152                         catch(InvalidPositionException &e)
153                         {
154                                 // Doing nothing here will block the player from
155                                 // walking over map borders
156                         }
157
158                         core::aabbox3d<f32> nodebox = Map::getNodeBox(
159                                         v3s16(x,y,z));
160                         
161                         // See if the player is touching ground
162                         if(
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
168                         ){
169                                 m_touching_ground = true;
170                         }
171                         
172                         if(objectbox.intersectsWithBox(nodebox))
173                         {
174                                         
175                 v3f dirs[3] = {
176                         v3f(0,0,1), // back
177                         v3f(0,1,0), // top
178                         v3f(1,0,0), // right
179                 };
180                 for(u16 i=0; i<3; i++)
181                 {
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]);
188
189                         bool main_edge_collides = 
190                                 ((nodemax > playermin && nodemax <= playermin_old + d
191                                         && m_speed.dotProduct(dirs[i]) < 0)
192                                 ||
193                                 (nodemin < playermax && nodemin >= playermax_old - d
194                                         && m_speed.dotProduct(dirs[i]) > 0));
195
196                         bool other_edges_collide = true;
197                         for(u16 j=0; j<3; j++)
198                         {
199                                 if(j == i)
200                                         continue;
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))
206                                 {
207                                         other_edges_collide = false;
208                                         break;
209                                 }
210                         }
211                         
212                         if(main_edge_collides && other_edges_collide)
213                         {
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];
217                         }
218                 
219                 }
220                 
221                         } // if(objectbox.intersectsWithBox(nodebox))
222                 } // for y
223
224         } // End of dtime limited loop
225         while(dtime > 0.001);
226
227         m_pos = position;
228 }
229
230 /*
231         MapBlockObjectList
232 */
233
234 MapBlockObjectList::MapBlockObjectList(MapBlock *block):
235         m_block(block)
236 {
237         m_mutex.Init();
238 }
239
240 MapBlockObjectList::~MapBlockObjectList()
241 {
242         clear();
243 }
244
245 /*
246         The serialization format:
247         [0] u16 number of entries
248         [2] entries (id, typeId, parameters)
249 */
250
251 void MapBlockObjectList::serialize(std::ostream &os, u8 version)
252 {
253         JMutexAutoLock lock(m_mutex);
254
255         u8 buf[2];
256         writeU16(buf, m_objects.size());
257         os.write((char*)buf, 2);
258
259         for(core::map<s16, MapBlockObject*>::Iterator
260                         i = m_objects.getIterator();
261                         i.atEnd() == false; i++)
262         {
263                 i.getNode()->getValue()->serialize(os, version);
264         }
265 }
266
267 void MapBlockObjectList::update(std::istream &is, u8 version,
268                 scene::ISceneManager *smgr)
269 {
270         JMutexAutoLock lock(m_mutex);
271
272         /*
273                 Collect all existing ids to a set.
274
275                 As things are updated, they are removed from this.
276
277                 All remaining ones are deleted.
278         */
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++)
283         {
284                 ids_to_delete.insert(i.getNode()->getKey(), true);
285         }
286         
287         u8 buf[6];
288         
289         is.read((char*)buf, 2);
290         u16 count = readU16(buf);
291
292         for(u16 i=0; i<count; i++)
293         {
294                 // Read id
295                 is.read((char*)buf, 2);
296                 s16 id = readS16(buf);
297                 
298                 // Read position
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);
305
306                 // Read typeId
307                 is.read((char*)buf, 2);
308                 u16 type_id = readU16(buf);
309                 
310                 bool create_new = false;
311
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
316                 if(n == NULL)
317                 {
318                         // Insert dummy pointer node
319                         m_objects.insert(id, NULL);
320                         // Get node
321                         n = m_objects.find(id);
322                         // A new object will be created at this node
323                         create_new = true;
324                 }
325                 // If type_id differs
326                 else if(n->getValue()->getTypeId() != type_id)
327                 {
328                         // Delete old object
329                         delete n->getValue();
330                         // A new object will be created at this node
331                         create_new = true;
332                 }
333
334                 MapBlockObject *obj = NULL;
335
336                 if(create_new)
337                 {
338                         /*dstream<<"MapBlockObjectList adding new object"
339                                         " id="<<id
340                                         <<std::endl;*/
341
342                         if(type_id == MAPBLOCKOBJECT_TYPE_SIGN)
343                         {
344                                 obj = new SignObject(m_block, id, pos);
345                         }
346                         else if(type_id == MAPBLOCKOBJECT_TYPE_RAT)
347                         {
348                                 obj = new RatObject(m_block, id, pos);
349                         }
350                         else
351                         {
352                                 throw SerializationError
353                                 ("MapBlockObjectList::update(): Unknown MapBlockObject type");
354                         }
355
356                         if(smgr != NULL)
357                                 obj->addToScene(smgr);
358
359                         n->setValue(obj);
360                 }
361                 else
362                 {
363                         obj = n->getValue();
364                         obj->updatePos(pos);
365                 }
366
367                 // Now there is an object in obj.
368                 // Update it.
369                 
370                 obj->update(is, version);
371                 
372                 // Remove from deletion list
373                 if(ids_to_delete.find(id) != NULL)
374                         ids_to_delete.remove(id);
375         }
376
377         // Delete all objects whose ids_to_delete remain in ids_to_delete
378         for(core::map<s16, bool>::Iterator
379                         i = ids_to_delete.getIterator();
380                         i.atEnd() == false; i++)
381         {
382                 s16 id = i.getNode()->getKey();
383
384                 /*dstream<<"MapBlockObjectList deleting object"
385                                 " id="<<id
386                                 <<std::endl;*/
387
388                 MapBlockObject *obj = m_objects[id];
389                 obj->removeFromScene();
390                 delete obj;
391                 m_objects.remove(id);
392         }
393 }
394
395 s16 MapBlockObjectList::getFreeId() throw(ContainerFullException)
396 {
397         s16 id = 0;
398         for(;;)
399         {
400                 if(m_objects.find(id) == NULL)
401                         return id;
402                 if(id == 32767)
403                         throw ContainerFullException
404                                         ("MapBlockObjectList doesn't fit more objects");
405                 id++;
406         }
407 }
408
409 void MapBlockObjectList::add(MapBlockObject *object)
410                 throw(ContainerFullException, AlreadyExistsException)
411 {
412         if(object == NULL)
413         {
414                 dstream<<"MapBlockObjectList::add(): NULL object"<<std::endl;
415                 return;
416         }
417
418         JMutexAutoLock lock(m_mutex);
419
420         // Create unique id if id==-1
421         if(object->m_id == -1)
422         {
423                 object->m_id = getFreeId();
424         }
425
426         if(m_objects.find(object->m_id) != NULL)
427         {
428                 dstream<<"MapBlockObjectList::add(): "
429                                 "object with same id already exists"<<std::endl;
430                 throw AlreadyExistsException
431                                 ("MapBlockObjectList already has given id");
432         }
433         
434         object->m_block = m_block;
435         
436         /*v3f p = object->m_pos;
437         dstream<<"MapBlockObjectList::add(): "
438                         <<"m_block->getPos()=("
439                         <<m_block->getPos().X<<","
440                         <<m_block->getPos().Y<<","
441                         <<m_block->getPos().Z<<")"
442                         <<" inserting object with id="<<object->m_id
443                         <<" pos="
444                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
445                         <<std::endl;*/
446         
447         m_objects.insert(object->m_id, object);
448 }
449
450 void MapBlockObjectList::clear()
451 {
452         JMutexAutoLock lock(m_mutex);
453
454         for(core::map<s16, MapBlockObject*>::Iterator
455                         i = m_objects.getIterator();
456                         i.atEnd() == false; i++)
457         {
458                 MapBlockObject *obj = i.getNode()->getValue();
459                 //FIXME: This really shouldn't be NULL at any time,
460                 //       but this condition was added because it was.
461                 if(obj != NULL)
462                 {
463                         obj->removeFromScene();
464                         delete obj;
465                 }
466         }
467
468         m_objects.clear();
469 }
470
471 void MapBlockObjectList::remove(s16 id)
472 {
473         JMutexAutoLock lock(m_mutex);
474
475         core::map<s16, MapBlockObject*>::Node *n;
476         n = m_objects.find(id);
477         if(n == NULL)
478                 return;
479         
480         n->getValue()->removeFromScene();
481         delete n->getValue();
482         m_objects.remove(id);
483 }
484
485 MapBlockObject * MapBlockObjectList::get(s16 id)
486 {
487         core::map<s16, MapBlockObject*>::Node *n;
488         n = m_objects.find(id);
489         if(n == NULL)
490                 return NULL;
491         else
492                 return n->getValue();
493 }
494
495 void MapBlockObjectList::step(float dtime, bool server)
496 {
497         DSTACK(__FUNCTION_NAME);
498         
499         JMutexAutoLock lock(m_mutex);
500         
501         core::map<s16, bool> ids_to_delete;
502
503         {
504                 DSTACK("%s: stepping objects", __FUNCTION_NAME);
505
506                 for(core::map<s16, MapBlockObject*>::Iterator
507                                 i = m_objects.getIterator();
508                                 i.atEnd() == false; i++)
509                 {
510                         MapBlockObject *obj = i.getNode()->getValue();
511                         
512                         DSTACK("%s: stepping object type %i", __FUNCTION_NAME,
513                                         obj->getTypeId());
514
515                         if(server)
516                         {
517                                 bool to_delete = obj->serverStep(dtime);
518
519                                 if(to_delete)
520                                         ids_to_delete.insert(obj->m_id, true);
521                         }
522                         else
523                         {
524                                 obj->clientStep(dtime);
525                         }
526                 }
527         }
528
529         {
530                 DSTACK("%s: deleting objects", __FUNCTION_NAME);
531
532                 // Delete objects in delete queue
533                 for(core::map<s16, bool>::Iterator
534                                 i = ids_to_delete.getIterator();
535                                 i.atEnd() == false; i++)
536                 {
537                         s16 id = i.getNode()->getKey();
538
539                         MapBlockObject *obj = m_objects[id];
540                         obj->removeFromScene();
541                         delete obj;
542                         m_objects.remove(id);
543                 }
544         }
545         
546         /*
547                 Wrap objects on server
548         */
549
550         if(server == false)
551                 return;
552         
553         {
554                 DSTACK("%s: object wrap loop", __FUNCTION_NAME);
555
556                 for(core::map<s16, MapBlockObject*>::Iterator
557                                 i = m_objects.getIterator();
558                                 i.atEnd() == false; i++)
559                 {
560                         MapBlockObject *obj = i.getNode()->getValue();
561
562                         v3s16 pos_i = floatToInt(obj->m_pos);
563
564                         if(m_block->isValidPosition(pos_i))
565                         {
566                                 // No wrap
567                                 continue;
568                         }
569
570                         bool impossible = wrapObject(obj);
571
572                         if(impossible)
573                         {
574                                 // No wrap
575                                 continue;
576                         }
577
578                         // Restart find
579                         i = m_objects.getIterator();
580                 }
581         }
582 }
583
584 bool MapBlockObjectList::wrapObject(MapBlockObject *object)
585 {
586         DSTACK(__FUNCTION_NAME);
587         
588         // No lock here; this is called so that the lock is already locked.
589         //JMutexAutoLock lock(m_mutex);
590
591         assert(object->m_block == m_block);
592         assert(m_objects.find(object->m_id) != NULL);
593         assert(m_objects[object->m_id] == object);
594
595         NodeContainer *parentcontainer = m_block->getParent();
596         // This will only work if the parent is the map
597         if(parentcontainer->nodeContainerId() != NODECONTAINER_ID_MAP)
598         {
599                 dstream<<"WARNING: Wrapping object not possible: "
600                                 "MapBlock's parent is not map"<<std::endl;
601                 return true;
602         }
603         // OK, we have the map!
604         Map *map = (Map*)parentcontainer;
605         
606         // Calculate blockpos on map
607         v3s16 oldblock_pos_i_on_map = m_block->getPosRelative();
608         v3f pos_f_on_oldblock = object->m_pos;
609         v3s16 pos_i_on_oldblock = floatToInt(pos_f_on_oldblock);
610         v3s16 pos_i_on_map = pos_i_on_oldblock + oldblock_pos_i_on_map;
611         v3s16 pos_blocks_on_map = getNodeBlockPos(pos_i_on_map);
612
613         // Get new block
614         MapBlock *newblock;
615         try{
616                 newblock = map->getBlockNoCreate(pos_blocks_on_map);
617         }
618         catch(InvalidPositionException &e)
619         {
620                 // Couldn't find block -> not wrapping
621                 /*dstream<<"WARNING: Wrapping object not possible: "
622                                 <<"could not find new block"
623                                 <<"("<<pos_blocks_on_map.X
624                                 <<","<<pos_blocks_on_map.Y
625                                 <<","<<pos_blocks_on_map.Z
626                                 <<")"<<std::endl;*/
627                 /*dstream<<"pos_f_on_oldblock=("
628                                 <<pos_f_on_oldblock.X<<","
629                                 <<pos_f_on_oldblock.Y<<","
630                                 <<pos_f_on_oldblock.Z<<")"
631                                 <<std::endl;*/
632                 return true;
633         }
634
635         if(newblock == m_block)
636         {
637                 dstream<<"WARNING: Wrapping object not possible: "
638                                 "newblock == oldblock"<<std::endl;
639                 return true;
640         }
641         
642         // Calculate position on new block
643         v3f oldblock_pos_f_on_map = intToFloat(oldblock_pos_i_on_map);
644         v3s16 newblock_pos_i_on_map = newblock->getPosRelative();
645         v3f newblock_pos_f_on_map = intToFloat(newblock_pos_i_on_map);
646         v3f pos_f_on_newblock = pos_f_on_oldblock
647                         - newblock_pos_f_on_map + oldblock_pos_f_on_map;
648
649         // Remove object from this block
650         m_objects.remove(object->m_id);
651         
652         // Add object to new block
653         object->m_pos = pos_f_on_newblock;
654         object->m_id = -1;
655         object->m_block = NULL;
656         newblock->addObject(object);
657
658         //dstream<<"NOTE: Wrapped object"<<std::endl;
659
660         return false;
661 }
662
663 void MapBlockObjectList::getObjects(v3f origin, f32 max_d,
664                 core::array<DistanceSortedObject> &dest)
665 {
666         for(core::map<s16, MapBlockObject*>::Iterator
667                         i = m_objects.getIterator();
668                         i.atEnd() == false; i++)
669         {
670                 MapBlockObject *obj = i.getNode()->getValue();
671
672                 f32 d = (obj->m_pos - origin).getLength();
673
674                 if(d > max_d)
675                         continue;
676
677                 DistanceSortedObject dso(obj, d);
678
679                 dest.push_back(dso);
680         }
681 }
682
683 //END