3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "content_sao.h"
21 #include "util/serialize.h"
22 #include "util/mathconstants.h"
23 #include "collision.h"
24 #include "environment.h"
26 #include "serialization.h" // For compressZlib
27 #include "tool.h" // For ToolCapabilities
31 #include "scripting_game.h"
32 #include "genericobject.h"
35 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
41 class TestSAO : public ServerActiveObject
44 TestSAO(ServerEnvironment *env, v3f pos):
45 ServerActiveObject(env, pos),
49 ServerActiveObject::registerType(getType(), create);
51 ActiveObjectType getType() const
52 { return ACTIVEOBJECT_TYPE_TEST; }
54 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
55 const std::string &data)
57 return new TestSAO(env, pos);
60 void step(float dtime, bool send_recommended)
69 m_base_position.Y += dtime * BS * 2;
70 if(m_base_position.Y > 8*BS)
71 m_base_position.Y = 2*BS;
73 if(send_recommended == false)
83 data += itos(0); // 0 = position
85 data += itos(m_base_position.X);
87 data += itos(m_base_position.Y);
89 data += itos(m_base_position.Z);
91 ActiveObjectMessage aom(getId(), false, data);
92 m_messages_out.push(aom);
96 bool getCollisionBox(aabb3f *toset) {
100 bool collideWithObjects() {
109 // Prototype (registers item for deserialization)
110 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
116 // Prototype (registers item for deserialization)
117 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
119 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
120 const std::string &name, const std::string &state):
121 ServerActiveObject(env, pos),
127 m_acceleration(0,0,0),
129 m_properties_sent(true),
131 m_last_sent_position(0,0,0),
132 m_last_sent_velocity(0,0,0),
133 m_last_sent_position_timer(0),
134 m_last_sent_move_precision(0),
135 m_armor_groups_sent(false),
136 m_animation_speed(0),
137 m_animation_blend(0),
138 m_animation_loop(true),
139 m_animation_sent(false),
140 m_bone_position_sent(false),
141 m_attachment_parent_id(0),
142 m_attachment_sent(false)
144 // Only register type if no environment supplied
146 ServerActiveObject::registerType(getType(), create);
150 // Initialize something to armor groups
151 m_armor_groups["fleshy"] = 100;
154 LuaEntitySAO::~LuaEntitySAO()
157 m_env->getScriptIface()->luaentity_Remove(m_id);
161 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
163 ServerActiveObject::addedToEnvironment(dtime_s);
165 // Create entity from name
166 m_registered = m_env->getScriptIface()->
167 luaentity_Add(m_id, m_init_name.c_str());
171 m_env->getScriptIface()->
172 luaentity_GetProperties(m_id, &m_prop);
173 // Initialize HP from properties
174 m_hp = m_prop.hp_max;
175 // Activate entity, supplying serialized state
176 m_env->getScriptIface()->
177 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
181 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
182 const std::string &data)
190 std::istringstream is(data, std::ios::binary);
192 u8 version = readU8(is);
193 // check if version is supported
195 name = deSerializeString(is);
196 state = deSerializeLongString(is);
198 else if(version == 1){
199 name = deSerializeString(is);
200 state = deSerializeLongString(is);
202 velocity = readV3F1000(is);
207 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
208 <<state<<"\")"<<std::endl;
209 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
211 sao->m_velocity = velocity;
216 bool LuaEntitySAO::isAttached()
218 if(!m_attachment_parent_id)
220 // Check if the parent still exists
221 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
227 void LuaEntitySAO::step(float dtime, bool send_recommended)
229 if(!m_properties_sent)
231 m_properties_sent = true;
232 std::string str = getPropertyPacket();
233 // create message and add to list
234 ActiveObjectMessage aom(getId(), true, str);
235 m_messages_out.push(aom);
238 // If attached, check that our parent is still there. If it isn't, detach.
239 if(m_attachment_parent_id && !isAttached())
241 m_attachment_parent_id = 0;
242 m_attachment_bone = "";
243 m_attachment_position = v3f(0,0,0);
244 m_attachment_rotation = v3f(0,0,0);
245 sendPosition(false, true);
248 m_last_sent_position_timer += dtime;
250 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
251 // If the object gets detached this comes into effect automatically from the last known origin
254 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
255 m_base_position = pos;
256 m_velocity = v3f(0,0,0);
257 m_acceleration = v3f(0,0,0);
262 aabb3f box = m_prop.collisionbox;
265 collisionMoveResult moveresult;
266 f32 pos_max_d = BS*0.25; // Distance per iteration
267 v3f p_pos = m_base_position;
268 v3f p_velocity = m_velocity;
269 v3f p_acceleration = m_acceleration;
270 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
271 pos_max_d, box, m_prop.stepheight, dtime,
272 &p_pos, &p_velocity, p_acceleration,
273 this, m_prop.collideWithObjects);
276 m_base_position = p_pos;
277 m_velocity = p_velocity;
278 m_acceleration = p_acceleration;
280 m_base_position += dtime * m_velocity + 0.5 * dtime
281 * dtime * m_acceleration;
282 m_velocity += dtime * m_acceleration;
285 if((m_prop.automatic_face_movement_dir) &&
286 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
288 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
289 + m_prop.automatic_face_movement_dir_offset;
290 float max_rotation_delta =
291 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
293 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
294 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
296 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
304 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
307 if(send_recommended == false)
312 // TODO: force send when acceleration changes enough?
313 float minchange = 0.2*BS;
314 if(m_last_sent_position_timer > 1.0){
316 } else if(m_last_sent_position_timer > 0.2){
319 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
320 move_d += m_last_sent_move_precision;
321 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
322 if(move_d > minchange || vel_d > minchange ||
323 fabs(m_yaw - m_last_sent_yaw) > 1.0){
324 sendPosition(true, false);
328 if(m_armor_groups_sent == false){
329 m_armor_groups_sent = true;
330 std::string str = gob_cmd_update_armor_groups(
332 // create message and add to list
333 ActiveObjectMessage aom(getId(), true, str);
334 m_messages_out.push(aom);
337 if(m_animation_sent == false){
338 m_animation_sent = true;
339 std::string str = gob_cmd_update_animation(
340 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
341 // create message and add to list
342 ActiveObjectMessage aom(getId(), true, str);
343 m_messages_out.push(aom);
346 if(m_bone_position_sent == false){
347 m_bone_position_sent = true;
348 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
349 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
350 // create message and add to list
351 ActiveObjectMessage aom(getId(), true, str);
352 m_messages_out.push(aom);
356 if(m_attachment_sent == false){
357 m_attachment_sent = true;
358 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
359 // create message and add to list
360 ActiveObjectMessage aom(getId(), true, str);
361 m_messages_out.push(aom);
365 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
367 std::ostringstream os(std::ios::binary);
369 if(protocol_version >= 14)
371 writeU8(os, 1); // version
372 os<<serializeString(""); // name
373 writeU8(os, 0); // is_player
374 writeS16(os, getId()); //id
375 writeV3F1000(os, m_base_position);
376 writeF1000(os, m_yaw);
379 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
380 os<<serializeLongString(getPropertyPacket()); // message 1
381 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
382 os<<serializeLongString(gob_cmd_update_animation(
383 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
384 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
385 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
387 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
391 writeU8(os, 0); // version
392 os<<serializeString(""); // name
393 writeU8(os, 0); // is_player
394 writeV3F1000(os, m_base_position);
395 writeF1000(os, m_yaw);
397 writeU8(os, 2); // number of messages stuffed in here
398 os<<serializeLongString(getPropertyPacket()); // message 1
399 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
406 std::string LuaEntitySAO::getStaticData()
408 verbosestream<<FUNCTION_NAME<<std::endl;
409 std::ostringstream os(std::ios::binary);
413 os<<serializeString(m_init_name);
416 std::string state = m_env->getScriptIface()->
417 luaentity_GetStaticdata(m_id);
418 os<<serializeLongString(state);
420 os<<serializeLongString(m_init_state);
425 writeV3F1000(os, m_velocity);
427 writeF1000(os, m_yaw);
431 int LuaEntitySAO::punch(v3f dir,
432 const ToolCapabilities *toolcap,
433 ServerActiveObject *puncher,
434 float time_from_last_punch)
437 // Delete unknown LuaEntities when punched
442 // It's best that attachments cannot be punched
446 ItemStack *punchitem = NULL;
447 ItemStack punchitem_static;
449 punchitem_static = puncher->getWieldedItem();
450 punchitem = &punchitem_static;
453 PunchDamageResult result = getPunchDamage(
457 time_from_last_punch);
459 if (result.did_punch) {
460 setHP(getHP() - result.damage);
462 if (result.damage > 0) {
463 std::string punchername = puncher ? puncher->getDescription() : "nil";
465 actionstream << getDescription() << " punched by "
466 << punchername << ", damage " << result.damage
467 << " hp, health now " << getHP() << " hp" << std::endl;
470 std::string str = gob_cmd_punched(result.damage, getHP());
471 // create message and add to list
472 ActiveObjectMessage aom(getId(), true, str);
473 m_messages_out.push(aom);
479 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
480 time_from_last_punch, toolcap, dir);
485 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
489 // It's best that attachments cannot be clicked
492 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
495 void LuaEntitySAO::setPos(v3f pos)
499 m_base_position = pos;
500 sendPosition(false, true);
503 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
507 m_base_position = pos;
509 sendPosition(true, true);
512 float LuaEntitySAO::getMinimumSavedMovement()
517 std::string LuaEntitySAO::getDescription()
519 std::ostringstream os(std::ios::binary);
520 os<<"LuaEntitySAO at (";
521 os<<(m_base_position.X/BS)<<",";
522 os<<(m_base_position.Y/BS)<<",";
523 os<<(m_base_position.Z/BS);
528 void LuaEntitySAO::setHP(s16 hp)
534 s16 LuaEntitySAO::getHP() const
539 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
541 m_armor_groups = armor_groups;
542 m_armor_groups_sent = false;
545 ItemGroupList LuaEntitySAO::getArmorGroups()
547 return m_armor_groups;
550 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
552 m_animation_range = frame_range;
553 m_animation_speed = frame_speed;
554 m_animation_blend = frame_blend;
555 m_animation_loop = frame_loop;
556 m_animation_sent = false;
559 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
561 *frame_range = m_animation_range;
562 *frame_speed = m_animation_speed;
563 *frame_blend = m_animation_blend;
564 *frame_loop = m_animation_loop;
567 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
569 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
570 m_bone_position_sent = false;
573 void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
575 *position = m_bone_position[bone].X;
576 *rotation = m_bone_position[bone].Y;
579 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
581 // Attachments need to be handled on both the server and client.
582 // If we just attach on the server, we can only copy the position of the parent. Attachments
583 // are still sent to clients at an interval so players might see them lagging, plus we can't
584 // read and attach to skeletal bones.
585 // If we just attach on the client, the server still sees the child at its original location.
586 // This breaks some things so we also give the server the most accurate representation
587 // even if players only see the client changes.
589 m_attachment_parent_id = parent_id;
590 m_attachment_bone = bone;
591 m_attachment_position = position;
592 m_attachment_rotation = rotation;
593 m_attachment_sent = false;
596 void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
599 *parent_id = m_attachment_parent_id;
600 *bone = m_attachment_bone;
601 *position = m_attachment_position;
602 *rotation = m_attachment_rotation;
605 void LuaEntitySAO::addAttachmentChild(int child_id)
607 m_attachment_child_ids.insert(child_id);
610 void LuaEntitySAO::removeAttachmentChild(int child_id)
612 m_attachment_child_ids.erase(child_id);
615 std::set<int> LuaEntitySAO::getAttachmentChildIds()
617 return m_attachment_child_ids;
620 ObjectProperties* LuaEntitySAO::accessObjectProperties()
625 void LuaEntitySAO::notifyObjectPropertiesModified()
627 m_properties_sent = false;
630 void LuaEntitySAO::setVelocity(v3f velocity)
632 m_velocity = velocity;
635 v3f LuaEntitySAO::getVelocity()
640 void LuaEntitySAO::setAcceleration(v3f acceleration)
642 m_acceleration = acceleration;
645 v3f LuaEntitySAO::getAcceleration()
647 return m_acceleration;
650 void LuaEntitySAO::setYaw(float yaw)
655 float LuaEntitySAO::getYaw()
660 void LuaEntitySAO::setTextureMod(const std::string &mod)
662 std::string str = gob_cmd_set_texture_mod(mod);
663 // create message and add to list
664 ActiveObjectMessage aom(getId(), true, str);
665 m_messages_out.push(aom);
668 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
669 bool select_horiz_by_yawpitch)
671 std::string str = gob_cmd_set_sprite(
675 select_horiz_by_yawpitch
677 // create message and add to list
678 ActiveObjectMessage aom(getId(), true, str);
679 m_messages_out.push(aom);
682 std::string LuaEntitySAO::getName()
687 std::string LuaEntitySAO::getPropertyPacket()
689 return gob_cmd_set_properties(m_prop);
692 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
694 // If the object is attached client-side, don't waste bandwidth sending its position to clients
698 m_last_sent_move_precision = m_base_position.getDistanceFrom(
699 m_last_sent_position);
700 m_last_sent_position_timer = 0;
701 m_last_sent_yaw = m_yaw;
702 m_last_sent_position = m_base_position;
703 m_last_sent_velocity = m_velocity;
704 //m_last_sent_acceleration = m_acceleration;
706 float update_interval = m_env->getSendRecommendedInterval();
708 std::string str = gob_cmd_update_position(
717 // create message and add to list
718 ActiveObjectMessage aom(getId(), false, str);
719 m_messages_out.push(aom);
722 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
725 //update collision box
726 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
727 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
729 toset->MinEdge += m_base_position;
730 toset->MaxEdge += m_base_position;
738 bool LuaEntitySAO::collideWithObjects(){
739 return m_prop.collideWithObjects;
746 // No prototype, PlayerSAO does not need to be deserialized
748 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
749 const std::set<std::string> &privs, bool is_singleplayer):
750 ServerActiveObject(env_, v3f(0,0,0)),
755 m_last_good_position(0,0,0),
756 m_time_from_last_punch(0),
757 m_nocheat_dig_pos(32767, 32767, 32767),
758 m_nocheat_dig_time(0),
760 m_position_not_sent(false),
761 m_armor_groups_sent(false),
762 m_properties_sent(true),
764 m_is_singleplayer(is_singleplayer),
765 m_animation_speed(0),
766 m_animation_blend(0),
767 m_animation_loop(true),
768 m_animation_sent(false),
769 m_bone_position_sent(false),
770 m_attachment_parent_id(0),
771 m_attachment_sent(false),
773 m_physics_override_speed(1),
774 m_physics_override_jump(1),
775 m_physics_override_gravity(1),
776 m_physics_override_sneak(true),
777 m_physics_override_sneak_glitch(true),
778 m_physics_override_sent(false)
780 assert(m_player); // pre-condition
781 assert(m_peer_id != 0); // pre-condition
782 setBasePosition(m_player->getPosition());
783 m_inventory = &m_player->inventory;
784 m_armor_groups["fleshy"] = 100;
786 m_prop.hp_max = PLAYER_MAX_HP;
787 m_prop.physical = false;
789 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
790 // start of default appearance, this should be overwritten by LUA
791 m_prop.visual = "upright_sprite";
792 m_prop.visual_size = v2f(1, 2);
793 m_prop.textures.clear();
794 m_prop.textures.push_back("player.png");
795 m_prop.textures.push_back("player_back.png");
796 m_prop.colors.clear();
797 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
798 m_prop.spritediv = v2s16(1,1);
799 // end of default appearance
800 m_prop.is_visible = true;
801 m_prop.makes_footstep_sound = true;
804 PlayerSAO::~PlayerSAO()
806 if(m_inventory != &m_player->inventory)
811 std::string PlayerSAO::getDescription()
813 return std::string("player ") + m_player->getName();
816 // Called after id has been set and has been inserted in environment
817 void PlayerSAO::addedToEnvironment(u32 dtime_s)
819 ServerActiveObject::addedToEnvironment(dtime_s);
820 ServerActiveObject::setBasePosition(m_player->getPosition());
821 m_player->setPlayerSAO(this);
822 m_player->peer_id = m_peer_id;
823 m_last_good_position = m_player->getPosition();
826 // Called before removing from environment
827 void PlayerSAO::removingFromEnvironment()
829 ServerActiveObject::removingFromEnvironment();
830 if(m_player->getPlayerSAO() == this)
832 m_player->setPlayerSAO(NULL);
833 m_player->peer_id = 0;
834 m_env->savePlayer((RemotePlayer*)m_player);
835 m_env->removePlayer(m_player);
839 bool PlayerSAO::isStaticAllowed() const
844 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
846 std::ostringstream os(std::ios::binary);
848 if(protocol_version >= 15)
850 writeU8(os, 1); // version
851 os<<serializeString(m_player->getName()); // name
852 writeU8(os, 1); // is_player
853 writeS16(os, getId()); //id
854 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
855 writeF1000(os, m_player->getYaw());
856 writeS16(os, getHP());
858 writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
859 os<<serializeLongString(getPropertyPacket()); // message 1
860 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
861 os<<serializeLongString(gob_cmd_update_animation(
862 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
863 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
864 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
866 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
867 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
868 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
869 m_physics_override_sneak_glitch)); // 5
870 os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
874 writeU8(os, 0); // version
875 os<<serializeString(m_player->getName()); // name
876 writeU8(os, 1); // is_player
877 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
878 writeF1000(os, m_player->getYaw());
879 writeS16(os, getHP());
880 writeU8(os, 2); // number of messages stuffed in here
881 os<<serializeLongString(getPropertyPacket()); // message 1
882 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
889 std::string PlayerSAO::getStaticData()
891 FATAL_ERROR("Deprecated function (?)");
895 bool PlayerSAO::isAttached()
897 if(!m_attachment_parent_id)
899 // Check if the parent still exists
900 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
906 void PlayerSAO::step(float dtime, bool send_recommended)
908 if(!m_properties_sent)
910 m_properties_sent = true;
911 std::string str = getPropertyPacket();
912 // create message and add to list
913 ActiveObjectMessage aom(getId(), true, str);
914 m_messages_out.push(aom);
917 // If attached, check that our parent is still there. If it isn't, detach.
918 if(m_attachment_parent_id && !isAttached())
920 m_attachment_parent_id = 0;
921 m_attachment_bone = "";
922 m_attachment_position = v3f(0,0,0);
923 m_attachment_rotation = v3f(0,0,0);
924 m_player->setPosition(m_last_good_position);
925 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
928 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
930 // Set lag pool maximums based on estimated lag
931 const float LAG_POOL_MIN = 5.0;
932 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
933 if(lag_pool_max < LAG_POOL_MIN)
934 lag_pool_max = LAG_POOL_MIN;
935 m_dig_pool.setMax(lag_pool_max);
936 m_move_pool.setMax(lag_pool_max);
938 // Increment cheat prevention timers
939 m_dig_pool.add(dtime);
940 m_move_pool.add(dtime);
941 m_time_from_last_punch += dtime;
942 m_nocheat_dig_time += dtime;
944 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
945 // If the object gets detached this comes into effect automatically from the last known origin
948 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
949 m_last_good_position = pos;
950 m_player->setPosition(pos);
953 if(send_recommended == false)
956 // If the object is attached client-side, don't waste bandwidth sending its position to clients
957 if(m_position_not_sent && !isAttached())
959 m_position_not_sent = false;
960 float update_interval = m_env->getSendRecommendedInterval();
962 if(isAttached()) // Just in case we ever do send attachment position too
963 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
965 pos = m_player->getPosition() + v3f(0,BS*1,0);
966 std::string str = gob_cmd_update_position(
975 // create message and add to list
976 ActiveObjectMessage aom(getId(), false, str);
977 m_messages_out.push(aom);
980 if(m_armor_groups_sent == false) {
981 m_armor_groups_sent = true;
982 std::string str = gob_cmd_update_armor_groups(
984 // create message and add to list
985 ActiveObjectMessage aom(getId(), true, str);
986 m_messages_out.push(aom);
989 if(m_physics_override_sent == false){
990 m_physics_override_sent = true;
991 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
992 m_physics_override_jump, m_physics_override_gravity,
993 m_physics_override_sneak, m_physics_override_sneak_glitch);
994 // create message and add to list
995 ActiveObjectMessage aom(getId(), true, str);
996 m_messages_out.push(aom);
999 if(m_animation_sent == false){
1000 m_animation_sent = true;
1001 std::string str = gob_cmd_update_animation(
1002 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1003 // create message and add to list
1004 ActiveObjectMessage aom(getId(), true, str);
1005 m_messages_out.push(aom);
1008 if(m_bone_position_sent == false){
1009 m_bone_position_sent = true;
1010 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1011 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1012 // create message and add to list
1013 ActiveObjectMessage aom(getId(), true, str);
1014 m_messages_out.push(aom);
1018 if(m_attachment_sent == false){
1019 m_attachment_sent = true;
1020 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1021 // create message and add to list
1022 ActiveObjectMessage aom(getId(), true, str);
1023 m_messages_out.push(aom);
1027 void PlayerSAO::setBasePosition(const v3f &position)
1029 // This needs to be ran for attachments too
1030 ServerActiveObject::setBasePosition(position);
1031 m_position_not_sent = true;
1034 void PlayerSAO::setPos(v3f pos)
1038 m_player->setPosition(pos);
1039 // Movement caused by this command is always valid
1040 m_last_good_position = pos;
1041 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1044 void PlayerSAO::moveTo(v3f pos, bool continuous)
1048 m_player->setPosition(pos);
1049 // Movement caused by this command is always valid
1050 m_last_good_position = pos;
1051 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1054 void PlayerSAO::setYaw(float yaw)
1056 m_player->setYaw(yaw);
1057 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1060 void PlayerSAO::setPitch(float pitch)
1062 m_player->setPitch(pitch);
1063 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1066 int PlayerSAO::punch(v3f dir,
1067 const ToolCapabilities *toolcap,
1068 ServerActiveObject *puncher,
1069 float time_from_last_punch)
1071 // It's best that attachments cannot be punched
1078 // No effect if PvP disabled
1079 if (g_settings->getBool("enable_pvp") == false) {
1080 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1081 std::string str = gob_cmd_punched(0, getHP());
1082 // create message and add to list
1083 ActiveObjectMessage aom(getId(), true, str);
1084 m_messages_out.push(aom);
1089 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1090 time_from_last_punch);
1092 std::string punchername = "nil";
1095 punchername = puncher->getDescription();
1097 PlayerSAO *playersao = m_player->getPlayerSAO();
1099 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1100 puncher, time_from_last_punch, toolcap, dir,
1103 if (!damage_handled) {
1104 setHP(getHP() - hitparams.hp);
1105 } else { // override client prediction
1106 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1107 std::string str = gob_cmd_punched(0, getHP());
1108 // create message and add to list
1109 ActiveObjectMessage aom(getId(), true, str);
1110 m_messages_out.push(aom);
1115 actionstream << "Player " << m_player->getName() << " punched by "
1117 if (!damage_handled) {
1118 actionstream << ", damage " << hitparams.hp << " HP";
1120 actionstream << ", damage handled by lua";
1122 actionstream << std::endl;
1124 return hitparams.wear;
1127 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1131 s16 PlayerSAO::getHP() const
1133 return m_player->hp;
1136 s16 PlayerSAO::readDamage()
1138 s16 damage = m_damage;
1143 void PlayerSAO::setHP(s16 hp)
1145 s16 oldhp = m_player->hp;
1147 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this,
1151 hp = oldhp + hp_change;
1155 else if (hp > PLAYER_MAX_HP)
1158 if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1165 m_damage += (oldhp - hp);
1167 // Update properties on death
1168 if ((hp == 0) != (oldhp == 0))
1169 m_properties_sent = false;
1172 u16 PlayerSAO::getBreath() const
1174 return m_player->getBreath();
1177 void PlayerSAO::setBreath(u16 breath)
1179 m_player->setBreath(breath);
1182 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1184 m_armor_groups = armor_groups;
1185 m_armor_groups_sent = false;
1188 ItemGroupList PlayerSAO::getArmorGroups()
1190 return m_armor_groups;
1193 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1195 // store these so they can be updated to clients
1196 m_animation_range = frame_range;
1197 m_animation_speed = frame_speed;
1198 m_animation_blend = frame_blend;
1199 m_animation_loop = frame_loop;
1200 m_animation_sent = false;
1203 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1205 *frame_range = m_animation_range;
1206 *frame_speed = m_animation_speed;
1207 *frame_blend = m_animation_blend;
1208 *frame_loop = m_animation_loop;
1211 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1213 // store these so they can be updated to clients
1214 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1215 m_bone_position_sent = false;
1218 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1220 *position = m_bone_position[bone].X;
1221 *rotation = m_bone_position[bone].Y;
1224 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1226 // Attachments need to be handled on both the server and client.
1227 // If we just attach on the server, we can only copy the position of the parent. Attachments
1228 // are still sent to clients at an interval so players might see them lagging, plus we can't
1229 // read and attach to skeletal bones.
1230 // If we just attach on the client, the server still sees the child at its original location.
1231 // This breaks some things so we also give the server the most accurate representation
1232 // even if players only see the client changes.
1234 m_attachment_parent_id = parent_id;
1235 m_attachment_bone = bone;
1236 m_attachment_position = position;
1237 m_attachment_rotation = rotation;
1238 m_attachment_sent = false;
1241 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1244 *parent_id = m_attachment_parent_id;
1245 *bone = m_attachment_bone;
1246 *position = m_attachment_position;
1247 *rotation = m_attachment_rotation;
1250 void PlayerSAO::addAttachmentChild(int child_id)
1252 m_attachment_child_ids.insert(child_id);
1255 void PlayerSAO::removeAttachmentChild(int child_id)
1257 m_attachment_child_ids.erase(child_id);
1260 std::set<int> PlayerSAO::getAttachmentChildIds()
1262 return m_attachment_child_ids;
1265 ObjectProperties* PlayerSAO::accessObjectProperties()
1270 void PlayerSAO::notifyObjectPropertiesModified()
1272 m_properties_sent = false;
1275 Inventory* PlayerSAO::getInventory()
1279 const Inventory* PlayerSAO::getInventory() const
1284 InventoryLocation PlayerSAO::getInventoryLocation() const
1286 InventoryLocation loc;
1287 loc.setPlayer(m_player->getName());
1291 std::string PlayerSAO::getWieldList() const
1296 int PlayerSAO::getWieldIndex() const
1298 return m_wield_index;
1301 void PlayerSAO::setWieldIndex(int i)
1303 if(i != m_wield_index) {
1308 void PlayerSAO::disconnected()
1312 if(m_player->getPlayerSAO() == this)
1314 m_player->setPlayerSAO(NULL);
1315 m_player->peer_id = 0;
1319 std::string PlayerSAO::getPropertyPacket()
1321 m_prop.is_visible = (true);
1322 return gob_cmd_set_properties(m_prop);
1325 bool PlayerSAO::checkMovementCheat()
1327 bool cheated = false;
1328 if(isAttached() || m_is_singleplayer ||
1329 g_settings->getBool("disable_anticheat"))
1331 m_last_good_position = m_player->getPosition();
1336 Check player movements
1338 NOTE: Actually the server should handle player physics like the
1339 client does and compare player's position to what is calculated
1340 on our side. This is required when eg. players fly due to an
1341 explosion. Altough a node-based alternative might be possible
1342 too, and much more lightweight.
1345 float player_max_speed = 0;
1346 if(m_privs.count("fast") != 0){
1348 player_max_speed = m_player->movement_speed_fast;
1351 player_max_speed = m_player->movement_speed_walk;
1353 // Tolerance. With the lag pool we shouldn't need it.
1354 //player_max_speed *= 2.5;
1355 //player_max_speed_up *= 2.5;
1357 v3f diff = (m_player->getPosition() - m_last_good_position);
1358 float d_vert = diff.Y;
1360 float d_horiz = diff.getLength();
1361 float required_time = d_horiz/player_max_speed;
1362 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1363 required_time = d_vert/player_max_speed;
1364 if(m_move_pool.grab(required_time)){
1365 m_last_good_position = m_player->getPosition();
1367 actionstream<<"Player "<<m_player->getName()
1368 <<" moved too fast; resetting position"
1370 m_player->setPosition(m_last_good_position);
1377 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1378 //update collision box
1379 *toset = m_player->getCollisionbox();
1381 toset->MinEdge += m_base_position;
1382 toset->MaxEdge += m_base_position;
1387 bool PlayerSAO::collideWithObjects(){