X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fcontent_sao.cpp;h=cc02a743146264f939141fe889e12fff628788be;hb=2e292b67a0a02b045969034c06aaf92b42a83a81;hp=0abeb0ef047e30675d235f44c72ea96d16681d51;hpb=75a0ca6bd67aa9ca87668bc27d36399b5028c2b1;p=oweals%2Fminetest.git diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 0abeb0ef0..cc02a7431 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -1,18 +1,18 @@ /* -Minetest-c55 -Copyright (C) 2010-2011 celeron55, Perttu Ahola +Minetest +Copyright (C) 2010-2013 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 -the Free Software Foundation; either version 2 of the License, or +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. +GNU Lesser General Public License for more details. -You should have received a copy of the GNU General Public License along +You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ @@ -21,1674 +21,1467 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "collision.h" #include "environment.h" #include "settings.h" +#include "main.h" // For g_profiler #include "profiler.h" +#include "serialization.h" // For compressZlib +#include "tool.h" // For ToolCapabilities +#include "gamedef.h" +#include "player.h" +#include "cpp_api/scriptapi.h" +#include "genericobject.h" +#include "util/serialize.h" -core::map ServerActiveObject::m_types; - -/* Some helper functions */ - -// Y is copied, X and Z change is limited -void accelerate_xz(v3f &speed, v3f target_speed, f32 max_increase) -{ - v3f d_wanted = target_speed - speed; - d_wanted.Y = 0; - f32 dl_wanted = d_wanted.getLength(); - f32 dl = dl_wanted; - if(dl > max_increase) - dl = max_increase; - - v3f d = d_wanted.normalize() * dl; - - speed.X += d.X; - speed.Z += d.Z; - speed.Y = target_speed.Y; -} +std::map ServerActiveObject::m_types; /* - TestSAO + DummyLoadSAO */ -// Prototype -TestSAO proto_TestSAO(NULL, v3f(0,0,0)); - -TestSAO::TestSAO(ServerEnvironment *env, v3f pos): - ServerActiveObject(env, pos), - m_timer1(0), - m_age(0) -{ - ServerActiveObject::registerType(getType(), create); -} - -ServerActiveObject* TestSAO::create(ServerEnvironment *env, v3f pos, - const std::string &data) +class DummyLoadSAO : public ServerActiveObject { - return new TestSAO(env, pos); -} - -void TestSAO::step(float dtime, bool send_recommended) -{ - m_age += dtime; - if(m_age > 10) +public: + DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type): + ServerActiveObject(env, pos) { - m_removed = true; - return; + ServerActiveObject::registerType(type, create); } - - m_base_position.Y += dtime * BS * 2; - if(m_base_position.Y > 8*BS) - m_base_position.Y = 2*BS; - - if(send_recommended == false) - return; - - m_timer1 -= dtime; - if(m_timer1 < 0.0) + // Pretend to be the test object (to fool the client) + u8 getType() const + { return ACTIVEOBJECT_TYPE_TEST; } + // And never save to disk + bool isStaticAllowed() const + { return false; } + + static ServerActiveObject* create(ServerEnvironment *env, v3f pos, + const std::string &data) { - m_timer1 += 0.125; - - std::string data; + return new DummyLoadSAO(env, pos, 0); + } - data += itos(0); // 0 = position - data += " "; - data += itos(m_base_position.X); - data += " "; - data += itos(m_base_position.Y); - data += " "; - data += itos(m_base_position.Z); + void step(float dtime, bool send_recommended) + { + m_removed = true; + infostream<<"DummyLoadSAO step"< box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.); - collisionMoveResult moveresult; - // Apply gravity - m_speed_f += v3f(0, -dtime*9.81*BS, 0); - // Maximum movement without glitches - f32 pos_max_d = BS*0.25; - // Limit speed - if(m_speed_f.getLength()*dtime > pos_max_d) - m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); - v3f pos_f = getBasePosition(); - v3f pos_f_old = pos_f; - moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d, - box, dtime, pos_f, m_speed_f); +public: + TestSAO(ServerEnvironment *env, v3f pos): + ServerActiveObject(env, pos), + m_timer1(0), + m_age(0) + { + ServerActiveObject::registerType(getType(), create); + } + u8 getType() const + { return ACTIVEOBJECT_TYPE_TEST; } - if(send_recommended == false) - return; + static ServerActiveObject* create(ServerEnvironment *env, v3f pos, + const std::string &data) + { + return new TestSAO(env, pos); + } - if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS) + void step(float dtime, bool send_recommended) { - setBasePosition(pos_f); - m_last_sent_position = pos_f; + m_age += dtime; + if(m_age > 10) + { + m_removed = true; + return; + } - std::ostringstream os(std::ios::binary); - char buf[6]; - // command (0 = update position) - buf[0] = 0; - os.write(buf, 1); - // pos - writeS32((u8*)buf, m_base_position.X*1000); - os.write(buf, 4); - writeS32((u8*)buf, m_base_position.Y*1000); - os.write(buf, 4); - writeS32((u8*)buf, m_base_position.Z*1000); - os.write(buf, 4); - // create message and add to list - ActiveObjectMessage aom(getId(), false, os.str()); - m_messages_out.push_back(aom); - } -} + m_base_position.Y += dtime * BS * 2; + if(m_base_position.Y > 8*BS) + m_base_position.Y = 2*BS; -std::string ItemSAO::getClientInitializationData() -{ - std::ostringstream os(std::ios::binary); - char buf[6]; - // version - buf[0] = 0; - os.write(buf, 1); - // pos - writeS32((u8*)buf, m_base_position.X*1000); - os.write(buf, 4); - writeS32((u8*)buf, m_base_position.Y*1000); - os.write(buf, 4); - writeS32((u8*)buf, m_base_position.Z*1000); - os.write(buf, 4); - // inventorystring - os< item="<use(m_env, player); +private: + float m_timer1; + float m_age; +}; - if(to_be_deleted) - m_removed = true; - else - // Reflect changes to the item here - m_inventorystring = item->getItemString(); - - delete item; -} +// Prototype (registers item for deserialization) +TestSAO proto_TestSAO(NULL, v3f(0,0,0)); /* - RatSAO -*/ + ItemSAO -// Prototype -RatSAO proto_RatSAO(NULL, v3f(0,0,0)); + DEPRECATED: New dropped items are implemented in Lua; see + builtin/item_entity.lua. +*/ -RatSAO::RatSAO(ServerEnvironment *env, v3f pos): - ServerActiveObject(env, pos), - m_is_active(false), - m_speed_f(0,0,0) +class ItemSAO : public ServerActiveObject { - ServerActiveObject::registerType(getType(), create); +public: + u8 getType() const + { return ACTIVEOBJECT_TYPE_ITEM; } + + float getMinimumSavedMovement() + { return 0.1*BS; } - m_oldpos = v3f(0,0,0); - m_last_sent_position = v3f(0,0,0); - m_yaw = myrand_range(0,PI*2); - m_counter1 = 0; - m_counter2 = 0; - m_age = 0; - m_touching_ground = false; -} + static ServerActiveObject* create(ServerEnvironment *env, v3f pos, + const std::string &data) + { + std::istringstream is(data, std::ios::binary); + char buf[1]; + // read version + is.read(buf, 1); + u8 version = buf[0]; + // check if version is supported + if(version != 0) + return NULL; + std::string itemstring = deSerializeString(is); + infostream<<"create(): Creating item \"" + < box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.); + collisionMoveResult moveresult; + // Apply gravity + m_speed_f += v3f(0, -dtime*9.81*BS, 0); + // Maximum movement without glitches + f32 pos_max_d = BS*0.25; + // Limit speed + if(m_speed_f.getLength()*dtime > pos_max_d) + m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); + v3f pos_f = getBasePosition(); + v3f pos_f_old = pos_f; + v3f accel_f = v3f(0,0,0); + f32 stepheight = 0; + moveresult = collisionMoveSimple(m_env,m_env->getGameDef(), + pos_max_d, box, stepheight, dtime, + pos_f, m_speed_f, accel_f); + + if(send_recommended == false) return; + + if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS) + { + setBasePosition(pos_f); + m_last_sent_position = pos_f; + + std::ostringstream os(std::ios::binary); + // command (0 = update position) + writeU8(os, 0); + // pos + writeV3F1000(os, m_base_position); + // create message and add to list + ActiveObjectMessage aom(getId(), false, os.str()); + m_messages_out.push_back(aom); + } + if(m_itemstring_changed) + { + m_itemstring_changed = false; + + std::ostringstream os(std::ios::binary); + // command (1 = update itemstring) + writeU8(os, 1); + // itemstring + os< 60) + std::string getStaticData() { - // Die - m_removed = true; - return; - }*/ - - // Apply gravity - m_speed_f.Y -= dtime*9.81*BS; - - /* - Move around if some player is close - */ - bool player_is_close = false; - // Check connected players - core::list players = m_env->getPlayers(true); - core::list::Iterator i; - for(i = players.begin(); - i != players.end(); i++) + infostream<<__FUNCTION_NAME<getPosition(); - if(m_base_position.getDistanceFrom(playerpos) < BS*10.0) + try{ + IItemDefManager *idef = m_env->getGameDef()->idef(); + ItemStack item; + item.deSerialize(m_itemstring, idef); + infostream<<__FUNCTION_NAME<<": m_itemstring=\""< item=\""<getInventory(); + if(inv != NULL) { - m_counter1 -= dtime; - if(m_counter1 < 0.0) + std::string wieldlist = puncher->getWieldList(); + ItemStack leftover = inv->addItem(wieldlist, item); + puncher->setInventoryModified(); + if(leftover.empty()) { - m_counter1 += 1.0; - m_speed_f.Y = 5.0*BS; + m_removed = true; } - } - - { - m_counter2 -= dtime; - if(m_counter2 < 0.0) + else { - m_counter2 += (float)(myrand()%100)/100*3.0; - m_yaw += ((float)(myrand()%200)-100)/100*180; - m_yaw = wrapDegrees(m_yaw); + m_itemstring = leftover.getItemString(); + m_itemstring_changed = true; } } + + return 0; } - - m_oldpos = m_base_position; - - /* - Move it, with collision detection - */ - - core::aabbox3d box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.); - collisionMoveResult moveresult; - // Maximum movement without glitches - f32 pos_max_d = BS*0.25; - // Limit speed - if(m_speed_f.getLength()*dtime > pos_max_d) - m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); - v3f pos_f = getBasePosition(); - v3f pos_f_old = pos_f; - moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d, - box, dtime, pos_f, m_speed_f); - m_touching_ground = moveresult.touching_ground; - - setBasePosition(pos_f); - - if(send_recommended == false) - return; - if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS) - { - m_last_sent_position = pos_f; - - std::ostringstream os(std::ios::binary); - // command (0 = update position) - writeU8(os, 0); - // pos - writeV3F1000(os, m_base_position); - // yaw - writeF1000(os, m_yaw); - // create message and add to list - ActiveObjectMessage aom(getId(), false, os.str()); - m_messages_out.push_back(aom); + bool getCollisionBox(aabb3f *toset) { + return false; } -} -std::string RatSAO::getClientInitializationData() -{ - std::ostringstream os(std::ios::binary); - // version - writeU8(os, 0); - // pos - writeV3F1000(os, m_base_position); - return os.str(); -} -std::string RatSAO::getStaticData() -{ - //infostream<<__FUNCTION_NAME<luaentity_Remove(m_id); + } +} - m_oldpos = v3f(0,0,0); - m_last_sent_position = v3f(0,0,0); - m_yaw = 0; - m_counter1 = 0; - m_counter2 = 0; - m_age = 0; - m_touching_ground = false; - m_hp = 20; - m_after_jump_timer = 0; +void LuaEntitySAO::addedToEnvironment(u32 dtime_s) +{ + ServerActiveObject::addedToEnvironment(dtime_s); + + // Create entity from name + m_registered = ENV_TO_SA(m_env)->luaentity_Add(m_id, m_init_name.c_str()); + + if(m_registered){ + // Get properties + ENV_TO_SA(m_env)->luaentity_GetProperties(m_id, &m_prop); + // Initialize HP from properties + m_hp = m_prop.hp_max; + // Activate entity, supplying serialized state + ENV_TO_SA(m_env)->luaentity_Activate(m_id, m_init_state.c_str(), dtime_s); + } } -ServerActiveObject* Oerkki1SAO::create(ServerEnvironment *env, v3f pos, +ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos, const std::string &data) { - std::istringstream is(data, std::ios::binary); - // read version - u8 version = readU8(is); - // read hp - u8 hp = readU8(is); - // check if version is supported - if(version != 0) - return NULL; - Oerkki1SAO *o = new Oerkki1SAO(env, pos); - o->m_hp = hp; - return o; + std::string name; + std::string state; + s16 hp = 1; + v3f velocity; + float yaw = 0; + if(data != ""){ + std::istringstream is(data, std::ios::binary); + // read version + u8 version = readU8(is); + // check if version is supported + if(version == 0){ + name = deSerializeString(is); + state = deSerializeLongString(is); + } + else if(version == 1){ + name = deSerializeString(is); + state = deSerializeLongString(is); + hp = readS16(is); + velocity = readV3F1000(is); + yaw = readF1000(is); + } + } + // create object + infostream<<"LuaEntitySAO::create(name=\""<m_hp = hp; + sao->m_velocity = velocity; + sao->m_yaw = yaw; + return sao; } -void Oerkki1SAO::step(float dtime, bool send_recommended) +bool LuaEntitySAO::isAttached() { - ScopeProfiler sp2(g_profiler, "Oerkki1SAO::step avg", SPT_AVG); + if(!m_attachment_parent_id) + return false; + // Check if the parent still exists + ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id); + if(obj) + return true; + return false; +} - assert(m_env); +void LuaEntitySAO::step(float dtime, bool send_recommended) +{ + if(!m_properties_sent) + { + m_properties_sent = true; + std::string str = getPropertyPacket(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } - if(m_is_active == false) + // If attached, check that our parent is still there. If it isn't, detach. + if(m_attachment_parent_id && !isAttached()) { - if(m_inactive_interval.step(dtime, 0.5)==false) - return; + m_attachment_parent_id = 0; + m_attachment_bone = ""; + m_attachment_position = v3f(0,0,0); + m_attachment_rotation = v3f(0,0,0); + sendPosition(false, true); } - /* - The AI - */ + m_last_sent_position_timer += dtime; - m_age += dtime; - if(m_age > 120) + // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally + // If the object gets detached this comes into effect automatically from the last known origin + if(isAttached()) { - // Die - m_removed = true; - return; + v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); + m_base_position = pos; + m_velocity = v3f(0,0,0); + m_acceleration = v3f(0,0,0); + } + else + { + if(m_prop.physical){ + core::aabbox3d box = m_prop.collisionbox; + box.MinEdge *= BS; + box.MaxEdge *= BS; + collisionMoveResult moveresult; + f32 pos_max_d = BS*0.25; // Distance per iteration + f32 stepheight = 0; // Maximum climbable step height + v3f p_pos = m_base_position; + v3f p_velocity = m_velocity; + v3f p_acceleration = m_acceleration; + moveresult = collisionMoveSimple(m_env,m_env->getGameDef(), + pos_max_d, box, stepheight, dtime, + p_pos, p_velocity, p_acceleration,this); + // Apply results + m_base_position = p_pos; + m_velocity = p_velocity; + m_acceleration = p_acceleration; + } else { + m_base_position += dtime * m_velocity + 0.5 * dtime + * dtime * m_acceleration; + m_velocity += dtime * m_acceleration; + } } - m_after_jump_timer -= dtime; - - v3f old_speed = m_speed_f; + if(m_registered){ + ENV_TO_SA(m_env)->luaentity_Step(m_id, dtime); + } - // Apply gravity - m_speed_f.Y -= dtime*9.81*BS; + if(send_recommended == false) + return; - /* - Move around if some player is close - */ - bool player_is_close = false; - bool player_is_too_close = false; - v3f near_player_pos; - // Check connected players - core::list players = m_env->getPlayers(true); - core::list::Iterator i; - for(i = players.begin(); - i != players.end(); i++) + if(!isAttached()) { - Player *player = *i; - v3f playerpos = player->getPosition(); - f32 dist = m_base_position.getDistanceFrom(playerpos); - if(dist < BS*0.6) - { - m_removed = true; - return; - player_is_too_close = true; - near_player_pos = playerpos; + // TODO: force send when acceleration changes enough? + float minchange = 0.2*BS; + if(m_last_sent_position_timer > 1.0){ + minchange = 0.01*BS; + } else if(m_last_sent_position_timer > 0.2){ + minchange = 0.05*BS; } - else if(dist < BS*15.0 && !player_is_too_close) - { - player_is_close = true; - near_player_pos = playerpos; + float move_d = m_base_position.getDistanceFrom(m_last_sent_position); + move_d += m_last_sent_move_precision; + float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity); + if(move_d > minchange || vel_d > minchange || + fabs(m_yaw - m_last_sent_yaw) > 1.0){ + sendPosition(true, false); } } - m_is_active = player_is_close; - - v3f target_speed = m_speed_f; + if(m_armor_groups_sent == false){ + m_armor_groups_sent = true; + std::string str = gob_cmd_update_armor_groups( + m_armor_groups); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } - if(!player_is_close) - { - target_speed = v3f(0,0,0); + if(m_animation_sent == false){ + m_animation_sent = true; + std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); } - else - { - // Move around - - v3f ndir = near_player_pos - m_base_position; - ndir.Y = 0; - ndir.normalize(); - - f32 nyaw = 180./PI*atan2(ndir.Z,ndir.X); - if(nyaw < m_yaw - 180) - nyaw += 360; - else if(nyaw > m_yaw + 180) - nyaw -= 360; - m_yaw = 0.95*m_yaw + 0.05*nyaw; - m_yaw = wrapDegrees(m_yaw); - - f32 speed = 2*BS; - if((m_touching_ground || m_after_jump_timer > 0.0) - && !player_is_too_close) - { - v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI)); - target_speed.X = speed * dir.X; - target_speed.Z = speed * dir.Z; + if(m_bone_position_sent == false){ + m_bone_position_sent = true; + for(std::map >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ + std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); } + } - if(m_touching_ground && (m_oldpos - m_base_position).getLength() - < dtime*speed/2) - { - m_counter1 -= dtime; - if(m_counter1 < 0.0) - { - m_counter1 += 0.2; - // Jump - target_speed.Y = 5.0*BS; - m_after_jump_timer = 1.0; - } - } + if(m_attachment_sent == false){ + m_attachment_sent = true; + std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } +} - { - m_counter2 -= dtime; - if(m_counter2 < 0.0) - { - m_counter2 += (float)(myrand()%100)/100*3.0; - //m_yaw += ((float)(myrand()%200)-100)/100*180; - m_yaw += ((float)(myrand()%200)-100)/100*90; - m_yaw = wrapDegrees(m_yaw); - } +std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) +{ + std::ostringstream os(std::ios::binary); + + if(protocol_version >= 14) + { + writeU8(os, 1); // version + os< >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ + os< BS*4 || player_is_too_close) - accelerate_xz(m_speed_f, target_speed, dtime*BS*8); else - accelerate_xz(m_speed_f, target_speed, dtime*BS*4); - - m_oldpos = m_base_position; - - /* - Move it, with collision detection - */ - - core::aabbox3d box(-BS/3.,0.0,-BS/3., BS/3.,BS*5./3.,BS/3.); - collisionMoveResult moveresult; - // Maximum movement without glitches - f32 pos_max_d = BS*0.25; - /*// Limit speed - if(m_speed_f.getLength()*dtime > pos_max_d) - m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);*/ - v3f pos_f = getBasePosition(); - v3f pos_f_old = pos_f; - moveresult = collisionMovePrecise(&m_env->getMap(), pos_max_d, - box, dtime, pos_f, m_speed_f); - m_touching_ground = moveresult.touching_ground; - - // Do collision damage - float tolerance = BS*30; - float factor = BS*0.5; - v3f speed_diff = old_speed - m_speed_f; - // Increase effect in X and Z - speed_diff.X *= 2; - speed_diff.Z *= 2; - float vel = speed_diff.getLength(); - if(vel > tolerance) - { - f32 damage_f = (vel - tolerance)/BS*factor; - u16 damage = (u16)(damage_f+0.5); - doDamage(damage); - } - - setBasePosition(pos_f); - - if(send_recommended == false && m_speed_f.getLength() < 3.0*BS) - return; - - if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS) { - m_last_sent_position = pos_f; - - std::ostringstream os(std::ios::binary); - // command (0 = update position) - writeU8(os, 0); - // pos + writeU8(os, 0); // version + os<luaentity_GetStaticdata(m_id); + os<getWieldedItem(); + punchitem = &punchitem_static; } + PunchDamageResult result = getPunchDamage( + m_armor_groups, + toolcap, + punchitem, + time_from_last_punch); + + if(result.did_punch) { - std::ostringstream os(std::ios::binary); - // command (1 = damage) - writeU8(os, 1); - // amount - writeU8(os, d); - // create message and add to list - ActiveObjectMessage aom(getId(), false, os.str()); - m_messages_out.push_back(aom); + setHP(getHP() - result.damage); + + + std::string punchername = "nil"; + + if ( puncher != 0 ) + punchername = puncher->getDescription(); + + actionstream<luaentity_Punch(m_id, puncher, + time_from_last_punch, toolcap, dir); + + return result.wear; } -/* - FireflySAO -*/ +void LuaEntitySAO::rightClick(ServerActiveObject *clicker) +{ + if(!m_registered) + return; + // It's best that attachments cannot be clicked + if(isAttached()) + return; + ENV_TO_SA(m_env)->luaentity_Rightclick(m_id, clicker); +} -// Prototype -FireflySAO proto_FireflySAO(NULL, v3f(0,0,0)); +void LuaEntitySAO::setPos(v3f pos) +{ + if(isAttached()) + return; + m_base_position = pos; + sendPosition(false, true); +} -FireflySAO::FireflySAO(ServerEnvironment *env, v3f pos): - ServerActiveObject(env, pos), - m_is_active(false), - m_speed_f(0,0,0) +void LuaEntitySAO::moveTo(v3f pos, bool continuous) { - ServerActiveObject::registerType(getType(), create); + if(isAttached()) + return; + m_base_position = pos; + if(!continuous) + sendPosition(true, true); +} - m_oldpos = v3f(0,0,0); - m_last_sent_position = v3f(0,0,0); - m_yaw = 0; - m_counter1 = 0; - m_counter2 = 0; - m_age = 0; - m_touching_ground = false; +float LuaEntitySAO::getMinimumSavedMovement() +{ + return 0.1 * BS; } -ServerActiveObject* FireflySAO::create(ServerEnvironment *env, v3f pos, - const std::string &data) +std::string LuaEntitySAO::getDescription() { - std::istringstream is(data, std::ios::binary); - char buf[1]; - // read version - is.read(buf, 1); - u8 version = buf[0]; - // check if version is supported - if(version != 0) - return NULL; - return new FireflySAO(env, pos); + std::ostringstream os(std::ios::binary); + os<<"LuaEntitySAO at ("; + os<<(m_base_position.X/BS)<<","; + os<<(m_base_position.Y/BS)<<","; + os<<(m_base_position.Z/BS); + os<<")"; + return os.str(); } -void FireflySAO::step(float dtime, bool send_recommended) +void LuaEntitySAO::setHP(s16 hp) { - ScopeProfiler sp2(g_profiler, "FireflySAO::step avg", SPT_AVG); + if(hp < 0) hp = 0; + m_hp = hp; +} - assert(m_env); +s16 LuaEntitySAO::getHP() const +{ + return m_hp; +} - if(m_is_active == false) - { - if(m_inactive_interval.step(dtime, 0.5)==false) - return; - } +void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups) +{ + m_armor_groups = armor_groups; + m_armor_groups_sent = false; +} - /* - The AI - */ - - // Apply (less) gravity - m_speed_f.Y -= dtime*3*BS; - - /* - Move around if some player is close - */ - bool player_is_close = false; - // Check connected players - core::list players = m_env->getPlayers(true); - core::list::Iterator i; - for(i = players.begin(); - i != players.end(); i++) - { - Player *player = *i; - v3f playerpos = player->getPosition(); - if(m_base_position.getDistanceFrom(playerpos) < BS*10.0) - { - player_is_close = true; - break; - } - } +void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend) +{ + m_animation_range = frame_range; + m_animation_speed = frame_speed; + m_animation_blend = frame_blend; + m_animation_sent = false; +} - m_is_active = player_is_close; - - if(player_is_close == false) - { - m_speed_f.X = 0; - m_speed_f.Z = 0; - } - else - { - // Move around - v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI)); - f32 speed = BS/2; - m_speed_f.X = speed * dir.X; - m_speed_f.Z = speed * dir.Z; - - if(m_touching_ground && (m_oldpos - m_base_position).getLength() - < dtime*speed/2) - { - m_counter1 -= dtime; - if(m_counter1 < 0.0) - { - m_counter1 += 1.0; - m_speed_f.Y = 5.0*BS; - } - } +void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation) +{ + m_bone_position[bone] = core::vector2d(position, rotation); + m_bone_position_sent = false; +} - { - m_counter2 -= dtime; - if(m_counter2 < 0.0) - { - m_counter2 += (float)(myrand()%100)/100*3.0; - m_yaw += ((float)(myrand()%200)-100)/100*180; - m_yaw = wrapDegrees(m_yaw); - } - } - } - - m_oldpos = m_base_position; - - /* - Move it, with collision detection - */ - - core::aabbox3d box(-BS/3.,-BS*2/3.0,-BS/3., BS/3.,BS*4./3.,BS/3.); - collisionMoveResult moveresult; - // Maximum movement without glitches - f32 pos_max_d = BS*0.25; - // Limit speed - if(m_speed_f.getLength()*dtime > pos_max_d) - m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); - v3f pos_f = getBasePosition(); - v3f pos_f_old = pos_f; - moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d, - box, dtime, pos_f, m_speed_f); - m_touching_ground = moveresult.touching_ground; - - setBasePosition(pos_f); +void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation) +{ + // Attachments need to be handled on both the server and client. + // If we just attach on the server, we can only copy the position of the parent. Attachments + // are still sent to clients at an interval so players might see them lagging, plus we can't + // read and attach to skeletal bones. + // If we just attach on the client, the server still sees the child at its original location. + // This breaks some things so we also give the server the most accurate representation + // even if players only see the client changes. + + m_attachment_parent_id = parent_id; + m_attachment_bone = bone; + m_attachment_position = position; + m_attachment_rotation = rotation; + m_attachment_sent = false; +} - if(send_recommended == false) - return; +ObjectProperties* LuaEntitySAO::accessObjectProperties() +{ + return &m_prop; +} - if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS) - { - m_last_sent_position = pos_f; +void LuaEntitySAO::notifyObjectPropertiesModified() +{ + m_properties_sent = false; +} - std::ostringstream os(std::ios::binary); - // command (0 = update position) - writeU8(os, 0); - // pos - writeV3F1000(os, m_base_position); - // yaw - writeF1000(os, m_yaw); - // create message and add to list - ActiveObjectMessage aom(getId(), false, os.str()); - m_messages_out.push_back(aom); - } +void LuaEntitySAO::setVelocity(v3f velocity) +{ + m_velocity = velocity; } -std::string FireflySAO::getClientInitializationData() +v3f LuaEntitySAO::getVelocity() { - std::ostringstream os(std::ios::binary); - // version - writeU8(os, 0); - // pos - writeV3F1000(os, m_base_position); - return os.str(); + return m_velocity; } -std::string FireflySAO::getStaticData() +void LuaEntitySAO::setAcceleration(v3f acceleration) { - //infostream<<__FUNCTION_NAME<update(*init_properties); - - m_properties->setV3F("pos", pos); - - setPropertyDefaults(); - readProperties(); +void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength, + bool select_horiz_by_yawpitch) +{ + std::string str = gob_cmd_set_sprite( + p, + num_frames, + framelength, + select_horiz_by_yawpitch + ); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); } - -MobV2SAO::~MobV2SAO() + +std::string LuaEntitySAO::getName() { - delete m_properties; + return m_init_name; } -ServerActiveObject* MobV2SAO::create(ServerEnvironment *env, v3f pos, - const std::string &data) +std::string LuaEntitySAO::getPropertyPacket() { - std::istringstream is(data, std::ios::binary); - Settings properties; - properties.parseConfigLines(is, "MobArgsEnd"); - MobV2SAO *o = new MobV2SAO(env, pos, &properties); - return o; + return gob_cmd_set_properties(m_prop); } -std::string MobV2SAO::getStaticData() +void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) { - updateProperties(); + // If the object is attached client-side, don't waste bandwidth sending its position to clients + if(isAttached()) + return; + + m_last_sent_move_precision = m_base_position.getDistanceFrom( + m_last_sent_position); + m_last_sent_position_timer = 0; + m_last_sent_yaw = m_yaw; + m_last_sent_position = m_base_position; + m_last_sent_velocity = m_velocity; + //m_last_sent_acceleration = m_acceleration; + + float update_interval = m_env->getSendRecommendedInterval(); - std::ostringstream os(std::ios::binary); - m_properties->writeLines(os); - return os.str(); + std::string str = gob_cmd_update_position( + m_base_position, + m_velocity, + m_acceleration, + m_yaw, + do_interpolate, + is_movement_end, + update_interval + ); + // create message and add to list + ActiveObjectMessage aom(getId(), false, str); + m_messages_out.push_back(aom); } -std::string MobV2SAO::getClientInitializationData() -{ - //infostream<<__FUNCTION_NAME<MinEdge = m_prop.collisionbox.MinEdge * BS; + toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS; - std::ostringstream os(std::ios::binary); + toset->MinEdge += m_base_position; + toset->MaxEdge += m_base_position; - // version - writeU8(os, 0); - - Settings client_properties; - - /*client_properties.set("version", "0"); - client_properties.updateValue(*m_properties, "pos"); - client_properties.updateValue(*m_properties, "yaw"); - client_properties.updateValue(*m_properties, "hp");*/ + return true; + } - // Just send everything for simplicity - client_properties.update(*m_properties); + return false; +} - std::ostringstream os2(std::ios::binary); - client_properties.writeLines(os2); - compressZlib(os2.str(), os); +/* + PlayerSAO +*/ - return os.str(); +// No prototype, PlayerSAO does not need to be deserialized + +PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_, + const std::set &privs, bool is_singleplayer): + ServerActiveObject(env_, v3f(0,0,0)), + m_player(player_), + m_peer_id(peer_id_), + m_inventory(NULL), + m_last_good_position(0,0,0), + m_last_good_position_age(0), + m_time_from_last_punch(0), + m_nocheat_dig_pos(32767, 32767, 32767), + m_nocheat_dig_time(0), + m_wield_index(0), + m_position_not_sent(false), + m_armor_groups_sent(false), + m_properties_sent(true), + m_privs(privs), + m_is_singleplayer(is_singleplayer), + m_animation_sent(false), + m_bone_position_sent(false), + m_attachment_sent(false), + // public + m_moved(false), + m_inventory_not_sent(false), + m_hp_not_sent(false), + m_wielded_item_not_sent(false), + m_physics_override_speed(1), + m_physics_override_jump(1), + m_physics_override_gravity(1), + m_physics_override_sent(false) +{ + assert(m_player); + assert(m_peer_id != 0); + setBasePosition(m_player->getPosition()); + m_inventory = &m_player->inventory; + m_armor_groups["fleshy"] = 100; + + m_prop.hp_max = PLAYER_MAX_HP; + m_prop.physical = false; + m_prop.weight = 75; + m_prop.collisionbox = core::aabbox3d(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.); + // start of default appearance, this should be overwritten by LUA + m_prop.visual = "upright_sprite"; + m_prop.visual_size = v2f(1, 2); + m_prop.textures.clear(); + m_prop.textures.push_back("player.png"); + m_prop.textures.push_back("player_back.png"); + m_prop.colors.clear(); + m_prop.colors.push_back(video::SColor(255, 255, 255, 255)); + m_prop.spritediv = v2s16(1,1); + // end of default appearance + m_prop.is_visible = true; + m_prop.makes_footstep_sound = true; } -bool checkFreePosition(Map *map, v3s16 p0, v3s16 size) +PlayerSAO::~PlayerSAO() { - for(int dx=0; dxgetNodeNoEx(p); - if(n.getContent() != CONTENT_AIR) - return false; - } - return true; + if(m_inventory != &m_player->inventory) + delete m_inventory; + } -bool checkWalkablePosition(Map *map, v3s16 p0) +std::string PlayerSAO::getDescription() { - v3s16 p = p0 + v3s16(0,-1,0); - MapNode n = map->getNodeNoEx(p); - if(n.getContent() != CONTENT_AIR) - return true; - return false; + return std::string("player ") + m_player->getName(); } -bool checkFreeAndWalkablePosition(Map *map, v3s16 p0, v3s16 size) +// Called after id has been set and has been inserted in environment +void PlayerSAO::addedToEnvironment(u32 dtime_s) { - if(!checkFreePosition(map, p0, size)) - return false; - if(!checkWalkablePosition(map, p0)) - return false; - return true; + ServerActiveObject::addedToEnvironment(dtime_s); + ServerActiveObject::setBasePosition(m_player->getPosition()); + m_player->setPlayerSAO(this); + m_player->peer_id = m_peer_id; + m_last_good_position = m_player->getPosition(); + m_last_good_position_age = 0.0; } -static void get_random_u32_array(u32 a[], u32 len) +// Called before removing from environment +void PlayerSAO::removingFromEnvironment() { - u32 i, n; - for(i=0; i 1){ - u32 k = myrand() % n; - n--; - u32 temp = a[n]; - a[n] = a[k]; - a[k] = temp; + ServerActiveObject::removingFromEnvironment(); + if(m_player->getPlayerSAO() == this) + { + m_player->setPlayerSAO(NULL); + m_player->peer_id = 0; } } -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" +bool PlayerSAO::isStaticAllowed() const +{ + return false; +} + +bool PlayerSAO::unlimitedTransferDistance() const +{ + return g_settings->getBool("unlimited_player_transfer_distance"); +} -static void explodeSquare(Map *map, v3s16 p0, v3s16 size) +std::string PlayerSAO::getClientInitializationData(u16 protocol_version) { - core::map modified_blocks; + std::ostringstream os(std::ios::binary); - for(int dx=0; dxgetNodeNoEx(p); - if(n.getContent() == CONTENT_IGNORE) - continue; - //map->removeNodeWithEvent(p); - map->removeNodeAndUpdate(p, modified_blocks); + if(protocol_version >= 15) + { + writeU8(os, 1); // version + os<getName()); // name + writeU8(os, 1); // is_player + writeS16(os, getId()); //id + writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0)); + writeF1000(os, m_player->getYaw()); + writeS16(os, getHP()); + + writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here + os< >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ + os<::Iterator - i = modified_blocks.getIterator(); - i.atEnd() == false; i++) + else { - v3s16 p = i.getNode()->getKey(); - event.modified_blocks.insert(p, true); + writeU8(os, 0); // version + os<getName()); // name + writeU8(os, 1); // is_player + writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0)); + writeF1000(os, m_player->getYaw()); + writeS16(os, getHP()); + writeU8(os, 2); // number of messages stuffed in here + os<dispatchEvent(&event); + + // return result + return os.str(); } -void MobV2SAO::step(float dtime, bool send_recommended) +std::string PlayerSAO::getStaticData() { - ScopeProfiler sp2(g_profiler, "MobV2SAO::step avg", SPT_AVG); - - assert(m_env); - Map *map = &m_env->getMap(); + assert(0); + return ""; +} - m_age += dtime; +bool PlayerSAO::isAttached() +{ + if(!m_attachment_parent_id) + return false; + // Check if the parent still exists + ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id); + if(obj) + return true; + return false; +} - if(m_die_age >= 0.0 && m_age >= m_die_age){ - m_removed = true; - return; +void PlayerSAO::step(float dtime, bool send_recommended) +{ + if(!m_properties_sent) + { + m_properties_sent = true; + std::string str = getPropertyPacket(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); } - m_random_disturb_timer += dtime; - if(m_random_disturb_timer >= 5.0) + // If attached, check that our parent is still there. If it isn't, detach. + if(m_attachment_parent_id && !isAttached()) { - m_random_disturb_timer = 0; - // Check connected players - core::list players = m_env->getPlayers(true); - core::list::Iterator i; - for(i = players.begin(); - i != players.end(); i++) - { - Player *player = *i; - v3f playerpos = player->getPosition(); - f32 dist = m_base_position.getDistanceFrom(playerpos); - if(dist < BS*16) - { - if(myrand_range(0,3) == 0){ - actionstream<<"Mob id="<getName()<getName(); - m_disturb_timer = 0; - break; - } - } - } + m_attachment_parent_id = 0; + m_attachment_bone = ""; + m_attachment_position = v3f(0,0,0); + m_attachment_rotation = v3f(0,0,0); + m_player->setPosition(m_last_good_position); + m_moved = true; } - Player *disturbing_player = - m_env->getPlayer(m_disturbing_player.c_str()); - v3f disturbing_player_off = v3f(0,1,0); - v3f disturbing_player_norm = v3f(0,1,0); - float disturbing_player_distance = 1000000; - float disturbing_player_dir = 0; - if(disturbing_player){ - disturbing_player_off = - disturbing_player->getPosition() - m_base_position; - disturbing_player_distance = disturbing_player_off.getLength(); - disturbing_player_norm = disturbing_player_off; - disturbing_player_norm.normalize(); - disturbing_player_dir = 180./PI*atan2(disturbing_player_norm.Z, - disturbing_player_norm.X); - } + m_time_from_last_punch += dtime; + m_nocheat_dig_time += dtime; - m_disturb_timer += dtime; - - if(!m_falling) + // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally + // If the object gets detached this comes into effect automatically from the last known origin + if(isAttached()) { - m_shooting_timer -= dtime; - if(m_shooting_timer <= 0.0 && m_shooting){ - m_shooting = false; - - std::string shoot_type = m_properties->get("shoot_type"); - v3f shoot_pos(0,0,0); - shoot_pos.Y += m_properties->getFloat("shoot_y") * BS; - if(shoot_type == "fireball"){ - v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI)); - dir.Y = m_shoot_y; - dir.normalize(); - v3f speed = dir * BS * 10.0; - v3f pos = m_base_position + shoot_pos; - infostream<<__FUNCTION_NAME<<": Mob id="<addActiveObjectAsStatic(obj); - m_env->addActiveObject(obj); - } else { - infostream<<__FUNCTION_NAME<<": Mob id="<= reload_time && - !m_next_pos_exists && - (m_disturb_timer <= 60.0 || shoot_without_player)) - { - m_shoot_y = 0; - if(m_disturb_timer < 60.0 && disturbing_player && - disturbing_player_distance < 16*BS && - fabs(disturbing_player_norm.Y) < 0.8){ - m_yaw = disturbing_player_dir; - sendPosition(); - m_shoot_y += disturbing_player_norm.Y; - } else { - m_shoot_y = 0.01 * myrand_range(-30,10); - } - m_shoot_reload_timer = 0.0; - m_shooting = true; - m_shooting_timer = 1.5; - { - std::ostringstream os(std::ios::binary); - // command (2 = shooting) - writeU8(os, 2); - // time - writeF1000(os, m_shooting_timer + 0.1); - // bright? - writeU8(os, true); - // create message and add to list - ActiveObjectMessage aom(getId(), false, os.str()); - m_messages_out.push_back(aom); - } - } + v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); + m_last_good_position = pos; + m_last_good_position_age = 0; + m_player->setPosition(pos); } - - if(m_move_type == "ground_nodes") + else { - if(!m_shooting){ - m_walk_around_timer -= dtime; - if(m_walk_around_timer <= 0.0){ - m_walk_around = !m_walk_around; - if(m_walk_around) - m_walk_around_timer = 0.1*myrand_range(10,50); - else - m_walk_around_timer = 0.1*myrand_range(30,70); - } - } - - /* Move */ - if(m_next_pos_exists){ - v3f pos_f = m_base_position; - v3f next_pos_f = intToFloat(m_next_pos_i, BS); - - v3f v = next_pos_f - pos_f; - m_yaw = atan2(v.Z, v.X) / PI * 180; - - v3f diff = next_pos_f - pos_f; - v3f dir = diff; - dir.normalize(); - float speed = BS * 0.5; - if(m_falling) - speed = BS * 3.0; - dir *= dtime * speed; - bool arrived = false; - if(dir.getLength() > diff.getLength()){ - dir = diff; - arrived = true; - } - pos_f += dir; - m_base_position = pos_f; - - if((pos_f - next_pos_f).getLength() < 0.1 || arrived){ - m_next_pos_exists = false; - } - } - - v3s16 pos_i = floatToInt(m_base_position, BS); - v3s16 size_blocks = v3s16(m_size.X+0.5,m_size.Y+0.5,m_size.X+0.5); - v3s16 pos_size_off(0,0,0); - if(m_size.X >= 2.5){ - pos_size_off.X = -1; - pos_size_off.Y = -1; + if(m_is_singleplayer || g_settings->getBool("disable_anticheat")) + { + m_last_good_position = m_player->getPosition(); + m_last_good_position_age = 0; } - - if(!m_next_pos_exists){ - /* Check whether to drop down */ - if(checkFreePosition(map, - pos_i + pos_size_off + v3s16(0,-1,0), size_blocks)){ - m_next_pos_i = pos_i + v3s16(0,-1,0); - m_next_pos_exists = true; - m_falling = true; + else + { + /* + Check player movements + + NOTE: Actually the server should handle player physics like the + client does and compare player's position to what is calculated + on our side. This is required when eg. players fly due to an + explosion. Altough a node-based alternative might be possible + too, and much more lightweight. + */ + + float player_max_speed = 0; + float player_max_speed_up = 0; + if(m_privs.count("fast") != 0){ + // Fast speed + player_max_speed = BS * 20; + player_max_speed_up = BS * 20; } else { - m_falling = false; + // Normal speed + player_max_speed = BS * 4.0; + player_max_speed_up = BS * 4.0; } - } - - if(m_walk_around) - { - if(!m_next_pos_exists){ - /* Find some position where to go next */ - v3s16 dps[3*3*3]; - int num_dps = 0; - for(int dx=-1; dx<=1; dx++) - for(int dy=-1; dy<=1; dy++) - for(int dz=-1; dz<=1; dz++){ - if(dx == 0 && dy == 0) - continue; - if(dx != 0 && dz != 0 && dy != 0) - continue; - dps[num_dps++] = v3s16(dx,dy,dz); - } - u32 order[3*3*3]; - get_random_u32_array(order, num_dps); - for(int i=0; i= 1.0){ + float age = m_last_good_position_age; + v3f diff = (m_player->getPosition() - m_last_good_position); + float d_vert = diff.Y; + diff.Y = 0; + float d_horiz = diff.getLength(); + /*infostream<getName()<<"'s horizontal speed is " + <<(d_horiz/age)<getPosition(); + } else { + actionstream<<"Player "<getName() + <<" moved too fast; resetting position" + <setPosition(m_last_good_position); + m_moved = true; } + m_last_good_position_age = 0; } } } - else if(m_move_type == "constant_speed") + + if(send_recommended == false) + return; + + // If the object is attached client-side, don't waste bandwidth sending its position to clients + if(m_position_not_sent && !isAttached()) { - m_base_position += m_speed * dtime; - - v3s16 pos_i = floatToInt(m_base_position, BS); - v3s16 size_blocks = v3s16(m_size.X+0.5,m_size.Y+0.5,m_size.X+0.5); - v3s16 pos_size_off(0,0,0); - if(m_size.X >= 2.5){ - pos_size_off.X = -1; - pos_size_off.Y = -1; - } - bool free = checkFreePosition(map, pos_i + pos_size_off, size_blocks); - if(!free){ - explodeSquare(map, pos_i, v3s16(3,3,3)); - m_removed = true; - return; - } + m_position_not_sent = false; + float update_interval = m_env->getSendRecommendedInterval(); + v3f pos; + if(isAttached()) // Just in case we ever do send attachment position too + pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); + else + pos = m_player->getPosition() + v3f(0,BS*1,0); + std::string str = gob_cmd_update_position( + pos, + v3f(0,0,0), + v3f(0,0,0), + m_player->getYaw(), + true, + false, + update_interval + ); + // create message and add to list + ActiveObjectMessage aom(getId(), false, str); + m_messages_out.push_back(aom); } - else + + if(m_wielded_item_not_sent) { - errorstream<<"MobV2SAO::step(): id="< 0.05*BS) - { - sendPosition(); + if(m_physics_override_sent == false){ + m_physics_override_sent = true; + std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); } -} -u16 MobV2SAO::punch(const std::string &toolname, v3f dir, - const std::string &playername) -{ - assert(m_env); - Map *map = &m_env->getMap(); - - actionstream<= 2.5){ - pos_size_off.X = -1; - pos_size_off.Y = -1; + if(m_bone_position_sent == false){ + m_bone_position_sent = true; + for(std::map >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ + std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); } - bool free = checkFreePosition(map, pos_i + pos_size_off, size_blocks); - if(free) - m_base_position = new_base_position; } - sendPosition(); - - u16 amount = 2; - /* See tool names in inventory.h */ - if(toolname == "WSword") - amount = 4; - if(toolname == "STSword") - amount = 6; - if(toolname == "SteelSword") - amount = 8; - if(toolname == "STAxe") - amount = 3; - if(toolname == "SteelAxe") - amount = 4; - if(toolname == "SteelPick") - amount = 3; - doDamage(amount); - return 65536/100; + + if(m_attachment_sent == false){ + m_attachment_sent = true; + std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } } -bool MobV2SAO::isPeaceful() +void PlayerSAO::setBasePosition(const v3f &position) { - return m_properties->getBool("is_peaceful"); + // This needs to be ran for attachments too + ServerActiveObject::setBasePosition(position); + m_position_not_sent = true; } -void MobV2SAO::sendPosition() +void PlayerSAO::setPos(v3f pos) { - m_last_sent_position = m_base_position; - - std::ostringstream os(std::ios::binary); - // command (0 = update position) - writeU8(os, 0); - // pos - writeV3F1000(os, m_base_position); - // yaw - writeF1000(os, m_yaw); - // create message and add to list - ActiveObjectMessage aom(getId(), false, os.str()); - m_messages_out.push_back(aom); + if(isAttached()) + return; + m_player->setPosition(pos); + // Movement caused by this command is always valid + m_last_good_position = pos; + m_last_good_position_age = 0; + // Force position change on client + m_moved = true; } -void MobV2SAO::setPropertyDefaults() +void PlayerSAO::moveTo(v3f pos, bool continuous) { - m_properties->setDefault("is_peaceful", "false"); - m_properties->setDefault("move_type", "ground_nodes"); - m_properties->setDefault("speed", "(0,0,0)"); - m_properties->setDefault("age", "0"); - m_properties->setDefault("yaw", "0"); - m_properties->setDefault("pos", "(0,0,0)"); - m_properties->setDefault("hp", "0"); - m_properties->setDefault("die_age", "-1"); - m_properties->setDefault("size", "(1,2)"); - m_properties->setDefault("shoot_type", "fireball"); - m_properties->setDefault("shoot_y", "0"); - m_properties->setDefault("mindless_rage", "false"); + if(isAttached()) + return; + m_player->setPosition(pos); + // Movement caused by this command is always valid + m_last_good_position = pos; + m_last_good_position_age = 0; + // Force position change on client + m_moved = true; } -void MobV2SAO::readProperties() + +void PlayerSAO::setYaw(float yaw) { - m_move_type = m_properties->get("move_type"); - m_speed = m_properties->getV3F("speed"); - m_age = m_properties->getFloat("age"); - m_yaw = m_properties->getFloat("yaw"); - m_base_position = m_properties->getV3F("pos"); - m_hp = m_properties->getS32("hp"); - m_die_age = m_properties->getFloat("die_age"); - m_size = m_properties->getV2F("size"); + m_player->setYaw(yaw); + // Force change on client + m_moved = true; } -void MobV2SAO::updateProperties() -{ - m_properties->set("move_type", m_move_type); - m_properties->setV3F("speed", m_speed); - m_properties->setFloat("age", m_age); - m_properties->setFloat("yaw", m_yaw); - m_properties->setV3F("pos", m_base_position); - m_properties->setS32("hp", m_hp); - m_properties->setFloat("die_age", m_die_age); - m_properties->setV2F("size", m_size); - m_properties->setS32("version", 0); +void PlayerSAO::setPitch(float pitch) +{ + m_player->setPitch(pitch); + // Force change on client + m_moved = true; } -void MobV2SAO::doDamage(u16 d) +int PlayerSAO::punch(v3f dir, + const ToolCapabilities *toolcap, + ServerActiveObject *puncher, + float time_from_last_punch) { - infostream<<"MobV2 hp="<getBool("enable_pvp") == false){ + if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){ + std::string str = gob_cmd_punched(0, getHP()); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + return 0; + } } + HitParams hitparams = getHitParams(m_armor_groups, toolcap, + time_from_last_punch); + + std::string punchername = "nil"; + + if ( puncher != 0 ) + punchername = puncher->getDescription(); + + actionstream<<"Player "<getName()<<" punched by " + <hp; +} -#include "scriptapi.h" -#include "luaentity_common.h" +void PlayerSAO::setHP(s16 hp) +{ + s16 oldhp = m_player->hp; -// Prototype -LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", ""); + if(hp < 0) + hp = 0; + else if(hp > PLAYER_MAX_HP) + hp = PLAYER_MAX_HP; -LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, - const std::string &name, const std::string &state): - ServerActiveObject(env, pos), - m_init_name(name), - m_init_state(state), - m_registered(false), - m_prop(new LuaEntityProperties), - m_yaw(0), - m_last_sent_yaw(0), - m_last_sent_position(0,0,0), - m_last_sent_position_timer(0), - m_last_sent_move_precision(0) -{ - // Only register type if no environment supplied - if(env == NULL){ - ServerActiveObject::registerType(getType(), create); + if(hp < oldhp && g_settings->getBool("enable_damage") == false) + { + m_hp_not_sent = true; // fix wrong prediction on client return; } -} -LuaEntitySAO::~LuaEntitySAO() -{ - if(m_registered){ - lua_State *L = m_env->getLua(); - scriptapi_luaentity_rm(L, m_id); + m_player->hp = hp; + + if(hp != oldhp) + m_hp_not_sent = true; + + // On death or reincarnation send an active object message + if((hp == 0) != (oldhp == 0)) + { + // Will send new is_visible value based on (getHP()!=0) + m_properties_sent = false; + // Send new HP + std::string str = gob_cmd_punched(0, getHP()); + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); } - delete m_prop; } -void LuaEntitySAO::addedToEnvironment(u16 id) +void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups) { - ServerActiveObject::addedToEnvironment(id); - - // Create entity from name and state - m_registered = true; - lua_State *L = m_env->getLua(); - scriptapi_luaentity_add(L, id, m_init_name.c_str(), m_init_state.c_str()); - - // Get properties - scriptapi_luaentity_get_properties(L, m_id, m_prop); + m_armor_groups = armor_groups; + m_armor_groups_sent = false; } -ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos, - const std::string &data) +void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend) { - std::istringstream is(data, std::ios::binary); - // read version - u8 version = readU8(is); - // check if version is supported - if(version != 0) - return NULL; - // read name - std::string name = deSerializeString(is); - // read state - std::string state = deSerializeLongString(is); - // create object - infostream<<"LuaEntitySAO::create(name=\""<getLua(); - scriptapi_luaentity_step(L, m_id, dtime); - } + // store these so they can be updated to clients + m_bone_position[bone] = core::vector2d(position, rotation); + m_bone_position_sent = false; +} - if(send_recommended == false) - return; - - float minchange = 0.2*BS; - if(m_last_sent_position_timer > 1.0){ - minchange = 0.01*BS; - } else if(m_last_sent_position_timer > 0.2){ - minchange = 0.05*BS; - } - float move_d = m_base_position.getDistanceFrom(m_last_sent_position); - move_d += m_last_sent_move_precision; - if(move_d > minchange || fabs(m_yaw - m_last_sent_yaw) > 1.0){ - sendPosition(true, false); - } +void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation) +{ + // Attachments need to be handled on both the server and client. + // If we just attach on the server, we can only copy the position of the parent. Attachments + // are still sent to clients at an interval so players might see them lagging, plus we can't + // read and attach to skeletal bones. + // If we just attach on the client, the server still sees the child at its original location. + // This breaks some things so we also give the server the most accurate representation + // even if players only see the client changes. + + m_attachment_parent_id = parent_id; + m_attachment_bone = bone; + m_attachment_position = position; + m_attachment_rotation = rotation; + m_attachment_sent = false; } -std::string LuaEntitySAO::getClientInitializationData() +ObjectProperties* PlayerSAO::accessObjectProperties() { - std::ostringstream os(std::ios::binary); - // version - writeU8(os, 0); - // pos - writeV3F1000(os, m_base_position); - // yaw - writeF1000(os, m_yaw); - // properties - std::ostringstream prop_os(std::ios::binary); - m_prop->serialize(prop_os); - os<getLua(); - std::string state = scriptapi_luaentity_get_state(L, m_id); - os<getName()); + return loc; } -void LuaEntitySAO::rightClick(Player *player) +void PlayerSAO::setInventoryModified() { - if(!m_registered) - return; - lua_State *L = m_env->getLua(); - scriptapi_luaentity_rightclick_player(L, m_id, player->getName()); + m_inventory_not_sent = true; } -void LuaEntitySAO::setPos(v3f pos) +std::string PlayerSAO::getWieldList() const { - m_base_position = pos; - sendPosition(false, true); + return "main"; } -void LuaEntitySAO::moveTo(v3f pos, bool continuous) +int PlayerSAO::getWieldIndex() const { - m_base_position = pos; - if(!continuous) - sendPosition(true, true); + return m_wield_index; } -float LuaEntitySAO::getMinimumSavedMovement() +void PlayerSAO::setWieldIndex(int i) { - return 0.1 * BS; + if(i != m_wield_index) + { + m_wield_index = i; + m_wielded_item_not_sent = true; + } } -void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) +void PlayerSAO::disconnected() { - m_last_sent_move_precision = m_base_position.getDistanceFrom( - m_last_sent_position); - m_last_sent_position_timer = 0; - m_last_sent_yaw = m_yaw; - m_last_sent_position = m_base_position; + m_peer_id = 0; + m_removed = true; + if(m_player->getPlayerSAO() == this) + { + m_player->setPlayerSAO(NULL); + m_player->peer_id = 0; + } +} - float update_interval = m_env->getSendRecommendedInterval(); +std::string PlayerSAO::getPropertyPacket() +{ + m_prop.is_visible = (true); + return gob_cmd_set_properties(m_prop); +} - std::ostringstream os(std::ios::binary); - // command (0 = update position) - writeU8(os, 0); +bool PlayerSAO::getCollisionBox(aabb3f *toset) { + //update collision box + *toset = m_player->getCollisionbox(); - // do_interpolate - writeU8(os, do_interpolate); - // pos - writeV3F1000(os, m_base_position); - // yaw - writeF1000(os, m_yaw); - // is_end_position (for interpolation) - writeU8(os, is_movement_end); - // update_interval (for interpolation) - writeF1000(os, update_interval); + toset->MinEdge += m_base_position; + toset->MaxEdge += m_base_position; - // create message and add to list - ActiveObjectMessage aom(getId(), false, os.str()); - m_messages_out.push_back(aom); + return true; } - -