X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fenvironment.cpp;h=e472916657ede944d490dfb9ae6ec8f603520e0e;hb=29932d4bddad6a0ad4269606e38701d544784951;hp=906bab611d3c2af5846aa13ab9bc7439d2a8f5ea;hpb=3fb0d2fb65c968f91c333a1d31d2d7a1a02ab7d1;p=dragonfireclient.git diff --git a/src/environment.cpp b/src/environment.cpp index 906bab611..e47291665 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -1,6 +1,6 @@ /* Minetest-c55 -Copyright (C) 2010 celeron55, Perttu Ahola +Copyright (C) 2010-2011 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,13 +18,13 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "environment.h" -#include "main.h" // g_device for timing debug +#include "filesys.h" +#include "porting.h" +#include "collision.h" -Environment::Environment(Map *map, std::ostream &dout): - m_dout(dout) +Environment::Environment() { - m_map = map; - m_daynight_ratio = 0.2; + m_daynight_ratio = 0.5; } Environment::~Environment() @@ -35,54 +35,1037 @@ Environment::~Environment() { delete (*i); } +} + +void Environment::addPlayer(Player *player) +{ + DSTACK(__FUNCTION_NAME); + /* + Check that peer_ids are unique. + Also check that names are unique. + Exception: there can be multiple players with peer_id=0 + */ + // If peer id is non-zero, it has to be unique. + if(player->peer_id != 0) + assert(getPlayer(player->peer_id) == NULL); + // Name has to be unique. + assert(getPlayer(player->getName()) == NULL); + // Add. + m_players.push_back(player); +} + +void Environment::removePlayer(u16 peer_id) +{ + DSTACK(__FUNCTION_NAME); +re_search: + for(core::list::Iterator i = m_players.begin(); + i != m_players.end(); i++) + { + Player *player = *i; + if(player->peer_id != peer_id) + continue; + + delete player; + m_players.erase(i); + // See if there is an another one + // (shouldn't be, but just to be sure) + goto re_search; + } +} + +Player * Environment::getPlayer(u16 peer_id) +{ + for(core::list::Iterator i = m_players.begin(); + i != m_players.end(); i++) + { + Player *player = *i; + if(player->peer_id == peer_id) + return player; + } + return NULL; +} + +Player * Environment::getPlayer(const char *name) +{ + for(core::list::Iterator i = m_players.begin(); + i != m_players.end(); i++) + { + Player *player = *i; + if(strcmp(player->getName(), name) == 0) + return player; + } + return NULL; +} + +Player * Environment::getRandomConnectedPlayer() +{ + core::list connected_players = getPlayers(true); + u32 chosen_one = myrand() % connected_players.size(); + u32 j = 0; + for(core::list::Iterator + i = connected_players.begin(); + i != connected_players.end(); i++) + { + if(j == chosen_one) + { + Player *player = *i; + return player; + } + j++; + } + return NULL; +} + +Player * Environment::getNearestConnectedPlayer(v3f pos) +{ + core::list connected_players = getPlayers(true); + f32 nearest_d = 0; + Player *nearest_player = NULL; + for(core::list::Iterator + i = connected_players.begin(); + i != connected_players.end(); i++) + { + Player *player = *i; + f32 d = player->getPosition().getDistanceFrom(pos); + if(d < nearest_d || nearest_player == NULL) + { + nearest_d = d; + nearest_player = player; + } + } + return nearest_player; +} + +core::list Environment::getPlayers() +{ + return m_players; +} + +core::list Environment::getPlayers(bool ignore_disconnected) +{ + core::list newlist; + for(core::list::Iterator + i = m_players.begin(); + i != m_players.end(); i++) + { + Player *player = *i; + + if(ignore_disconnected) + { + // Ignore disconnected players + if(player->peer_id == 0) + continue; + } + + newlist.push_back(player); + } + return newlist; +} + +void Environment::printPlayers(std::ostream &o) +{ + o<<"Players in environment:"<::Iterator i = m_players.begin(); + i != m_players.end(); i++) + { + Player *player = *i; + o<<"Player peer_id="<peer_id<drop(); +} + +void ServerEnvironment::serializePlayers(const std::string &savedir) +{ + std::string players_path = savedir + "/players"; + fs::CreateDir(players_path); + + core::map saved_players; + + std::vector player_files = fs::GetDirListing(players_path); + for(u32 i=0; iserialize(os); + saved_players.insert(player, true); + } + } + + for(core::list::Iterator i = m_players.begin(); + i != m_players.end(); i++) + { + Player *player = *i; + if(saved_players.find(player) != NULL) + { + /*dstream<<"Player "<getName() + <<" was already saved."<getName(); + // Don't save unnamed player + if(playername == "") + { + //dstream<<"Not saving unnamed player."<getName()<<" to " + <serialize(os); + saved_players.insert(player, true); + } + } + + //dstream<<"Saved "< saved_players; + + std::vector player_files = fs::GetDirListing(players_path); + for(u32 i=0; ideSerialize(is); + } + + if(newplayer) + addPlayer(player); + } +} + +#if 0 +void spawnRandomObjects(MapBlock *block) +{ + for(s16 z0=0; z0getNodeNoEx(p); + if(n.d == CONTENT_IGNORE) + continue; + if(content_features(n.d).liquid_type != LIQUID_NONE) + continue; + if(content_features(n.d).walkable) + { + last_node_walkable = true; + continue; + } + if(last_node_walkable) + { + // If block contains light information + if(content_features(n.d).param_type == CPT_LIGHT) + { + if(n.getLight(LIGHTBANK_DAY) <= 5) + { + if(myrand() % 1000 == 0) + { + v3f pos_f = intToFloat(p+block->getPosRelative(), BS); + pos_f.Y -= BS*0.4; + ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f); + std::string data = obj->getStaticData(); + StaticObject s_obj(obj->getType(), + obj->getBasePosition(), data); + // Add one + block->m_static_objects.insert(0, s_obj); + delete obj; + block->setChangedFlag(); + } + } + } + } + last_node_walkable = false; + } + } +} +#endif + +void ServerEnvironment::step(float dtime) +{ + DSTACK(__FUNCTION_NAME); + + //TimeTaker timer("ServerEnv step"); + + // Get some settings + //bool free_move = g_settings.getBool("free_move"); + bool footprints = g_settings.getBool("footprints"); + + { + //TimeTaker timer("Server m_map->timerUpdate()"); + m_map->timerUpdate(dtime); + } + + /* + Handle players + */ + for(core::list::Iterator i = m_players.begin(); + i != m_players.end(); i++) + { + Player *player = *i; + + // Ignore disconnected players + if(player->peer_id == 0) + continue; + + v3f playerpos = player->getPosition(); + + // Move + player->move(dtime, *m_map, 100*BS); + + /* + Add footsteps to grass + */ + if(footprints) + { + // Get node that is at BS/4 under player + v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS); + try{ + MapNode n = m_map->getNode(bottompos); + if(n.d == CONTENT_GRASS) + { + n.d = CONTENT_GRASS_FOOTSTEPS; + m_map->setNode(bottompos, n); + } + } + catch(InvalidPositionException &e) + { + } + } + } + + /* + Step active objects + */ + { + //TimeTaker timer("Step active objects"); + + bool send_recommended = false; + m_send_recommended_timer += dtime; + if(m_send_recommended_timer > 0.15) + { + m_send_recommended_timer = 0; + send_recommended = true; + } + + for(core::map::Iterator + i = m_active_objects.getIterator(); + i.atEnd()==false; i++) + { + ServerActiveObject* obj = i.getNode()->getValue(); + // Step object, putting messages directly to the queue + obj->step(dtime, m_active_object_messages, send_recommended); + } + } + + if(m_object_management_interval.step(dtime, 0.5)) + { + //TimeTaker timer("ServerEnv object management"); + + /* + Remove objects that satisfy (m_removed && m_known_by_count==0) + */ + { + core::list objects_to_remove; + for(core::map::Iterator + i = m_active_objects.getIterator(); + i.atEnd()==false; i++) + { + u16 id = i.getNode()->getKey(); + ServerActiveObject* obj = i.getNode()->getValue(); + // This shouldn't happen but check it + if(obj == NULL) + { + dstream<<"WARNING: NULL object found in ServerEnvironment" + <<" while finding removed objects. id="<m_removed == false) + continue; + // Delete static data from block + if(obj->m_static_exists) + { + MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block); + if(block) + { + block->m_static_objects.remove(id); + block->setChangedFlag(); + } + } + // If m_known_by_count > 0, don't actually remove. + if(obj->m_known_by_count > 0) + continue; + // Delete + delete obj; + // Id to be removed from m_active_objects + objects_to_remove.push_back(id); + } + // Remove references from m_active_objects + for(core::list::Iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); i++) + { + m_active_objects.remove(*i); + } + } + + + const s16 to_active_max_blocks = 3; + const f32 to_static_max_f = (to_active_max_blocks+2)*MAP_BLOCKSIZE*BS; + + /* + Convert stored objects from blocks near the players to active. + */ + for(core::list::Iterator i = m_players.begin(); + i != m_players.end(); i++) + { + Player *player = *i; + + // Ignore disconnected players + if(player->peer_id == 0) + continue; + + v3f playerpos = player->getPosition(); + + v3s16 blockpos0 = getNodeBlockPos(floatToInt(playerpos, BS)); + v3s16 bpmin = blockpos0 - v3s16(1,1,1)*to_active_max_blocks; + v3s16 bpmax = blockpos0 + v3s16(1,1,1)*to_active_max_blocks; + // Loop through all nearby blocks + for(s16 x=bpmin.X; x<=bpmax.X; x++) + for(s16 y=bpmin.Y; y<=bpmax.Y; y++) + for(s16 z=bpmin.Z; z<=bpmax.Z; z++) + { + v3s16 blockpos(x,y,z); + MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); + if(block==NULL) + continue; + // Ignore if no stored objects (to not set changed flag) + if(block->m_static_objects.m_stored.size() == 0) + continue; + // This will contain the leftovers of the stored list + core::list new_stored; + // Loop through stored static objects + for(core::list::Iterator + i = block->m_static_objects.m_stored.begin(); + i != block->m_static_objects.m_stored.end(); i++) + { + /*dstream<<"INFO: Server: Creating an active object from " + <<"static data"<m_static_objects.m_stored.clear(); + // Add leftover stuff to stored list + for(core::list::Iterator + i = new_stored.begin(); + i != new_stored.end(); i++) + { + StaticObject &s_obj = *i; + block->m_static_objects.m_stored.push_back(s_obj); + } + block->setChangedFlag(); + } + } + + /* + Convert objects that are far away from all the players to static. + */ + { + core::list objects_to_remove; + for(core::map::Iterator + i = m_active_objects.getIterator(); + i.atEnd()==false; i++) + { + ServerActiveObject* obj = i.getNode()->getValue(); + u16 id = i.getNode()->getKey(); + v3f objectpos = obj->getBasePosition(); + + // This shouldn't happen but check it + if(obj == NULL) + { + dstream<<"WARNING: NULL object found in ServerEnvironment" + <m_known_by_count > 0) + continue; + + // If close to some player, don't convert to static. + bool close_to_player = false; + for(core::list::Iterator i = m_players.begin(); + i != m_players.end(); i++) + { + Player *player = *i; + + // Ignore disconnected players + if(player->peer_id == 0) + continue; + + v3f playerpos = player->getPosition(); + f32 d = playerpos.getDistanceFrom(objectpos); + if(d < to_static_max_f) + { + close_to_player = true; + break; + } + } + + if(close_to_player) + continue; + + /* + Update the static data and remove the active object. + */ + + // Delete old static object + MapBlock *oldblock = NULL; + if(obj->m_static_exists) + { + MapBlock *block = m_map->getBlockNoCreateNoEx + (obj->m_static_block); + if(block) + { + block->m_static_objects.remove(id); + oldblock = block; + } + } + // Add new static object + std::string staticdata = obj->getStaticData(); + StaticObject s_obj(obj->getType(), objectpos, staticdata); + // Add to the block where the object is located in + v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); + MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); + if(block) + { + block->m_static_objects.insert(0, s_obj); + block->setChangedFlag(); + obj->m_static_exists = true; + obj->m_static_block = block->getPos(); + } + // If not possible, add back to previous block + else if(oldblock) + { + oldblock->m_static_objects.insert(0, s_obj); + oldblock->setChangedFlag(); + obj->m_static_exists = true; + obj->m_static_block = oldblock->getPos(); + } + else{ + dstream<<"WARNING: Server: Could not find a block for " + <<"storing static object"<m_static_exists = false; + continue; + } + /*dstream<<"INFO: Server: Stored static data. Deleting object." + <::Iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); i++) + { + m_active_objects.remove(*i); + } + } + } + + if(g_settings.getBool("enable_experimental")) + { + + /* + TEST CODE + */ +#if 1 + m_random_spawn_timer -= dtime; + if(m_random_spawn_timer < 0) + { + //m_random_spawn_timer += myrand_range(2.0, 20.0); + //m_random_spawn_timer += 2.0; + m_random_spawn_timer += 200.0; + + /* + Find some position + */ + + /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5)); + s16 y = 1 + getServerMap().findGroundLevel(p2d); + v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/ + + Player *player = getRandomConnectedPlayer(); + v3f pos(0,0,0); + if(player) + pos = player->getPosition(); + pos += v3f( + myrand_range(-3,3)*BS, + 0, + myrand_range(-3,3)*BS + ); + + /* + Create a ServerActiveObject + */ + + //TestSAO *obj = new TestSAO(this, 0, pos); + //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1"); + //ServerActiveObject *obj = new RatSAO(this, 0, pos); + ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos); + addActiveObject(obj); + } +#endif + + } // enable_experimental +} + +ServerActiveObject* ServerEnvironment::getActiveObject(u16 id) +{ + core::map::Node *n; + n = m_active_objects.find(id); + if(n == NULL) + return NULL; + return n->getValue(); +} + +bool isFreeServerActiveObjectId(u16 id, + core::map &objects) +{ + if(id == 0) + return false; + + for(core::map::Iterator + i = objects.getIterator(); + i.atEnd()==false; i++) + { + if(i.getNode()->getKey() == id) + return false; + } + return true; +} + +u16 getFreeServerActiveObjectId( + core::map &objects) +{ + u16 new_id = 1; + for(;;) + { + if(isFreeServerActiveObjectId(new_id, objects)) + return new_id; + + if(new_id == 65535) + return 0; + + new_id++; + } +} + +u16 ServerEnvironment::addActiveObject(ServerActiveObject *object) +{ + assert(object); + if(object->getId() == 0) + { + u16 new_id = getFreeServerActiveObjectId(m_active_objects); + if(new_id == 0) + { + dstream<<"WARNING: ServerEnvironment::addActiveObject(): " + <<"no free ids available"<setId(new_id); + } + if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false) + { + dstream<<"WARNING: ServerEnvironment::addActiveObject(): " + <<"id is not free ("<getId()<<")"<getId(), object); + + // Add static object to active static list of the block + v3f objectpos = object->getBasePosition(); + std::string staticdata = object->getStaticData(); + StaticObject s_obj(object->getType(), objectpos, staticdata); + // Add to the block where the object is located in + v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); + MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); + if(block) + { + block->m_static_objects.m_active.insert(object->getId(), s_obj); + object->m_static_exists = true; + object->m_static_block = blockpos; + } + else{ + dstream<<"WARNING: Server: Could not find a block for " + <<"storing newly added static active object"<getId(); +} + +/* + Finds out what new objects have been added to + inside a radius around a position +*/ +void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius, + core::map ¤t_objects, + core::map &added_objects) +{ + v3f pos_f = intToFloat(pos, BS); + f32 radius_f = radius * BS; + /* + Go through the object list, + - discard m_removed objects, + - discard objects that are too far away, + - discard objects that are found in current_objects. + - add remaining objects to added_objects + */ + for(core::map::Iterator + i = m_active_objects.getIterator(); + i.atEnd()==false; i++) + { + u16 id = i.getNode()->getKey(); + // Get object + ServerActiveObject *object = i.getNode()->getValue(); + if(object == NULL) + continue; + // Discard if removed + if(object->m_removed) + continue; + // Discard if too far + f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f); + if(distance_f > radius_f) + continue; + // Discard if already on current_objects + core::map::Node *n; + n = current_objects.find(id); + if(n != NULL) + continue; + // Add to added_objects + added_objects.insert(id, false); + } +} + +/* + Finds out what objects have been removed from + inside a radius around a position +*/ +void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius, + core::map ¤t_objects, + core::map &removed_objects) +{ + v3f pos_f = intToFloat(pos, BS); + f32 radius_f = radius * BS; + /* + Go through current_objects; object is removed if: + - object is not found in m_active_objects (this is actually an + error condition; objects should be set m_removed=true and removed + only after all clients have been informed about removal), or + - object has m_removed=true, or + - object is too far away + */ + for(core::map::Iterator + i = current_objects.getIterator(); + i.atEnd()==false; i++) + { + u16 id = i.getNode()->getKey(); + ServerActiveObject *object = getActiveObject(id); + if(object == NULL) + { + dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():" + <<" object in current_objects is NULL"<m_removed == false) + { + f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f); + /*dstream<<"removed == false" + <<"distance_f = "<timerUpdate()", g_device); - // 0ms - m_map->timerUpdate(dtime); - //maptimerupdatetimer.stop(); + assert(!(player->isLocal() == true && getLocalPlayer() != NULL)); - /* - Get the highest speed some player is going - */ - //TimeTaker playerspeed("playerspeed", g_device); - // 0ms - f32 maximum_player_speed = 0.001; // just some small value + Environment::addPlayer(player); +} + +LocalPlayer * ClientEnvironment::getLocalPlayer() +{ for(core::list::Iterator i = m_players.begin(); i != m_players.end(); i++) { - f32 speed = (*i)->getSpeed().getLength(); - if(speed > maximum_player_speed) - maximum_player_speed = speed; + Player *player = *i; + if(player->isLocal()) + return (LocalPlayer*)player; + } + return NULL; +} + +void ClientEnvironment::step(float dtime) +{ + DSTACK(__FUNCTION_NAME); + + // Get some settings + bool free_move = g_settings.getBool("free_move"); + bool footprints = g_settings.getBool("footprints"); + + { + //TimeTaker timer("Client m_map->timerUpdate()"); + m_map->timerUpdate(dtime); } - //playerspeed.stop(); + // Get local player + LocalPlayer *lplayer = getLocalPlayer(); + assert(lplayer); + // collision info queue + core::list player_collisions; + + /* + Get the speed the player is going + */ + f32 player_speed = 0.001; // just some small value + player_speed = lplayer->getSpeed().getLength(); + + /* + Maximum position increment + */ + //f32 position_max_increment = 0.05*BS; + f32 position_max_increment = 0.1*BS; + // Maximum time increment (for collision detection etc) - // Allow 0.1 blocks per increment // time = distance / speed - f32 dtime_max_increment = 0.1*BS / maximum_player_speed; + f32 dtime_max_increment = position_max_increment / player_speed; + // Maximum time increment is 10ms or lower if(dtime_max_increment > 0.01) dtime_max_increment = 0.01; - //TimeTaker playerupdate("playerupdate", g_device); + // Don't allow overly huge dtime + if(dtime > 0.5) + dtime = 0.5; + f32 dtime_downcount = dtime; + /* Stuff that has a maximum time increment */ - // Don't allow overly huge dtime - if(dtime > 0.5) - dtime = 0.5; u32 loopcount = 0; do @@ -90,35 +1073,41 @@ void Environment::step(float dtime) loopcount++; f32 dtime_part; - if(dtime > dtime_max_increment) + if(dtime_downcount > dtime_max_increment) + { dtime_part = dtime_max_increment; + dtime_downcount -= dtime_part; + } else - dtime_part = dtime; - dtime -= dtime_part; + { + dtime_part = dtime_downcount; + /* + Setting this to 0 (no -=dtime_part) disables an infinite loop + when dtime_part is so small that dtime_downcount -= dtime_part + does nothing + */ + dtime_downcount = 0; + } /* - Handle players + Handle local player */ - for(core::list::Iterator i = m_players.begin(); - i != m_players.end(); i++) + { - Player *player = *i; - - v3f playerpos = player->getPosition(); + v3f lplayerpos = lplayer->getPosition(); - // Apply physics to local player - if(player->isLocal()) + // Apply physics + if(free_move == false) { - // Apply gravity to local player - v3f speed = player->getSpeed(); - speed.Y -= 9.81 * BS * dtime_part * 2; + // Gravity + v3f speed = lplayer->getSpeed(); + if(lplayer->swimming_up == false) + speed.Y -= 9.81 * BS * dtime_part * 2; - /* - Apply water resistance - */ - if(player->in_water) + // Water resistance + if(lplayer->in_water_stable || lplayer->in_water) { - f32 max_down = 1.0*BS; + f32 max_down = 2.0*BS; if(speed.Y < -max_down) speed.Y = -max_down; f32 max = 2.5*BS; @@ -128,48 +1117,98 @@ void Environment::step(float dtime) } } - player->setSpeed(speed); + lplayer->setSpeed(speed); } /* - Move the player. - For local player, this also calculates collision detection. - */ - player->move(dtime_part, *m_map); - - /* - Update lighting on remote players on client + Move the lplayer. + This also does collision detection. */ + lplayer->move(dtime_part, *m_map, position_max_increment, + &player_collisions); + } + } + while(dtime_downcount > 0.001); + + //std::cout<<"Looped "<::Iterator + i = player_collisions.begin(); + i != player_collisions.end(); i++) + { + CollisionInfo &info = *i; + if(info.t == COLLISION_FALL) + { + //f32 tolerance = BS*10; // 2 without damage + f32 tolerance = BS*12; // 3 without damage + f32 factor = 1; + if(info.speed > tolerance) + { + f32 damage_f = (info.speed - tolerance)/BS*factor; + u16 damage = (u16)(damage_f+0.5); + if(lplayer->hp > damage) + lplayer->hp -= damage; + else + lplayer->hp = 0; + + ClientEnvEvent event; + event.type = CEE_PLAYER_DAMAGE; + event.player_damage.amount = damage; + m_client_event_queue.push_back(event); + } + } + } + + /* + Stuff that can be done in an arbitarily large dtime + */ + for(core::list::Iterator i = m_players.begin(); + i != m_players.end(); i++) + { + Player *player = *i; + v3f playerpos = player->getPosition(); + + /* + Handle non-local players + */ + if(player->isLocal() == false) + { + // Move + player->move(dtime, *m_map, 100*BS); + + // Update lighting on remote players on client u8 light = LIGHT_MAX; try{ - // Get node at feet - v3s16 p = floatToInt(playerpos + v3f(0,BS/4,0)); + // Get node at head + v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS); MapNode n = m_map->getNode(p); light = n.getLightBlend(m_daynight_ratio); } catch(InvalidPositionException &e) {} player->updateLight(light); - - /* - Add footsteps to grass - */ + } + + /* + Add footsteps to grass + */ + if(footprints) + { // Get node that is at BS/4 under player - v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0)); + v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS); try{ MapNode n = m_map->getNode(bottompos); if(n.d == CONTENT_GRASS) { n.d = CONTENT_GRASS_FOOTSTEPS; m_map->setNode(bottompos, n); -#ifndef SERVER // Update mesh on client if(m_map->mapType() == MAPTYPE_CLIENT) { v3s16 p_blocks = getNodeBlockPos(bottompos); MapBlock *b = m_map->getBlockNoCreate(p_blocks); - b->updateMesh(m_daynight_ratio); + //b->updateMesh(m_daynight_ratio); + b->setMeshExpired(true); } -#endif } } catch(InvalidPositionException &e) @@ -177,140 +1216,216 @@ void Environment::step(float dtime) } } } - while(dtime > 0.001); - //std::cout<<"Looped "<::Iterator + i = m_active_objects.getIterator(); + i.atEnd()==false; i++) + { + ClientActiveObject* obj = i.getNode()->getValue(); + // Step object + obj->step(dtime, this); + // Update lighting + //u8 light = LIGHT_MAX; + u8 light = 0; + try{ + // Get node at head + v3s16 p = obj->getLightPosition(); + MapNode n = m_map->getNode(p); + light = n.getLightBlend(m_daynight_ratio); + } + catch(InvalidPositionException &e) {} + obj->updateLight(light); + } } -Map & Environment::getMap() +void ClientEnvironment::updateMeshes(v3s16 blockpos) { - return *m_map; + m_map->updateMeshes(blockpos, m_daynight_ratio); } -void Environment::addPlayer(Player *player) +void ClientEnvironment::expireMeshes(bool only_daynight_diffed) { - DSTACK(__FUNCTION_NAME); - //Check that only one local player exists and peer_ids are unique -#ifndef SERVER - assert(player->isLocal() == false || getLocalPlayer() == NULL); -#endif - assert(getPlayer(player->peer_id) == NULL); - m_players.push_back(player); + m_map->expireMeshes(only_daynight_diffed); } -void Environment::removePlayer(u16 peer_id) +ClientActiveObject* ClientEnvironment::getActiveObject(u16 id) { - DSTACK(__FUNCTION_NAME); -re_search: - for(core::list::Iterator i = m_players.begin(); - i != m_players.end(); i++) - { - Player *player = *i; - if(player->peer_id != peer_id) - continue; - - delete player; - m_players.erase(i); - // See if there is an another one - // (shouldn't be, but just to be sure) - goto re_search; - } + core::map::Node *n; + n = m_active_objects.find(id); + if(n == NULL) + return NULL; + return n->getValue(); } -#ifndef SERVER -LocalPlayer * Environment::getLocalPlayer() +bool isFreeClientActiveObjectId(u16 id, + core::map &objects) { - for(core::list::Iterator i = m_players.begin(); - i != m_players.end(); i++) + if(id == 0) + return false; + + for(core::map::Iterator + i = objects.getIterator(); + i.atEnd()==false; i++) { - Player *player = *i; - if(player->isLocal()) - return (LocalPlayer*)player; + if(i.getNode()->getKey() == id) + return false; } - return NULL; + return true; } -#endif -Player * Environment::getPlayer(u16 peer_id) +u16 getFreeClientActiveObjectId( + core::map &objects) { - for(core::list::Iterator i = m_players.begin(); - i != m_players.end(); i++) + u16 new_id = 1; + for(;;) { - Player *player = *i; - if(player->peer_id == peer_id) - return player; + if(isFreeClientActiveObjectId(new_id, objects)) + return new_id; + + if(new_id == 65535) + return 0; + + new_id++; } - return NULL; } -Player * Environment::getPlayer(const char *name) +u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) { - for(core::list::Iterator i = m_players.begin(); - i != m_players.end(); i++) + assert(object); + if(object->getId() == 0) { - Player *player = *i; - if(strcmp(player->getName(), name) == 0) - return player; + u16 new_id = getFreeClientActiveObjectId(m_active_objects); + if(new_id == 0) + { + dstream<<"WARNING: ClientEnvironment::addActiveObject(): " + <<"no free ids available"<setId(new_id); } - return NULL; + if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false) + { + dstream<<"WARNING: ClientEnvironment::addActiveObject(): " + <<"id is not free ("<getId()<<")"<getId(), object); + object->addToScene(m_smgr); + return object->getId(); } -core::list Environment::getPlayers() +void ClientEnvironment::addActiveObject(u16 id, u8 type, + const std::string &init_data) { - return m_players; + ClientActiveObject* obj = ClientActiveObject::create(type); + if(obj == NULL) + { + dstream<<"WARNING: ClientEnvironment::addActiveObject(): " + <<"id="<setId(id); + + addActiveObject(obj); + + obj->initialize(init_data); } -core::list Environment::getPlayers(bool ignore_disconnected) +void ClientEnvironment::removeActiveObject(u16 id) { - core::list newlist; - for(core::list::Iterator - i = m_players.begin(); - i != m_players.end(); i++) + dstream<<"ClientEnvironment::removeActiveObject(): " + <<"id="<peer_id == 0) - continue; - } - - newlist.push_back(player); + dstream<<"WARNING: ClientEnvironment::removeActiveObject(): " + <<"id="<removeFromScene(); + delete obj; + m_active_objects.remove(id); } -void Environment::printPlayers(std::ostream &o) +void ClientEnvironment::processActiveObjectMessage(u16 id, + const std::string &data) { - o<<"Players in environment:"<::Iterator i = m_players.begin(); - i != m_players.end(); i++) + ClientActiveObject* obj = getActiveObject(id); + if(obj == NULL) { - Player *player = *i; - o<<"Player peer_id="<peer_id<processMessage(data); } -#ifndef SERVER -void Environment::updateMeshes(v3s16 blockpos) -{ - m_map->updateMeshes(blockpos, m_daynight_ratio); -} +/* + Callbacks for activeobjects +*/ -void Environment::expireMeshes(bool only_daynight_diffed) +void ClientEnvironment::damageLocalPlayer(u8 damage) { - m_map->expireMeshes(only_daynight_diffed); + LocalPlayer *lplayer = getLocalPlayer(); + assert(lplayer); + + if(lplayer->hp > damage) + lplayer->hp -= damage; + else + lplayer->hp = 0; + + ClientEnvEvent event; + event.type = CEE_PLAYER_DAMAGE; + event.player_damage.amount = damage; + m_client_event_queue.push_back(event); } -#endif -void Environment::setDayNightRatio(u32 r) +/* + Client likes to call these +*/ + +void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, + core::array &dest) { - m_daynight_ratio = r; + for(core::map::Iterator + i = m_active_objects.getIterator(); + i.atEnd()==false; i++) + { + ClientActiveObject* obj = i.getNode()->getValue(); + + f32 d = (obj->getPosition() - origin).getLength(); + + if(d > max_d) + continue; + + DistanceSortedActiveObject dso(obj, d); + + dest.push_back(dso); + } } -u32 Environment::getDayNightRatio() +ClientEnvEvent ClientEnvironment::getClientEvent() { - return m_daynight_ratio; + if(m_client_event_queue.size() == 0) + { + ClientEnvEvent event; + event.type = CEE_NONE; + return event; + } + return m_client_event_queue.pop_front(); } +#endif // #ifndef SERVER + +