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"
27 #include "serialization.h" // For compressZlib
28 #include "tool.h" // For ToolCapabilities
32 #include "scripting_game.h"
33 #include "genericobject.h"
36 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
42 class TestSAO : public ServerActiveObject
45 TestSAO(ServerEnvironment *env, v3f pos):
46 ServerActiveObject(env, pos),
50 ServerActiveObject::registerType(getType(), create);
52 ActiveObjectType getType() const
53 { return ACTIVEOBJECT_TYPE_TEST; }
55 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
56 const std::string &data)
58 return new TestSAO(env, pos);
61 void step(float dtime, bool send_recommended)
70 m_base_position.Y += dtime * BS * 2;
71 if(m_base_position.Y > 8*BS)
72 m_base_position.Y = 2*BS;
74 if(send_recommended == false)
84 data += itos(0); // 0 = position
86 data += itos(m_base_position.X);
88 data += itos(m_base_position.Y);
90 data += itos(m_base_position.Z);
92 ActiveObjectMessage aom(getId(), false, data);
93 m_messages_out.push(aom);
97 bool getCollisionBox(aabb3f *toset) {
101 bool collideWithObjects() {
110 // Prototype (registers item for deserialization)
111 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
117 // Prototype (registers item for deserialization)
118 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
120 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
121 const std::string &name, const std::string &state):
122 ServerActiveObject(env, pos),
128 m_acceleration(0,0,0),
130 m_properties_sent(true),
132 m_last_sent_position(0,0,0),
133 m_last_sent_velocity(0,0,0),
134 m_last_sent_position_timer(0),
135 m_last_sent_move_precision(0),
136 m_armor_groups_sent(false),
137 m_animation_speed(0),
138 m_animation_blend(0),
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 core::aabbox3d<f32> 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)){
287 m_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI + m_prop.automatic_face_movement_dir_offset;
292 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
295 if(send_recommended == false)
300 // TODO: force send when acceleration changes enough?
301 float minchange = 0.2*BS;
302 if(m_last_sent_position_timer > 1.0){
304 } else if(m_last_sent_position_timer > 0.2){
307 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
308 move_d += m_last_sent_move_precision;
309 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
310 if(move_d > minchange || vel_d > minchange ||
311 fabs(m_yaw - m_last_sent_yaw) > 1.0){
312 sendPosition(true, false);
316 if(m_armor_groups_sent == false){
317 m_armor_groups_sent = true;
318 std::string str = gob_cmd_update_armor_groups(
320 // create message and add to list
321 ActiveObjectMessage aom(getId(), true, str);
322 m_messages_out.push(aom);
325 if(m_animation_sent == false){
326 m_animation_sent = true;
327 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
328 // create message and add to list
329 ActiveObjectMessage aom(getId(), true, str);
330 m_messages_out.push(aom);
333 if(m_bone_position_sent == false){
334 m_bone_position_sent = true;
335 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
336 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
337 // create message and add to list
338 ActiveObjectMessage aom(getId(), true, str);
339 m_messages_out.push(aom);
343 if(m_attachment_sent == false){
344 m_attachment_sent = true;
345 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
346 // create message and add to list
347 ActiveObjectMessage aom(getId(), true, str);
348 m_messages_out.push(aom);
352 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
354 std::ostringstream os(std::ios::binary);
356 if(protocol_version >= 14)
358 writeU8(os, 1); // version
359 os<<serializeString(""); // name
360 writeU8(os, 0); // is_player
361 writeS16(os, getId()); //id
362 writeV3F1000(os, m_base_position);
363 writeF1000(os, m_yaw);
366 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
367 os<<serializeLongString(getPropertyPacket()); // message 1
368 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
369 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
370 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
371 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
373 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
377 writeU8(os, 0); // version
378 os<<serializeString(""); // name
379 writeU8(os, 0); // is_player
380 writeV3F1000(os, m_base_position);
381 writeF1000(os, m_yaw);
383 writeU8(os, 2); // number of messages stuffed in here
384 os<<serializeLongString(getPropertyPacket()); // message 1
385 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
392 std::string LuaEntitySAO::getStaticData()
394 verbosestream<<__FUNCTION_NAME<<std::endl;
395 std::ostringstream os(std::ios::binary);
399 os<<serializeString(m_init_name);
402 std::string state = m_env->getScriptIface()->
403 luaentity_GetStaticdata(m_id);
404 os<<serializeLongString(state);
406 os<<serializeLongString(m_init_state);
411 writeV3F1000(os, m_velocity);
413 writeF1000(os, m_yaw);
417 int LuaEntitySAO::punch(v3f dir,
418 const ToolCapabilities *toolcap,
419 ServerActiveObject *puncher,
420 float time_from_last_punch)
423 // Delete unknown LuaEntities when punched
428 // It's best that attachments cannot be punched
432 ItemStack *punchitem = NULL;
433 ItemStack punchitem_static;
435 punchitem_static = puncher->getWieldedItem();
436 punchitem = &punchitem_static;
439 PunchDamageResult result = getPunchDamage(
443 time_from_last_punch);
445 if (result.did_punch) {
446 setHP(getHP() - result.damage);
448 if (result.damage > 0) {
449 std::string punchername = puncher ? puncher->getDescription() : "nil";
451 actionstream << getDescription() << " punched by "
452 << punchername << ", damage " << result.damage
453 << " hp, health now " << getHP() << " hp" << std::endl;
456 std::string str = gob_cmd_punched(result.damage, getHP());
457 // create message and add to list
458 ActiveObjectMessage aom(getId(), true, str);
459 m_messages_out.push(aom);
465 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
466 time_from_last_punch, toolcap, dir);
471 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
475 // It's best that attachments cannot be clicked
478 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
481 void LuaEntitySAO::setPos(v3f pos)
485 m_base_position = pos;
486 sendPosition(false, true);
489 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
493 m_base_position = pos;
495 sendPosition(true, true);
498 float LuaEntitySAO::getMinimumSavedMovement()
503 std::string LuaEntitySAO::getDescription()
505 std::ostringstream os(std::ios::binary);
506 os<<"LuaEntitySAO at (";
507 os<<(m_base_position.X/BS)<<",";
508 os<<(m_base_position.Y/BS)<<",";
509 os<<(m_base_position.Z/BS);
514 void LuaEntitySAO::setHP(s16 hp)
520 s16 LuaEntitySAO::getHP() const
525 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
527 m_armor_groups = armor_groups;
528 m_armor_groups_sent = false;
531 ItemGroupList LuaEntitySAO::getArmorGroups()
533 return m_armor_groups;
536 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
538 m_animation_range = frame_range;
539 m_animation_speed = frame_speed;
540 m_animation_blend = frame_blend;
541 m_animation_sent = false;
544 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend)
546 *frame_range = m_animation_range;
547 *frame_speed = m_animation_speed;
548 *frame_blend = m_animation_blend;
551 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
553 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
554 m_bone_position_sent = false;
557 void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
559 *position = m_bone_position[bone].X;
560 *rotation = m_bone_position[bone].Y;
563 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
565 // Attachments need to be handled on both the server and client.
566 // If we just attach on the server, we can only copy the position of the parent. Attachments
567 // are still sent to clients at an interval so players might see them lagging, plus we can't
568 // read and attach to skeletal bones.
569 // If we just attach on the client, the server still sees the child at its original location.
570 // This breaks some things so we also give the server the most accurate representation
571 // even if players only see the client changes.
573 m_attachment_parent_id = parent_id;
574 m_attachment_bone = bone;
575 m_attachment_position = position;
576 m_attachment_rotation = rotation;
577 m_attachment_sent = false;
580 void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
583 *parent_id = m_attachment_parent_id;
584 *bone = m_attachment_bone;
585 *position = m_attachment_position;
586 *rotation = m_attachment_rotation;
589 ObjectProperties* LuaEntitySAO::accessObjectProperties()
594 void LuaEntitySAO::notifyObjectPropertiesModified()
596 m_properties_sent = false;
599 void LuaEntitySAO::setVelocity(v3f velocity)
601 m_velocity = velocity;
604 v3f LuaEntitySAO::getVelocity()
609 void LuaEntitySAO::setAcceleration(v3f acceleration)
611 m_acceleration = acceleration;
614 v3f LuaEntitySAO::getAcceleration()
616 return m_acceleration;
619 void LuaEntitySAO::setYaw(float yaw)
624 float LuaEntitySAO::getYaw()
629 void LuaEntitySAO::setTextureMod(const std::string &mod)
631 std::string str = gob_cmd_set_texture_mod(mod);
632 // create message and add to list
633 ActiveObjectMessage aom(getId(), true, str);
634 m_messages_out.push(aom);
637 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
638 bool select_horiz_by_yawpitch)
640 std::string str = gob_cmd_set_sprite(
644 select_horiz_by_yawpitch
646 // create message and add to list
647 ActiveObjectMessage aom(getId(), true, str);
648 m_messages_out.push(aom);
651 std::string LuaEntitySAO::getName()
656 std::string LuaEntitySAO::getPropertyPacket()
658 return gob_cmd_set_properties(m_prop);
661 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
663 // If the object is attached client-side, don't waste bandwidth sending its position to clients
667 m_last_sent_move_precision = m_base_position.getDistanceFrom(
668 m_last_sent_position);
669 m_last_sent_position_timer = 0;
670 m_last_sent_yaw = m_yaw;
671 m_last_sent_position = m_base_position;
672 m_last_sent_velocity = m_velocity;
673 //m_last_sent_acceleration = m_acceleration;
675 float update_interval = m_env->getSendRecommendedInterval();
677 std::string str = gob_cmd_update_position(
686 // create message and add to list
687 ActiveObjectMessage aom(getId(), false, str);
688 m_messages_out.push(aom);
691 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
694 //update collision box
695 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
696 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
698 toset->MinEdge += m_base_position;
699 toset->MaxEdge += m_base_position;
707 bool LuaEntitySAO::collideWithObjects(){
708 return m_prop.collideWithObjects;
715 // No prototype, PlayerSAO does not need to be deserialized
717 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
718 const std::set<std::string> &privs, bool is_singleplayer):
719 ServerActiveObject(env_, v3f(0,0,0)),
724 m_last_good_position(0,0,0),
725 m_time_from_last_punch(0),
726 m_nocheat_dig_pos(32767, 32767, 32767),
727 m_nocheat_dig_time(0),
729 m_position_not_sent(false),
730 m_armor_groups_sent(false),
731 m_properties_sent(true),
733 m_is_singleplayer(is_singleplayer),
734 m_animation_speed(0),
735 m_animation_blend(0),
736 m_animation_sent(false),
737 m_bone_position_sent(false),
738 m_attachment_parent_id(0),
739 m_attachment_sent(false),
740 m_nametag_color(video::SColor(255, 255, 255, 255)),
741 m_nametag_sent(false),
743 m_physics_override_speed(1),
744 m_physics_override_jump(1),
745 m_physics_override_gravity(1),
746 m_physics_override_sneak(true),
747 m_physics_override_sneak_glitch(true),
748 m_physics_override_sent(false)
750 assert(m_player); // pre-condition
751 assert(m_peer_id != 0); // pre-condition
752 setBasePosition(m_player->getPosition());
753 m_inventory = &m_player->inventory;
754 m_armor_groups["fleshy"] = 100;
756 m_prop.hp_max = PLAYER_MAX_HP;
757 m_prop.physical = false;
759 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
760 // start of default appearance, this should be overwritten by LUA
761 m_prop.visual = "upright_sprite";
762 m_prop.visual_size = v2f(1, 2);
763 m_prop.textures.clear();
764 m_prop.textures.push_back("player.png");
765 m_prop.textures.push_back("player_back.png");
766 m_prop.colors.clear();
767 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
768 m_prop.spritediv = v2s16(1,1);
769 // end of default appearance
770 m_prop.is_visible = true;
771 m_prop.makes_footstep_sound = true;
774 PlayerSAO::~PlayerSAO()
776 if(m_inventory != &m_player->inventory)
781 std::string PlayerSAO::getDescription()
783 return std::string("player ") + m_player->getName();
786 // Called after id has been set and has been inserted in environment
787 void PlayerSAO::addedToEnvironment(u32 dtime_s)
789 ServerActiveObject::addedToEnvironment(dtime_s);
790 ServerActiveObject::setBasePosition(m_player->getPosition());
791 m_player->setPlayerSAO(this);
792 m_player->peer_id = m_peer_id;
793 m_last_good_position = m_player->getPosition();
796 // Called before removing from environment
797 void PlayerSAO::removingFromEnvironment()
799 ServerActiveObject::removingFromEnvironment();
800 if(m_player->getPlayerSAO() == this)
802 m_player->setPlayerSAO(NULL);
803 m_player->peer_id = 0;
804 m_env->savePlayer(m_player->getName());
805 m_env->removePlayer(m_player->getName());
809 bool PlayerSAO::isStaticAllowed() const
814 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
816 std::ostringstream os(std::ios::binary);
818 if(protocol_version >= 15)
820 writeU8(os, 1); // version
821 os<<serializeString(m_player->getName()); // name
822 writeU8(os, 1); // is_player
823 writeS16(os, getId()); //id
824 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
825 writeF1000(os, m_player->getYaw());
826 writeS16(os, getHP());
828 writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
829 os<<serializeLongString(getPropertyPacket()); // message 1
830 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
831 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
832 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
833 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
835 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
836 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
837 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
838 m_physics_override_sneak_glitch)); // 5
839 os << serializeLongString(gob_cmd_update_nametag_attributes(m_nametag_color)); // 6
843 writeU8(os, 0); // version
844 os<<serializeString(m_player->getName()); // name
845 writeU8(os, 1); // is_player
846 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
847 writeF1000(os, m_player->getYaw());
848 writeS16(os, getHP());
849 writeU8(os, 2); // number of messages stuffed in here
850 os<<serializeLongString(getPropertyPacket()); // message 1
851 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
858 std::string PlayerSAO::getStaticData()
860 FATAL_ERROR("Deprecated function (?)");
864 bool PlayerSAO::isAttached()
866 if(!m_attachment_parent_id)
868 // Check if the parent still exists
869 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
875 void PlayerSAO::step(float dtime, bool send_recommended)
877 if(!m_properties_sent)
879 m_properties_sent = true;
880 std::string str = getPropertyPacket();
881 // create message and add to list
882 ActiveObjectMessage aom(getId(), true, str);
883 m_messages_out.push(aom);
886 // If attached, check that our parent is still there. If it isn't, detach.
887 if(m_attachment_parent_id && !isAttached())
889 m_attachment_parent_id = 0;
890 m_attachment_bone = "";
891 m_attachment_position = v3f(0,0,0);
892 m_attachment_rotation = v3f(0,0,0);
893 m_player->setPosition(m_last_good_position);
894 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
897 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
899 // Set lag pool maximums based on estimated lag
900 const float LAG_POOL_MIN = 5.0;
901 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
902 if(lag_pool_max < LAG_POOL_MIN)
903 lag_pool_max = LAG_POOL_MIN;
904 m_dig_pool.setMax(lag_pool_max);
905 m_move_pool.setMax(lag_pool_max);
907 // Increment cheat prevention timers
908 m_dig_pool.add(dtime);
909 m_move_pool.add(dtime);
910 m_time_from_last_punch += dtime;
911 m_nocheat_dig_time += dtime;
913 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
914 // If the object gets detached this comes into effect automatically from the last known origin
917 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
918 m_last_good_position = pos;
919 m_player->setPosition(pos);
922 if(send_recommended == false)
925 // If the object is attached client-side, don't waste bandwidth sending its position to clients
926 if(m_position_not_sent && !isAttached())
928 m_position_not_sent = false;
929 float update_interval = m_env->getSendRecommendedInterval();
931 if(isAttached()) // Just in case we ever do send attachment position too
932 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
934 pos = m_player->getPosition() + v3f(0,BS*1,0);
935 std::string str = gob_cmd_update_position(
944 // create message and add to list
945 ActiveObjectMessage aom(getId(), false, str);
946 m_messages_out.push(aom);
949 if(m_armor_groups_sent == false) {
950 m_armor_groups_sent = true;
951 std::string str = gob_cmd_update_armor_groups(
953 // create message and add to list
954 ActiveObjectMessage aom(getId(), true, str);
955 m_messages_out.push(aom);
958 if(m_physics_override_sent == false){
959 m_physics_override_sent = true;
960 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
961 m_physics_override_jump, m_physics_override_gravity,
962 m_physics_override_sneak, m_physics_override_sneak_glitch);
963 // create message and add to list
964 ActiveObjectMessage aom(getId(), true, str);
965 m_messages_out.push(aom);
968 if(m_animation_sent == false){
969 m_animation_sent = true;
970 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
971 // create message and add to list
972 ActiveObjectMessage aom(getId(), true, str);
973 m_messages_out.push(aom);
976 if(m_bone_position_sent == false){
977 m_bone_position_sent = true;
978 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
979 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
980 // create message and add to list
981 ActiveObjectMessage aom(getId(), true, str);
982 m_messages_out.push(aom);
986 if(m_attachment_sent == false){
987 m_attachment_sent = true;
988 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
989 // create message and add to list
990 ActiveObjectMessage aom(getId(), true, str);
991 m_messages_out.push(aom);
994 if (m_nametag_sent == false) {
995 m_nametag_sent = true;
996 std::string str = gob_cmd_update_nametag_attributes(m_nametag_color);
997 // create message and add to list
998 ActiveObjectMessage aom(getId(), true, str);
999 m_messages_out.push(aom);
1003 void PlayerSAO::setBasePosition(const v3f &position)
1005 // This needs to be ran for attachments too
1006 ServerActiveObject::setBasePosition(position);
1007 m_position_not_sent = true;
1010 void PlayerSAO::setPos(v3f pos)
1014 m_player->setPosition(pos);
1015 // Movement caused by this command is always valid
1016 m_last_good_position = pos;
1017 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1020 void PlayerSAO::moveTo(v3f pos, bool continuous)
1024 m_player->setPosition(pos);
1025 // Movement caused by this command is always valid
1026 m_last_good_position = pos;
1027 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1030 void PlayerSAO::setYaw(float yaw)
1032 m_player->setYaw(yaw);
1033 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1036 void PlayerSAO::setPitch(float pitch)
1038 m_player->setPitch(pitch);
1039 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1042 int PlayerSAO::punch(v3f dir,
1043 const ToolCapabilities *toolcap,
1044 ServerActiveObject *puncher,
1045 float time_from_last_punch)
1047 // It's best that attachments cannot be punched
1054 // No effect if PvP disabled
1055 if (g_settings->getBool("enable_pvp") == false) {
1056 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1057 std::string str = gob_cmd_punched(0, getHP());
1058 // create message and add to list
1059 ActiveObjectMessage aom(getId(), true, str);
1060 m_messages_out.push(aom);
1065 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1066 time_from_last_punch);
1068 std::string punchername = "nil";
1071 punchername = puncher->getDescription();
1073 PlayerSAO *playersao = m_player->getPlayerSAO();
1075 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1076 puncher, time_from_last_punch, toolcap, dir,
1079 if (!damage_handled) {
1080 setHP(getHP() - hitparams.hp);
1081 } else { // override client prediction
1082 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1083 std::string str = gob_cmd_punched(0, getHP());
1084 // create message and add to list
1085 ActiveObjectMessage aom(getId(), true, str);
1086 m_messages_out.push(aom);
1091 actionstream << "Player " << m_player->getName() << " punched by "
1093 if (!damage_handled) {
1094 actionstream << ", damage " << hitparams.hp << " HP";
1096 actionstream << ", damage handled by lua";
1098 actionstream << std::endl;
1100 return hitparams.wear;
1103 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1107 s16 PlayerSAO::getHP() const
1109 return m_player->hp;
1112 s16 PlayerSAO::readDamage()
1114 s16 damage = m_damage;
1119 void PlayerSAO::setHP(s16 hp)
1121 s16 oldhp = m_player->hp;
1123 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this,
1127 hp = oldhp + hp_change;
1131 else if (hp > PLAYER_MAX_HP)
1134 if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1141 m_damage += (oldhp - hp);
1143 // Update properties on death
1144 if ((hp == 0) != (oldhp == 0))
1145 m_properties_sent = false;
1148 u16 PlayerSAO::getBreath() const
1150 return m_player->getBreath();
1153 void PlayerSAO::setBreath(u16 breath)
1155 m_player->setBreath(breath);
1158 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1160 m_armor_groups = armor_groups;
1161 m_armor_groups_sent = false;
1164 ItemGroupList PlayerSAO::getArmorGroups()
1166 return m_armor_groups;
1169 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1171 // store these so they can be updated to clients
1172 m_animation_range = frame_range;
1173 m_animation_speed = frame_speed;
1174 m_animation_blend = frame_blend;
1175 m_animation_sent = false;
1178 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend)
1180 *frame_range = m_animation_range;
1181 *frame_speed = m_animation_speed;
1182 *frame_blend = m_animation_blend;
1185 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1187 // store these so they can be updated to clients
1188 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1189 m_bone_position_sent = false;
1192 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1194 *position = m_bone_position[bone].X;
1195 *rotation = m_bone_position[bone].Y;
1198 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1200 // Attachments need to be handled on both the server and client.
1201 // If we just attach on the server, we can only copy the position of the parent. Attachments
1202 // are still sent to clients at an interval so players might see them lagging, plus we can't
1203 // read and attach to skeletal bones.
1204 // If we just attach on the client, the server still sees the child at its original location.
1205 // This breaks some things so we also give the server the most accurate representation
1206 // even if players only see the client changes.
1208 m_attachment_parent_id = parent_id;
1209 m_attachment_bone = bone;
1210 m_attachment_position = position;
1211 m_attachment_rotation = rotation;
1212 m_attachment_sent = false;
1215 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1218 *parent_id = m_attachment_parent_id;
1219 *bone = m_attachment_bone;
1220 *position = m_attachment_position;
1221 *rotation = m_attachment_rotation;
1224 ObjectProperties* PlayerSAO::accessObjectProperties()
1229 void PlayerSAO::notifyObjectPropertiesModified()
1231 m_properties_sent = false;
1234 void PlayerSAO::setNametagColor(video::SColor color)
1236 m_nametag_color = color;
1237 m_nametag_sent = false;
1240 video::SColor PlayerSAO::getNametagColor()
1242 return m_nametag_color;
1245 Inventory* PlayerSAO::getInventory()
1249 const Inventory* PlayerSAO::getInventory() const
1254 InventoryLocation PlayerSAO::getInventoryLocation() const
1256 InventoryLocation loc;
1257 loc.setPlayer(m_player->getName());
1261 std::string PlayerSAO::getWieldList() const
1266 int PlayerSAO::getWieldIndex() const
1268 return m_wield_index;
1271 void PlayerSAO::setWieldIndex(int i)
1273 if(i != m_wield_index) {
1278 void PlayerSAO::disconnected()
1282 if(m_player->getPlayerSAO() == this)
1284 m_player->setPlayerSAO(NULL);
1285 m_player->peer_id = 0;
1289 std::string PlayerSAO::getPropertyPacket()
1291 m_prop.is_visible = (true);
1292 return gob_cmd_set_properties(m_prop);
1295 bool PlayerSAO::checkMovementCheat()
1297 bool cheated = false;
1298 if(isAttached() || m_is_singleplayer ||
1299 g_settings->getBool("disable_anticheat"))
1301 m_last_good_position = m_player->getPosition();
1306 Check player movements
1308 NOTE: Actually the server should handle player physics like the
1309 client does and compare player's position to what is calculated
1310 on our side. This is required when eg. players fly due to an
1311 explosion. Altough a node-based alternative might be possible
1312 too, and much more lightweight.
1315 float player_max_speed = 0;
1316 if(m_privs.count("fast") != 0){
1318 player_max_speed = m_player->movement_speed_fast;
1321 player_max_speed = m_player->movement_speed_walk;
1323 // Tolerance. With the lag pool we shouldn't need it.
1324 //player_max_speed *= 2.5;
1325 //player_max_speed_up *= 2.5;
1327 v3f diff = (m_player->getPosition() - m_last_good_position);
1328 float d_vert = diff.Y;
1330 float d_horiz = diff.getLength();
1331 float required_time = d_horiz/player_max_speed;
1332 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1333 required_time = d_vert/player_max_speed;
1334 if(m_move_pool.grab(required_time)){
1335 m_last_good_position = m_player->getPosition();
1337 actionstream<<"Player "<<m_player->getName()
1338 <<" moved too fast; resetting position"
1340 m_player->setPosition(m_last_good_position);
1347 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1348 //update collision box
1349 *toset = m_player->getCollisionbox();
1351 toset->MinEdge += m_base_position;
1352 toset->MaxEdge += m_base_position;
1357 bool PlayerSAO::collideWithObjects(){