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),
760 m_nametag_color(video::SColor(255, 255, 255, 255)),
761 m_nametag_sent(false),
763 m_physics_override_speed(1),
764 m_physics_override_jump(1),
765 m_physics_override_gravity(1),
766 m_physics_override_sneak(true),
767 m_physics_override_sneak_glitch(true),
768 m_physics_override_sent(false)
770 assert(m_player); // pre-condition
771 assert(m_peer_id != 0); // pre-condition
772 setBasePosition(m_player->getPosition());
773 m_inventory = &m_player->inventory;
774 m_armor_groups["fleshy"] = 100;
776 m_prop.hp_max = PLAYER_MAX_HP;
777 m_prop.physical = false;
779 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
780 // start of default appearance, this should be overwritten by LUA
781 m_prop.visual = "upright_sprite";
782 m_prop.visual_size = v2f(1, 2);
783 m_prop.textures.clear();
784 m_prop.textures.push_back("player.png");
785 m_prop.textures.push_back("player_back.png");
786 m_prop.colors.clear();
787 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
788 m_prop.spritediv = v2s16(1,1);
789 // end of default appearance
790 m_prop.is_visible = true;
791 m_prop.makes_footstep_sound = true;
794 PlayerSAO::~PlayerSAO()
796 if(m_inventory != &m_player->inventory)
801 std::string PlayerSAO::getDescription()
803 return std::string("player ") + m_player->getName();
806 // Called after id has been set and has been inserted in environment
807 void PlayerSAO::addedToEnvironment(u32 dtime_s)
809 ServerActiveObject::addedToEnvironment(dtime_s);
810 ServerActiveObject::setBasePosition(m_player->getPosition());
811 m_player->setPlayerSAO(this);
812 m_player->peer_id = m_peer_id;
813 m_last_good_position = m_player->getPosition();
816 // Called before removing from environment
817 void PlayerSAO::removingFromEnvironment()
819 ServerActiveObject::removingFromEnvironment();
820 if(m_player->getPlayerSAO() == this)
822 m_player->setPlayerSAO(NULL);
823 m_player->peer_id = 0;
824 m_env->savePlayer((RemotePlayer*)m_player);
825 m_env->removePlayer(m_player);
829 bool PlayerSAO::isStaticAllowed() const
834 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
836 std::ostringstream os(std::ios::binary);
838 if(protocol_version >= 15)
840 writeU8(os, 1); // version
841 os<<serializeString(m_player->getName()); // name
842 writeU8(os, 1); // is_player
843 writeS16(os, getId()); //id
844 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
845 writeF1000(os, m_player->getYaw());
846 writeS16(os, getHP());
848 writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
849 os<<serializeLongString(getPropertyPacket()); // message 1
850 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
851 os<<serializeLongString(gob_cmd_update_animation(
852 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
853 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
854 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
856 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
857 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
858 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
859 m_physics_override_sneak_glitch)); // 5
860 os << serializeLongString(gob_cmd_update_nametag_attributes(m_nametag_color)); // 6
864 writeU8(os, 0); // version
865 os<<serializeString(m_player->getName()); // name
866 writeU8(os, 1); // is_player
867 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
868 writeF1000(os, m_player->getYaw());
869 writeS16(os, getHP());
870 writeU8(os, 2); // number of messages stuffed in here
871 os<<serializeLongString(getPropertyPacket()); // message 1
872 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
879 std::string PlayerSAO::getStaticData()
881 FATAL_ERROR("Deprecated function (?)");
885 bool PlayerSAO::isAttached()
887 if(!m_attachment_parent_id)
889 // Check if the parent still exists
890 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
896 void PlayerSAO::step(float dtime, bool send_recommended)
898 if(!m_properties_sent)
900 m_properties_sent = true;
901 std::string str = getPropertyPacket();
902 // create message and add to list
903 ActiveObjectMessage aom(getId(), true, str);
904 m_messages_out.push(aom);
907 // If attached, check that our parent is still there. If it isn't, detach.
908 if(m_attachment_parent_id && !isAttached())
910 m_attachment_parent_id = 0;
911 m_attachment_bone = "";
912 m_attachment_position = v3f(0,0,0);
913 m_attachment_rotation = v3f(0,0,0);
914 m_player->setPosition(m_last_good_position);
915 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
918 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
920 // Set lag pool maximums based on estimated lag
921 const float LAG_POOL_MIN = 5.0;
922 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
923 if(lag_pool_max < LAG_POOL_MIN)
924 lag_pool_max = LAG_POOL_MIN;
925 m_dig_pool.setMax(lag_pool_max);
926 m_move_pool.setMax(lag_pool_max);
928 // Increment cheat prevention timers
929 m_dig_pool.add(dtime);
930 m_move_pool.add(dtime);
931 m_time_from_last_punch += dtime;
932 m_nocheat_dig_time += dtime;
934 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
935 // If the object gets detached this comes into effect automatically from the last known origin
938 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
939 m_last_good_position = pos;
940 m_player->setPosition(pos);
943 if(send_recommended == false)
946 // If the object is attached client-side, don't waste bandwidth sending its position to clients
947 if(m_position_not_sent && !isAttached())
949 m_position_not_sent = false;
950 float update_interval = m_env->getSendRecommendedInterval();
952 if(isAttached()) // Just in case we ever do send attachment position too
953 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
955 pos = m_player->getPosition() + v3f(0,BS*1,0);
956 std::string str = gob_cmd_update_position(
965 // create message and add to list
966 ActiveObjectMessage aom(getId(), false, str);
967 m_messages_out.push(aom);
970 if(m_armor_groups_sent == false) {
971 m_armor_groups_sent = true;
972 std::string str = gob_cmd_update_armor_groups(
974 // create message and add to list
975 ActiveObjectMessage aom(getId(), true, str);
976 m_messages_out.push(aom);
979 if(m_physics_override_sent == false){
980 m_physics_override_sent = true;
981 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
982 m_physics_override_jump, m_physics_override_gravity,
983 m_physics_override_sneak, m_physics_override_sneak_glitch);
984 // create message and add to list
985 ActiveObjectMessage aom(getId(), true, str);
986 m_messages_out.push(aom);
989 if(m_animation_sent == false){
990 m_animation_sent = true;
991 std::string str = gob_cmd_update_animation(
992 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
993 // create message and add to list
994 ActiveObjectMessage aom(getId(), true, str);
995 m_messages_out.push(aom);
998 if(m_bone_position_sent == false){
999 m_bone_position_sent = true;
1000 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1001 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1002 // create message and add to list
1003 ActiveObjectMessage aom(getId(), true, str);
1004 m_messages_out.push(aom);
1008 if(m_attachment_sent == false){
1009 m_attachment_sent = true;
1010 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1011 // create message and add to list
1012 ActiveObjectMessage aom(getId(), true, str);
1013 m_messages_out.push(aom);
1016 if (m_nametag_sent == false) {
1017 m_nametag_sent = true;
1018 std::string str = gob_cmd_update_nametag_attributes(m_nametag_color);
1019 // create message and add to list
1020 ActiveObjectMessage aom(getId(), true, str);
1021 m_messages_out.push(aom);
1025 void PlayerSAO::setBasePosition(const v3f &position)
1027 // This needs to be ran for attachments too
1028 ServerActiveObject::setBasePosition(position);
1029 m_position_not_sent = true;
1032 void PlayerSAO::setPos(v3f pos)
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::moveTo(v3f pos, bool continuous)
1046 m_player->setPosition(pos);
1047 // Movement caused by this command is always valid
1048 m_last_good_position = pos;
1049 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1052 void PlayerSAO::setYaw(float yaw)
1054 m_player->setYaw(yaw);
1055 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1058 void PlayerSAO::setPitch(float pitch)
1060 m_player->setPitch(pitch);
1061 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1064 int PlayerSAO::punch(v3f dir,
1065 const ToolCapabilities *toolcap,
1066 ServerActiveObject *puncher,
1067 float time_from_last_punch)
1069 // It's best that attachments cannot be punched
1076 // No effect if PvP disabled
1077 if (g_settings->getBool("enable_pvp") == false) {
1078 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1079 std::string str = gob_cmd_punched(0, getHP());
1080 // create message and add to list
1081 ActiveObjectMessage aom(getId(), true, str);
1082 m_messages_out.push(aom);
1087 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1088 time_from_last_punch);
1090 std::string punchername = "nil";
1093 punchername = puncher->getDescription();
1095 PlayerSAO *playersao = m_player->getPlayerSAO();
1097 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1098 puncher, time_from_last_punch, toolcap, dir,
1101 if (!damage_handled) {
1102 setHP(getHP() - hitparams.hp);
1103 } else { // override client prediction
1104 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1105 std::string str = gob_cmd_punched(0, getHP());
1106 // create message and add to list
1107 ActiveObjectMessage aom(getId(), true, str);
1108 m_messages_out.push(aom);
1113 actionstream << "Player " << m_player->getName() << " punched by "
1115 if (!damage_handled) {
1116 actionstream << ", damage " << hitparams.hp << " HP";
1118 actionstream << ", damage handled by lua";
1120 actionstream << std::endl;
1122 return hitparams.wear;
1125 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1129 s16 PlayerSAO::getHP() const
1131 return m_player->hp;
1134 s16 PlayerSAO::readDamage()
1136 s16 damage = m_damage;
1141 void PlayerSAO::setHP(s16 hp)
1143 s16 oldhp = m_player->hp;
1145 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this,
1149 hp = oldhp + hp_change;
1153 else if (hp > PLAYER_MAX_HP)
1156 if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1163 m_damage += (oldhp - hp);
1165 // Update properties on death
1166 if ((hp == 0) != (oldhp == 0))
1167 m_properties_sent = false;
1170 u16 PlayerSAO::getBreath() const
1172 return m_player->getBreath();
1175 void PlayerSAO::setBreath(u16 breath)
1177 m_player->setBreath(breath);
1180 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1182 m_armor_groups = armor_groups;
1183 m_armor_groups_sent = false;
1186 ItemGroupList PlayerSAO::getArmorGroups()
1188 return m_armor_groups;
1191 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1193 // store these so they can be updated to clients
1194 m_animation_range = frame_range;
1195 m_animation_speed = frame_speed;
1196 m_animation_blend = frame_blend;
1197 m_animation_loop = frame_loop;
1198 m_animation_sent = false;
1201 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1203 *frame_range = m_animation_range;
1204 *frame_speed = m_animation_speed;
1205 *frame_blend = m_animation_blend;
1206 *frame_loop = m_animation_loop;
1209 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1211 // store these so they can be updated to clients
1212 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1213 m_bone_position_sent = false;
1216 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1218 *position = m_bone_position[bone].X;
1219 *rotation = m_bone_position[bone].Y;
1222 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1224 // Attachments need to be handled on both the server and client.
1225 // If we just attach on the server, we can only copy the position of the parent. Attachments
1226 // are still sent to clients at an interval so players might see them lagging, plus we can't
1227 // read and attach to skeletal bones.
1228 // If we just attach on the client, the server still sees the child at its original location.
1229 // This breaks some things so we also give the server the most accurate representation
1230 // even if players only see the client changes.
1232 m_attachment_parent_id = parent_id;
1233 m_attachment_bone = bone;
1234 m_attachment_position = position;
1235 m_attachment_rotation = rotation;
1236 m_attachment_sent = false;
1239 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1242 *parent_id = m_attachment_parent_id;
1243 *bone = m_attachment_bone;
1244 *position = m_attachment_position;
1245 *rotation = m_attachment_rotation;
1248 void PlayerSAO::addAttachmentChild(int child_id)
1250 m_attachment_child_ids.insert(child_id);
1253 void PlayerSAO::removeAttachmentChild(int child_id)
1255 m_attachment_child_ids.erase(child_id);
1258 std::set<int> PlayerSAO::getAttachmentChildIds()
1260 return m_attachment_child_ids;
1263 ObjectProperties* PlayerSAO::accessObjectProperties()
1268 void PlayerSAO::notifyObjectPropertiesModified()
1270 m_properties_sent = false;
1273 void PlayerSAO::setNametagColor(video::SColor color)
1275 m_nametag_color = color;
1276 m_nametag_sent = false;
1279 video::SColor PlayerSAO::getNametagColor()
1281 return m_nametag_color;
1284 Inventory* PlayerSAO::getInventory()
1288 const Inventory* PlayerSAO::getInventory() const
1293 InventoryLocation PlayerSAO::getInventoryLocation() const
1295 InventoryLocation loc;
1296 loc.setPlayer(m_player->getName());
1300 std::string PlayerSAO::getWieldList() const
1305 int PlayerSAO::getWieldIndex() const
1307 return m_wield_index;
1310 void PlayerSAO::setWieldIndex(int i)
1312 if(i != m_wield_index) {
1317 void PlayerSAO::disconnected()
1321 if(m_player->getPlayerSAO() == this)
1323 m_player->setPlayerSAO(NULL);
1324 m_player->peer_id = 0;
1328 std::string PlayerSAO::getPropertyPacket()
1330 m_prop.is_visible = (true);
1331 return gob_cmd_set_properties(m_prop);
1334 bool PlayerSAO::checkMovementCheat()
1336 bool cheated = false;
1337 if(isAttached() || m_is_singleplayer ||
1338 g_settings->getBool("disable_anticheat"))
1340 m_last_good_position = m_player->getPosition();
1345 Check player movements
1347 NOTE: Actually the server should handle player physics like the
1348 client does and compare player's position to what is calculated
1349 on our side. This is required when eg. players fly due to an
1350 explosion. Altough a node-based alternative might be possible
1351 too, and much more lightweight.
1354 float player_max_speed = 0;
1355 if(m_privs.count("fast") != 0){
1357 player_max_speed = m_player->movement_speed_fast;
1360 player_max_speed = m_player->movement_speed_walk;
1362 // Tolerance. With the lag pool we shouldn't need it.
1363 //player_max_speed *= 2.5;
1364 //player_max_speed_up *= 2.5;
1366 v3f diff = (m_player->getPosition() - m_last_good_position);
1367 float d_vert = diff.Y;
1369 float d_horiz = diff.getLength();
1370 float required_time = d_horiz/player_max_speed;
1371 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1372 required_time = d_vert/player_max_speed;
1373 if(m_move_pool.grab(required_time)){
1374 m_last_good_position = m_player->getPosition();
1376 actionstream<<"Player "<<m_player->getName()
1377 <<" moved too fast; resetting position"
1379 m_player->setPosition(m_last_good_position);
1386 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1387 //update collision box
1388 *toset = m_player->getCollisionbox();
1390 toset->MinEdge += m_base_position;
1391 toset->MaxEdge += m_base_position;
1396 bool PlayerSAO::collideWithObjects(){