int m_frame_speed;
int m_frame_blend;
std::map<std::string, core::vector2d<v3f> > m_bone_posrot;
+ ClientActiveObject* m_attachment_parent;
+ std::string m_attachment_bone;
+ v3f m_attacmhent_position;
+ v3f m_attachment_rotation;
int m_anim_frame;
int m_anim_num_frames;
float m_anim_framelength;
m_tx_basepos(0,0),
m_initial_tx_basepos_set(false),
m_tx_select_horiz_by_yawpitch(false),
+ m_frames(v2f(0,0)),
+ m_frame_speed(15),
+ m_frame_blend(0),
+ // Nothing to do for m_bone_posrot
+ m_attachment_parent(NULL),
+ m_attachment_bone(""),
+ m_attacmhent_position(v3f(0,0,0)),
+ m_attachment_rotation(v3f(0,0,0)),
m_anim_frame(0),
m_anim_num_frames(1),
m_anim_framelength(0.2),
void updateNodePos()
{
+ if(m_attachment_parent != NULL)
+ return;
+
if(m_meshnode){
m_meshnode->setPosition(pos_translator.vect_show);
v3f rot = m_meshnode->getRotation();
addToScene(m_smgr, m_gamedef->tsrc(), m_irr);
updateAnimations();
updateBonePosRot();
- updateAttachment();
}
- 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.125; // Distance per iteration
- f32 stepheight = 0;
- v3f p_pos = m_position;
- v3f p_velocity = m_velocity;
- v3f p_acceleration = m_acceleration;
- IGameDef *gamedef = env->getGameDef();
- moveresult = collisionMoveSimple(&env->getMap(), gamedef,
- pos_max_d, box, stepheight, dtime,
- p_pos, p_velocity, p_acceleration);
- // Apply results
- m_position = p_pos;
- m_velocity = p_velocity;
- m_acceleration = p_acceleration;
-
- bool is_end_position = moveresult.collides;
- pos_translator.update(m_position, is_end_position, dtime);
- pos_translator.translate(dtime);
- updateNodePos();
- } else {
- m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
- m_velocity += dtime * m_acceleration;
- pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
- pos_translator.translate(dtime);
- updateNodePos();
- }
+ if(m_attachment_parent == NULL) // Attachments should be glued to their parent by Irrlicht
+ {
+ 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.125; // Distance per iteration
+ f32 stepheight = 0;
+ v3f p_pos = m_position;
+ v3f p_velocity = m_velocity;
+ v3f p_acceleration = m_acceleration;
+ IGameDef *gamedef = env->getGameDef();
+ moveresult = collisionMoveSimple(&env->getMap(), gamedef,
+ pos_max_d, box, stepheight, dtime,
+ p_pos, p_velocity, p_acceleration);
+ // Apply results
+ m_position = p_pos;
+ m_velocity = p_velocity;
+ m_acceleration = p_acceleration;
+
+ bool is_end_position = moveresult.collides;
+ pos_translator.update(m_position, is_end_position, dtime);
+ pos_translator.translate(dtime);
+ updateNodePos();
+ } else {
+ m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
+ m_velocity += dtime * m_acceleration;
+ pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
+ pos_translator.translate(dtime);
+ updateNodePos();
+ }
- float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
- m_step_distance_counter += moved;
- if(m_step_distance_counter > 1.5*BS){
- m_step_distance_counter = 0;
- if(!m_is_local_player && m_prop.makes_footstep_sound){
- INodeDefManager *ndef = m_gamedef->ndef();
- v3s16 p = floatToInt(getPosition() + v3f(0,
- (m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
- MapNode n = m_env->getMap().getNodeNoEx(p);
- SimpleSoundSpec spec = ndef->get(n).sound_footstep;
- m_gamedef->sound()->playSoundAt(spec, false, getPosition());
+ float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
+ m_step_distance_counter += moved;
+ if(m_step_distance_counter > 1.5*BS){
+ m_step_distance_counter = 0;
+ if(!m_is_local_player && m_prop.makes_footstep_sound){
+ INodeDefManager *ndef = m_gamedef->ndef();
+ v3s16 p = floatToInt(getPosition() + v3f(0,
+ (m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
+ MapNode n = m_env->getMap().getNodeNoEx(p);
+ SimpleSoundSpec spec = ndef->get(n).sound_footstep;
+ m_gamedef->sound()->playSoundAt(spec, false, getPosition());
+ }
}
}
}
}
}
-
- void updateAttachment()
+
+ void updateAttachments()
{
- // Code for attachments goes here
+ // REMAINING ATTACHMENT ISSUES:
+ // We get to this function when the object is an attachment that needs to
+ // be attached to its parent. If a bone is set we attach it to that skeletal
+ // bone, otherwise just to the object's origin. Attachments should not copy parent
+ // position as that's laggy... instead the Irrlicht function(s) to attach should
+ // be used. If the parent object is NULL that means this object should be detached.
+ // This function is only called whenever a GENERIC_CMD_SET_ATTACHMENT message is received.
+
+ // We already attach our entity on the server too (copy position). Reason we attach
+ // to the client as well is first of all lag. The server sends the position
+ // of the child separately than that of the parent, so even on localhost
+ // you'd see the child lagging behind. Models are also client-side, so this is
+ // needed to read bone data and attach to joints.
+
+ // Functions:
+ // - m_attachment_parent is ClientActiveObject* for the parent entity.
+ // - m_attachment_bone is std::string of the bone, "" means none.
+ // - m_attachment_position is v3f and represents the position offset of the attachment.
+ // - m_attachment_rotation is v3f and represents the rotation offset of the attachment.
+
+ // Implementation information:
+ // From what I know, we need to get the AnimatedMeshSceneNode of m_attachment_parent then
+ // use parent_node->addChild(m_animated_meshnode) for position attachment. For skeletal
+ // attachment I don't know yet. Same must be used to detach when a NULL parent is received.
+
+ //Useful links:
+ // http://irrlicht.sourceforge.net/forum/viewtopic.php?t=7514
+ // http://www.irrlicht3d.org/wiki/index.php?n=Main.HowToUseTheNewAnimationSystem
+ // Irrlicht documentation: http://irrlicht.sourceforge.net/docu/
}
void processMessage(const std::string &data)
}
else if(cmd == GENERIC_CMD_UPDATE_POSITION)
{
+ // Not sent by the server if the object is an attachment
+
m_position = readV3F1000(is);
m_velocity = readV3F1000(is);
m_acceleration = readV3F1000(is);
// the ground due to sucky collision detection...
if(m_prop.physical)
m_position += v3f(0,0.002,0);
-
+
if(do_interpolate){
if(!m_prop.physical)
pos_translator.update(m_position, is_end_position, update_interval);
}
else if(cmd == GENERIC_CMD_SET_ATTACHMENT)
{
- // Part of the attachment structure, not used yet!
-
- // Get properties here.
+ ClientActiveObject *obj;
+ int parent_id = readS16(is);
+ if(parent_id > 0)
+ obj = m_env->getActiveObject(parent_id);
+ else
+ obj = NULL;
+ m_attachment_parent = obj;
+ m_attachment_bone = deSerializeString(is);
+ m_attacmhent_position = readV3F1000(is);
+ m_attachment_rotation = readV3F1000(is);
- updateAttachment();
- expireVisuals();
+ updateAttachments();
}
else if(cmd == GENERIC_CMD_PUNCHED)
{
if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
{
+ // TODO: We shouldn't be sending this when the object is attached, but we can't check m_parent here
setBasePosition(pos_f);
m_last_sent_position = pos_f;
// Create entity from name
lua_State *L = m_env->getLua();
m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
+ m_parent = NULL;
if(m_registered){
// Get properties
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
- 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_parent != NULL)
+ {
+ // REMAINING ATTACHMENT ISSUES:
+ // This is causing a segmentation fault, investigate why!
+ //m_base_position = m_parent->getBasePosition();
+ 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){
m_removed = true;
return 0;
}
+
+ // It's best that attachments cannot be punched
+ if(m_parent != NULL)
+ return 0;
ItemStack *punchitem = NULL;
ItemStack punchitem_static;
void LuaEntitySAO::setPos(v3f pos)
{
+ if(m_parent != NULL)
+ return;
m_base_position = pos;
sendPosition(false, true);
}
void LuaEntitySAO::moveTo(v3f pos, bool continuous)
{
+ if(m_parent != NULL)
+ return;
m_base_position = pos;
if(!continuous)
sendPosition(true, true);
m_messages_out.push_back(aom);
}
-// Part of the attachment structure, not used yet!
void LuaEntitySAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
{
- // Parent should be translated from a ServerActiveObject into something
- // the client will recognize (as a ClientActiveObject) then sent in
- // gob_cmd_set_attachment that way.
+ // 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 would see them following the parent
+ // instead of sticking to it, 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 can break some things, so we also give the server the most accurate representation
+ // even if players will only see the client changes since they override server-sent position.
- std::string str = gob_cmd_set_attachment(); // <- parameters here
+ // Server attachment:
+ m_parent = parent;
+
+ // Client attachment:
+ std::string str = gob_cmd_set_attachment(parent->getId(), bone, position, rotation);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
{
+ if(m_parent != NULL)
+ return;
+
m_last_sent_move_precision = m_base_position.getDistanceFrom(
m_last_sent_position);
m_last_sent_position_timer = 0;
{
ServerActiveObject::addedToEnvironment(dtime_s);
ServerActiveObject::setBasePosition(m_player->getPosition());
+ m_parent = NULL;
m_player->setPlayerSAO(this);
m_player->peer_id = m_peer_id;
m_last_good_position = m_player->getPosition();
}
void PlayerSAO::step(float dtime, bool send_recommended)
-{
+{
if(!m_properties_sent)
{
m_properties_sent = true;
m_time_from_last_punch += dtime;
m_nocheat_dig_time += dtime;
-
- if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
- {
- m_last_good_position = m_player->getPosition();
- m_last_good_position_age = 0;
- }
- else
+
+ if(m_parent == NULL)
{
- /*
- 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;
+ if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
+ {
+ m_last_good_position = m_player->getPosition();
+ m_last_good_position_age = 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
+ {
+ /*
+ 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 {
- actionstream<<"Player "<<m_player->getName()
- <<" moved too fast; resetting position"
- <<std::endl;
- m_player->setPosition(m_last_good_position);
- m_teleported = true;
+ // 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_teleported = 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 and send its position to clients
+ if(m_position_not_sent && m_parent == NULL)
{
m_position_not_sent = false;
float update_interval = m_env->getSendRecommendedInterval();
+ v3f pos;
+ // REMAINING ATTACHMENT ISSUES:
+ // This is causing a segmentation fault, investigate why!
+ if(m_parent != NULL)
+ pos = m_parent->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(),
void PlayerSAO::setBasePosition(const v3f &position)
{
+ if(m_parent != NULL)
+ return;
ServerActiveObject::setBasePosition(position);
m_position_not_sent = true;
}
void PlayerSAO::setPos(v3f pos)
{
+ if(m_parent != NULL)
+ return;
m_player->setPosition(pos);
// Movement caused by this command is always valid
m_last_good_position = pos;
void PlayerSAO::moveTo(v3f pos, bool continuous)
{
+ if(m_parent != NULL)
+ return;
m_player->setPosition(pos);
// Movement caused by this command is always valid
m_last_good_position = pos;
ServerActiveObject *puncher,
float time_from_last_punch)
{
+ // It's best that attachments cannot be punched
+ if(m_parent != NULL)
+ return 0;
+
if(!toolcap)
return 0;
m_messages_out.push_back(aom);
}
-// Part of the attachment structure, not used yet!
void PlayerSAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
-{
- std::string str = gob_cmd_set_attachment(); // <- parameters here
+{
+ // 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 would see them following the parent
+ // instead of sticking to it, 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 can break some things, so we also give the server the most accurate representation
+ // even if players will only see the client changes since they override server-sent position.
+
+ // Server attachment:
+ m_parent = parent;
+
+ // Client attachment:
+ std::string str = gob_cmd_set_attachment(parent->getId(), bone, position, rotation);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);