Line data Source code
1 : /*
2 : Minetest
3 : Copyright (C) 2010-2013 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 Lesser General Public License as published by
7 : the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14 :
15 : You should have received a copy of the GNU Lesser 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 <fstream>
21 : #include "environment.h"
22 : #include "filesys.h"
23 : #include "porting.h"
24 : #include "collision.h"
25 : #include "content_mapnode.h"
26 : #include "mapblock.h"
27 : #include "serverobject.h"
28 : #include "content_sao.h"
29 : #include "settings.h"
30 : #include "log.h"
31 : #include "profiler.h"
32 : #include "scripting_game.h"
33 : #include "nodedef.h"
34 : #include "nodemetadata.h"
35 : #include "gamedef.h"
36 : #ifndef SERVER
37 : #include "clientmap.h"
38 : #include "localplayer.h"
39 : #include "mapblock_mesh.h"
40 : #include "event.h"
41 : #endif
42 : #include "daynightratio.h"
43 : #include "map.h"
44 : #include "emerge.h"
45 : #include "util/serialize.h"
46 : #include "jthread/jmutexautolock.h"
47 :
48 : #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
49 :
50 1 : Environment::Environment():
51 : m_time_of_day(9000),
52 : m_time_of_day_f(9000./24000),
53 : m_time_of_day_speed(0),
54 : m_time_counter(0),
55 : m_enable_day_night_ratio_override(false),
56 1 : m_day_night_ratio_override(0.0f)
57 : {
58 1 : m_cache_enable_shaders = g_settings->getBool("enable_shaders");
59 1 : }
60 :
61 2 : Environment::~Environment()
62 : {
63 : // Deallocate players
64 6 : for(std::vector<Player*>::iterator i = m_players.begin();
65 4 : i != m_players.end(); ++i) {
66 1 : delete (*i);
67 : }
68 1 : }
69 :
70 1 : void Environment::addPlayer(Player *player)
71 : {
72 2 : DSTACK(__FUNCTION_NAME);
73 : /*
74 : Check that peer_ids are unique.
75 : Also check that names are unique.
76 : Exception: there can be multiple players with peer_id=0
77 : */
78 : // If peer id is non-zero, it has to be unique.
79 1 : if(player->peer_id != 0)
80 0 : FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
81 : // Name has to be unique.
82 1 : FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
83 : // Add.
84 1 : m_players.push_back(player);
85 1 : }
86 :
87 0 : void Environment::removePlayer(u16 peer_id)
88 : {
89 0 : DSTACK(__FUNCTION_NAME);
90 :
91 0 : for(std::vector<Player*>::iterator i = m_players.begin();
92 0 : i != m_players.end();)
93 : {
94 0 : Player *player = *i;
95 0 : if(player->peer_id == peer_id) {
96 0 : delete player;
97 0 : i = m_players.erase(i);
98 : } else {
99 0 : ++i;
100 : }
101 : }
102 0 : }
103 :
104 0 : void Environment::removePlayer(const char *name)
105 : {
106 0 : for (std::vector<Player*>::iterator it = m_players.begin();
107 0 : it != m_players.end(); ++it) {
108 0 : if (strcmp((*it)->getName(), name) == 0) {
109 0 : delete *it;
110 0 : m_players.erase(it);
111 0 : return;
112 : }
113 : }
114 : }
115 :
116 0 : Player * Environment::getPlayer(u16 peer_id)
117 : {
118 0 : for(std::vector<Player*>::iterator i = m_players.begin();
119 0 : i != m_players.end(); ++i) {
120 0 : Player *player = *i;
121 0 : if(player->peer_id == peer_id)
122 0 : return player;
123 : }
124 0 : return NULL;
125 : }
126 :
127 2 : Player * Environment::getPlayer(const char *name)
128 : {
129 6 : for(std::vector<Player*>::iterator i = m_players.begin();
130 4 : i != m_players.end(); ++i) {
131 1 : Player *player = *i;
132 1 : if(strcmp(player->getName(), name) == 0)
133 1 : return player;
134 : }
135 1 : return NULL;
136 : }
137 :
138 0 : Player * Environment::getRandomConnectedPlayer()
139 : {
140 0 : std::vector<Player*> connected_players = getPlayers(true);
141 0 : u32 chosen_one = myrand() % connected_players.size();
142 0 : u32 j = 0;
143 0 : for(std::vector<Player*>::iterator
144 0 : i = connected_players.begin();
145 0 : i != connected_players.end(); ++i) {
146 0 : if(j == chosen_one) {
147 0 : Player *player = *i;
148 0 : return player;
149 : }
150 0 : j++;
151 : }
152 0 : return NULL;
153 : }
154 :
155 0 : Player * Environment::getNearestConnectedPlayer(v3f pos)
156 : {
157 0 : std::vector<Player*> connected_players = getPlayers(true);
158 0 : f32 nearest_d = 0;
159 0 : Player *nearest_player = NULL;
160 0 : for(std::vector<Player*>::iterator
161 0 : i = connected_players.begin();
162 0 : i != connected_players.end(); ++i) {
163 0 : Player *player = *i;
164 0 : f32 d = player->getPosition().getDistanceFrom(pos);
165 0 : if(d < nearest_d || nearest_player == NULL) {
166 0 : nearest_d = d;
167 0 : nearest_player = player;
168 : }
169 : }
170 0 : return nearest_player;
171 : }
172 :
173 0 : std::vector<Player*> Environment::getPlayers()
174 : {
175 0 : return m_players;
176 : }
177 :
178 0 : std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
179 : {
180 0 : std::vector<Player*> newlist;
181 0 : for(std::vector<Player*>::iterator
182 0 : i = m_players.begin();
183 0 : i != m_players.end(); ++i) {
184 0 : Player *player = *i;
185 :
186 0 : if(ignore_disconnected) {
187 : // Ignore disconnected players
188 0 : if(player->peer_id == 0)
189 0 : continue;
190 : }
191 :
192 0 : newlist.push_back(player);
193 : }
194 0 : return newlist;
195 : }
196 :
197 1460846 : u32 Environment::getDayNightRatio()
198 : {
199 1460846 : if(m_enable_day_night_ratio_override)
200 0 : return m_day_night_ratio_override;
201 1460846 : return time_to_daynight_ratio(m_time_of_day_f*24000, m_cache_enable_shaders);
202 : }
203 :
204 10 : void Environment::setTimeOfDaySpeed(float speed)
205 : {
206 10 : JMutexAutoLock(this->m_timeofday_lock);
207 10 : m_time_of_day_speed = speed;
208 10 : }
209 :
210 1186 : float Environment::getTimeOfDaySpeed()
211 : {
212 1186 : JMutexAutoLock(this->m_timeofday_lock);
213 1186 : float retval = m_time_of_day_speed;
214 1186 : return retval;
215 : }
216 :
217 10 : void Environment::setTimeOfDay(u32 time)
218 : {
219 10 : JMutexAutoLock(this->m_time_lock);
220 10 : m_time_of_day = time;
221 10 : m_time_of_day_f = (float)time / 24000.0;
222 10 : }
223 :
224 0 : u32 Environment::getTimeOfDay()
225 : {
226 0 : JMutexAutoLock(this->m_time_lock);
227 0 : u32 retval = m_time_of_day;
228 0 : return retval;
229 : }
230 :
231 1166 : float Environment::getTimeOfDayF()
232 : {
233 1166 : JMutexAutoLock(this->m_time_lock);
234 1166 : float retval = m_time_of_day_f;
235 1166 : return retval;
236 : }
237 :
238 1186 : void Environment::stepTimeOfDay(float dtime)
239 : {
240 : // getTimeOfDaySpeed lock the value we need to prevent MT problems
241 1186 : float day_speed = getTimeOfDaySpeed();
242 :
243 1186 : m_time_counter += dtime;
244 1186 : f32 speed = day_speed * 24000./(24.*3600);
245 1186 : u32 units = (u32)(m_time_counter*speed);
246 1186 : bool sync_f = false;
247 1186 : if(units > 0){
248 : // Sync at overflow
249 614 : if(m_time_of_day + units >= 24000)
250 0 : sync_f = true;
251 614 : m_time_of_day = (m_time_of_day + units) % 24000;
252 614 : if(sync_f)
253 0 : m_time_of_day_f = (float)m_time_of_day / 24000.0;
254 : }
255 1186 : if (speed > 0) {
256 1166 : m_time_counter -= (f32)units / speed;
257 : }
258 1186 : if(!sync_f){
259 1186 : m_time_of_day_f += day_speed/24/3600*dtime;
260 1186 : if(m_time_of_day_f > 1.0)
261 0 : m_time_of_day_f -= 1.0;
262 1186 : if(m_time_of_day_f < 0.0)
263 0 : m_time_of_day_f += 1.0;
264 : }
265 1186 : }
266 :
267 : /*
268 : ABMWithState
269 : */
270 :
271 0 : ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
272 : abm(abm_),
273 0 : timer(0)
274 : {
275 : // Initialize timer to random value to spread processing
276 0 : float itv = abm->getTriggerInterval();
277 0 : itv = MYMAX(0.001, itv); // No less than 1ms
278 0 : int minval = MYMAX(-0.51*itv, -60); // Clamp to
279 0 : int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
280 0 : timer = myrand_range(minval, maxval);
281 0 : }
282 :
283 : /*
284 : ActiveBlockList
285 : */
286 :
287 0 : void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
288 : {
289 0 : v3s16 p;
290 0 : for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
291 0 : for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
292 0 : for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
293 : {
294 : // Set in list
295 0 : list.insert(p);
296 : }
297 0 : }
298 :
299 0 : void ActiveBlockList::update(std::vector<v3s16> &active_positions,
300 : s16 radius,
301 : std::set<v3s16> &blocks_removed,
302 : std::set<v3s16> &blocks_added)
303 : {
304 : /*
305 : Create the new list
306 : */
307 0 : std::set<v3s16> newlist = m_forceloaded_list;
308 0 : for(std::vector<v3s16>::iterator i = active_positions.begin();
309 0 : i != active_positions.end(); ++i)
310 : {
311 0 : fillRadiusBlock(*i, radius, newlist);
312 : }
313 :
314 : /*
315 : Find out which blocks on the old list are not on the new list
316 : */
317 : // Go through old list
318 0 : for(std::set<v3s16>::iterator i = m_list.begin();
319 0 : i != m_list.end(); ++i)
320 : {
321 0 : v3s16 p = *i;
322 : // If not on new list, it's been removed
323 0 : if(newlist.find(p) == newlist.end())
324 0 : blocks_removed.insert(p);
325 : }
326 :
327 : /*
328 : Find out which blocks on the new list are not on the old list
329 : */
330 : // Go through new list
331 0 : for(std::set<v3s16>::iterator i = newlist.begin();
332 0 : i != newlist.end(); ++i)
333 : {
334 0 : v3s16 p = *i;
335 : // If not on old list, it's been added
336 0 : if(m_list.find(p) == m_list.end())
337 0 : blocks_added.insert(p);
338 : }
339 :
340 : /*
341 : Update m_list
342 : */
343 0 : m_list.clear();
344 0 : for(std::set<v3s16>::iterator i = newlist.begin();
345 0 : i != newlist.end(); ++i)
346 : {
347 0 : v3s16 p = *i;
348 0 : m_list.insert(p);
349 : }
350 0 : }
351 :
352 : /*
353 : ServerEnvironment
354 : */
355 :
356 0 : ServerEnvironment::ServerEnvironment(ServerMap *map,
357 : GameScripting *scriptIface, IGameDef *gamedef,
358 : const std::string &path_world) :
359 : m_map(map),
360 : m_script(scriptIface),
361 : m_gamedef(gamedef),
362 : m_path_world(path_world),
363 : m_send_recommended_timer(0),
364 : m_active_block_interval_overload_skip(0),
365 : m_game_time(0),
366 : m_game_time_fraction_counter(0),
367 : m_recommended_send_interval(0.1),
368 0 : m_max_lag_estimate(0.1)
369 : {
370 0 : }
371 :
372 0 : ServerEnvironment::~ServerEnvironment()
373 : {
374 : // Clear active block list.
375 : // This makes the next one delete all active objects.
376 0 : m_active_blocks.clear();
377 :
378 : // Convert all objects to static and delete the active objects
379 0 : deactivateFarObjects(true);
380 :
381 : // Drop/delete map
382 0 : m_map->drop();
383 :
384 : // Delete ActiveBlockModifiers
385 0 : for(std::vector<ABMWithState>::iterator
386 0 : i = m_abms.begin(); i != m_abms.end(); ++i){
387 0 : delete i->abm;
388 : }
389 0 : }
390 :
391 0 : Map & ServerEnvironment::getMap()
392 : {
393 0 : return *m_map;
394 : }
395 :
396 0 : ServerMap & ServerEnvironment::getServerMap()
397 : {
398 0 : return *m_map;
399 : }
400 :
401 0 : bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
402 : {
403 0 : float distance = pos1.getDistanceFrom(pos2);
404 :
405 : //calculate normalized direction vector
406 0 : v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
407 0 : (pos2.Y - pos1.Y)/distance,
408 0 : (pos2.Z - pos1.Z)/distance);
409 :
410 : //find out if there's a node on path between pos1 and pos2
411 0 : for (float i = 1; i < distance; i += stepsize) {
412 0 : v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
413 0 : normalized_vector.Y * i,
414 0 : normalized_vector.Z * i) +pos1,BS);
415 :
416 0 : MapNode n = getMap().getNodeNoEx(pos);
417 :
418 0 : if(n.param0 != CONTENT_AIR) {
419 0 : if (p) {
420 0 : *p = pos;
421 : }
422 0 : return false;
423 : }
424 : }
425 0 : return true;
426 : }
427 :
428 0 : void ServerEnvironment::saveLoadedPlayers()
429 : {
430 0 : std::string players_path = m_path_world + DIR_DELIM "players";
431 0 : fs::CreateDir(players_path);
432 :
433 0 : for (std::vector<Player*>::iterator it = m_players.begin();
434 0 : it != m_players.end();
435 : ++it) {
436 0 : RemotePlayer *player = static_cast<RemotePlayer*>(*it);
437 0 : if (player->checkModified()) {
438 0 : player->save(players_path);
439 : }
440 : }
441 0 : }
442 :
443 0 : void ServerEnvironment::savePlayer(const std::string &playername)
444 : {
445 0 : std::string players_path = m_path_world + DIR_DELIM "players";
446 0 : fs::CreateDir(players_path);
447 :
448 0 : RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
449 0 : if (player) {
450 0 : player->save(players_path);
451 : }
452 0 : }
453 :
454 0 : Player *ServerEnvironment::loadPlayer(const std::string &playername)
455 : {
456 0 : bool newplayer = false;
457 0 : bool found = false;
458 0 : std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
459 0 : std::string path = players_path + playername;
460 :
461 0 : RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
462 0 : if (!player) {
463 0 : player = new RemotePlayer(m_gamedef, "");
464 0 : newplayer = true;
465 : }
466 :
467 0 : for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
468 : //// Open file and deserialize
469 0 : std::ifstream is(path.c_str(), std::ios_base::binary);
470 0 : if (!is.good())
471 0 : continue;
472 0 : player->deSerialize(is, path);
473 0 : is.close();
474 :
475 0 : if (player->getName() == playername) {
476 0 : found = true;
477 0 : break;
478 : }
479 :
480 0 : path = players_path + playername + itos(i);
481 : }
482 :
483 0 : if (!found) {
484 0 : infostream << "Player file for player " << playername
485 0 : << " not found" << std::endl;
486 0 : if (newplayer)
487 0 : delete player;
488 0 : return NULL;
489 : }
490 :
491 0 : if (newplayer)
492 0 : addPlayer(player);
493 0 : player->setModified(false);
494 0 : return player;
495 : }
496 :
497 0 : void ServerEnvironment::saveMeta()
498 : {
499 0 : std::string path = m_path_world + DIR_DELIM "env_meta.txt";
500 :
501 : // Open file and serialize
502 0 : std::ostringstream ss(std::ios_base::binary);
503 :
504 0 : Settings args;
505 0 : args.setU64("game_time", m_game_time);
506 0 : args.setU64("time_of_day", getTimeOfDay());
507 0 : args.writeLines(ss);
508 0 : ss<<"EnvArgsEnd\n";
509 :
510 0 : if(!fs::safeWriteToFile(path, ss.str()))
511 : {
512 0 : infostream<<"ServerEnvironment::saveMeta(): Failed to write "
513 0 : <<path<<std::endl;
514 0 : throw SerializationError("Couldn't save env meta");
515 : }
516 0 : }
517 :
518 0 : void ServerEnvironment::loadMeta()
519 : {
520 0 : std::string path = m_path_world + DIR_DELIM "env_meta.txt";
521 :
522 : // Open file and deserialize
523 0 : std::ifstream is(path.c_str(), std::ios_base::binary);
524 0 : if (!is.good()) {
525 0 : infostream << "ServerEnvironment::loadMeta(): Failed to open "
526 0 : << path << std::endl;
527 0 : throw SerializationError("Couldn't load env meta");
528 : }
529 :
530 0 : Settings args;
531 :
532 0 : if (!args.parseConfigLines(is, "EnvArgsEnd")) {
533 : throw SerializationError("ServerEnvironment::loadMeta(): "
534 0 : "EnvArgsEnd not found!");
535 : }
536 :
537 : try {
538 0 : m_game_time = args.getU64("game_time");
539 0 : } catch (SettingNotFoundException &e) {
540 : // Getting this is crucial, otherwise timestamps are useless
541 0 : throw SerializationError("Couldn't load env meta game_time");
542 : }
543 :
544 : try {
545 0 : m_time_of_day = args.getU64("time_of_day");
546 0 : } catch (SettingNotFoundException &e) {
547 : // This is not as important
548 0 : m_time_of_day = 9000;
549 : }
550 0 : }
551 :
552 0 : struct ActiveABM
553 : {
554 : ActiveBlockModifier *abm;
555 : int chance;
556 : std::set<content_t> required_neighbors;
557 : };
558 :
559 0 : class ABMHandler
560 : {
561 : private:
562 : ServerEnvironment *m_env;
563 : std::map<content_t, std::vector<ActiveABM> > m_aabms;
564 : public:
565 0 : ABMHandler(std::vector<ABMWithState> &abms,
566 : float dtime_s, ServerEnvironment *env,
567 : bool use_timers):
568 0 : m_env(env)
569 : {
570 0 : if(dtime_s < 0.001)
571 0 : return;
572 0 : INodeDefManager *ndef = env->getGameDef()->ndef();
573 0 : for(std::vector<ABMWithState>::iterator
574 0 : i = abms.begin(); i != abms.end(); ++i) {
575 0 : ActiveBlockModifier *abm = i->abm;
576 0 : float trigger_interval = abm->getTriggerInterval();
577 0 : if(trigger_interval < 0.001)
578 0 : trigger_interval = 0.001;
579 0 : float actual_interval = dtime_s;
580 0 : if(use_timers){
581 0 : i->timer += dtime_s;
582 0 : if(i->timer < trigger_interval)
583 0 : continue;
584 0 : i->timer -= trigger_interval;
585 0 : actual_interval = trigger_interval;
586 : }
587 0 : float intervals = actual_interval / trigger_interval;
588 0 : if(intervals == 0)
589 0 : continue;
590 0 : float chance = abm->getTriggerChance();
591 0 : if(chance == 0)
592 0 : chance = 1;
593 0 : ActiveABM aabm;
594 0 : aabm.abm = abm;
595 0 : aabm.chance = chance / intervals;
596 0 : if(aabm.chance == 0)
597 0 : aabm.chance = 1;
598 : // Trigger neighbors
599 : std::set<std::string> required_neighbors_s
600 0 : = abm->getRequiredNeighbors();
601 0 : for(std::set<std::string>::iterator
602 0 : i = required_neighbors_s.begin();
603 0 : i != required_neighbors_s.end(); i++)
604 : {
605 0 : ndef->getIds(*i, aabm.required_neighbors);
606 : }
607 : // Trigger contents
608 0 : std::set<std::string> contents_s = abm->getTriggerContents();
609 0 : for(std::set<std::string>::iterator
610 0 : i = contents_s.begin(); i != contents_s.end(); i++)
611 : {
612 0 : std::set<content_t> ids;
613 0 : ndef->getIds(*i, ids);
614 0 : for(std::set<content_t>::const_iterator k = ids.begin();
615 0 : k != ids.end(); k++)
616 : {
617 0 : content_t c = *k;
618 0 : std::map<content_t, std::vector<ActiveABM> >::iterator j;
619 0 : j = m_aabms.find(c);
620 0 : if(j == m_aabms.end()){
621 0 : std::vector<ActiveABM> aabmlist;
622 0 : m_aabms[c] = aabmlist;
623 0 : j = m_aabms.find(c);
624 : }
625 0 : j->second.push_back(aabm);
626 : }
627 : }
628 : }
629 : }
630 : // Find out how many objects the given block and its neighbours contain.
631 : // Returns the number of objects in the block, and also in 'wider' the
632 : // number of objects in the block and all its neighbours. The latter
633 : // may an estimate if any neighbours are unloaded.
634 0 : u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
635 : {
636 0 : wider = 0;
637 0 : u32 wider_unknown_count = 0;
638 0 : for(s16 x=-1; x<=1; x++)
639 0 : for(s16 y=-1; y<=1; y++)
640 0 : for(s16 z=-1; z<=1; z++)
641 : {
642 0 : MapBlock *block2 = map->getBlockNoCreateNoEx(
643 0 : block->getPos() + v3s16(x,y,z));
644 0 : if(block2==NULL){
645 0 : wider_unknown_count++;
646 0 : continue;
647 : }
648 0 : wider += block2->m_static_objects.m_active.size()
649 0 : + block2->m_static_objects.m_stored.size();
650 : }
651 : // Extrapolate
652 0 : u32 active_object_count = block->m_static_objects.m_active.size();
653 0 : u32 wider_known_count = 3*3*3 - wider_unknown_count;
654 0 : wider += wider_unknown_count * wider / wider_known_count;
655 0 : return active_object_count;
656 :
657 : }
658 0 : void apply(MapBlock *block)
659 : {
660 0 : if(m_aabms.empty())
661 0 : return;
662 :
663 0 : ServerMap *map = &m_env->getServerMap();
664 :
665 : u32 active_object_count_wider;
666 0 : u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
667 0 : m_env->m_added_objects = 0;
668 :
669 0 : v3s16 p0;
670 0 : for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
671 0 : for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
672 0 : for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
673 : {
674 0 : MapNode n = block->getNodeNoEx(p0);
675 0 : content_t c = n.getContent();
676 0 : v3s16 p = p0 + block->getPosRelative();
677 :
678 0 : std::map<content_t, std::vector<ActiveABM> >::iterator j;
679 0 : j = m_aabms.find(c);
680 0 : if(j == m_aabms.end())
681 0 : continue;
682 :
683 0 : for(std::vector<ActiveABM>::iterator
684 0 : i = j->second.begin(); i != j->second.end(); i++) {
685 0 : if(myrand() % i->chance != 0)
686 0 : continue;
687 :
688 : // Check neighbors
689 0 : if(!i->required_neighbors.empty())
690 : {
691 0 : v3s16 p1;
692 0 : for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
693 0 : for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
694 0 : for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
695 : {
696 0 : if(p1 == p)
697 0 : continue;
698 0 : MapNode n = map->getNodeNoEx(p1);
699 0 : content_t c = n.getContent();
700 0 : std::set<content_t>::const_iterator k;
701 0 : k = i->required_neighbors.find(c);
702 0 : if(k != i->required_neighbors.end()){
703 0 : goto neighbor_found;
704 : }
705 : }
706 : // No required neighbor found
707 0 : continue;
708 : }
709 : neighbor_found:
710 :
711 : // Call all the trigger variations
712 0 : i->abm->trigger(m_env, p, n);
713 0 : i->abm->trigger(m_env, p, n,
714 0 : active_object_count, active_object_count_wider);
715 :
716 : // Count surrounding objects again if the abms added any
717 0 : if(m_env->m_added_objects > 0) {
718 0 : active_object_count = countObjects(block, map, active_object_count_wider);
719 0 : m_env->m_added_objects = 0;
720 : }
721 : }
722 : }
723 : }
724 : };
725 :
726 0 : void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
727 : {
728 : // Reset usage timer immediately, otherwise a block that becomes active
729 : // again at around the same time as it would normally be unloaded will
730 : // get unloaded incorrectly. (I think this still leaves a small possibility
731 : // of a race condition between this and server::AsyncRunStep, which only
732 : // some kind of synchronisation will fix, but it at least reduces the window
733 : // of opportunity for it to break from seconds to nanoseconds)
734 0 : block->resetUsageTimer();
735 :
736 : // Get time difference
737 0 : u32 dtime_s = 0;
738 0 : u32 stamp = block->getTimestamp();
739 0 : if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
740 0 : dtime_s = m_game_time - block->getTimestamp();
741 0 : dtime_s += additional_dtime;
742 :
743 : /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
744 : <<stamp<<", game time: "<<m_game_time<<std::endl;*/
745 :
746 : // Set current time as timestamp
747 0 : block->setTimestampNoChangedFlag(m_game_time);
748 :
749 : /*infostream<<"ServerEnvironment::activateBlock(): block is "
750 : <<dtime_s<<" seconds old."<<std::endl;*/
751 :
752 : // Activate stored objects
753 0 : activateObjects(block, dtime_s);
754 :
755 : // Run node timers
756 : std::map<v3s16, NodeTimer> elapsed_timers =
757 0 : block->m_node_timers.step((float)dtime_s);
758 0 : if(!elapsed_timers.empty()){
759 0 : MapNode n;
760 0 : for(std::map<v3s16, NodeTimer>::iterator
761 0 : i = elapsed_timers.begin();
762 0 : i != elapsed_timers.end(); i++){
763 0 : n = block->getNodeNoEx(i->first);
764 0 : v3s16 p = i->first + block->getPosRelative();
765 0 : if(m_script->node_on_timer(p,n,i->second.elapsed))
766 0 : block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
767 : }
768 : }
769 :
770 : /* Handle ActiveBlockModifiers */
771 0 : ABMHandler abmhandler(m_abms, dtime_s, this, false);
772 0 : abmhandler.apply(block);
773 0 : }
774 :
775 0 : void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
776 : {
777 0 : m_abms.push_back(ABMWithState(abm));
778 0 : }
779 :
780 0 : bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
781 : {
782 0 : INodeDefManager *ndef = m_gamedef->ndef();
783 0 : MapNode n_old = m_map->getNodeNoEx(p);
784 :
785 : // Call destructor
786 0 : if (ndef->get(n_old).has_on_destruct)
787 0 : m_script->node_on_destruct(p, n_old);
788 :
789 : // Replace node
790 0 : if (!m_map->addNodeWithEvent(p, n))
791 0 : return false;
792 :
793 : // Update active VoxelManipulator if a mapgen thread
794 0 : m_map->updateVManip(p);
795 :
796 : // Call post-destructor
797 0 : if (ndef->get(n_old).has_after_destruct)
798 0 : m_script->node_after_destruct(p, n_old);
799 :
800 : // Call constructor
801 0 : if (ndef->get(n).has_on_construct)
802 0 : m_script->node_on_construct(p, n);
803 :
804 0 : return true;
805 : }
806 :
807 0 : bool ServerEnvironment::removeNode(v3s16 p)
808 : {
809 0 : INodeDefManager *ndef = m_gamedef->ndef();
810 0 : MapNode n_old = m_map->getNodeNoEx(p);
811 :
812 : // Call destructor
813 0 : if (ndef->get(n_old).has_on_destruct)
814 0 : m_script->node_on_destruct(p, n_old);
815 :
816 : // Replace with air
817 : // This is slightly optimized compared to addNodeWithEvent(air)
818 0 : if (!m_map->removeNodeWithEvent(p))
819 0 : return false;
820 :
821 : // Update active VoxelManipulator if a mapgen thread
822 0 : m_map->updateVManip(p);
823 :
824 : // Call post-destructor
825 0 : if (ndef->get(n_old).has_after_destruct)
826 0 : m_script->node_after_destruct(p, n_old);
827 :
828 : // Air doesn't require constructor
829 0 : return true;
830 : }
831 :
832 0 : bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
833 : {
834 0 : if (!m_map->addNodeWithEvent(p, n, false))
835 0 : return false;
836 :
837 : // Update active VoxelManipulator if a mapgen thread
838 0 : m_map->updateVManip(p);
839 :
840 0 : return true;
841 : }
842 :
843 0 : void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
844 : {
845 0 : for(std::map<u16, ServerActiveObject*>::iterator
846 0 : i = m_active_objects.begin();
847 0 : i != m_active_objects.end(); ++i)
848 : {
849 0 : ServerActiveObject* obj = i->second;
850 0 : u16 id = i->first;
851 0 : v3f objectpos = obj->getBasePosition();
852 0 : if(objectpos.getDistanceFrom(pos) > radius)
853 0 : continue;
854 0 : objects.push_back(id);
855 : }
856 0 : }
857 :
858 0 : void ServerEnvironment::clearAllObjects()
859 : {
860 0 : infostream<<"ServerEnvironment::clearAllObjects(): "
861 0 : <<"Removing all active objects"<<std::endl;
862 0 : std::vector<u16> objects_to_remove;
863 0 : for(std::map<u16, ServerActiveObject*>::iterator
864 0 : i = m_active_objects.begin();
865 0 : i != m_active_objects.end(); ++i) {
866 0 : ServerActiveObject* obj = i->second;
867 0 : if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
868 0 : continue;
869 0 : u16 id = i->first;
870 : // Delete static object if block is loaded
871 0 : if(obj->m_static_exists){
872 0 : MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
873 0 : if(block){
874 0 : block->m_static_objects.remove(id);
875 : block->raiseModified(MOD_STATE_WRITE_NEEDED,
876 0 : MOD_REASON_CLEAR_ALL_OBJECTS);
877 0 : obj->m_static_exists = false;
878 : }
879 : }
880 : // If known by some client, don't delete immediately
881 0 : if(obj->m_known_by_count > 0){
882 0 : obj->m_pending_deactivation = true;
883 0 : obj->m_removed = true;
884 0 : continue;
885 : }
886 :
887 : // Tell the object about removal
888 0 : obj->removingFromEnvironment();
889 : // Deregister in scripting api
890 0 : m_script->removeObjectReference(obj);
891 :
892 : // Delete active object
893 0 : if(obj->environmentDeletes())
894 0 : delete obj;
895 : // Id to be removed from m_active_objects
896 0 : objects_to_remove.push_back(id);
897 : }
898 :
899 : // Remove references from m_active_objects
900 0 : for(std::vector<u16>::iterator i = objects_to_remove.begin();
901 0 : i != objects_to_remove.end(); ++i) {
902 0 : m_active_objects.erase(*i);
903 : }
904 :
905 : // Get list of loaded blocks
906 0 : std::vector<v3s16> loaded_blocks;
907 0 : infostream<<"ServerEnvironment::clearAllObjects(): "
908 0 : <<"Listing all loaded blocks"<<std::endl;
909 0 : m_map->listAllLoadedBlocks(loaded_blocks);
910 0 : infostream<<"ServerEnvironment::clearAllObjects(): "
911 0 : <<"Done listing all loaded blocks: "
912 0 : <<loaded_blocks.size()<<std::endl;
913 :
914 : // Get list of loadable blocks
915 0 : std::vector<v3s16> loadable_blocks;
916 0 : infostream<<"ServerEnvironment::clearAllObjects(): "
917 0 : <<"Listing all loadable blocks"<<std::endl;
918 0 : m_map->listAllLoadableBlocks(loadable_blocks);
919 0 : infostream<<"ServerEnvironment::clearAllObjects(): "
920 0 : <<"Done listing all loadable blocks: "
921 0 : <<loadable_blocks.size()
922 0 : <<", now clearing"<<std::endl;
923 :
924 : // Grab a reference on each loaded block to avoid unloading it
925 0 : for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
926 0 : i != loaded_blocks.end(); ++i) {
927 0 : v3s16 p = *i;
928 0 : MapBlock *block = m_map->getBlockNoCreateNoEx(p);
929 : assert(block != NULL);
930 0 : block->refGrab();
931 : }
932 :
933 : // Remove objects in all loadable blocks
934 0 : u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
935 0 : unload_interval = MYMAX(unload_interval, 1);
936 0 : u32 report_interval = loadable_blocks.size() / 10;
937 0 : u32 num_blocks_checked = 0;
938 0 : u32 num_blocks_cleared = 0;
939 0 : u32 num_objs_cleared = 0;
940 0 : for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
941 0 : i != loadable_blocks.end(); ++i) {
942 0 : v3s16 p = *i;
943 0 : MapBlock *block = m_map->emergeBlock(p, false);
944 0 : if(!block){
945 0 : errorstream<<"ServerEnvironment::clearAllObjects(): "
946 0 : <<"Failed to emerge block "<<PP(p)<<std::endl;
947 0 : continue;
948 : }
949 0 : u32 num_stored = block->m_static_objects.m_stored.size();
950 0 : u32 num_active = block->m_static_objects.m_active.size();
951 0 : if(num_stored != 0 || num_active != 0){
952 0 : block->m_static_objects.m_stored.clear();
953 0 : block->m_static_objects.m_active.clear();
954 : block->raiseModified(MOD_STATE_WRITE_NEEDED,
955 0 : MOD_REASON_CLEAR_ALL_OBJECTS);
956 0 : num_objs_cleared += num_stored + num_active;
957 0 : num_blocks_cleared++;
958 : }
959 0 : num_blocks_checked++;
960 :
961 0 : if(report_interval != 0 &&
962 0 : num_blocks_checked % report_interval == 0){
963 0 : float percent = 100.0 * (float)num_blocks_checked /
964 0 : loadable_blocks.size();
965 0 : infostream<<"ServerEnvironment::clearAllObjects(): "
966 0 : <<"Cleared "<<num_objs_cleared<<" objects"
967 0 : <<" in "<<num_blocks_cleared<<" blocks ("
968 0 : <<percent<<"%)"<<std::endl;
969 : }
970 0 : if(num_blocks_checked % unload_interval == 0){
971 0 : m_map->unloadUnreferencedBlocks();
972 : }
973 : }
974 0 : m_map->unloadUnreferencedBlocks();
975 :
976 : // Drop references that were added above
977 0 : for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
978 0 : i != loaded_blocks.end(); ++i) {
979 0 : v3s16 p = *i;
980 0 : MapBlock *block = m_map->getBlockNoCreateNoEx(p);
981 : assert(block);
982 0 : block->refDrop();
983 : }
984 :
985 0 : infostream<<"ServerEnvironment::clearAllObjects(): "
986 0 : <<"Finished: Cleared "<<num_objs_cleared<<" objects"
987 0 : <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
988 0 : }
989 :
990 0 : void ServerEnvironment::step(float dtime)
991 : {
992 0 : DSTACK(__FUNCTION_NAME);
993 :
994 : //TimeTaker timer("ServerEnv step");
995 :
996 : /* Step time of day */
997 0 : stepTimeOfDay(dtime);
998 :
999 : // Update this one
1000 : // NOTE: This is kind of funny on a singleplayer game, but doesn't
1001 : // really matter that much.
1002 0 : m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1003 :
1004 : /*
1005 : Increment game time
1006 : */
1007 : {
1008 0 : m_game_time_fraction_counter += dtime;
1009 0 : u32 inc_i = (u32)m_game_time_fraction_counter;
1010 0 : m_game_time += inc_i;
1011 0 : m_game_time_fraction_counter -= (float)inc_i;
1012 : }
1013 :
1014 : /*
1015 : Handle players
1016 : */
1017 : {
1018 0 : ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1019 0 : for(std::vector<Player*>::iterator i = m_players.begin();
1020 0 : i != m_players.end(); ++i)
1021 : {
1022 0 : Player *player = *i;
1023 :
1024 : // Ignore disconnected players
1025 0 : if(player->peer_id == 0)
1026 0 : continue;
1027 :
1028 : // Move
1029 0 : player->move(dtime, this, 100*BS);
1030 : }
1031 : }
1032 :
1033 : /*
1034 : Manage active block list
1035 : */
1036 0 : if(m_active_blocks_management_interval.step(dtime, 2.0))
1037 : {
1038 0 : ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1039 : /*
1040 : Get player block positions
1041 : */
1042 0 : std::vector<v3s16> players_blockpos;
1043 0 : for(std::vector<Player*>::iterator
1044 0 : i = m_players.begin();
1045 0 : i != m_players.end(); ++i) {
1046 0 : Player *player = *i;
1047 : // Ignore disconnected players
1048 0 : if(player->peer_id == 0)
1049 0 : continue;
1050 :
1051 : v3s16 blockpos = getNodeBlockPos(
1052 0 : floatToInt(player->getPosition(), BS));
1053 0 : players_blockpos.push_back(blockpos);
1054 : }
1055 :
1056 : /*
1057 : Update list of active blocks, collecting changes
1058 : */
1059 0 : const s16 active_block_range = g_settings->getS16("active_block_range");
1060 0 : std::set<v3s16> blocks_removed;
1061 0 : std::set<v3s16> blocks_added;
1062 0 : m_active_blocks.update(players_blockpos, active_block_range,
1063 0 : blocks_removed, blocks_added);
1064 :
1065 : /*
1066 : Handle removed blocks
1067 : */
1068 :
1069 : // Convert active objects that are no more in active blocks to static
1070 0 : deactivateFarObjects(false);
1071 :
1072 0 : for(std::set<v3s16>::iterator
1073 0 : i = blocks_removed.begin();
1074 0 : i != blocks_removed.end(); ++i)
1075 : {
1076 0 : v3s16 p = *i;
1077 :
1078 : /* infostream<<"Server: Block " << PP(p)
1079 : << " became inactive"<<std::endl; */
1080 :
1081 0 : MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1082 0 : if(block==NULL)
1083 0 : continue;
1084 :
1085 : // Set current time as timestamp (and let it set ChangedFlag)
1086 0 : block->setTimestamp(m_game_time);
1087 : }
1088 :
1089 : /*
1090 : Handle added blocks
1091 : */
1092 :
1093 0 : for(std::set<v3s16>::iterator
1094 0 : i = blocks_added.begin();
1095 0 : i != blocks_added.end(); ++i)
1096 : {
1097 0 : v3s16 p = *i;
1098 :
1099 0 : MapBlock *block = m_map->getBlockOrEmerge(p);
1100 0 : if(block==NULL){
1101 0 : m_active_blocks.m_list.erase(p);
1102 0 : continue;
1103 : }
1104 :
1105 0 : activateBlock(block);
1106 : /* infostream<<"Server: Block " << PP(p)
1107 : << " became active"<<std::endl; */
1108 : }
1109 : }
1110 :
1111 : /*
1112 : Mess around in active blocks
1113 : */
1114 0 : if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1115 : {
1116 0 : ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1117 :
1118 0 : float dtime = 1.0;
1119 :
1120 0 : for(std::set<v3s16>::iterator
1121 0 : i = m_active_blocks.m_list.begin();
1122 0 : i != m_active_blocks.m_list.end(); ++i)
1123 : {
1124 0 : v3s16 p = *i;
1125 :
1126 : /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1127 : <<") being handled"<<std::endl;*/
1128 :
1129 0 : MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1130 0 : if(block==NULL)
1131 0 : continue;
1132 :
1133 : // Reset block usage timer
1134 0 : block->resetUsageTimer();
1135 :
1136 : // Set current time as timestamp
1137 0 : block->setTimestampNoChangedFlag(m_game_time);
1138 : // If time has changed much from the one on disk,
1139 : // set block to be saved when it is unloaded
1140 0 : if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1141 : block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1142 0 : MOD_REASON_BLOCK_EXPIRED);
1143 :
1144 : // Run node timers
1145 : std::map<v3s16, NodeTimer> elapsed_timers =
1146 0 : block->m_node_timers.step((float)dtime);
1147 0 : if(!elapsed_timers.empty()){
1148 0 : MapNode n;
1149 0 : for(std::map<v3s16, NodeTimer>::iterator
1150 0 : i = elapsed_timers.begin();
1151 0 : i != elapsed_timers.end(); i++){
1152 0 : n = block->getNodeNoEx(i->first);
1153 0 : p = i->first + block->getPosRelative();
1154 0 : if(m_script->node_on_timer(p,n,i->second.elapsed))
1155 0 : block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1156 : }
1157 : }
1158 : }
1159 : }
1160 :
1161 0 : const float abm_interval = 1.0;
1162 0 : if(m_active_block_modifier_interval.step(dtime, abm_interval))
1163 : do{ // breakable
1164 0 : if(m_active_block_interval_overload_skip > 0){
1165 0 : ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1166 0 : m_active_block_interval_overload_skip--;
1167 0 : break;
1168 : }
1169 0 : ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1170 0 : TimeTaker timer("modify in active blocks");
1171 :
1172 : // Initialize handling of ActiveBlockModifiers
1173 0 : ABMHandler abmhandler(m_abms, abm_interval, this, true);
1174 :
1175 0 : for(std::set<v3s16>::iterator
1176 0 : i = m_active_blocks.m_list.begin();
1177 0 : i != m_active_blocks.m_list.end(); ++i)
1178 : {
1179 0 : v3s16 p = *i;
1180 :
1181 : /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1182 : <<") being handled"<<std::endl;*/
1183 :
1184 0 : MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1185 0 : if(block == NULL)
1186 0 : continue;
1187 :
1188 : // Set current time as timestamp
1189 0 : block->setTimestampNoChangedFlag(m_game_time);
1190 :
1191 : /* Handle ActiveBlockModifiers */
1192 0 : abmhandler.apply(block);
1193 : }
1194 :
1195 0 : u32 time_ms = timer.stop(true);
1196 0 : u32 max_time_ms = 200;
1197 0 : if(time_ms > max_time_ms){
1198 0 : infostream<<"WARNING: active block modifiers took "
1199 0 : <<time_ms<<"ms (longer than "
1200 0 : <<max_time_ms<<"ms)"<<std::endl;
1201 0 : m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1202 : }
1203 : }while(0);
1204 :
1205 : /*
1206 : Step script environment (run global on_step())
1207 : */
1208 0 : m_script->environment_Step(dtime);
1209 :
1210 : /*
1211 : Step active objects
1212 : */
1213 : {
1214 0 : ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1215 : //TimeTaker timer("Step active objects");
1216 :
1217 0 : g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1218 :
1219 : // This helps the objects to send data at the same time
1220 0 : bool send_recommended = false;
1221 0 : m_send_recommended_timer += dtime;
1222 0 : if(m_send_recommended_timer > getSendRecommendedInterval())
1223 : {
1224 0 : m_send_recommended_timer -= getSendRecommendedInterval();
1225 0 : send_recommended = true;
1226 : }
1227 :
1228 0 : for(std::map<u16, ServerActiveObject*>::iterator
1229 0 : i = m_active_objects.begin();
1230 0 : i != m_active_objects.end(); ++i)
1231 : {
1232 0 : ServerActiveObject* obj = i->second;
1233 : // Don't step if is to be removed or stored statically
1234 0 : if(obj->m_removed || obj->m_pending_deactivation)
1235 0 : continue;
1236 : // Step object
1237 0 : obj->step(dtime, send_recommended);
1238 : // Read messages from object
1239 0 : while(!obj->m_messages_out.empty())
1240 : {
1241 0 : m_active_object_messages.push(
1242 0 : obj->m_messages_out.front());
1243 0 : obj->m_messages_out.pop();
1244 : }
1245 : }
1246 : }
1247 :
1248 : /*
1249 : Manage active objects
1250 : */
1251 0 : if(m_object_management_interval.step(dtime, 0.5))
1252 : {
1253 0 : ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1254 : /*
1255 : Remove objects that satisfy (m_removed && m_known_by_count==0)
1256 : */
1257 0 : removeRemovedObjects();
1258 : }
1259 0 : }
1260 :
1261 0 : ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1262 : {
1263 0 : std::map<u16, ServerActiveObject*>::iterator n;
1264 0 : n = m_active_objects.find(id);
1265 0 : if(n == m_active_objects.end())
1266 0 : return NULL;
1267 0 : return n->second;
1268 : }
1269 :
1270 0 : bool isFreeServerActiveObjectId(u16 id,
1271 : std::map<u16, ServerActiveObject*> &objects)
1272 : {
1273 0 : if(id == 0)
1274 0 : return false;
1275 :
1276 0 : return objects.find(id) == objects.end();
1277 : }
1278 :
1279 0 : u16 getFreeServerActiveObjectId(
1280 : std::map<u16, ServerActiveObject*> &objects)
1281 : {
1282 : //try to reuse id's as late as possible
1283 : static u16 last_used_id = 0;
1284 0 : u16 startid = last_used_id;
1285 0 : for(;;)
1286 : {
1287 0 : last_used_id ++;
1288 0 : if(isFreeServerActiveObjectId(last_used_id, objects))
1289 0 : return last_used_id;
1290 :
1291 0 : if(last_used_id == startid)
1292 0 : return 0;
1293 : }
1294 : }
1295 :
1296 0 : u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1297 : {
1298 : assert(object); // Pre-condition
1299 0 : m_added_objects++;
1300 0 : u16 id = addActiveObjectRaw(object, true, 0);
1301 0 : return id;
1302 : }
1303 :
1304 : #if 0
1305 : bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1306 : {
1307 : assert(obj);
1308 :
1309 : v3f objectpos = obj->getBasePosition();
1310 :
1311 : // The block in which the object resides in
1312 : v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1313 :
1314 : /*
1315 : Update the static data
1316 : */
1317 :
1318 : // Create new static object
1319 : std::string staticdata = obj->getStaticData();
1320 : StaticObject s_obj(obj->getType(), objectpos, staticdata);
1321 : // Add to the block where the object is located in
1322 : v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1323 : // Get or generate the block
1324 : MapBlock *block = m_map->emergeBlock(blockpos);
1325 :
1326 : bool succeeded = false;
1327 :
1328 : if(block)
1329 : {
1330 : block->m_static_objects.insert(0, s_obj);
1331 : block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1332 : "addActiveObjectAsStatic");
1333 : succeeded = true;
1334 : }
1335 : else{
1336 : infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1337 : <<"Could not find or generate "
1338 : <<"a block for storing static object"<<std::endl;
1339 : succeeded = false;
1340 : }
1341 :
1342 : if(obj->environmentDeletes())
1343 : delete obj;
1344 :
1345 : return succeeded;
1346 : }
1347 : #endif
1348 :
1349 : /*
1350 : Finds out what new objects have been added to
1351 : inside a radius around a position
1352 : */
1353 0 : void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1354 : s16 player_radius,
1355 : std::set<u16> ¤t_objects,
1356 : std::set<u16> &added_objects)
1357 : {
1358 0 : v3f pos_f = intToFloat(pos, BS);
1359 0 : f32 radius_f = radius * BS;
1360 0 : f32 player_radius_f = player_radius * BS;
1361 :
1362 0 : if (player_radius_f < 0)
1363 0 : player_radius_f = 0;
1364 :
1365 : /*
1366 : Go through the object list,
1367 : - discard m_removed objects,
1368 : - discard objects that are too far away,
1369 : - discard objects that are found in current_objects.
1370 : - add remaining objects to added_objects
1371 : */
1372 0 : for(std::map<u16, ServerActiveObject*>::iterator
1373 0 : i = m_active_objects.begin();
1374 0 : i != m_active_objects.end(); ++i)
1375 : {
1376 0 : u16 id = i->first;
1377 : // Get object
1378 0 : ServerActiveObject *object = i->second;
1379 0 : if(object == NULL)
1380 0 : continue;
1381 : // Discard if removed or deactivating
1382 0 : if(object->m_removed || object->m_pending_deactivation)
1383 0 : continue;
1384 :
1385 0 : f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1386 0 : if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1387 : // Discard if too far
1388 0 : if (distance_f > player_radius_f && player_radius_f != 0)
1389 0 : continue;
1390 0 : } else if (distance_f > radius_f)
1391 0 : continue;
1392 :
1393 : // Discard if already on current_objects
1394 0 : std::set<u16>::iterator n;
1395 0 : n = current_objects.find(id);
1396 0 : if(n != current_objects.end())
1397 0 : continue;
1398 : // Add to added_objects
1399 0 : added_objects.insert(id);
1400 : }
1401 0 : }
1402 :
1403 : /*
1404 : Finds out what objects have been removed from
1405 : inside a radius around a position
1406 : */
1407 0 : void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1408 : s16 player_radius,
1409 : std::set<u16> ¤t_objects,
1410 : std::set<u16> &removed_objects)
1411 : {
1412 0 : v3f pos_f = intToFloat(pos, BS);
1413 0 : f32 radius_f = radius * BS;
1414 0 : f32 player_radius_f = player_radius * BS;
1415 :
1416 0 : if (player_radius_f < 0)
1417 0 : player_radius_f = 0;
1418 :
1419 : /*
1420 : Go through current_objects; object is removed if:
1421 : - object is not found in m_active_objects (this is actually an
1422 : error condition; objects should be set m_removed=true and removed
1423 : only after all clients have been informed about removal), or
1424 : - object has m_removed=true, or
1425 : - object is too far away
1426 : */
1427 0 : for(std::set<u16>::iterator
1428 0 : i = current_objects.begin();
1429 0 : i != current_objects.end(); ++i)
1430 : {
1431 0 : u16 id = *i;
1432 0 : ServerActiveObject *object = getActiveObject(id);
1433 :
1434 0 : if(object == NULL){
1435 0 : infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1436 0 : <<" object in current_objects is NULL"<<std::endl;
1437 0 : removed_objects.insert(id);
1438 0 : continue;
1439 : }
1440 :
1441 0 : if(object->m_removed || object->m_pending_deactivation)
1442 : {
1443 0 : removed_objects.insert(id);
1444 0 : continue;
1445 : }
1446 :
1447 0 : f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1448 0 : if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1449 0 : if (distance_f <= player_radius_f || player_radius_f == 0)
1450 0 : continue;
1451 0 : } else if (distance_f <= radius_f)
1452 0 : continue;
1453 :
1454 : // Object is no longer visible
1455 0 : removed_objects.insert(id);
1456 : }
1457 0 : }
1458 :
1459 0 : ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1460 : {
1461 0 : if(m_active_object_messages.empty())
1462 0 : return ActiveObjectMessage(0);
1463 :
1464 0 : ActiveObjectMessage message = m_active_object_messages.front();
1465 0 : m_active_object_messages.pop();
1466 0 : return message;
1467 : }
1468 :
1469 : /*
1470 : ************ Private methods *************
1471 : */
1472 :
1473 0 : u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1474 : bool set_changed, u32 dtime_s)
1475 : {
1476 : assert(object); // Pre-condition
1477 0 : if(object->getId() == 0){
1478 0 : u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1479 0 : if(new_id == 0)
1480 : {
1481 0 : errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1482 0 : <<"no free ids available"<<std::endl;
1483 0 : if(object->environmentDeletes())
1484 0 : delete object;
1485 0 : return 0;
1486 : }
1487 0 : object->setId(new_id);
1488 : }
1489 : else{
1490 0 : verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1491 0 : <<"supplied with id "<<object->getId()<<std::endl;
1492 : }
1493 0 : if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1494 : {
1495 0 : errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1496 0 : <<"id is not free ("<<object->getId()<<")"<<std::endl;
1497 0 : if(object->environmentDeletes())
1498 0 : delete object;
1499 0 : return 0;
1500 : }
1501 : /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1502 : <<"added (id="<<object->getId()<<")"<<std::endl;*/
1503 :
1504 0 : m_active_objects[object->getId()] = object;
1505 :
1506 0 : verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1507 0 : <<"Added id="<<object->getId()<<"; there are now "
1508 0 : <<m_active_objects.size()<<" active objects."
1509 0 : <<std::endl;
1510 :
1511 : // Register reference in scripting api (must be done before post-init)
1512 0 : m_script->addObjectReference(object);
1513 : // Post-initialize object
1514 0 : object->addedToEnvironment(dtime_s);
1515 :
1516 : // Add static data to block
1517 0 : if(object->isStaticAllowed())
1518 : {
1519 : // Add static object to active static list of the block
1520 0 : v3f objectpos = object->getBasePosition();
1521 0 : std::string staticdata = object->getStaticData();
1522 0 : StaticObject s_obj(object->getType(), objectpos, staticdata);
1523 : // Add to the block where the object is located in
1524 0 : v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1525 0 : MapBlock *block = m_map->emergeBlock(blockpos);
1526 0 : if(block){
1527 0 : block->m_static_objects.m_active[object->getId()] = s_obj;
1528 0 : object->m_static_exists = true;
1529 0 : object->m_static_block = blockpos;
1530 :
1531 0 : if(set_changed)
1532 : block->raiseModified(MOD_STATE_WRITE_NEEDED,
1533 0 : MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1534 : } else {
1535 0 : v3s16 p = floatToInt(objectpos, BS);
1536 0 : errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1537 0 : <<"could not emerge block for storing id="<<object->getId()
1538 0 : <<" statically (pos="<<PP(p)<<")"<<std::endl;
1539 : }
1540 : }
1541 :
1542 0 : return object->getId();
1543 : }
1544 :
1545 : /*
1546 : Remove objects that satisfy (m_removed && m_known_by_count==0)
1547 : */
1548 0 : void ServerEnvironment::removeRemovedObjects()
1549 : {
1550 0 : std::vector<u16> objects_to_remove;
1551 0 : for(std::map<u16, ServerActiveObject*>::iterator
1552 0 : i = m_active_objects.begin();
1553 0 : i != m_active_objects.end(); ++i) {
1554 0 : u16 id = i->first;
1555 0 : ServerActiveObject* obj = i->second;
1556 : // This shouldn't happen but check it
1557 0 : if(obj == NULL)
1558 : {
1559 0 : infostream<<"NULL object found in ServerEnvironment"
1560 0 : <<" while finding removed objects. id="<<id<<std::endl;
1561 : // Id to be removed from m_active_objects
1562 0 : objects_to_remove.push_back(id);
1563 0 : continue;
1564 : }
1565 :
1566 : /*
1567 : We will delete objects that are marked as removed or thatare
1568 : waiting for deletion after deactivation
1569 : */
1570 0 : if(obj->m_removed == false && obj->m_pending_deactivation == false)
1571 0 : continue;
1572 :
1573 : /*
1574 : Delete static data from block if is marked as removed
1575 : */
1576 0 : if(obj->m_static_exists && obj->m_removed)
1577 : {
1578 0 : MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1579 0 : if (block) {
1580 0 : block->m_static_objects.remove(id);
1581 : block->raiseModified(MOD_STATE_WRITE_NEEDED,
1582 0 : MOD_REASON_REMOVE_OBJECTS_REMOVE);
1583 0 : obj->m_static_exists = false;
1584 : } else {
1585 0 : infostream<<"Failed to emerge block from which an object to "
1586 0 : <<"be removed was loaded from. id="<<id<<std::endl;
1587 : }
1588 : }
1589 :
1590 : // If m_known_by_count > 0, don't actually remove. On some future
1591 : // invocation this will be 0, which is when removal will continue.
1592 0 : if(obj->m_known_by_count > 0)
1593 0 : continue;
1594 :
1595 : /*
1596 : Move static data from active to stored if not marked as removed
1597 : */
1598 0 : if(obj->m_static_exists && !obj->m_removed){
1599 0 : MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1600 0 : if (block) {
1601 : std::map<u16, StaticObject>::iterator i =
1602 0 : block->m_static_objects.m_active.find(id);
1603 0 : if(i != block->m_static_objects.m_active.end()){
1604 0 : block->m_static_objects.m_stored.push_back(i->second);
1605 0 : block->m_static_objects.m_active.erase(id);
1606 : block->raiseModified(MOD_STATE_WRITE_NEEDED,
1607 0 : MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1608 : }
1609 : } else {
1610 0 : infostream<<"Failed to emerge block from which an object to "
1611 0 : <<"be deactivated was loaded from. id="<<id<<std::endl;
1612 : }
1613 : }
1614 :
1615 : // Tell the object about removal
1616 0 : obj->removingFromEnvironment();
1617 : // Deregister in scripting api
1618 0 : m_script->removeObjectReference(obj);
1619 :
1620 : // Delete
1621 0 : if(obj->environmentDeletes())
1622 0 : delete obj;
1623 :
1624 : // Id to be removed from m_active_objects
1625 0 : objects_to_remove.push_back(id);
1626 : }
1627 : // Remove references from m_active_objects
1628 0 : for(std::vector<u16>::iterator i = objects_to_remove.begin();
1629 0 : i != objects_to_remove.end(); ++i) {
1630 0 : m_active_objects.erase(*i);
1631 : }
1632 0 : }
1633 :
1634 0 : static void print_hexdump(std::ostream &o, const std::string &data)
1635 : {
1636 0 : const int linelength = 16;
1637 0 : for(int l=0; ; l++){
1638 0 : int i0 = linelength * l;
1639 0 : bool at_end = false;
1640 0 : int thislinelength = linelength;
1641 0 : if(i0 + thislinelength > (int)data.size()){
1642 0 : thislinelength = data.size() - i0;
1643 0 : at_end = true;
1644 : }
1645 0 : for(int di=0; di<linelength; di++){
1646 0 : int i = i0 + di;
1647 : char buf[4];
1648 0 : if(di<thislinelength)
1649 0 : snprintf(buf, 4, "%.2x ", data[i]);
1650 : else
1651 0 : snprintf(buf, 4, " ");
1652 0 : o<<buf;
1653 : }
1654 0 : o<<" ";
1655 0 : for(int di=0; di<thislinelength; di++){
1656 0 : int i = i0 + di;
1657 0 : if(data[i] >= 32)
1658 0 : o<<data[i];
1659 : else
1660 0 : o<<".";
1661 : }
1662 0 : o<<std::endl;
1663 0 : if(at_end)
1664 0 : break;
1665 : }
1666 0 : }
1667 :
1668 : /*
1669 : Convert stored objects from blocks near the players to active.
1670 : */
1671 0 : void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1672 : {
1673 0 : if(block == NULL)
1674 0 : return;
1675 :
1676 : // Ignore if no stored objects (to not set changed flag)
1677 0 : if(block->m_static_objects.m_stored.empty())
1678 0 : return;
1679 :
1680 0 : verbosestream<<"ServerEnvironment::activateObjects(): "
1681 0 : <<"activating objects of block "<<PP(block->getPos())
1682 0 : <<" ("<<block->m_static_objects.m_stored.size()
1683 0 : <<" objects)"<<std::endl;
1684 0 : bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1685 0 : if (large_amount) {
1686 0 : errorstream<<"suspiciously large amount of objects detected: "
1687 0 : <<block->m_static_objects.m_stored.size()<<" in "
1688 0 : <<PP(block->getPos())
1689 0 : <<"; removing all of them."<<std::endl;
1690 : // Clear stored list
1691 0 : block->m_static_objects.m_stored.clear();
1692 : block->raiseModified(MOD_STATE_WRITE_NEEDED,
1693 0 : MOD_REASON_TOO_MANY_OBJECTS);
1694 0 : return;
1695 : }
1696 :
1697 : // Activate stored objects
1698 0 : std::vector<StaticObject> new_stored;
1699 0 : for (std::vector<StaticObject>::iterator
1700 0 : i = block->m_static_objects.m_stored.begin();
1701 0 : i != block->m_static_objects.m_stored.end(); ++i) {
1702 0 : StaticObject &s_obj = *i;
1703 :
1704 : // Create an active object from the data
1705 : ServerActiveObject *obj = ServerActiveObject::create
1706 0 : ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1707 : // If couldn't create object, store static data back.
1708 0 : if(obj == NULL) {
1709 0 : errorstream<<"ServerEnvironment::activateObjects(): "
1710 0 : <<"failed to create active object from static object "
1711 0 : <<"in block "<<PP(s_obj.pos/BS)
1712 0 : <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1713 0 : print_hexdump(verbosestream, s_obj.data);
1714 :
1715 0 : new_stored.push_back(s_obj);
1716 0 : continue;
1717 : }
1718 0 : verbosestream<<"ServerEnvironment::activateObjects(): "
1719 0 : <<"activated static object pos="<<PP(s_obj.pos/BS)
1720 0 : <<" type="<<(int)s_obj.type<<std::endl;
1721 : // This will also add the object to the active static list
1722 0 : addActiveObjectRaw(obj, false, dtime_s);
1723 : }
1724 : // Clear stored list
1725 0 : block->m_static_objects.m_stored.clear();
1726 : // Add leftover failed stuff to stored list
1727 0 : for(std::vector<StaticObject>::iterator
1728 0 : i = new_stored.begin();
1729 0 : i != new_stored.end(); ++i) {
1730 0 : StaticObject &s_obj = *i;
1731 0 : block->m_static_objects.m_stored.push_back(s_obj);
1732 : }
1733 :
1734 : // Turn the active counterparts of activated objects not pending for
1735 : // deactivation
1736 0 : for(std::map<u16, StaticObject>::iterator
1737 0 : i = block->m_static_objects.m_active.begin();
1738 0 : i != block->m_static_objects.m_active.end(); ++i)
1739 : {
1740 0 : u16 id = i->first;
1741 0 : ServerActiveObject *object = getActiveObject(id);
1742 : assert(object);
1743 0 : object->m_pending_deactivation = false;
1744 : }
1745 :
1746 : /*
1747 : Note: Block hasn't really been modified here.
1748 : The objects have just been activated and moved from the stored
1749 : static list to the active static list.
1750 : As such, the block is essentially the same.
1751 : Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1752 : Otherwise there would be a huge amount of unnecessary I/O.
1753 : */
1754 : }
1755 :
1756 : /*
1757 : Convert objects that are not standing inside active blocks to static.
1758 :
1759 : If m_known_by_count != 0, active object is not deleted, but static
1760 : data is still updated.
1761 :
1762 : If force_delete is set, active object is deleted nevertheless. It
1763 : shall only be set so in the destructor of the environment.
1764 :
1765 : If block wasn't generated (not in memory or on disk),
1766 : */
1767 0 : void ServerEnvironment::deactivateFarObjects(bool force_delete)
1768 : {
1769 0 : std::vector<u16> objects_to_remove;
1770 0 : for(std::map<u16, ServerActiveObject*>::iterator
1771 0 : i = m_active_objects.begin();
1772 0 : i != m_active_objects.end(); ++i) {
1773 0 : ServerActiveObject* obj = i->second;
1774 : assert(obj);
1775 :
1776 : // Do not deactivate if static data creation not allowed
1777 0 : if(!force_delete && !obj->isStaticAllowed())
1778 0 : continue;
1779 :
1780 : // If pending deactivation, let removeRemovedObjects() do it
1781 0 : if(!force_delete && obj->m_pending_deactivation)
1782 0 : continue;
1783 :
1784 0 : u16 id = i->first;
1785 0 : v3f objectpos = obj->getBasePosition();
1786 :
1787 : // The block in which the object resides in
1788 0 : v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1789 :
1790 : // If object's static data is stored in a deactivated block and object
1791 : // is actually located in an active block, re-save to the block in
1792 : // which the object is actually located in.
1793 0 : if(!force_delete &&
1794 0 : obj->m_static_exists &&
1795 0 : !m_active_blocks.contains(obj->m_static_block) &&
1796 0 : m_active_blocks.contains(blockpos_o))
1797 : {
1798 0 : v3s16 old_static_block = obj->m_static_block;
1799 :
1800 : // Save to block where object is located
1801 0 : MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1802 0 : if(!block){
1803 0 : errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1804 0 : <<"Could not save object id="<<id
1805 0 : <<" to it's current block "<<PP(blockpos_o)
1806 0 : <<std::endl;
1807 0 : continue;
1808 : }
1809 0 : std::string staticdata_new = obj->getStaticData();
1810 0 : StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1811 0 : block->m_static_objects.insert(id, s_obj);
1812 0 : obj->m_static_block = blockpos_o;
1813 : block->raiseModified(MOD_STATE_WRITE_NEEDED,
1814 0 : MOD_REASON_STATIC_DATA_ADDED);
1815 :
1816 : // Delete from block where object was located
1817 0 : block = m_map->emergeBlock(old_static_block, false);
1818 0 : if(!block){
1819 0 : errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1820 0 : <<"Could not delete object id="<<id
1821 0 : <<" from it's previous block "<<PP(old_static_block)
1822 0 : <<std::endl;
1823 0 : continue;
1824 : }
1825 0 : block->m_static_objects.remove(id);
1826 : block->raiseModified(MOD_STATE_WRITE_NEEDED,
1827 0 : MOD_REASON_STATIC_DATA_REMOVED);
1828 0 : continue;
1829 : }
1830 :
1831 : // If block is active, don't remove
1832 0 : if(!force_delete && m_active_blocks.contains(blockpos_o))
1833 0 : continue;
1834 :
1835 0 : verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1836 0 : <<"deactivating object id="<<id<<" on inactive block "
1837 0 : <<PP(blockpos_o)<<std::endl;
1838 :
1839 : // If known by some client, don't immediately delete.
1840 0 : bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1841 :
1842 : /*
1843 : Update the static data
1844 : */
1845 :
1846 0 : if(obj->isStaticAllowed())
1847 : {
1848 : // Create new static object
1849 0 : std::string staticdata_new = obj->getStaticData();
1850 0 : StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1851 :
1852 0 : bool stays_in_same_block = false;
1853 0 : bool data_changed = true;
1854 :
1855 0 : if(obj->m_static_exists){
1856 0 : if(obj->m_static_block == blockpos_o)
1857 0 : stays_in_same_block = true;
1858 :
1859 0 : MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1860 :
1861 : std::map<u16, StaticObject>::iterator n =
1862 0 : block->m_static_objects.m_active.find(id);
1863 0 : if(n != block->m_static_objects.m_active.end()){
1864 0 : StaticObject static_old = n->second;
1865 :
1866 0 : float save_movem = obj->getMinimumSavedMovement();
1867 :
1868 0 : if(static_old.data == staticdata_new &&
1869 0 : (static_old.pos - objectpos).getLength() < save_movem)
1870 0 : data_changed = false;
1871 : } else {
1872 0 : errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1873 0 : <<"id="<<id<<" m_static_exists=true but "
1874 0 : <<"static data doesn't actually exist in "
1875 0 : <<PP(obj->m_static_block)<<std::endl;
1876 : }
1877 : }
1878 :
1879 0 : bool shall_be_written = (!stays_in_same_block || data_changed);
1880 :
1881 : // Delete old static object
1882 0 : if(obj->m_static_exists)
1883 : {
1884 0 : MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1885 0 : if(block)
1886 : {
1887 0 : block->m_static_objects.remove(id);
1888 0 : obj->m_static_exists = false;
1889 : // Only mark block as modified if data changed considerably
1890 0 : if(shall_be_written)
1891 : block->raiseModified(MOD_STATE_WRITE_NEEDED,
1892 0 : MOD_REASON_STATIC_DATA_CHANGED);
1893 : }
1894 : }
1895 :
1896 : // Add to the block where the object is located in
1897 0 : v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1898 : // Get or generate the block
1899 0 : MapBlock *block = NULL;
1900 : try{
1901 0 : block = m_map->emergeBlock(blockpos);
1902 0 : } catch(InvalidPositionException &e){
1903 : // Handled via NULL pointer
1904 : // NOTE: emergeBlock's failure is usually determined by it
1905 : // actually returning NULL
1906 : }
1907 :
1908 0 : if(block)
1909 : {
1910 0 : if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1911 0 : errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1912 0 : <<" statically but block "<<PP(blockpos)
1913 0 : <<" already contains "
1914 0 : <<block->m_static_objects.m_stored.size()
1915 0 : <<" objects."
1916 0 : <<" Forcing delete."<<std::endl;
1917 0 : force_delete = true;
1918 : } else {
1919 : // If static counterpart already exists in target block,
1920 : // remove it first.
1921 : // This shouldn't happen because the object is removed from
1922 : // the previous block before this according to
1923 : // obj->m_static_block, but happens rarely for some unknown
1924 : // reason. Unsuccessful attempts have been made to find
1925 : // said reason.
1926 0 : if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1927 0 : infostream<<"ServerEnv: WARNING: Performing hack #83274"
1928 0 : <<std::endl;
1929 0 : block->m_static_objects.remove(id);
1930 : }
1931 : // Store static data
1932 0 : u16 store_id = pending_delete ? id : 0;
1933 0 : block->m_static_objects.insert(store_id, s_obj);
1934 :
1935 : // Only mark block as modified if data changed considerably
1936 0 : if(shall_be_written)
1937 : block->raiseModified(MOD_STATE_WRITE_NEEDED,
1938 0 : MOD_REASON_STATIC_DATA_CHANGED);
1939 :
1940 0 : obj->m_static_exists = true;
1941 0 : obj->m_static_block = block->getPos();
1942 : }
1943 : }
1944 : else{
1945 0 : if(!force_delete){
1946 0 : v3s16 p = floatToInt(objectpos, BS);
1947 0 : errorstream<<"ServerEnv: Could not find or generate "
1948 0 : <<"a block for storing id="<<obj->getId()
1949 0 : <<" statically (pos="<<PP(p)<<")"<<std::endl;
1950 0 : continue;
1951 : }
1952 : }
1953 : }
1954 :
1955 : /*
1956 : If known by some client, set pending deactivation.
1957 : Otherwise delete it immediately.
1958 : */
1959 :
1960 0 : if(pending_delete && !force_delete)
1961 : {
1962 0 : verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1963 0 : <<"object id="<<id<<" is known by clients"
1964 0 : <<"; not deleting yet"<<std::endl;
1965 :
1966 0 : obj->m_pending_deactivation = true;
1967 0 : continue;
1968 : }
1969 :
1970 0 : verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1971 0 : <<"object id="<<id<<" is not known by clients"
1972 0 : <<"; deleting"<<std::endl;
1973 :
1974 : // Tell the object about removal
1975 0 : obj->removingFromEnvironment();
1976 : // Deregister in scripting api
1977 0 : m_script->removeObjectReference(obj);
1978 :
1979 : // Delete active object
1980 0 : if(obj->environmentDeletes())
1981 0 : delete obj;
1982 : // Id to be removed from m_active_objects
1983 0 : objects_to_remove.push_back(id);
1984 : }
1985 :
1986 : // Remove references from m_active_objects
1987 0 : for(std::vector<u16>::iterator i = objects_to_remove.begin();
1988 0 : i != objects_to_remove.end(); ++i) {
1989 0 : m_active_objects.erase(*i);
1990 : }
1991 0 : }
1992 :
1993 :
1994 : #ifndef SERVER
1995 :
1996 : #include "clientsimpleobject.h"
1997 :
1998 : /*
1999 : ClientEnvironment
2000 : */
2001 :
2002 1 : ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2003 : ITextureSource *texturesource, IGameDef *gamedef,
2004 : IrrlichtDevice *irr):
2005 : m_map(map),
2006 : m_smgr(smgr),
2007 : m_texturesource(texturesource),
2008 : m_gamedef(gamedef),
2009 1 : m_irr(irr)
2010 : {
2011 1 : char zero = 0;
2012 1 : memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2013 1 : }
2014 :
2015 2 : ClientEnvironment::~ClientEnvironment()
2016 : {
2017 : // delete active objects
2018 77 : for(std::map<u16, ClientActiveObject*>::iterator
2019 1 : i = m_active_objects.begin();
2020 52 : i != m_active_objects.end(); ++i)
2021 : {
2022 25 : delete i->second;
2023 : }
2024 :
2025 2 : for(std::vector<ClientSimpleObject*>::iterator
2026 2 : i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2027 0 : delete *i;
2028 : }
2029 :
2030 : // Drop/delete map
2031 1 : m_map->drop();
2032 1 : }
2033 :
2034 25583 : Map & ClientEnvironment::getMap()
2035 : {
2036 25583 : return *m_map;
2037 : }
2038 :
2039 36324 : ClientMap & ClientEnvironment::getClientMap()
2040 : {
2041 36324 : return *m_map;
2042 : }
2043 :
2044 1 : void ClientEnvironment::addPlayer(Player *player)
2045 : {
2046 2 : DSTACK(__FUNCTION_NAME);
2047 : /*
2048 : It is a failure if player is local and there already is a local
2049 : player
2050 : */
2051 2 : FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2052 1 : "Player is local but there is already a local player");
2053 :
2054 1 : Environment::addPlayer(player);
2055 1 : }
2056 :
2057 1440547 : LocalPlayer * ClientEnvironment::getLocalPlayer()
2058 : {
2059 4321641 : for(std::vector<Player*>::iterator i = m_players.begin();
2060 2881094 : i != m_players.end(); ++i) {
2061 1440546 : Player *player = *i;
2062 1440546 : if(player->isLocal())
2063 1440546 : return (LocalPlayer*)player;
2064 : }
2065 1 : return NULL;
2066 : }
2067 :
2068 1186 : void ClientEnvironment::step(float dtime)
2069 : {
2070 2372 : DSTACK(__FUNCTION_NAME);
2071 :
2072 : /* Step time of day */
2073 1186 : stepTimeOfDay(dtime);
2074 :
2075 : // Get some settings
2076 1186 : bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2077 1186 : bool free_move = fly_allowed && g_settings->getBool("free_move");
2078 :
2079 : // Get local player
2080 1186 : LocalPlayer *lplayer = getLocalPlayer();
2081 : assert(lplayer);
2082 : // collision info queue
2083 2372 : std::vector<CollisionInfo> player_collisions;
2084 :
2085 : /*
2086 : Get the speed the player is going
2087 : */
2088 1186 : bool is_climbing = lplayer->is_climbing;
2089 :
2090 1186 : f32 player_speed = lplayer->getSpeed().getLength();
2091 :
2092 : /*
2093 : Maximum position increment
2094 : */
2095 : //f32 position_max_increment = 0.05*BS;
2096 1186 : f32 position_max_increment = 0.1*BS;
2097 :
2098 : // Maximum time increment (for collision detection etc)
2099 : // time = distance / speed
2100 1186 : f32 dtime_max_increment = 1;
2101 1186 : if(player_speed > 0.001)
2102 633 : dtime_max_increment = position_max_increment / player_speed;
2103 :
2104 : // Maximum time increment is 10ms or lower
2105 1186 : if(dtime_max_increment > 0.01)
2106 1186 : dtime_max_increment = 0.01;
2107 :
2108 : // Don't allow overly huge dtime
2109 1186 : if(dtime > 0.5)
2110 1 : dtime = 0.5;
2111 :
2112 1186 : f32 dtime_downcount = dtime;
2113 :
2114 : /*
2115 : Stuff that has a maximum time increment
2116 : */
2117 :
2118 1186 : u32 loopcount = 0;
2119 2562 : do
2120 : {
2121 3748 : loopcount++;
2122 :
2123 : f32 dtime_part;
2124 3748 : if(dtime_downcount > dtime_max_increment)
2125 : {
2126 2564 : dtime_part = dtime_max_increment;
2127 2564 : dtime_downcount -= dtime_part;
2128 : }
2129 : else
2130 : {
2131 1184 : dtime_part = dtime_downcount;
2132 : /*
2133 : Setting this to 0 (no -=dtime_part) disables an infinite loop
2134 : when dtime_part is so small that dtime_downcount -= dtime_part
2135 : does nothing
2136 : */
2137 1184 : dtime_downcount = 0;
2138 : }
2139 :
2140 : /*
2141 : Handle local player
2142 : */
2143 :
2144 : {
2145 : // Apply physics
2146 3748 : if(free_move == false && is_climbing == false)
2147 : {
2148 : // Gravity
2149 3735 : v3f speed = lplayer->getSpeed();
2150 3735 : if(lplayer->in_liquid == false)
2151 3735 : speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2152 :
2153 : // Liquid floating / sinking
2154 3735 : if(lplayer->in_liquid && !lplayer->swimming_vertical)
2155 0 : speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2156 :
2157 : // Liquid resistance
2158 3735 : if(lplayer->in_liquid_stable || lplayer->in_liquid)
2159 : {
2160 : // How much the node's viscosity blocks movement, ranges between 0 and 1
2161 : // Should match the scale at which viscosity increase affects other liquid attributes
2162 0 : const f32 viscosity_factor = 0.3;
2163 :
2164 0 : v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2165 0 : f32 dl = d_wanted.getLength();
2166 0 : if(dl > lplayer->movement_liquid_fluidity_smooth)
2167 0 : dl = lplayer->movement_liquid_fluidity_smooth;
2168 0 : dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2169 :
2170 0 : v3f d = d_wanted.normalize() * dl;
2171 0 : speed += d;
2172 :
2173 : #if 0 // old code
2174 : if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2175 : if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2176 : if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2177 : if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2178 : if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2179 : if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2180 : #endif
2181 : }
2182 :
2183 3735 : lplayer->setSpeed(speed);
2184 : }
2185 :
2186 : /*
2187 : Move the lplayer.
2188 : This also does collision detection.
2189 : */
2190 3748 : lplayer->move(dtime_part, this, position_max_increment,
2191 7496 : &player_collisions);
2192 : }
2193 : }
2194 3748 : while(dtime_downcount > 0.001);
2195 :
2196 : //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2197 :
2198 13203 : for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2199 8802 : i != player_collisions.end(); ++i) {
2200 3215 : CollisionInfo &info = *i;
2201 3215 : v3f speed_diff = info.new_speed - info.old_speed;;
2202 : // Handle only fall damage
2203 : // (because otherwise walking against something in fast_move kills you)
2204 3215 : if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2205 0 : continue;
2206 : // Get rid of other components
2207 3215 : speed_diff.X = 0;
2208 3215 : speed_diff.Z = 0;
2209 3215 : f32 pre_factor = 1; // 1 hp per node/s
2210 3215 : f32 tolerance = BS*14; // 5 without damage
2211 3215 : f32 post_factor = 1; // 1 hp per node/s
2212 3215 : if(info.type == COLLISION_NODE)
2213 : {
2214 3215 : const ContentFeatures &f = m_gamedef->ndef()->
2215 3215 : get(m_map->getNodeNoEx(info.node_p));
2216 : // Determine fall damage multiplier
2217 3215 : int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2218 3215 : pre_factor = 1.0 + (float)addp/100.0;
2219 : }
2220 3215 : float speed = pre_factor * speed_diff.getLength();
2221 3215 : if(speed > tolerance)
2222 : {
2223 0 : f32 damage_f = (speed - tolerance)/BS * post_factor;
2224 0 : u16 damage = (u16)(damage_f+0.5);
2225 0 : if(damage != 0){
2226 0 : damageLocalPlayer(damage, true);
2227 0 : MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2228 0 : m_gamedef->event()->put(e);
2229 : }
2230 : }
2231 : }
2232 :
2233 : /*
2234 : A quick draft of lava damage
2235 : */
2236 1186 : if(m_lava_hurt_interval.step(dtime, 1.0))
2237 : {
2238 32 : v3f pf = lplayer->getPosition();
2239 :
2240 : // Feet, middle and head
2241 32 : v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2242 32 : MapNode n1 = m_map->getNodeNoEx(p1);
2243 32 : v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2244 32 : MapNode n2 = m_map->getNodeNoEx(p2);
2245 32 : v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2246 32 : MapNode n3 = m_map->getNodeNoEx(p3);
2247 :
2248 32 : u32 damage_per_second = 0;
2249 64 : damage_per_second = MYMAX(damage_per_second,
2250 64 : m_gamedef->ndef()->get(n1).damage_per_second);
2251 64 : damage_per_second = MYMAX(damage_per_second,
2252 64 : m_gamedef->ndef()->get(n2).damage_per_second);
2253 64 : damage_per_second = MYMAX(damage_per_second,
2254 64 : m_gamedef->ndef()->get(n3).damage_per_second);
2255 :
2256 32 : if(damage_per_second != 0)
2257 : {
2258 0 : damageLocalPlayer(damage_per_second, true);
2259 : }
2260 : }
2261 :
2262 : /*
2263 : Drowning
2264 : */
2265 1186 : if(m_drowning_interval.step(dtime, 2.0))
2266 : {
2267 16 : v3f pf = lplayer->getPosition();
2268 :
2269 : // head
2270 16 : v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2271 16 : MapNode n = m_map->getNodeNoEx(p);
2272 32 : ContentFeatures c = m_gamedef->ndef()->get(n);
2273 16 : u8 drowning_damage = c.drowning;
2274 16 : if(drowning_damage > 0 && lplayer->hp > 0){
2275 0 : u16 breath = lplayer->getBreath();
2276 0 : if(breath > 10){
2277 0 : breath = 11;
2278 : }
2279 0 : if(breath > 0){
2280 0 : breath -= 1;
2281 : }
2282 0 : lplayer->setBreath(breath);
2283 0 : updateLocalPlayerBreath(breath);
2284 : }
2285 :
2286 16 : if(lplayer->getBreath() == 0 && drowning_damage > 0){
2287 0 : damageLocalPlayer(drowning_damage, true);
2288 : }
2289 : }
2290 1186 : if(m_breathing_interval.step(dtime, 0.5))
2291 : {
2292 65 : v3f pf = lplayer->getPosition();
2293 :
2294 : // head
2295 65 : v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2296 65 : MapNode n = m_map->getNodeNoEx(p);
2297 130 : ContentFeatures c = m_gamedef->ndef()->get(n);
2298 65 : if (!lplayer->hp){
2299 1 : lplayer->setBreath(11);
2300 : }
2301 64 : else if(c.drowning == 0){
2302 64 : u16 breath = lplayer->getBreath();
2303 64 : if(breath <= 10){
2304 0 : breath += 1;
2305 0 : lplayer->setBreath(breath);
2306 0 : updateLocalPlayerBreath(breath);
2307 : }
2308 : }
2309 : }
2310 :
2311 : /*
2312 : Stuff that can be done in an arbitarily large dtime
2313 : */
2314 7116 : for(std::vector<Player*>::iterator i = m_players.begin();
2315 4744 : i != m_players.end(); ++i) {
2316 1186 : Player *player = *i;
2317 :
2318 : /*
2319 : Handle non-local players
2320 : */
2321 1186 : if(player->isLocal() == false) {
2322 : // Move
2323 0 : player->move(dtime, this, 100*BS);
2324 :
2325 : }
2326 : }
2327 :
2328 : // Update lighting on local player (used for wield item)
2329 1186 : u32 day_night_ratio = getDayNightRatio();
2330 : {
2331 : // Get node at head
2332 :
2333 : // On InvalidPositionException, use this as default
2334 : // (day: LIGHT_SUN, night: 0)
2335 1186 : MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2336 :
2337 1186 : v3s16 p = lplayer->getLightPosition();
2338 1186 : node_at_lplayer = m_map->getNodeNoEx(p);
2339 :
2340 1186 : u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2341 1186 : u8 day = light & 0xff;
2342 1186 : u8 night = (light >> 8) & 0xff;
2343 1186 : finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2344 : }
2345 :
2346 : /*
2347 : Step active objects and update lighting of them
2348 : */
2349 :
2350 1186 : g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2351 1186 : bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2352 80942 : for(std::map<u16, ClientActiveObject*>::iterator
2353 1186 : i = m_active_objects.begin();
2354 54752 : i != m_active_objects.end(); ++i)
2355 : {
2356 26190 : ClientActiveObject* obj = i->second;
2357 : // Step object
2358 26190 : obj->step(dtime, this);
2359 :
2360 26190 : if(update_lighting)
2361 : {
2362 : // Update lighting
2363 3510 : u8 light = 0;
2364 : bool pos_ok;
2365 :
2366 : // Get node at head
2367 3510 : v3s16 p = obj->getLightPosition();
2368 3510 : MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2369 3510 : if (pos_ok)
2370 3320 : light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2371 : else
2372 190 : light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2373 :
2374 3510 : obj->updateLight(light);
2375 : }
2376 : }
2377 :
2378 : /*
2379 : Step and handle simple objects
2380 : */
2381 1186 : g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2382 2372 : for(std::vector<ClientSimpleObject*>::iterator
2383 2372 : i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2384 0 : std::vector<ClientSimpleObject*>::iterator cur = i;
2385 0 : ClientSimpleObject *simple = *cur;
2386 :
2387 0 : simple->step(dtime);
2388 0 : if(simple->m_to_be_removed) {
2389 0 : delete simple;
2390 0 : i = m_simple_objects.erase(cur);
2391 : }
2392 : else {
2393 0 : ++i;
2394 : }
2395 : }
2396 1186 : }
2397 :
2398 0 : void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2399 : {
2400 0 : m_simple_objects.push_back(simple);
2401 0 : }
2402 :
2403 79 : GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2404 : {
2405 79 : ClientActiveObject *obj = getActiveObject(id);
2406 79 : if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2407 0 : return (GenericCAO*) obj;
2408 : else
2409 79 : return NULL;
2410 : }
2411 :
2412 28727 : ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2413 : {
2414 28727 : std::map<u16, ClientActiveObject*>::iterator n;
2415 28727 : n = m_active_objects.find(id);
2416 28727 : if(n == m_active_objects.end())
2417 27185 : return NULL;
2418 1542 : return n->second;
2419 : }
2420 :
2421 44 : bool isFreeClientActiveObjectId(u16 id,
2422 : std::map<u16, ClientActiveObject*> &objects)
2423 : {
2424 44 : if(id == 0)
2425 0 : return false;
2426 :
2427 44 : return objects.find(id) == objects.end();
2428 : }
2429 :
2430 0 : u16 getFreeClientActiveObjectId(
2431 : std::map<u16, ClientActiveObject*> &objects)
2432 : {
2433 : //try to reuse id's as late as possible
2434 : static u16 last_used_id = 0;
2435 0 : u16 startid = last_used_id;
2436 0 : for(;;)
2437 : {
2438 0 : last_used_id ++;
2439 0 : if(isFreeClientActiveObjectId(last_used_id, objects))
2440 0 : return last_used_id;
2441 :
2442 0 : if(last_used_id == startid)
2443 0 : return 0;
2444 : }
2445 : }
2446 :
2447 44 : u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2448 : {
2449 : assert(object); // Pre-condition
2450 44 : if(object->getId() == 0)
2451 : {
2452 0 : u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2453 0 : if(new_id == 0)
2454 : {
2455 0 : infostream<<"ClientEnvironment::addActiveObject(): "
2456 0 : <<"no free ids available"<<std::endl;
2457 0 : delete object;
2458 0 : return 0;
2459 : }
2460 0 : object->setId(new_id);
2461 : }
2462 44 : if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2463 : {
2464 0 : infostream<<"ClientEnvironment::addActiveObject(): "
2465 0 : <<"id is not free ("<<object->getId()<<")"<<std::endl;
2466 0 : delete object;
2467 0 : return 0;
2468 : }
2469 44 : infostream<<"ClientEnvironment::addActiveObject(): "
2470 88 : <<"added (id="<<object->getId()<<")"<<std::endl;
2471 44 : m_active_objects[object->getId()] = object;
2472 44 : object->addToScene(m_smgr, m_texturesource, m_irr);
2473 : { // Update lighting immediately
2474 44 : u8 light = 0;
2475 : bool pos_ok;
2476 :
2477 : // Get node at head
2478 44 : v3s16 p = object->getLightPosition();
2479 44 : MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2480 44 : if (pos_ok)
2481 30 : light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2482 : else
2483 14 : light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2484 :
2485 44 : object->updateLight(light);
2486 : }
2487 44 : return object->getId();
2488 : }
2489 :
2490 44 : void ClientEnvironment::addActiveObject(u16 id, u8 type,
2491 : const std::string &init_data)
2492 : {
2493 : ClientActiveObject* obj =
2494 44 : ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2495 44 : if(obj == NULL)
2496 : {
2497 0 : infostream<<"ClientEnvironment::addActiveObject(): "
2498 0 : <<"id="<<id<<" type="<<type<<": Couldn't create object"
2499 0 : <<std::endl;
2500 0 : return;
2501 : }
2502 :
2503 44 : obj->setId(id);
2504 :
2505 : try
2506 : {
2507 44 : obj->initialize(init_data);
2508 : }
2509 0 : catch(SerializationError &e)
2510 : {
2511 0 : errorstream<<"ClientEnvironment::addActiveObject():"
2512 0 : <<" id="<<id<<" type="<<type
2513 0 : <<": SerializationError in initialize(): "
2514 0 : <<e.what()
2515 0 : <<": init_data="<<serializeJsonString(init_data)
2516 0 : <<std::endl;
2517 : }
2518 :
2519 44 : addActiveObject(obj);
2520 : }
2521 :
2522 19 : void ClientEnvironment::removeActiveObject(u16 id)
2523 : {
2524 19 : verbosestream<<"ClientEnvironment::removeActiveObject(): "
2525 38 : <<"id="<<id<<std::endl;
2526 19 : ClientActiveObject* obj = getActiveObject(id);
2527 19 : if(obj == NULL)
2528 : {
2529 0 : infostream<<"ClientEnvironment::removeActiveObject(): "
2530 0 : <<"id="<<id<<" not found"<<std::endl;
2531 0 : return;
2532 : }
2533 19 : obj->removeFromScene(true);
2534 19 : delete obj;
2535 19 : m_active_objects.erase(id);
2536 : }
2537 :
2538 28569 : void ClientEnvironment::processActiveObjectMessage(u16 id,
2539 : const std::string &data)
2540 : {
2541 28569 : ClientActiveObject* obj = getActiveObject(id);
2542 28569 : if(obj == NULL)
2543 : {
2544 27106 : infostream<<"ClientEnvironment::processActiveObjectMessage():"
2545 54212 : <<" got message for id="<<id<<", which doesn't exist."
2546 27106 : <<std::endl;
2547 27106 : return;
2548 : }
2549 : try
2550 : {
2551 1463 : obj->processMessage(data);
2552 : }
2553 0 : catch(SerializationError &e)
2554 : {
2555 0 : errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2556 0 : <<" id="<<id<<" type="<<obj->getType()
2557 0 : <<" SerializationError in processMessage(),"
2558 0 : <<" message="<<serializeJsonString(data)
2559 0 : <<std::endl;
2560 : }
2561 : }
2562 :
2563 : /*
2564 : Callbacks for activeobjects
2565 : */
2566 :
2567 0 : void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2568 : {
2569 0 : LocalPlayer *lplayer = getLocalPlayer();
2570 : assert(lplayer);
2571 :
2572 0 : if (handle_hp) {
2573 0 : if (lplayer->hp > damage)
2574 0 : lplayer->hp -= damage;
2575 : else
2576 0 : lplayer->hp = 0;
2577 : }
2578 :
2579 : ClientEnvEvent event;
2580 0 : event.type = CEE_PLAYER_DAMAGE;
2581 0 : event.player_damage.amount = damage;
2582 0 : event.player_damage.send_to_server = handle_hp;
2583 0 : m_client_event_queue.push_back(event);
2584 0 : }
2585 :
2586 0 : void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2587 : {
2588 : ClientEnvEvent event;
2589 0 : event.type = CEE_PLAYER_BREATH;
2590 0 : event.player_breath.amount = breath;
2591 0 : m_client_event_queue.push_back(event);
2592 0 : }
2593 :
2594 : /*
2595 : Client likes to call these
2596 : */
2597 :
2598 12581 : void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2599 : std::vector<DistanceSortedActiveObject> &dest)
2600 : {
2601 904366 : for(std::map<u16, ClientActiveObject*>::iterator
2602 12581 : i = m_active_objects.begin();
2603 611298 : i != m_active_objects.end(); ++i)
2604 : {
2605 293068 : ClientActiveObject* obj = i->second;
2606 :
2607 293068 : f32 d = (obj->getPosition() - origin).getLength();
2608 :
2609 293068 : if(d > max_d)
2610 281169 : continue;
2611 :
2612 11899 : DistanceSortedActiveObject dso(obj, d);
2613 :
2614 11899 : dest.push_back(dso);
2615 : }
2616 12581 : }
2617 :
2618 1186 : ClientEnvEvent ClientEnvironment::getClientEvent()
2619 : {
2620 : ClientEnvEvent event;
2621 1186 : if(m_client_event_queue.empty())
2622 1186 : event.type = CEE_NONE;
2623 : else {
2624 0 : event = m_client_event_queue.front();
2625 0 : m_client_event_queue.pop_front();
2626 : }
2627 1186 : return event;
2628 3 : }
2629 :
2630 : #endif // #ifndef SERVER
2631 :
2632 :
|