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_loop(true),
140 m_animation_sent(false),
141 m_bone_position_sent(false),
142 m_attachment_parent_id(0),
143 m_attachment_sent(false)
145 // Only register type if no environment supplied
147 ServerActiveObject::registerType(getType(), create);
151 // Initialize something to armor groups
152 m_armor_groups["fleshy"] = 100;
155 LuaEntitySAO::~LuaEntitySAO()
158 m_env->getScriptIface()->luaentity_Remove(m_id);
162 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
164 ServerActiveObject::addedToEnvironment(dtime_s);
166 // Create entity from name
167 m_registered = m_env->getScriptIface()->
168 luaentity_Add(m_id, m_init_name.c_str());
172 m_env->getScriptIface()->
173 luaentity_GetProperties(m_id, &m_prop);
174 // Initialize HP from properties
175 m_hp = m_prop.hp_max;
176 // Activate entity, supplying serialized state
177 m_env->getScriptIface()->
178 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
182 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
183 const std::string &data)
191 std::istringstream is(data, std::ios::binary);
193 u8 version = readU8(is);
194 // check if version is supported
196 name = deSerializeString(is);
197 state = deSerializeLongString(is);
199 else if(version == 1){
200 name = deSerializeString(is);
201 state = deSerializeLongString(is);
203 velocity = readV3F1000(is);
208 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
209 <<state<<"\")"<<std::endl;
210 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
212 sao->m_velocity = velocity;
217 bool LuaEntitySAO::isAttached()
219 if(!m_attachment_parent_id)
221 // Check if the parent still exists
222 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
228 void LuaEntitySAO::step(float dtime, bool send_recommended)
230 if(!m_properties_sent)
232 m_properties_sent = true;
233 std::string str = getPropertyPacket();
234 // create message and add to list
235 ActiveObjectMessage aom(getId(), true, str);
236 m_messages_out.push(aom);
239 // If attached, check that our parent is still there. If it isn't, detach.
240 if(m_attachment_parent_id && !isAttached())
242 m_attachment_parent_id = 0;
243 m_attachment_bone = "";
244 m_attachment_position = v3f(0,0,0);
245 m_attachment_rotation = v3f(0,0,0);
246 sendPosition(false, true);
249 m_last_sent_position_timer += dtime;
251 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
252 // If the object gets detached this comes into effect automatically from the last known origin
255 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
256 m_base_position = pos;
257 m_velocity = v3f(0,0,0);
258 m_acceleration = v3f(0,0,0);
263 core::aabbox3d<f32> box = m_prop.collisionbox;
266 collisionMoveResult moveresult;
267 f32 pos_max_d = BS*0.25; // Distance per iteration
268 v3f p_pos = m_base_position;
269 v3f p_velocity = m_velocity;
270 v3f p_acceleration = m_acceleration;
271 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
272 pos_max_d, box, m_prop.stepheight, dtime,
273 p_pos, p_velocity, p_acceleration,
274 this, m_prop.collideWithObjects);
277 m_base_position = p_pos;
278 m_velocity = p_velocity;
279 m_acceleration = p_acceleration;
281 m_base_position += dtime * m_velocity + 0.5 * dtime
282 * dtime * m_acceleration;
283 m_velocity += dtime * m_acceleration;
286 if((m_prop.automatic_face_movement_dir) &&
287 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)){
288 m_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI + m_prop.automatic_face_movement_dir_offset;
293 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
296 if(send_recommended == false)
301 // TODO: force send when acceleration changes enough?
302 float minchange = 0.2*BS;
303 if(m_last_sent_position_timer > 1.0){
305 } else if(m_last_sent_position_timer > 0.2){
308 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
309 move_d += m_last_sent_move_precision;
310 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
311 if(move_d > minchange || vel_d > minchange ||
312 fabs(m_yaw - m_last_sent_yaw) > 1.0){
313 sendPosition(true, false);
317 if(m_armor_groups_sent == false){
318 m_armor_groups_sent = true;
319 std::string str = gob_cmd_update_armor_groups(
321 // create message and add to list
322 ActiveObjectMessage aom(getId(), true, str);
323 m_messages_out.push(aom);
326 if(m_animation_sent == false){
327 m_animation_sent = true;
328 std::string str = gob_cmd_update_animation(
329 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
330 // create message and add to list
331 ActiveObjectMessage aom(getId(), true, str);
332 m_messages_out.push(aom);
335 if(m_bone_position_sent == false){
336 m_bone_position_sent = true;
337 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
338 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
339 // create message and add to list
340 ActiveObjectMessage aom(getId(), true, str);
341 m_messages_out.push(aom);
345 if(m_attachment_sent == false){
346 m_attachment_sent = true;
347 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
348 // create message and add to list
349 ActiveObjectMessage aom(getId(), true, str);
350 m_messages_out.push(aom);
354 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
356 std::ostringstream os(std::ios::binary);
358 if(protocol_version >= 14)
360 writeU8(os, 1); // version
361 os<<serializeString(""); // name
362 writeU8(os, 0); // is_player
363 writeS16(os, getId()); //id
364 writeV3F1000(os, m_base_position);
365 writeF1000(os, m_yaw);
368 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
369 os<<serializeLongString(getPropertyPacket()); // message 1
370 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
371 os<<serializeLongString(gob_cmd_update_animation(
372 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
373 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
374 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
376 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
380 writeU8(os, 0); // version
381 os<<serializeString(""); // name
382 writeU8(os, 0); // is_player
383 writeV3F1000(os, m_base_position);
384 writeF1000(os, m_yaw);
386 writeU8(os, 2); // number of messages stuffed in here
387 os<<serializeLongString(getPropertyPacket()); // message 1
388 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
395 std::string LuaEntitySAO::getStaticData()
397 verbosestream<<__FUNCTION_NAME<<std::endl;
398 std::ostringstream os(std::ios::binary);
402 os<<serializeString(m_init_name);
405 std::string state = m_env->getScriptIface()->
406 luaentity_GetStaticdata(m_id);
407 os<<serializeLongString(state);
409 os<<serializeLongString(m_init_state);
414 writeV3F1000(os, m_velocity);
416 writeF1000(os, m_yaw);
420 int LuaEntitySAO::punch(v3f dir,
421 const ToolCapabilities *toolcap,
422 ServerActiveObject *puncher,
423 float time_from_last_punch)
426 // Delete unknown LuaEntities when punched
431 // It's best that attachments cannot be punched
435 ItemStack *punchitem = NULL;
436 ItemStack punchitem_static;
438 punchitem_static = puncher->getWieldedItem();
439 punchitem = &punchitem_static;
442 PunchDamageResult result = getPunchDamage(
446 time_from_last_punch);
448 if (result.did_punch) {
449 setHP(getHP() - result.damage);
451 if (result.damage > 0) {
452 std::string punchername = puncher ? puncher->getDescription() : "nil";
454 actionstream << getDescription() << " punched by "
455 << punchername << ", damage " << result.damage
456 << " hp, health now " << getHP() << " hp" << std::endl;
459 std::string str = gob_cmd_punched(result.damage, getHP());
460 // create message and add to list
461 ActiveObjectMessage aom(getId(), true, str);
462 m_messages_out.push(aom);
468 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
469 time_from_last_punch, toolcap, dir);
474 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
478 // It's best that attachments cannot be clicked
481 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
484 void LuaEntitySAO::setPos(v3f pos)
488 m_base_position = pos;
489 sendPosition(false, true);
492 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
496 m_base_position = pos;
498 sendPosition(true, true);
501 float LuaEntitySAO::getMinimumSavedMovement()
506 std::string LuaEntitySAO::getDescription()
508 std::ostringstream os(std::ios::binary);
509 os<<"LuaEntitySAO at (";
510 os<<(m_base_position.X/BS)<<",";
511 os<<(m_base_position.Y/BS)<<",";
512 os<<(m_base_position.Z/BS);
517 void LuaEntitySAO::setHP(s16 hp)
523 s16 LuaEntitySAO::getHP() const
528 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
530 m_armor_groups = armor_groups;
531 m_armor_groups_sent = false;
534 ItemGroupList LuaEntitySAO::getArmorGroups()
536 return m_armor_groups;
539 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
541 m_animation_range = frame_range;
542 m_animation_speed = frame_speed;
543 m_animation_blend = frame_blend;
544 m_animation_loop = frame_loop;
545 m_animation_sent = false;
548 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
550 *frame_range = m_animation_range;
551 *frame_speed = m_animation_speed;
552 *frame_blend = m_animation_blend;
553 *frame_loop = m_animation_loop;
556 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
558 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
559 m_bone_position_sent = false;
562 void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
564 *position = m_bone_position[bone].X;
565 *rotation = m_bone_position[bone].Y;
568 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
570 // Attachments need to be handled on both the server and client.
571 // If we just attach on the server, we can only copy the position of the parent. Attachments
572 // are still sent to clients at an interval so players might see them lagging, plus we can't
573 // read and attach to skeletal bones.
574 // If we just attach on the client, the server still sees the child at its original location.
575 // This breaks some things so we also give the server the most accurate representation
576 // even if players only see the client changes.
578 m_attachment_parent_id = parent_id;
579 m_attachment_bone = bone;
580 m_attachment_position = position;
581 m_attachment_rotation = rotation;
582 m_attachment_sent = false;
585 void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
588 *parent_id = m_attachment_parent_id;
589 *bone = m_attachment_bone;
590 *position = m_attachment_position;
591 *rotation = m_attachment_rotation;
594 ObjectProperties* LuaEntitySAO::accessObjectProperties()
599 void LuaEntitySAO::notifyObjectPropertiesModified()
601 m_properties_sent = false;
604 void LuaEntitySAO::setVelocity(v3f velocity)
606 m_velocity = velocity;
609 v3f LuaEntitySAO::getVelocity()
614 void LuaEntitySAO::setAcceleration(v3f acceleration)
616 m_acceleration = acceleration;
619 v3f LuaEntitySAO::getAcceleration()
621 return m_acceleration;
624 void LuaEntitySAO::setYaw(float yaw)
629 float LuaEntitySAO::getYaw()
634 void LuaEntitySAO::setTextureMod(const std::string &mod)
636 std::string str = gob_cmd_set_texture_mod(mod);
637 // create message and add to list
638 ActiveObjectMessage aom(getId(), true, str);
639 m_messages_out.push(aom);
642 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
643 bool select_horiz_by_yawpitch)
645 std::string str = gob_cmd_set_sprite(
649 select_horiz_by_yawpitch
651 // create message and add to list
652 ActiveObjectMessage aom(getId(), true, str);
653 m_messages_out.push(aom);
656 std::string LuaEntitySAO::getName()
661 std::string LuaEntitySAO::getPropertyPacket()
663 return gob_cmd_set_properties(m_prop);
666 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
668 // If the object is attached client-side, don't waste bandwidth sending its position to clients
672 m_last_sent_move_precision = m_base_position.getDistanceFrom(
673 m_last_sent_position);
674 m_last_sent_position_timer = 0;
675 m_last_sent_yaw = m_yaw;
676 m_last_sent_position = m_base_position;
677 m_last_sent_velocity = m_velocity;
678 //m_last_sent_acceleration = m_acceleration;
680 float update_interval = m_env->getSendRecommendedInterval();
682 std::string str = gob_cmd_update_position(
691 // create message and add to list
692 ActiveObjectMessage aom(getId(), false, str);
693 m_messages_out.push(aom);
696 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
699 //update collision box
700 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
701 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
703 toset->MinEdge += m_base_position;
704 toset->MaxEdge += m_base_position;
712 bool LuaEntitySAO::collideWithObjects(){
713 return m_prop.collideWithObjects;
720 // No prototype, PlayerSAO does not need to be deserialized
722 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
723 const std::set<std::string> &privs, bool is_singleplayer):
724 ServerActiveObject(env_, v3f(0,0,0)),
729 m_last_good_position(0,0,0),
730 m_time_from_last_punch(0),
731 m_nocheat_dig_pos(32767, 32767, 32767),
732 m_nocheat_dig_time(0),
734 m_position_not_sent(false),
735 m_armor_groups_sent(false),
736 m_properties_sent(true),
738 m_is_singleplayer(is_singleplayer),
739 m_animation_speed(0),
740 m_animation_blend(0),
741 m_animation_loop(true),
742 m_animation_sent(false),
743 m_bone_position_sent(false),
744 m_attachment_parent_id(0),
745 m_attachment_sent(false),
746 m_nametag_color(video::SColor(255, 255, 255, 255)),
747 m_nametag_sent(false),
749 m_physics_override_speed(1),
750 m_physics_override_jump(1),
751 m_physics_override_gravity(1),
752 m_physics_override_sneak(true),
753 m_physics_override_sneak_glitch(true),
754 m_physics_override_sent(false)
756 assert(m_player); // pre-condition
757 assert(m_peer_id != 0); // pre-condition
758 setBasePosition(m_player->getPosition());
759 m_inventory = &m_player->inventory;
760 m_armor_groups["fleshy"] = 100;
762 m_prop.hp_max = PLAYER_MAX_HP;
763 m_prop.physical = false;
765 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
766 // start of default appearance, this should be overwritten by LUA
767 m_prop.visual = "upright_sprite";
768 m_prop.visual_size = v2f(1, 2);
769 m_prop.textures.clear();
770 m_prop.textures.push_back("player.png");
771 m_prop.textures.push_back("player_back.png");
772 m_prop.colors.clear();
773 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
774 m_prop.spritediv = v2s16(1,1);
775 // end of default appearance
776 m_prop.is_visible = true;
777 m_prop.makes_footstep_sound = true;
780 PlayerSAO::~PlayerSAO()
782 if(m_inventory != &m_player->inventory)
787 std::string PlayerSAO::getDescription()
789 return std::string("player ") + m_player->getName();
792 // Called after id has been set and has been inserted in environment
793 void PlayerSAO::addedToEnvironment(u32 dtime_s)
795 ServerActiveObject::addedToEnvironment(dtime_s);
796 ServerActiveObject::setBasePosition(m_player->getPosition());
797 m_player->setPlayerSAO(this);
798 m_player->peer_id = m_peer_id;
799 m_last_good_position = m_player->getPosition();
802 // Called before removing from environment
803 void PlayerSAO::removingFromEnvironment()
805 ServerActiveObject::removingFromEnvironment();
806 if(m_player->getPlayerSAO() == this)
808 m_player->setPlayerSAO(NULL);
809 m_player->peer_id = 0;
810 m_env->savePlayer(m_player->getName());
811 m_env->removePlayer(m_player->getName());
815 bool PlayerSAO::isStaticAllowed() const
820 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
822 std::ostringstream os(std::ios::binary);
824 if(protocol_version >= 15)
826 writeU8(os, 1); // version
827 os<<serializeString(m_player->getName()); // name
828 writeU8(os, 1); // is_player
829 writeS16(os, getId()); //id
830 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
831 writeF1000(os, m_player->getYaw());
832 writeS16(os, getHP());
834 writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
835 os<<serializeLongString(getPropertyPacket()); // message 1
836 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
837 os<<serializeLongString(gob_cmd_update_animation(
838 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
839 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
840 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
842 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
843 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
844 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
845 m_physics_override_sneak_glitch)); // 5
846 os << serializeLongString(gob_cmd_update_nametag_attributes(m_nametag_color)); // 6
850 writeU8(os, 0); // version
851 os<<serializeString(m_player->getName()); // name
852 writeU8(os, 1); // is_player
853 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
854 writeF1000(os, m_player->getYaw());
855 writeS16(os, getHP());
856 writeU8(os, 2); // number of messages stuffed in here
857 os<<serializeLongString(getPropertyPacket()); // message 1
858 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
865 std::string PlayerSAO::getStaticData()
867 FATAL_ERROR("Deprecated function (?)");
871 bool PlayerSAO::isAttached()
873 if(!m_attachment_parent_id)
875 // Check if the parent still exists
876 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
882 void PlayerSAO::step(float dtime, bool send_recommended)
884 if(!m_properties_sent)
886 m_properties_sent = true;
887 std::string str = getPropertyPacket();
888 // create message and add to list
889 ActiveObjectMessage aom(getId(), true, str);
890 m_messages_out.push(aom);
893 // If attached, check that our parent is still there. If it isn't, detach.
894 if(m_attachment_parent_id && !isAttached())
896 m_attachment_parent_id = 0;
897 m_attachment_bone = "";
898 m_attachment_position = v3f(0,0,0);
899 m_attachment_rotation = v3f(0,0,0);
900 m_player->setPosition(m_last_good_position);
901 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
904 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
906 // Set lag pool maximums based on estimated lag
907 const float LAG_POOL_MIN = 5.0;
908 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
909 if(lag_pool_max < LAG_POOL_MIN)
910 lag_pool_max = LAG_POOL_MIN;
911 m_dig_pool.setMax(lag_pool_max);
912 m_move_pool.setMax(lag_pool_max);
914 // Increment cheat prevention timers
915 m_dig_pool.add(dtime);
916 m_move_pool.add(dtime);
917 m_time_from_last_punch += dtime;
918 m_nocheat_dig_time += dtime;
920 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
921 // If the object gets detached this comes into effect automatically from the last known origin
924 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
925 m_last_good_position = pos;
926 m_player->setPosition(pos);
929 if(send_recommended == false)
932 // If the object is attached client-side, don't waste bandwidth sending its position to clients
933 if(m_position_not_sent && !isAttached())
935 m_position_not_sent = false;
936 float update_interval = m_env->getSendRecommendedInterval();
938 if(isAttached()) // Just in case we ever do send attachment position too
939 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
941 pos = m_player->getPosition() + v3f(0,BS*1,0);
942 std::string str = gob_cmd_update_position(
951 // create message and add to list
952 ActiveObjectMessage aom(getId(), false, str);
953 m_messages_out.push(aom);
956 if(m_armor_groups_sent == false) {
957 m_armor_groups_sent = true;
958 std::string str = gob_cmd_update_armor_groups(
960 // create message and add to list
961 ActiveObjectMessage aom(getId(), true, str);
962 m_messages_out.push(aom);
965 if(m_physics_override_sent == false){
966 m_physics_override_sent = true;
967 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
968 m_physics_override_jump, m_physics_override_gravity,
969 m_physics_override_sneak, m_physics_override_sneak_glitch);
970 // create message and add to list
971 ActiveObjectMessage aom(getId(), true, str);
972 m_messages_out.push(aom);
975 if(m_animation_sent == false){
976 m_animation_sent = true;
977 std::string str = gob_cmd_update_animation(
978 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
979 // create message and add to list
980 ActiveObjectMessage aom(getId(), true, str);
981 m_messages_out.push(aom);
984 if(m_bone_position_sent == false){
985 m_bone_position_sent = true;
986 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
987 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
988 // create message and add to list
989 ActiveObjectMessage aom(getId(), true, str);
990 m_messages_out.push(aom);
994 if(m_attachment_sent == false){
995 m_attachment_sent = true;
996 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
997 // create message and add to list
998 ActiveObjectMessage aom(getId(), true, str);
999 m_messages_out.push(aom);
1002 if (m_nametag_sent == false) {
1003 m_nametag_sent = true;
1004 std::string str = gob_cmd_update_nametag_attributes(m_nametag_color);
1005 // create message and add to list
1006 ActiveObjectMessage aom(getId(), true, str);
1007 m_messages_out.push(aom);
1011 void PlayerSAO::setBasePosition(const v3f &position)
1013 // This needs to be ran for attachments too
1014 ServerActiveObject::setBasePosition(position);
1015 m_position_not_sent = true;
1018 void PlayerSAO::setPos(v3f pos)
1022 m_player->setPosition(pos);
1023 // Movement caused by this command is always valid
1024 m_last_good_position = pos;
1025 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1028 void PlayerSAO::moveTo(v3f pos, bool continuous)
1032 m_player->setPosition(pos);
1033 // Movement caused by this command is always valid
1034 m_last_good_position = pos;
1035 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1038 void PlayerSAO::setYaw(float yaw)
1040 m_player->setYaw(yaw);
1041 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1044 void PlayerSAO::setPitch(float pitch)
1046 m_player->setPitch(pitch);
1047 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1050 int PlayerSAO::punch(v3f dir,
1051 const ToolCapabilities *toolcap,
1052 ServerActiveObject *puncher,
1053 float time_from_last_punch)
1055 // It's best that attachments cannot be punched
1062 // No effect if PvP disabled
1063 if (g_settings->getBool("enable_pvp") == false) {
1064 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1065 std::string str = gob_cmd_punched(0, getHP());
1066 // create message and add to list
1067 ActiveObjectMessage aom(getId(), true, str);
1068 m_messages_out.push(aom);
1073 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1074 time_from_last_punch);
1076 std::string punchername = "nil";
1079 punchername = puncher->getDescription();
1081 PlayerSAO *playersao = m_player->getPlayerSAO();
1083 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1084 puncher, time_from_last_punch, toolcap, dir,
1087 if (!damage_handled) {
1088 setHP(getHP() - hitparams.hp);
1089 } else { // override client prediction
1090 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1091 std::string str = gob_cmd_punched(0, getHP());
1092 // create message and add to list
1093 ActiveObjectMessage aom(getId(), true, str);
1094 m_messages_out.push(aom);
1099 actionstream << "Player " << m_player->getName() << " punched by "
1101 if (!damage_handled) {
1102 actionstream << ", damage " << hitparams.hp << " HP";
1104 actionstream << ", damage handled by lua";
1106 actionstream << std::endl;
1108 return hitparams.wear;
1111 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1115 s16 PlayerSAO::getHP() const
1117 return m_player->hp;
1120 s16 PlayerSAO::readDamage()
1122 s16 damage = m_damage;
1127 void PlayerSAO::setHP(s16 hp)
1129 s16 oldhp = m_player->hp;
1131 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this,
1135 hp = oldhp + hp_change;
1139 else if (hp > PLAYER_MAX_HP)
1142 if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1149 m_damage += (oldhp - hp);
1151 // Update properties on death
1152 if ((hp == 0) != (oldhp == 0))
1153 m_properties_sent = false;
1156 u16 PlayerSAO::getBreath() const
1158 return m_player->getBreath();
1161 void PlayerSAO::setBreath(u16 breath)
1163 m_player->setBreath(breath);
1166 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1168 m_armor_groups = armor_groups;
1169 m_armor_groups_sent = false;
1172 ItemGroupList PlayerSAO::getArmorGroups()
1174 return m_armor_groups;
1177 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1179 // store these so they can be updated to clients
1180 m_animation_range = frame_range;
1181 m_animation_speed = frame_speed;
1182 m_animation_blend = frame_blend;
1183 m_animation_loop = frame_loop;
1184 m_animation_sent = false;
1187 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1189 *frame_range = m_animation_range;
1190 *frame_speed = m_animation_speed;
1191 *frame_blend = m_animation_blend;
1192 *frame_loop = m_animation_loop;
1195 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1197 // store these so they can be updated to clients
1198 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1199 m_bone_position_sent = false;
1202 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1204 *position = m_bone_position[bone].X;
1205 *rotation = m_bone_position[bone].Y;
1208 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1210 // Attachments need to be handled on both the server and client.
1211 // If we just attach on the server, we can only copy the position of the parent. Attachments
1212 // are still sent to clients at an interval so players might see them lagging, plus we can't
1213 // read and attach to skeletal bones.
1214 // If we just attach on the client, the server still sees the child at its original location.
1215 // This breaks some things so we also give the server the most accurate representation
1216 // even if players only see the client changes.
1218 m_attachment_parent_id = parent_id;
1219 m_attachment_bone = bone;
1220 m_attachment_position = position;
1221 m_attachment_rotation = rotation;
1222 m_attachment_sent = false;
1225 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1228 *parent_id = m_attachment_parent_id;
1229 *bone = m_attachment_bone;
1230 *position = m_attachment_position;
1231 *rotation = m_attachment_rotation;
1234 ObjectProperties* PlayerSAO::accessObjectProperties()
1239 void PlayerSAO::notifyObjectPropertiesModified()
1241 m_properties_sent = false;
1244 void PlayerSAO::setNametagColor(video::SColor color)
1246 m_nametag_color = color;
1247 m_nametag_sent = false;
1250 video::SColor PlayerSAO::getNametagColor()
1252 return m_nametag_color;
1255 Inventory* PlayerSAO::getInventory()
1259 const Inventory* PlayerSAO::getInventory() const
1264 InventoryLocation PlayerSAO::getInventoryLocation() const
1266 InventoryLocation loc;
1267 loc.setPlayer(m_player->getName());
1271 std::string PlayerSAO::getWieldList() const
1276 int PlayerSAO::getWieldIndex() const
1278 return m_wield_index;
1281 void PlayerSAO::setWieldIndex(int i)
1283 if(i != m_wield_index) {
1288 void PlayerSAO::disconnected()
1292 if(m_player->getPlayerSAO() == this)
1294 m_player->setPlayerSAO(NULL);
1295 m_player->peer_id = 0;
1299 std::string PlayerSAO::getPropertyPacket()
1301 m_prop.is_visible = (true);
1302 return gob_cmd_set_properties(m_prop);
1305 bool PlayerSAO::checkMovementCheat()
1307 bool cheated = false;
1308 if(isAttached() || m_is_singleplayer ||
1309 g_settings->getBool("disable_anticheat"))
1311 m_last_good_position = m_player->getPosition();
1316 Check player movements
1318 NOTE: Actually the server should handle player physics like the
1319 client does and compare player's position to what is calculated
1320 on our side. This is required when eg. players fly due to an
1321 explosion. Altough a node-based alternative might be possible
1322 too, and much more lightweight.
1325 float player_max_speed = 0;
1326 if(m_privs.count("fast") != 0){
1328 player_max_speed = m_player->movement_speed_fast;
1331 player_max_speed = m_player->movement_speed_walk;
1333 // Tolerance. With the lag pool we shouldn't need it.
1334 //player_max_speed *= 2.5;
1335 //player_max_speed_up *= 2.5;
1337 v3f diff = (m_player->getPosition() - m_last_good_position);
1338 float d_vert = diff.Y;
1340 float d_horiz = diff.getLength();
1341 float required_time = d_horiz/player_max_speed;
1342 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1343 required_time = d_vert/player_max_speed;
1344 if(m_move_pool.grab(required_time)){
1345 m_last_good_position = m_player->getPosition();
1347 actionstream<<"Player "<<m_player->getName()
1348 <<" moved too fast; resetting position"
1350 m_player->setPosition(m_last_good_position);
1357 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1358 //update collision box
1359 *toset = m_player->getCollisionbox();
1361 toset->MinEdge += m_base_position;
1362 toset->MaxEdge += m_base_position;
1367 bool PlayerSAO::collideWithObjects(){