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 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(
328 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
329 // create message and add to list
330 ActiveObjectMessage aom(getId(), true, str);
331 m_messages_out.push(aom);
334 if(m_bone_position_sent == false){
335 m_bone_position_sent = true;
336 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
337 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
338 // create message and add to list
339 ActiveObjectMessage aom(getId(), true, str);
340 m_messages_out.push(aom);
344 if(m_attachment_sent == false){
345 m_attachment_sent = true;
346 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
347 // create message and add to list
348 ActiveObjectMessage aom(getId(), true, str);
349 m_messages_out.push(aom);
353 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
355 std::ostringstream os(std::ios::binary);
357 if(protocol_version >= 14)
359 writeU8(os, 1); // version
360 os<<serializeString(""); // name
361 writeU8(os, 0); // is_player
362 writeS16(os, getId()); //id
363 writeV3F1000(os, m_base_position);
364 writeF1000(os, m_yaw);
367 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
368 os<<serializeLongString(getPropertyPacket()); // message 1
369 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
370 os<<serializeLongString(gob_cmd_update_animation(
371 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
372 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
373 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
375 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
379 writeU8(os, 0); // version
380 os<<serializeString(""); // name
381 writeU8(os, 0); // is_player
382 writeV3F1000(os, m_base_position);
383 writeF1000(os, m_yaw);
385 writeU8(os, 2); // number of messages stuffed in here
386 os<<serializeLongString(getPropertyPacket()); // message 1
387 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
394 std::string LuaEntitySAO::getStaticData()
396 verbosestream<<FUNCTION_NAME<<std::endl;
397 std::ostringstream os(std::ios::binary);
401 os<<serializeString(m_init_name);
404 std::string state = m_env->getScriptIface()->
405 luaentity_GetStaticdata(m_id);
406 os<<serializeLongString(state);
408 os<<serializeLongString(m_init_state);
413 writeV3F1000(os, m_velocity);
415 writeF1000(os, m_yaw);
419 int LuaEntitySAO::punch(v3f dir,
420 const ToolCapabilities *toolcap,
421 ServerActiveObject *puncher,
422 float time_from_last_punch)
425 // Delete unknown LuaEntities when punched
430 // It's best that attachments cannot be punched
434 ItemStack *punchitem = NULL;
435 ItemStack punchitem_static;
437 punchitem_static = puncher->getWieldedItem();
438 punchitem = &punchitem_static;
441 PunchDamageResult result = getPunchDamage(
445 time_from_last_punch);
447 if (result.did_punch) {
448 setHP(getHP() - result.damage);
450 if (result.damage > 0) {
451 std::string punchername = puncher ? puncher->getDescription() : "nil";
453 actionstream << getDescription() << " punched by "
454 << punchername << ", damage " << result.damage
455 << " hp, health now " << getHP() << " hp" << std::endl;
458 std::string str = gob_cmd_punched(result.damage, getHP());
459 // create message and add to list
460 ActiveObjectMessage aom(getId(), true, str);
461 m_messages_out.push(aom);
467 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
468 time_from_last_punch, toolcap, dir);
473 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
477 // It's best that attachments cannot be clicked
480 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
483 void LuaEntitySAO::setPos(v3f pos)
487 m_base_position = pos;
488 sendPosition(false, true);
491 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
495 m_base_position = pos;
497 sendPosition(true, true);
500 float LuaEntitySAO::getMinimumSavedMovement()
505 std::string LuaEntitySAO::getDescription()
507 std::ostringstream os(std::ios::binary);
508 os<<"LuaEntitySAO at (";
509 os<<(m_base_position.X/BS)<<",";
510 os<<(m_base_position.Y/BS)<<",";
511 os<<(m_base_position.Z/BS);
516 void LuaEntitySAO::setHP(s16 hp)
522 s16 LuaEntitySAO::getHP() const
527 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
529 m_armor_groups = armor_groups;
530 m_armor_groups_sent = false;
533 ItemGroupList LuaEntitySAO::getArmorGroups()
535 return m_armor_groups;
538 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
540 m_animation_range = frame_range;
541 m_animation_speed = frame_speed;
542 m_animation_blend = frame_blend;
543 m_animation_loop = frame_loop;
544 m_animation_sent = false;
547 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
549 *frame_range = m_animation_range;
550 *frame_speed = m_animation_speed;
551 *frame_blend = m_animation_blend;
552 *frame_loop = m_animation_loop;
555 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
557 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
558 m_bone_position_sent = false;
561 void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
563 *position = m_bone_position[bone].X;
564 *rotation = m_bone_position[bone].Y;
567 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
569 // Attachments need to be handled on both the server and client.
570 // If we just attach on the server, we can only copy the position of the parent. Attachments
571 // are still sent to clients at an interval so players might see them lagging, plus we can't
572 // read and attach to skeletal bones.
573 // If we just attach on the client, the server still sees the child at its original location.
574 // This breaks some things so we also give the server the most accurate representation
575 // even if players only see the client changes.
577 m_attachment_parent_id = parent_id;
578 m_attachment_bone = bone;
579 m_attachment_position = position;
580 m_attachment_rotation = rotation;
581 m_attachment_sent = false;
584 void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
587 *parent_id = m_attachment_parent_id;
588 *bone = m_attachment_bone;
589 *position = m_attachment_position;
590 *rotation = m_attachment_rotation;
593 void LuaEntitySAO::addAttachmentChild(int child_id)
595 m_attachment_child_ids.insert(child_id);
598 void LuaEntitySAO::removeAttachmentChild(int child_id)
600 m_attachment_child_ids.erase(child_id);
603 std::set<int> LuaEntitySAO::getAttachmentChildIds()
605 return m_attachment_child_ids;
608 ObjectProperties* LuaEntitySAO::accessObjectProperties()
613 void LuaEntitySAO::notifyObjectPropertiesModified()
615 m_properties_sent = false;
618 void LuaEntitySAO::setVelocity(v3f velocity)
620 m_velocity = velocity;
623 v3f LuaEntitySAO::getVelocity()
628 void LuaEntitySAO::setAcceleration(v3f acceleration)
630 m_acceleration = acceleration;
633 v3f LuaEntitySAO::getAcceleration()
635 return m_acceleration;
638 void LuaEntitySAO::setYaw(float yaw)
643 float LuaEntitySAO::getYaw()
648 void LuaEntitySAO::setTextureMod(const std::string &mod)
650 std::string str = gob_cmd_set_texture_mod(mod);
651 // create message and add to list
652 ActiveObjectMessage aom(getId(), true, str);
653 m_messages_out.push(aom);
656 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
657 bool select_horiz_by_yawpitch)
659 std::string str = gob_cmd_set_sprite(
663 select_horiz_by_yawpitch
665 // create message and add to list
666 ActiveObjectMessage aom(getId(), true, str);
667 m_messages_out.push(aom);
670 std::string LuaEntitySAO::getName()
675 std::string LuaEntitySAO::getPropertyPacket()
677 return gob_cmd_set_properties(m_prop);
680 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
682 // If the object is attached client-side, don't waste bandwidth sending its position to clients
686 m_last_sent_move_precision = m_base_position.getDistanceFrom(
687 m_last_sent_position);
688 m_last_sent_position_timer = 0;
689 m_last_sent_yaw = m_yaw;
690 m_last_sent_position = m_base_position;
691 m_last_sent_velocity = m_velocity;
692 //m_last_sent_acceleration = m_acceleration;
694 float update_interval = m_env->getSendRecommendedInterval();
696 std::string str = gob_cmd_update_position(
705 // create message and add to list
706 ActiveObjectMessage aom(getId(), false, str);
707 m_messages_out.push(aom);
710 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
713 //update collision box
714 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
715 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
717 toset->MinEdge += m_base_position;
718 toset->MaxEdge += m_base_position;
726 bool LuaEntitySAO::collideWithObjects(){
727 return m_prop.collideWithObjects;
734 // No prototype, PlayerSAO does not need to be deserialized
736 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
737 const std::set<std::string> &privs, bool is_singleplayer):
738 ServerActiveObject(env_, v3f(0,0,0)),
743 m_last_good_position(0,0,0),
744 m_time_from_last_punch(0),
745 m_nocheat_dig_pos(32767, 32767, 32767),
746 m_nocheat_dig_time(0),
748 m_position_not_sent(false),
749 m_armor_groups_sent(false),
750 m_properties_sent(true),
752 m_is_singleplayer(is_singleplayer),
753 m_animation_speed(0),
754 m_animation_blend(0),
755 m_animation_loop(true),
756 m_animation_sent(false),
757 m_bone_position_sent(false),
758 m_attachment_parent_id(0),
759 m_attachment_sent(false),
761 m_physics_override_speed(1),
762 m_physics_override_jump(1),
763 m_physics_override_gravity(1),
764 m_physics_override_sneak(true),
765 m_physics_override_sneak_glitch(true),
766 m_physics_override_sent(false)
768 assert(m_player); // pre-condition
769 assert(m_peer_id != 0); // pre-condition
770 setBasePosition(m_player->getPosition());
771 m_inventory = &m_player->inventory;
772 m_armor_groups["fleshy"] = 100;
774 m_prop.hp_max = PLAYER_MAX_HP;
775 m_prop.physical = false;
777 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
778 // start of default appearance, this should be overwritten by LUA
779 m_prop.visual = "upright_sprite";
780 m_prop.visual_size = v2f(1, 2);
781 m_prop.textures.clear();
782 m_prop.textures.push_back("player.png");
783 m_prop.textures.push_back("player_back.png");
784 m_prop.colors.clear();
785 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
786 m_prop.spritediv = v2s16(1,1);
787 // end of default appearance
788 m_prop.is_visible = true;
789 m_prop.makes_footstep_sound = true;
792 PlayerSAO::~PlayerSAO()
794 if(m_inventory != &m_player->inventory)
799 std::string PlayerSAO::getDescription()
801 return std::string("player ") + m_player->getName();
804 // Called after id has been set and has been inserted in environment
805 void PlayerSAO::addedToEnvironment(u32 dtime_s)
807 ServerActiveObject::addedToEnvironment(dtime_s);
808 ServerActiveObject::setBasePosition(m_player->getPosition());
809 m_player->setPlayerSAO(this);
810 m_player->peer_id = m_peer_id;
811 m_last_good_position = m_player->getPosition();
814 // Called before removing from environment
815 void PlayerSAO::removingFromEnvironment()
817 ServerActiveObject::removingFromEnvironment();
818 if(m_player->getPlayerSAO() == this)
820 m_player->setPlayerSAO(NULL);
821 m_player->peer_id = 0;
822 m_env->savePlayer((RemotePlayer*)m_player);
823 m_env->removePlayer(m_player);
827 bool PlayerSAO::isStaticAllowed() const
832 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
834 std::ostringstream os(std::ios::binary);
836 if(protocol_version >= 15)
838 writeU8(os, 1); // version
839 os<<serializeString(m_player->getName()); // name
840 writeU8(os, 1); // is_player
841 writeS16(os, getId()); //id
842 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
843 writeF1000(os, m_player->getYaw());
844 writeS16(os, getHP());
846 writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
847 os<<serializeLongString(getPropertyPacket()); // message 1
848 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
849 os<<serializeLongString(gob_cmd_update_animation(
850 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
851 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
852 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
854 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
855 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
856 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
857 m_physics_override_sneak_glitch)); // 5
858 os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
862 writeU8(os, 0); // version
863 os<<serializeString(m_player->getName()); // name
864 writeU8(os, 1); // is_player
865 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
866 writeF1000(os, m_player->getYaw());
867 writeS16(os, getHP());
868 writeU8(os, 2); // number of messages stuffed in here
869 os<<serializeLongString(getPropertyPacket()); // message 1
870 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
877 std::string PlayerSAO::getStaticData()
879 FATAL_ERROR("Deprecated function (?)");
883 bool PlayerSAO::isAttached()
885 if(!m_attachment_parent_id)
887 // Check if the parent still exists
888 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
894 void PlayerSAO::step(float dtime, bool send_recommended)
896 if(!m_properties_sent)
898 m_properties_sent = true;
899 std::string str = getPropertyPacket();
900 // create message and add to list
901 ActiveObjectMessage aom(getId(), true, str);
902 m_messages_out.push(aom);
905 // If attached, check that our parent is still there. If it isn't, detach.
906 if(m_attachment_parent_id && !isAttached())
908 m_attachment_parent_id = 0;
909 m_attachment_bone = "";
910 m_attachment_position = v3f(0,0,0);
911 m_attachment_rotation = v3f(0,0,0);
912 m_player->setPosition(m_last_good_position);
913 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
916 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
918 // Set lag pool maximums based on estimated lag
919 const float LAG_POOL_MIN = 5.0;
920 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
921 if(lag_pool_max < LAG_POOL_MIN)
922 lag_pool_max = LAG_POOL_MIN;
923 m_dig_pool.setMax(lag_pool_max);
924 m_move_pool.setMax(lag_pool_max);
926 // Increment cheat prevention timers
927 m_dig_pool.add(dtime);
928 m_move_pool.add(dtime);
929 m_time_from_last_punch += dtime;
930 m_nocheat_dig_time += dtime;
932 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
933 // If the object gets detached this comes into effect automatically from the last known origin
936 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
937 m_last_good_position = pos;
938 m_player->setPosition(pos);
941 if(send_recommended == false)
944 // If the object is attached client-side, don't waste bandwidth sending its position to clients
945 if(m_position_not_sent && !isAttached())
947 m_position_not_sent = false;
948 float update_interval = m_env->getSendRecommendedInterval();
950 if(isAttached()) // Just in case we ever do send attachment position too
951 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
953 pos = m_player->getPosition() + v3f(0,BS*1,0);
954 std::string str = gob_cmd_update_position(
963 // create message and add to list
964 ActiveObjectMessage aom(getId(), false, str);
965 m_messages_out.push(aom);
968 if(m_armor_groups_sent == false) {
969 m_armor_groups_sent = true;
970 std::string str = gob_cmd_update_armor_groups(
972 // create message and add to list
973 ActiveObjectMessage aom(getId(), true, str);
974 m_messages_out.push(aom);
977 if(m_physics_override_sent == false){
978 m_physics_override_sent = true;
979 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
980 m_physics_override_jump, m_physics_override_gravity,
981 m_physics_override_sneak, m_physics_override_sneak_glitch);
982 // create message and add to list
983 ActiveObjectMessage aom(getId(), true, str);
984 m_messages_out.push(aom);
987 if(m_animation_sent == false){
988 m_animation_sent = true;
989 std::string str = gob_cmd_update_animation(
990 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
991 // create message and add to list
992 ActiveObjectMessage aom(getId(), true, str);
993 m_messages_out.push(aom);
996 if(m_bone_position_sent == false){
997 m_bone_position_sent = true;
998 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
999 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1000 // create message and add to list
1001 ActiveObjectMessage aom(getId(), true, str);
1002 m_messages_out.push(aom);
1006 if(m_attachment_sent == false){
1007 m_attachment_sent = true;
1008 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1009 // create message and add to list
1010 ActiveObjectMessage aom(getId(), true, str);
1011 m_messages_out.push(aom);
1015 void PlayerSAO::setBasePosition(const v3f &position)
1017 // This needs to be ran for attachments too
1018 ServerActiveObject::setBasePosition(position);
1019 m_position_not_sent = true;
1022 void PlayerSAO::setPos(v3f pos)
1026 m_player->setPosition(pos);
1027 // Movement caused by this command is always valid
1028 m_last_good_position = pos;
1029 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1032 void PlayerSAO::moveTo(v3f pos, bool continuous)
1036 m_player->setPosition(pos);
1037 // Movement caused by this command is always valid
1038 m_last_good_position = pos;
1039 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1042 void PlayerSAO::setYaw(float yaw)
1044 m_player->setYaw(yaw);
1045 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1048 void PlayerSAO::setPitch(float pitch)
1050 m_player->setPitch(pitch);
1051 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1054 int PlayerSAO::punch(v3f dir,
1055 const ToolCapabilities *toolcap,
1056 ServerActiveObject *puncher,
1057 float time_from_last_punch)
1059 // It's best that attachments cannot be punched
1066 // No effect if PvP disabled
1067 if (g_settings->getBool("enable_pvp") == false) {
1068 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1069 std::string str = gob_cmd_punched(0, getHP());
1070 // create message and add to list
1071 ActiveObjectMessage aom(getId(), true, str);
1072 m_messages_out.push(aom);
1077 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1078 time_from_last_punch);
1080 std::string punchername = "nil";
1083 punchername = puncher->getDescription();
1085 PlayerSAO *playersao = m_player->getPlayerSAO();
1087 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1088 puncher, time_from_last_punch, toolcap, dir,
1091 if (!damage_handled) {
1092 setHP(getHP() - hitparams.hp);
1093 } else { // override client prediction
1094 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1095 std::string str = gob_cmd_punched(0, getHP());
1096 // create message and add to list
1097 ActiveObjectMessage aom(getId(), true, str);
1098 m_messages_out.push(aom);
1103 actionstream << "Player " << m_player->getName() << " punched by "
1105 if (!damage_handled) {
1106 actionstream << ", damage " << hitparams.hp << " HP";
1108 actionstream << ", damage handled by lua";
1110 actionstream << std::endl;
1112 return hitparams.wear;
1115 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1119 s16 PlayerSAO::getHP() const
1121 return m_player->hp;
1124 s16 PlayerSAO::readDamage()
1126 s16 damage = m_damage;
1131 void PlayerSAO::setHP(s16 hp)
1133 s16 oldhp = m_player->hp;
1135 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this,
1139 hp = oldhp + hp_change;
1143 else if (hp > PLAYER_MAX_HP)
1146 if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1153 m_damage += (oldhp - hp);
1155 // Update properties on death
1156 if ((hp == 0) != (oldhp == 0))
1157 m_properties_sent = false;
1160 u16 PlayerSAO::getBreath() const
1162 return m_player->getBreath();
1165 void PlayerSAO::setBreath(u16 breath)
1167 m_player->setBreath(breath);
1170 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1172 m_armor_groups = armor_groups;
1173 m_armor_groups_sent = false;
1176 ItemGroupList PlayerSAO::getArmorGroups()
1178 return m_armor_groups;
1181 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1183 // store these so they can be updated to clients
1184 m_animation_range = frame_range;
1185 m_animation_speed = frame_speed;
1186 m_animation_blend = frame_blend;
1187 m_animation_loop = frame_loop;
1188 m_animation_sent = false;
1191 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1193 *frame_range = m_animation_range;
1194 *frame_speed = m_animation_speed;
1195 *frame_blend = m_animation_blend;
1196 *frame_loop = m_animation_loop;
1199 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1201 // store these so they can be updated to clients
1202 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1203 m_bone_position_sent = false;
1206 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1208 *position = m_bone_position[bone].X;
1209 *rotation = m_bone_position[bone].Y;
1212 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1214 // Attachments need to be handled on both the server and client.
1215 // If we just attach on the server, we can only copy the position of the parent. Attachments
1216 // are still sent to clients at an interval so players might see them lagging, plus we can't
1217 // read and attach to skeletal bones.
1218 // If we just attach on the client, the server still sees the child at its original location.
1219 // This breaks some things so we also give the server the most accurate representation
1220 // even if players only see the client changes.
1222 m_attachment_parent_id = parent_id;
1223 m_attachment_bone = bone;
1224 m_attachment_position = position;
1225 m_attachment_rotation = rotation;
1226 m_attachment_sent = false;
1229 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1232 *parent_id = m_attachment_parent_id;
1233 *bone = m_attachment_bone;
1234 *position = m_attachment_position;
1235 *rotation = m_attachment_rotation;
1238 void PlayerSAO::addAttachmentChild(int child_id)
1240 m_attachment_child_ids.insert(child_id);
1243 void PlayerSAO::removeAttachmentChild(int child_id)
1245 m_attachment_child_ids.erase(child_id);
1248 std::set<int> PlayerSAO::getAttachmentChildIds()
1250 return m_attachment_child_ids;
1253 ObjectProperties* PlayerSAO::accessObjectProperties()
1258 void PlayerSAO::notifyObjectPropertiesModified()
1260 m_properties_sent = false;
1263 Inventory* PlayerSAO::getInventory()
1267 const Inventory* PlayerSAO::getInventory() const
1272 InventoryLocation PlayerSAO::getInventoryLocation() const
1274 InventoryLocation loc;
1275 loc.setPlayer(m_player->getName());
1279 std::string PlayerSAO::getWieldList() const
1284 int PlayerSAO::getWieldIndex() const
1286 return m_wield_index;
1289 void PlayerSAO::setWieldIndex(int i)
1291 if(i != m_wield_index) {
1296 void PlayerSAO::disconnected()
1300 if(m_player->getPlayerSAO() == this)
1302 m_player->setPlayerSAO(NULL);
1303 m_player->peer_id = 0;
1307 std::string PlayerSAO::getPropertyPacket()
1309 m_prop.is_visible = (true);
1310 return gob_cmd_set_properties(m_prop);
1313 bool PlayerSAO::checkMovementCheat()
1315 bool cheated = false;
1316 if(isAttached() || m_is_singleplayer ||
1317 g_settings->getBool("disable_anticheat"))
1319 m_last_good_position = m_player->getPosition();
1324 Check player movements
1326 NOTE: Actually the server should handle player physics like the
1327 client does and compare player's position to what is calculated
1328 on our side. This is required when eg. players fly due to an
1329 explosion. Altough a node-based alternative might be possible
1330 too, and much more lightweight.
1333 float player_max_speed = 0;
1334 if(m_privs.count("fast") != 0){
1336 player_max_speed = m_player->movement_speed_fast;
1339 player_max_speed = m_player->movement_speed_walk;
1341 // Tolerance. With the lag pool we shouldn't need it.
1342 //player_max_speed *= 2.5;
1343 //player_max_speed_up *= 2.5;
1345 v3f diff = (m_player->getPosition() - m_last_good_position);
1346 float d_vert = diff.Y;
1348 float d_horiz = diff.getLength();
1349 float required_time = d_horiz/player_max_speed;
1350 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1351 required_time = d_vert/player_max_speed;
1352 if(m_move_pool.grab(required_time)){
1353 m_last_good_position = m_player->getPosition();
1355 actionstream<<"Player "<<m_player->getName()
1356 <<" moved too fast; resetting position"
1358 m_player->setPosition(m_last_good_position);
1365 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1366 //update collision box
1367 *toset = m_player->getCollisionbox();
1369 toset->MinEdge += m_base_position;
1370 toset->MaxEdge += m_base_position;
1375 bool PlayerSAO::collideWithObjects(){