int StorageRef::gc_object(lua_State *L) { StorageRef *o = *(StorageRef **)(lua_touserdata(L, 1)); // Server side if (IGameDef *gamedef = getGameDef(L)) gamedef->unregisterModStorage(getobject(o)->getModName()); delete o; return 0; }
// find_node_near(pos, radius, nodenames, search_center) -> pos or nil // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" int ModApiEnvMod::l_find_node_near(lua_State *L) { Environment *env = getEnv(L); if (!env) { return 0; } INodeDefManager *ndef = getGameDef(L)->ndef(); v3s16 pos = read_v3s16(L, 1); int radius = luaL_checkinteger(L, 2); std::vector<content_t> filter; if (lua_istable(L, 3)) { lua_pushnil(L); while (lua_next(L, 3) != 0) { // key at index -2 and value at index -1 luaL_checktype(L, -1, LUA_TSTRING); ndef->getIds(lua_tostring(L, -1), filter); // removes value, keeps key for next iteration lua_pop(L, 1); } } else if (lua_isstring(L, 3)) { ndef->getIds(lua_tostring(L, 3), filter); } int start_radius = (lua_toboolean(L, 4)) ? 0 : 1; #ifndef SERVER // Client API limitations if (getClient(L) && getClient(L)->checkCSMFlavourLimit(CSMFlavourLimit::CSM_FL_LOOKUP_NODES)) { radius = std::max<int>(radius, getClient(L)->getCSMNodeRangeLimit()); } #endif for (int d = start_radius; d <= radius; d++) { std::vector<v3s16> list = FacePositionCache::getFacePositions(d); for (const v3s16 &i : list) { v3s16 p = pos + i; content_t c = env->getMap().getNodeNoEx(p).getContent(); if (CONTAINS(filter, c)) { push_v3s16(L, p); return 1; } } } return 0; }
// get_node_def(nodename) int ModApiClient::l_get_node_def(lua_State *L) { IGameDef *gdef = getGameDef(L); assert(gdef); INodeDefManager *ndef = gdef->ndef(); assert(ndef); if (!lua_isstring(L, 1)) return 0; const std::string &name = lua_tostring(L, 1); const ContentFeatures &cf = ndef->get(ndef->getId(name)); if (cf.name != name) // Unknown node. | name = <whatever>, cf.name = ignore return 0; push_content_features(L, cf); return 1; }
// get_item_def(itemstring) int ModApiClient::l_get_item_def(lua_State *L) { IGameDef *gdef = getGameDef(L); assert(gdef); IItemDefManager *idef = gdef->idef(); assert(idef); if (!lua_isstring(L, 1)) return 0; const std::string &name(lua_tostring(L, 1)); if (!idef->isKnown(name)) return 0; const ItemDefinition &def = idef->get(name); push_item_definition_full(L, def); return 1; }
// find_node_near(pos, radius, nodenames, search_center) -> pos or nil // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" int ModApiEnvMod::l_find_node_near(lua_State *L) { Environment *env = getEnv(L); if (!env) { return 0; } INodeDefManager *ndef = getGameDef(L)->ndef(); v3s16 pos = read_v3s16(L, 1); int radius = luaL_checkinteger(L, 2); std::set<content_t> filter; if (lua_istable(L, 3)) { lua_pushnil(L); while (lua_next(L, 3) != 0) { // key at index -2 and value at index -1 luaL_checktype(L, -1, LUA_TSTRING); ndef->getIds(lua_tostring(L, -1), filter); // removes value, keeps key for next iteration lua_pop(L, 1); } } else if (lua_isstring(L, 3)) { ndef->getIds(lua_tostring(L, 3), filter); } int start_radius = (lua_toboolean(L, 4)) ? 0 : 1; for (int d = start_radius; d <= radius; d++) { std::vector<v3s16> list = FacePositionCache::getFacePositions(d); for (std::vector<v3s16>::iterator i = list.begin(); i != list.end(); ++i) { v3s16 p = pos + (*i); content_t c = env->getMap().getNodeNoEx(p).getContent(); if (filter.count(c) != 0) { push_v3s16(L, p); return 1; } } } return 0; }
// get_item_def(itemstring) int ModApiClient::l_get_item_def(lua_State *L) { IGameDef *gdef = getGameDef(L); assert(gdef); IItemDefManager *idef = gdef->idef(); assert(idef); if (getClient(L)->checkCSMFlavourLimit(CSMFlavourLimit::CSM_FL_READ_ITEMDEFS)) return 0; if (!lua_isstring(L, 1)) return 0; const std::string &name(lua_tostring(L, 1)); if (!idef->isKnown(name)) return 0; const ItemDefinition &def = idef->get(name); push_item_definition_full(L, def); return 1; }
int ModApiStorage::l_get_mod_storage(lua_State *L) { lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); if (!lua_isstring(L, -1)) { return 0; } std::string mod_name = lua_tostring(L, -1); ModMetadata *store = new ModMetadata(mod_name); if (IGameDef *gamedef = getGameDef(L)) { store->load(gamedef->getModStoragePath()); gamedef->registerModStorage(store); } else { assert(false); // this should not happen } StorageRef::create(L, store); int object = lua_gettop(L); lua_pushvalue(L, object); return 1; }
void ClientEnvironment::step(float dtime) { DSTACK(FUNCTION_NAME); /* Step time of day */ stepTimeOfDay(dtime); // Get some settings bool fly_allowed = m_client->checkLocalPrivilege("fly"); bool free_move = fly_allowed && g_settings->getBool("free_move"); // Get local player LocalPlayer *lplayer = getLocalPlayer(); assert(lplayer); // collision info queue std::vector<CollisionInfo> player_collisions; /* Get the speed the player is going */ bool is_climbing = lplayer->is_climbing; f32 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) // time = distance / speed f32 dtime_max_increment = 1; if(player_speed > 0.001) 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; // Don't allow overly huge dtime if(dtime > 0.5) dtime = 0.5; f32 dtime_downcount = dtime; /* Stuff that has a maximum time increment */ u32 loopcount = 0; do { loopcount++; f32 dtime_part; if(dtime_downcount > dtime_max_increment) { dtime_part = dtime_max_increment; dtime_downcount -= dtime_part; } else { 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 local player */ { // Apply physics if(!free_move && !is_climbing) { // Gravity v3f speed = lplayer->getSpeed(); if(!lplayer->in_liquid) speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2; // Liquid floating / sinking if(lplayer->in_liquid && !lplayer->swimming_vertical) speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2; // Liquid resistance if(lplayer->in_liquid_stable || lplayer->in_liquid) { // How much the node's viscosity blocks movement, ranges between 0 and 1 // Should match the scale at which viscosity increase affects other liquid attributes const f32 viscosity_factor = 0.3; v3f d_wanted = -speed / lplayer->movement_liquid_fluidity; f32 dl = d_wanted.getLength(); if(dl > lplayer->movement_liquid_fluidity_smooth) dl = lplayer->movement_liquid_fluidity_smooth; dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor); v3f d = d_wanted.normalize() * dl; speed += d; } lplayer->setSpeed(speed); } /* Move the lplayer. This also does collision detection. */ lplayer->move(dtime_part, this, position_max_increment, &player_collisions); } } while(dtime_downcount > 0.001); //std::cout<<"Looped "<<loopcount<<" times."<<std::endl; for(std::vector<CollisionInfo>::iterator i = player_collisions.begin(); i != player_collisions.end(); ++i) { CollisionInfo &info = *i; v3f speed_diff = info.new_speed - info.old_speed;; // Handle only fall damage // (because otherwise walking against something in fast_move kills you) if(speed_diff.Y < 0 || info.old_speed.Y >= 0) continue; // Get rid of other components speed_diff.X = 0; speed_diff.Z = 0; f32 pre_factor = 1; // 1 hp per node/s f32 tolerance = BS*14; // 5 without damage f32 post_factor = 1; // 1 hp per node/s if(info.type == COLLISION_NODE) { const ContentFeatures &f = m_client->ndef()-> get(m_map->getNodeNoEx(info.node_p)); // Determine fall damage multiplier int addp = itemgroup_get(f.groups, "fall_damage_add_percent"); pre_factor = 1.0 + (float)addp/100.0; } float speed = pre_factor * speed_diff.getLength(); if(speed > tolerance) { f32 damage_f = (speed - tolerance)/BS * post_factor; u16 damage = (u16)(damage_f+0.5); if(damage != 0){ damageLocalPlayer(damage, true); MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage"); m_client->event()->put(e); } } } /* A quick draft of lava damage */ if(m_lava_hurt_interval.step(dtime, 1.0)) { v3f pf = lplayer->getPosition(); // Feet, middle and head v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS); MapNode n1 = m_map->getNodeNoEx(p1); v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS); MapNode n2 = m_map->getNodeNoEx(p2); v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS); MapNode n3 = m_map->getNodeNoEx(p3); u32 damage_per_second = 0; damage_per_second = MYMAX(damage_per_second, m_client->ndef()->get(n1).damage_per_second); damage_per_second = MYMAX(damage_per_second, m_client->ndef()->get(n2).damage_per_second); damage_per_second = MYMAX(damage_per_second, m_client->ndef()->get(n3).damage_per_second); if(damage_per_second != 0) { damageLocalPlayer(damage_per_second, true); } } // Protocol v29 make this behaviour obsolete if (getGameDef()->getProtoVersion() < 29) { /* Drowning */ if (m_drowning_interval.step(dtime, 2.0)) { v3f pf = lplayer->getPosition(); // head v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); ContentFeatures c = m_client->ndef()->get(n); u8 drowning_damage = c.drowning; if (drowning_damage > 0 && lplayer->hp > 0) { u16 breath = lplayer->getBreath(); if (breath > 10) { breath = 11; } if (breath > 0) { breath -= 1; } lplayer->setBreath(breath); updateLocalPlayerBreath(breath); } if (lplayer->getBreath() == 0 && drowning_damage > 0) { damageLocalPlayer(drowning_damage, true); } } if (m_breathing_interval.step(dtime, 0.5)) { v3f pf = lplayer->getPosition(); // head v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); ContentFeatures c = m_client->ndef()->get(n); if (!lplayer->hp) { lplayer->setBreath(11); } else if (c.drowning == 0) { u16 breath = lplayer->getBreath(); if (breath <= 10) { breath += 1; lplayer->setBreath(breath); updateLocalPlayerBreath(breath); } } } } // Update lighting on local player (used for wield item) u32 day_night_ratio = getDayNightRatio(); { // Get node at head // On InvalidPositionException, use this as default // (day: LIGHT_SUN, night: 0) MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0); v3s16 p = lplayer->getLightPosition(); node_at_lplayer = m_map->getNodeNoEx(p); u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef()); final_color_blend(&lplayer->light_color, light, day_night_ratio); } /* Step active objects and update lighting of them */ g_profiler->avg("CEnv: num of objects", m_active_objects.size()); bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21); for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin(); i != m_active_objects.end(); ++i) { ClientActiveObject* obj = i->second; // Step object obj->step(dtime, this); if(update_lighting) { // Update lighting u8 light = 0; bool pos_ok; // Get node at head v3s16 p = obj->getLightPosition(); MapNode n = m_map->getNodeNoEx(p, &pos_ok); if (pos_ok) light = n.getLightBlend(day_night_ratio, m_client->ndef()); else light = blend_light(day_night_ratio, LIGHT_SUN, 0); obj->updateLight(light); } } /* Step and handle simple objects */ g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size()); for(std::vector<ClientSimpleObject*>::iterator i = m_simple_objects.begin(); i != m_simple_objects.end();) { std::vector<ClientSimpleObject*>::iterator cur = i; ClientSimpleObject *simple = *cur; simple->step(dtime); if(simple->m_to_be_removed) { delete simple; i = m_simple_objects.erase(cur); } else { ++i; } } }