Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
*/
#include "player.h"
#include "scriptapi.h"
#include "genericobject.h"
+#include "util/serialize.h"
core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
/*
ItemSAO
+
+ DEPRECATED: New dropped items are implemented in Lua; see
+ builtin/item_entity.lua.
*/
class ItemSAO : public ServerActiveObject
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;
IGameDef *gamedef = m_env->getGameDef();
moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
- pos_max_d, box, dtime, pos_f, m_speed_f);
+ pos_max_d, box, stepheight, dtime,
+ pos_f, m_speed_f, accel_f);
if(send_recommended == false)
return;
}
}
- std::string getClientInitializationData()
+ std::string getClientInitializationData(u16 protocol_version)
{
std::ostringstream os(std::ios::binary);
// version
ServerActiveObject *puncher,
float time_from_last_punch)
{
- // Directly delete item in creative mode
- if(g_settings->getBool("creative_mode") == true)
- {
- m_removed = true;
- return 0;
- }
-
// Take item into inventory
ItemStack item = createItemStack();
Inventory *inv = puncher->getInventory();
m_last_sent_velocity(0,0,0),
m_last_sent_position_timer(0),
m_last_sent_move_precision(0),
- m_armor_groups_sent(false)
+ m_armor_groups_sent(false),
+ m_animation_sent(false),
+ m_bone_position_sent(false),
+ m_attachment_sent(false)
{
// Only register type if no environment supplied
if(env == NULL){
}
}
-void LuaEntitySAO::addedToEnvironment()
+void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
{
- ServerActiveObject::addedToEnvironment();
+ ServerActiveObject::addedToEnvironment(dtime_s);
// Create entity from name
lua_State *L = m_env->getLua();
scriptapi_luaentity_get_properties(L, m_id, &m_prop);
// Initialize HP from properties
m_hp = m_prop.hp_max;
+ // Activate entity, supplying serialized state
+ scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
}
-
- // Activate entity, supplying serialized state
- scriptapi_luaentity_activate(L, m_id, m_init_state.c_str());
}
ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
return sao;
}
+bool LuaEntitySAO::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;
+}
+
void LuaEntitySAO::step(float dtime, bool send_recommended)
{
if(!m_properties_sent)
m_messages_out.push_back(aom);
}
+ // If attached, check that our parent is still there. If it isn't, detach.
+ if(m_attachment_parent_id && !isAttached())
+ {
+ 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);
+ }
+
m_last_sent_position_timer += dtime;
-
- if(m_prop.physical){
- core::aabbox3d<f32> box = m_prop.collisionbox;
- box.MinEdge *= BS;
- box.MaxEdge *= BS;
- collisionMoveResult moveresult;
- f32 pos_max_d = BS*0.25; // Distance per iteration
- v3f p_pos = getBasePosition();
- v3f p_velocity = m_velocity;
- IGameDef *gamedef = m_env->getGameDef();
- moveresult = collisionMovePrecise(&m_env->getMap(), gamedef,
- pos_max_d, box, dtime, p_pos, p_velocity);
- // Apply results
- setBasePosition(p_pos);
- m_velocity = p_velocity;
- m_velocity += dtime * m_acceleration;
- } else {
- m_base_position += dtime * m_velocity + 0.5 * dtime
- * dtime * m_acceleration;
- m_velocity += dtime * m_acceleration;
+ // 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())
+ {
+ 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<f32> 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;
+ IGameDef *gamedef = m_env->getGameDef();
+ moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
+ pos_max_d, box, stepheight, dtime,
+ p_pos, p_velocity, p_acceleration);
+ // 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;
+ }
}
if(m_registered){
if(send_recommended == false)
return;
-
- // 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;
- }
- 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);
+
+ if(!isAttached())
+ {
+ // 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;
+ }
+ 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);
+ }
}
if(m_armor_groups_sent == false){
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
+
+ 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);
+ }
+
+ if(m_bone_position_sent == false){
+ m_bone_position_sent = true;
+ for(std::map<std::string, core::vector2d<v3f> >::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_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);
+ }
}
-std::string LuaEntitySAO::getClientInitializationData()
+std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
{
std::ostringstream os(std::ios::binary);
- writeU8(os, 0); // version
- os<<serializeString(""); // name
- writeU8(os, 0); // is_player
- writeV3F1000(os, m_base_position);
- writeF1000(os, m_yaw);
- writeS16(os, m_hp);
- writeU8(os, 2); // number of messages stuffed in here
- os<<serializeLongString(getPropertyPacket()); // message 1
- os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
+
+ if(protocol_version >= 14)
+ {
+ writeU8(os, 1); // version
+ os<<serializeString(""); // name
+ writeU8(os, 0); // is_player
+ writeS16(os, getId()); //id
+ writeV3F1000(os, m_base_position);
+ writeF1000(os, m_yaw);
+ writeS16(os, m_hp);
+
+ writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
+ os<<serializeLongString(getPropertyPacket()); // message 1
+ os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
+ os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
+ for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
+ os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
+ }
+ os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
+ }
+ else
+ {
+ writeU8(os, 0); // version
+ os<<serializeString(""); // name
+ writeU8(os, 0); // is_player
+ writeV3F1000(os, m_base_position);
+ writeF1000(os, m_yaw);
+ writeS16(os, m_hp);
+ writeU8(os, 2); // number of messages stuffed in here
+ os<<serializeLongString(getPropertyPacket()); // message 1
+ os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
+ }
+
// return result
return os.str();
}
m_removed = true;
return 0;
}
+
+ // It's best that attachments cannot be punched
+ if(isAttached())
+ return 0;
ItemStack *punchitem = NULL;
ItemStack punchitem_static;
{
if(!m_registered)
return;
+ // It's best that attachments cannot be clicked
+ if(isAttached())
+ return;
lua_State *L = m_env->getLua();
scriptapi_luaentity_rightclick(L, m_id, clicker);
}
void LuaEntitySAO::setPos(v3f pos)
{
+ if(isAttached())
+ return;
m_base_position = pos;
sendPosition(false, true);
}
void LuaEntitySAO::moveTo(v3f pos, bool continuous)
{
+ if(isAttached())
+ return;
m_base_position = pos;
if(!continuous)
sendPosition(true, true);
m_armor_groups_sent = false;
}
+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;
+}
+
+void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
+{
+ m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
+ m_bone_position_sent = false;
+}
+
+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;
+}
+
+ObjectProperties* LuaEntitySAO::accessObjectProperties()
+{
+ return &m_prop;
+}
+
+void LuaEntitySAO::notifyObjectPropertiesModified()
+{
+ m_properties_sent = false;
+}
+
void LuaEntitySAO::setVelocity(v3f velocity)
{
m_velocity = velocity;
void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
{
+ // 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;
);
// create message and add to list
ActiveObjectMessage aom(getId(), false, str);
+ m_messages_out.push_back(aom);
}
/*
// No prototype, PlayerSAO does not need to be deserialized
-PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_):
+PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
+ const std::set<std::string> &privs, bool is_singleplayer):
ServerActiveObject(env_, v3f(0,0,0)),
m_player(player_),
m_peer_id(peer_id_),
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_teleported(false),
+ 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_prop.physical = false;
m_prop.weight = 75;
m_prop.collisionbox = core::aabbox3d<f32>(-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);
- m_prop.is_visible = (getHP() != 0);
+ // end of default appearance
+ m_prop.is_visible = true;
m_prop.makes_footstep_sound = true;
}
}
// Called after id has been set and has been inserted in environment
-void PlayerSAO::addedToEnvironment()
+void PlayerSAO::addedToEnvironment(u32 dtime_s)
{
- ServerActiveObject::addedToEnvironment();
+ ServerActiveObject::addedToEnvironment(dtime_s);
ServerActiveObject::setBasePosition(m_player->getPosition());
m_player->setPlayerSAO(this);
m_player->peer_id = m_peer_id;
return g_settings->getBool("unlimited_player_transfer_distance");
}
-std::string PlayerSAO::getClientInitializationData()
+std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
{
std::ostringstream os(std::ios::binary);
- writeU8(os, 0); // version
- os<<serializeString(m_player->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<<serializeLongString(getPropertyPacket()); // message 1
- os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
+
+ if(protocol_version >= 15)
+ {
+ writeU8(os, 1); // version
+ os<<serializeString(m_player->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, 4 + m_bone_position.size()); // number of messages stuffed in here
+ os<<serializeLongString(getPropertyPacket()); // message 1
+ os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
+ os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
+ for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
+ os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
+ }
+ os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
+ }
+ else
+ {
+ writeU8(os, 0); // version
+ os<<serializeString(m_player->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<<serializeLongString(getPropertyPacket()); // message 1
+ os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
+ }
+
+ // return result
return os.str();
}
return "";
}
+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;
+}
+
void PlayerSAO::step(float dtime, bool send_recommended)
{
if(!m_properties_sent)
m_messages_out.push_back(aom);
}
+ // If attached, check that our parent is still there. If it isn't, detach.
+ if(m_attachment_parent_id && !isAttached())
+ {
+ 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;
+ }
+
m_time_from_last_punch += dtime;
+ m_nocheat_dig_time += dtime;
- /*
- 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.
- */
-
- //float player_max_speed = BS * 4.0; // Normal speed
- float player_max_speed = BS * 20; // Fast speed
- float player_max_speed_up = BS * 20;
- player_max_speed *= 2.5; // Tolerance
- player_max_speed_up *= 2.5;
-
- m_last_good_position_age += dtime;
- if(m_last_good_position_age >= 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<<m_player->getName()<<"'s horizontal speed is "
- <<(d_horiz/age)<<std::endl;*/
- if(d_horiz <= age * player_max_speed &&
- (d_vert < 0 || d_vert < age * player_max_speed_up)){
+ // 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())
+ {
+ 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);
+ }
+ else
+ {
+ if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
+ {
m_last_good_position = m_player->getPosition();
- } else {
- actionstream<<"Player "<<m_player->getName()
- <<" moved too fast; resetting position"
- <<std::endl;
- m_player->setPosition(m_last_good_position);
- m_teleported = true;
+ m_last_good_position_age = 0;
+ }
+ 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 {
+ // Normal speed
+ player_max_speed = BS * 4.0;
+ player_max_speed_up = BS * 4.0;
+ }
+ // Tolerance
+ player_max_speed *= 2.5;
+ player_max_speed_up *= 2.5;
+
+ m_last_good_position_age += dtime;
+ if(m_last_good_position_age >= 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<<m_player->getName()<<"'s horizontal speed is "
+ <<(d_horiz/age)<<std::endl;*/
+ if(d_horiz <= age * player_max_speed &&
+ (d_vert < 0 || d_vert < age * player_max_speed_up)){
+ m_last_good_position = m_player->getPosition();
+ } else {
+ actionstream<<"Player "<<m_player->getName()
+ <<" moved too fast; resetting position"
+ <<std::endl;
+ m_player->setPosition(m_last_good_position);
+ m_moved = true;
+ }
+ m_last_good_position_age = 0;
+ }
}
- m_last_good_position_age = 0;
}
if(send_recommended == false)
return;
- if(m_position_not_sent)
+ // If the object is attached client-side, don't waste bandwidth sending its position to clients
+ if(m_position_not_sent && !isAttached())
{
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(
- m_player->getPosition() + v3f(0,BS*1,0),
+ pos,
v3f(0,0,0),
v3f(0,0,0),
m_player->getYaw(),
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
+
+ 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);
+ }
+
+ if(m_bone_position_sent == false){
+ m_bone_position_sent = true;
+ for(std::map<std::string, core::vector2d<v3f> >::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_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);
+ }
}
void PlayerSAO::setBasePosition(const v3f &position)
{
+ // This needs to be ran for attachments too
ServerActiveObject::setBasePosition(position);
m_position_not_sent = true;
}
void PlayerSAO::setPos(v3f pos)
{
+ 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_teleported = true;
+ m_moved = true;
}
void PlayerSAO::moveTo(v3f pos, bool continuous)
{
+ 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_teleported = true;
+ m_moved = true;
}
int PlayerSAO::punch(v3f dir,
ServerActiveObject *puncher,
float time_from_last_punch)
{
+ // It's best that attachments cannot be punched
+ if(isAttached())
+ return 0;
+
if(!toolcap)
return 0;
m_armor_groups_sent = false;
}
+void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
+{
+ // store these so they can be updated to clients
+ m_animation_range = frame_range;
+ m_animation_speed = frame_speed;
+ m_animation_blend = frame_blend;
+ m_animation_sent = false;
+}
+
+void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
+{
+ // store these so they can be updated to clients
+ m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
+ m_bone_position_sent = 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;
+}
+
+ObjectProperties* PlayerSAO::accessObjectProperties()
+{
+ return &m_prop;
+}
+
+void PlayerSAO::notifyObjectPropertiesModified()
+{
+ m_properties_sent = false;
+}
+
Inventory* PlayerSAO::getInventory()
{
return m_inventory;
}
}
-void PlayerSAO::createCreativeInventory()
-{
- if(m_inventory != &m_player->inventory)
- delete m_inventory;
-
- m_inventory = new Inventory(m_player->inventory);
- m_inventory->clearContents();
- scriptapi_get_creative_inventory(m_env->getLua(), this);
-}
-
std::string PlayerSAO::getPropertyPacket()
{
- m_prop.is_visible = (getHP() != 0);
+ m_prop.is_visible = (true);
return gob_cmd_set_properties(m_prop);
}