3 Copyright (C) 2010-2011 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 "collision.h"
22 #include "environment.h"
24 #include "main.h" // For g_profiler
26 #include "serialization.h" // For compressZlib
27 #include "tool.h" // For ToolCapabilities
30 #include "scriptapi.h"
31 #include "genericobject.h"
32 #include "util/serialize.h"
34 core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
40 class DummyLoadSAO : public ServerActiveObject
43 DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type):
44 ServerActiveObject(env, pos)
46 ServerActiveObject::registerType(type, create);
48 // Pretend to be the test object (to fool the client)
50 { return ACTIVEOBJECT_TYPE_TEST; }
51 // And never save to disk
52 bool isStaticAllowed() const
55 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
56 const std::string &data)
58 return new DummyLoadSAO(env, pos, 0);
61 void step(float dtime, bool send_recommended)
64 infostream<<"DummyLoadSAO step"<<std::endl;
70 // Prototype (registers item for deserialization)
71 DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
72 DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
73 DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
74 DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
80 class TestSAO : public ServerActiveObject
83 TestSAO(ServerEnvironment *env, v3f pos):
84 ServerActiveObject(env, pos),
88 ServerActiveObject::registerType(getType(), create);
91 { return ACTIVEOBJECT_TYPE_TEST; }
93 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
94 const std::string &data)
96 return new TestSAO(env, pos);
99 void step(float dtime, bool send_recommended)
108 m_base_position.Y += dtime * BS * 2;
109 if(m_base_position.Y > 8*BS)
110 m_base_position.Y = 2*BS;
112 if(send_recommended == false)
122 data += itos(0); // 0 = position
124 data += itos(m_base_position.X);
126 data += itos(m_base_position.Y);
128 data += itos(m_base_position.Z);
130 ActiveObjectMessage aom(getId(), false, data);
131 m_messages_out.push_back(aom);
140 // Prototype (registers item for deserialization)
141 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
146 DEPRECATED: New dropped items are implemented in Lua; see
147 builtin/item_entity.lua.
150 class ItemSAO : public ServerActiveObject
154 { return ACTIVEOBJECT_TYPE_ITEM; }
156 float getMinimumSavedMovement()
159 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
160 const std::string &data)
162 std::istringstream is(data, std::ios::binary);
167 // check if version is supported
170 std::string itemstring = deSerializeString(is);
171 infostream<<"create(): Creating item \""
172 <<itemstring<<"\""<<std::endl;
173 return new ItemSAO(env, pos, itemstring);
176 ItemSAO(ServerEnvironment *env, v3f pos,
177 const std::string itemstring):
178 ServerActiveObject(env, pos),
179 m_itemstring(itemstring),
180 m_itemstring_changed(false),
182 m_last_sent_position(0,0,0)
184 ServerActiveObject::registerType(getType(), create);
187 void step(float dtime, bool send_recommended)
189 ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
193 const float interval = 0.2;
194 if(m_move_interval.step(dtime, interval)==false)
198 core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
199 collisionMoveResult moveresult;
201 m_speed_f += v3f(0, -dtime*9.81*BS, 0);
202 // Maximum movement without glitches
203 f32 pos_max_d = BS*0.25;
205 if(m_speed_f.getLength()*dtime > pos_max_d)
206 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
207 v3f pos_f = getBasePosition();
208 v3f pos_f_old = pos_f;
209 v3f accel_f = v3f(0,0,0);
211 IGameDef *gamedef = m_env->getGameDef();
212 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
213 pos_max_d, box, stepheight, dtime,
214 pos_f, m_speed_f, accel_f);
216 if(send_recommended == false)
219 if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
221 // TODO: We shouldn't be sending this when the object is attached, but we can't check m_parent here
222 setBasePosition(pos_f);
223 m_last_sent_position = pos_f;
225 std::ostringstream os(std::ios::binary);
226 // command (0 = update position)
229 writeV3F1000(os, m_base_position);
230 // create message and add to list
231 ActiveObjectMessage aom(getId(), false, os.str());
232 m_messages_out.push_back(aom);
234 if(m_itemstring_changed)
236 m_itemstring_changed = false;
238 std::ostringstream os(std::ios::binary);
239 // command (1 = update itemstring)
242 os<<serializeString(m_itemstring);
243 // create message and add to list
244 ActiveObjectMessage aom(getId(), false, os.str());
245 m_messages_out.push_back(aom);
249 std::string getClientInitializationData()
251 std::ostringstream os(std::ios::binary);
255 writeV3F1000(os, m_base_position);
257 os<<serializeString(m_itemstring);
261 std::string getStaticData()
263 infostream<<__FUNCTION_NAME<<std::endl;
264 std::ostringstream os(std::ios::binary);
268 os<<serializeString(m_itemstring);
272 ItemStack createItemStack()
275 IItemDefManager *idef = m_env->getGameDef()->idef();
277 item.deSerialize(m_itemstring, idef);
278 infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
279 <<"\" -> item=\""<<item.getItemString()<<"\""
283 catch(SerializationError &e)
285 infostream<<__FUNCTION_NAME<<": serialization error: "
286 <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
292 const ToolCapabilities *toolcap,
293 ServerActiveObject *puncher,
294 float time_from_last_punch)
296 // Take item into inventory
297 ItemStack item = createItemStack();
298 Inventory *inv = puncher->getInventory();
301 std::string wieldlist = puncher->getWieldList();
302 ItemStack leftover = inv->addItem(wieldlist, item);
303 puncher->setInventoryModified();
310 m_itemstring = leftover.getItemString();
311 m_itemstring_changed = true;
320 std::string m_itemstring;
321 bool m_itemstring_changed;
323 v3f m_last_sent_position;
324 IntervalLimiter m_move_interval;
327 // Prototype (registers item for deserialization)
328 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
330 ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
331 const std::string itemstring)
333 return new ItemSAO(env, pos, itemstring);
340 // Prototype (registers item for deserialization)
341 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
343 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
344 const std::string &name, const std::string &state):
345 ServerActiveObject(env, pos),
351 m_acceleration(0,0,0),
353 m_properties_sent(true),
355 m_last_sent_position(0,0,0),
356 m_last_sent_velocity(0,0,0),
357 m_last_sent_position_timer(0),
358 m_last_sent_move_precision(0),
359 m_armor_groups_sent(false),
360 m_animations_sent(false),
361 m_animations_bone_sent(false),
362 m_attachment_sent(false)
364 // Only register type if no environment supplied
366 ServerActiveObject::registerType(getType(), create);
370 // Initialize something to armor groups
371 m_armor_groups["fleshy"] = 3;
372 m_armor_groups["snappy"] = 2;
375 LuaEntitySAO::~LuaEntitySAO()
378 lua_State *L = m_env->getLua();
379 scriptapi_luaentity_rm(L, m_id);
383 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
385 ServerActiveObject::addedToEnvironment(dtime_s);
387 // Create entity from name
388 lua_State *L = m_env->getLua();
389 m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
394 scriptapi_luaentity_get_properties(L, m_id, &m_prop);
395 // Initialize HP from properties
396 m_hp = m_prop.hp_max;
397 // Activate entity, supplying serialized state
398 scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
402 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
403 const std::string &data)
411 std::istringstream is(data, std::ios::binary);
413 u8 version = readU8(is);
414 // check if version is supported
416 name = deSerializeString(is);
417 state = deSerializeLongString(is);
419 else if(version == 1){
420 name = deSerializeString(is);
421 state = deSerializeLongString(is);
423 velocity = readV3F1000(is);
428 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
429 <<state<<"\")"<<std::endl;
430 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
432 sao->m_velocity = velocity;
437 void LuaEntitySAO::step(float dtime, bool send_recommended)
439 if(!m_properties_sent)
441 m_properties_sent = true;
442 std::string str = getPropertyPacket();
443 // create message and add to list
444 ActiveObjectMessage aom(getId(), true, str);
445 m_messages_out.push_back(aom);
448 m_last_sent_position_timer += dtime;
450 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
451 // If the object gets detached this comes into effect automatically from the last known origin
454 v3f pos = m_parent->getBasePosition();
455 m_base_position = pos;
456 m_velocity = v3f(0,0,0);
457 m_acceleration = v3f(0,0,0);
462 core::aabbox3d<f32> box = m_prop.collisionbox;
465 collisionMoveResult moveresult;
466 f32 pos_max_d = BS*0.25; // Distance per iteration
467 f32 stepheight = 0; // Maximum climbable step height
468 v3f p_pos = m_base_position;
469 v3f p_velocity = m_velocity;
470 v3f p_acceleration = m_acceleration;
471 IGameDef *gamedef = m_env->getGameDef();
472 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
473 pos_max_d, box, stepheight, dtime,
474 p_pos, p_velocity, p_acceleration);
476 m_base_position = p_pos;
477 m_velocity = p_velocity;
478 m_acceleration = p_acceleration;
480 m_base_position += dtime * m_velocity + 0.5 * dtime
481 * dtime * m_acceleration;
482 m_velocity += dtime * m_acceleration;
487 lua_State *L = m_env->getLua();
488 scriptapi_luaentity_step(L, m_id, dtime);
491 if(send_recommended == false)
496 // TODO: force send when acceleration changes enough?
497 float minchange = 0.2*BS;
498 if(m_last_sent_position_timer > 1.0){
500 } else if(m_last_sent_position_timer > 0.2){
503 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
504 move_d += m_last_sent_move_precision;
505 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
506 if(move_d > minchange || vel_d > minchange ||
507 fabs(m_yaw - m_last_sent_yaw) > 1.0){
508 sendPosition(true, false);
512 if(m_armor_groups_sent == false){
513 m_armor_groups_sent = true;
514 std::string str = gob_cmd_update_armor_groups(
516 // create message and add to list
517 ActiveObjectMessage aom(getId(), true, str);
518 m_messages_out.push_back(aom);
521 if(m_animations_sent == false){
522 m_animations_sent = true;
523 std::string str = gob_cmd_update_animations(m_animation_frames, m_animation_speed, m_animation_blend);
524 // create message and add to list
525 ActiveObjectMessage aom(getId(), true, str);
526 m_messages_out.push_back(aom);
529 if(m_animations_bone_sent == false){
530 m_animations_bone_sent = true;
531 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
532 std::string str = gob_cmd_update_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y);
533 // create message and add to list
534 ActiveObjectMessage aom(getId(), true, str);
535 m_messages_out.push_back(aom);
539 if(m_attachment_sent == false){
540 m_attachment_sent = true;
541 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
542 // create message and add to list
543 ActiveObjectMessage aom(getId(), true, str);
544 m_messages_out.push_back(aom);
548 std::string LuaEntitySAO::getClientInitializationData()
550 std::ostringstream os(std::ios::binary);
551 writeU8(os, 0); // version
552 os<<serializeString(""); // name
553 writeS16(os, getId()); //id
554 writeU8(os, 0); // is_player
555 writeV3F1000(os, m_base_position);
556 writeF1000(os, m_yaw);
559 writeU8(os, 4 + m_animation_bone.size()); // number of messages stuffed in here
560 os<<serializeLongString(getPropertyPacket()); // message 1
561 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
562 os<<serializeLongString(gob_cmd_update_animations(m_animation_frames, m_animation_speed, m_animation_blend)); // 3
563 if(m_animation_bone.size()){
564 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
565 os<<serializeLongString(gob_cmd_update_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_animation_bone.size
568 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
574 std::string LuaEntitySAO::getStaticData()
576 verbosestream<<__FUNCTION_NAME<<std::endl;
577 std::ostringstream os(std::ios::binary);
581 os<<serializeString(m_init_name);
584 lua_State *L = m_env->getLua();
585 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
586 os<<serializeLongString(state);
588 os<<serializeLongString(m_init_state);
593 writeV3F1000(os, m_velocity);
595 writeF1000(os, m_yaw);
599 int LuaEntitySAO::punch(v3f dir,
600 const ToolCapabilities *toolcap,
601 ServerActiveObject *puncher,
602 float time_from_last_punch)
605 // Delete unknown LuaEntities when punched
610 // It's best that attachments cannot be punched
614 ItemStack *punchitem = NULL;
615 ItemStack punchitem_static;
617 punchitem_static = puncher->getWieldedItem();
618 punchitem = &punchitem_static;
621 PunchDamageResult result = getPunchDamage(
625 time_from_last_punch);
629 setHP(getHP() - result.damage);
631 actionstream<<getDescription()<<" punched by "
632 <<puncher->getDescription()<<", damage "<<result.damage
633 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
636 std::string str = gob_cmd_punched(result.damage, getHP());
637 // create message and add to list
638 ActiveObjectMessage aom(getId(), true, str);
639 m_messages_out.push_back(aom);
646 lua_State *L = m_env->getLua();
647 scriptapi_luaentity_punch(L, m_id, puncher,
648 time_from_last_punch, toolcap, dir);
653 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
657 lua_State *L = m_env->getLua();
658 scriptapi_luaentity_rightclick(L, m_id, clicker);
661 void LuaEntitySAO::setPos(v3f pos)
665 m_base_position = pos;
666 sendPosition(false, true);
669 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
673 m_base_position = pos;
675 sendPosition(true, true);
678 float LuaEntitySAO::getMinimumSavedMovement()
683 std::string LuaEntitySAO::getDescription()
685 std::ostringstream os(std::ios::binary);
686 os<<"LuaEntitySAO at (";
687 os<<(m_base_position.X/BS)<<",";
688 os<<(m_base_position.Y/BS)<<",";
689 os<<(m_base_position.Z/BS);
694 void LuaEntitySAO::setHP(s16 hp)
700 s16 LuaEntitySAO::getHP() const
705 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
707 m_armor_groups = armor_groups;
708 m_armor_groups_sent = false;
711 void LuaEntitySAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
713 m_animation_frames = frames;
714 m_animation_speed = frame_speed;
715 m_animation_blend = frame_blend;
716 m_animations_sent = false;
719 void LuaEntitySAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
721 m_animation_bone[bone] = core::vector2d<v3f>(position, rotation);
722 m_animations_bone_sent = false;
725 void LuaEntitySAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
727 // Attachments need to be handled on both the server and client.
728 // If we just attach on the server, we can only copy the position of the parent. Attachments
729 // are still sent to clients at an interval so players might see them lagging, plus we can't
730 // read and attach to skeletal bones.
731 // If we just attach on the client, the server still sees the child at its original location.
732 // This breaks some things so we also give the server the most accurate representation
733 // even if players only see the client changes.
735 // Server attachment:
738 // Client attachment:
739 m_attachment_parent_id = parent->getId();
740 m_attachment_bone = bone;
741 m_attachment_position = position;
742 m_attachment_rotation = rotation;
743 m_attachment_sent = false;
746 ObjectProperties* LuaEntitySAO::accessObjectProperties()
751 void LuaEntitySAO::notifyObjectPropertiesModified()
753 m_properties_sent = false;
756 void LuaEntitySAO::setVelocity(v3f velocity)
758 m_velocity = velocity;
761 v3f LuaEntitySAO::getVelocity()
766 void LuaEntitySAO::setAcceleration(v3f acceleration)
768 m_acceleration = acceleration;
771 v3f LuaEntitySAO::getAcceleration()
773 return m_acceleration;
776 void LuaEntitySAO::setYaw(float yaw)
781 float LuaEntitySAO::getYaw()
786 void LuaEntitySAO::setTextureMod(const std::string &mod)
788 std::string str = gob_cmd_set_texture_mod(mod);
789 // create message and add to list
790 ActiveObjectMessage aom(getId(), true, str);
791 m_messages_out.push_back(aom);
794 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
795 bool select_horiz_by_yawpitch)
797 std::string str = gob_cmd_set_sprite(
801 select_horiz_by_yawpitch
803 // create message and add to list
804 ActiveObjectMessage aom(getId(), true, str);
805 m_messages_out.push_back(aom);
808 std::string LuaEntitySAO::getName()
813 std::string LuaEntitySAO::getPropertyPacket()
815 return gob_cmd_set_properties(m_prop);
818 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
820 // If the object is attached client-side, don't waste bandwidth sending its position to clients
824 m_last_sent_move_precision = m_base_position.getDistanceFrom(
825 m_last_sent_position);
826 m_last_sent_position_timer = 0;
827 m_last_sent_yaw = m_yaw;
828 m_last_sent_position = m_base_position;
829 m_last_sent_velocity = m_velocity;
830 //m_last_sent_acceleration = m_acceleration;
832 float update_interval = m_env->getSendRecommendedInterval();
834 std::string str = gob_cmd_update_position(
843 // create message and add to list
844 ActiveObjectMessage aom(getId(), false, str);
845 m_messages_out.push_back(aom);
852 // No prototype, PlayerSAO does not need to be deserialized
854 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
855 const std::set<std::string> &privs, bool is_singleplayer):
856 ServerActiveObject(env_, v3f(0,0,0)),
860 m_last_good_position(0,0,0),
861 m_last_good_position_age(0),
862 m_time_from_last_punch(0),
863 m_nocheat_dig_pos(32767, 32767, 32767),
864 m_nocheat_dig_time(0),
866 m_position_not_sent(false),
867 m_armor_groups_sent(false),
868 m_properties_sent(true),
870 m_is_singleplayer(is_singleplayer),
871 m_animations_sent(false),
872 m_animations_bone_sent(false),
873 m_attachment_sent(false),
876 m_inventory_not_sent(false),
877 m_hp_not_sent(false),
878 m_wielded_item_not_sent(false)
881 assert(m_peer_id != 0);
882 setBasePosition(m_player->getPosition());
883 m_inventory = &m_player->inventory;
884 m_armor_groups["choppy"] = 2;
885 m_armor_groups["fleshy"] = 3;
887 m_prop.hp_max = PLAYER_MAX_HP;
888 m_prop.physical = false;
890 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
891 // start of default appearance, this should be overwritten by LUA
892 m_prop.visual = "upright_sprite";
893 m_prop.visual_size = v2f(1, 2);
894 m_prop.textures.clear();
895 m_prop.textures.push_back("player.png");
896 m_prop.textures.push_back("player_back.png");
897 m_prop.colors.clear();
898 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
899 m_prop.spritediv = v2s16(1,1);
900 // end of default appearance
901 m_prop.is_visible = (getHP() != 0); // TODO: Use a death animation instead for mesh players
902 m_prop.makes_footstep_sound = true;
905 PlayerSAO::~PlayerSAO()
907 if(m_inventory != &m_player->inventory)
912 std::string PlayerSAO::getDescription()
914 return std::string("player ") + m_player->getName();
917 // Called after id has been set and has been inserted in environment
918 void PlayerSAO::addedToEnvironment(u32 dtime_s)
920 ServerActiveObject::addedToEnvironment(dtime_s);
921 ServerActiveObject::setBasePosition(m_player->getPosition());
923 m_player->setPlayerSAO(this);
924 m_player->peer_id = m_peer_id;
925 m_last_good_position = m_player->getPosition();
926 m_last_good_position_age = 0.0;
929 // Called before removing from environment
930 void PlayerSAO::removingFromEnvironment()
932 ServerActiveObject::removingFromEnvironment();
933 if(m_player->getPlayerSAO() == this)
935 m_player->setPlayerSAO(NULL);
936 m_player->peer_id = 0;
940 bool PlayerSAO::isStaticAllowed() const
945 bool PlayerSAO::unlimitedTransferDistance() const
947 return g_settings->getBool("unlimited_player_transfer_distance");
950 std::string PlayerSAO::getClientInitializationData()
952 std::ostringstream os(std::ios::binary);
953 writeU8(os, 0); // version
954 os<<serializeString(m_player->getName()); // name
955 writeU8(os, 1); // is_player
956 writeS16(os, getId()); //id
957 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
958 writeF1000(os, m_player->getYaw());
959 writeS16(os, getHP());
961 writeU8(os, 4 + m_animation_bone.size()); // number of messages stuffed in here
962 os<<serializeLongString(getPropertyPacket()); // message 1
963 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
964 os<<serializeLongString(gob_cmd_update_animations(m_animation_frames, m_animation_speed, m_animation_blend)); // 3
965 if(m_animation_bone.size()){
966 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
967 os<<serializeLongString(gob_cmd_update_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_animation_bone.size
970 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
976 std::string PlayerSAO::getStaticData()
982 void PlayerSAO::step(float dtime, bool send_recommended)
984 if(!m_properties_sent)
986 m_properties_sent = true;
987 std::string str = getPropertyPacket();
988 // create message and add to list
989 ActiveObjectMessage aom(getId(), true, str);
990 m_messages_out.push_back(aom);
993 m_time_from_last_punch += dtime;
994 m_nocheat_dig_time += dtime;
996 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
997 // If the object gets detached this comes into effect automatically from the last known origin
1000 v3f pos = m_parent->getBasePosition();
1001 m_last_good_position = pos;
1002 m_last_good_position_age = 0;
1003 m_player->setPosition(pos);
1007 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
1009 m_last_good_position = m_player->getPosition();
1010 m_last_good_position_age = 0;
1015 Check player movements
1017 NOTE: Actually the server should handle player physics like the
1018 client does and compare player's position to what is calculated
1019 on our side. This is required when eg. players fly due to an
1020 explosion. Altough a node-based alternative might be possible
1021 too, and much more lightweight.
1024 float player_max_speed = 0;
1025 float player_max_speed_up = 0;
1026 if(m_privs.count("fast") != 0){
1028 player_max_speed = BS * 20;
1029 player_max_speed_up = BS * 20;
1032 player_max_speed = BS * 4.0;
1033 player_max_speed_up = BS * 4.0;
1036 player_max_speed *= 2.5;
1037 player_max_speed_up *= 2.5;
1039 m_last_good_position_age += dtime;
1040 if(m_last_good_position_age >= 1.0){
1041 float age = m_last_good_position_age;
1042 v3f diff = (m_player->getPosition() - m_last_good_position);
1043 float d_vert = diff.Y;
1045 float d_horiz = diff.getLength();
1046 /*infostream<<m_player->getName()<<"'s horizontal speed is "
1047 <<(d_horiz/age)<<std::endl;*/
1048 if(d_horiz <= age * player_max_speed &&
1049 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1050 m_last_good_position = m_player->getPosition();
1052 actionstream<<"Player "<<m_player->getName()
1053 <<" moved too fast; resetting position"
1055 m_player->setPosition(m_last_good_position);
1056 m_teleported = true;
1058 m_last_good_position_age = 0;
1063 if(send_recommended == false)
1066 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1067 if(m_position_not_sent && m_parent == NULL)
1069 m_position_not_sent = false;
1070 float update_interval = m_env->getSendRecommendedInterval();
1072 if(m_parent != NULL) // Just in case we ever do send attachment position too
1073 pos = m_parent->getBasePosition();
1075 pos = m_player->getPosition() + v3f(0,BS*1,0);
1076 std::string str = gob_cmd_update_position(
1085 // create message and add to list
1086 ActiveObjectMessage aom(getId(), false, str);
1087 m_messages_out.push_back(aom);
1090 if(m_wielded_item_not_sent)
1092 m_wielded_item_not_sent = false;
1093 // GenericCAO has no special way to show this
1096 if(m_armor_groups_sent == false){
1097 m_armor_groups_sent = true;
1098 std::string str = gob_cmd_update_armor_groups(
1100 // create message and add to list
1101 ActiveObjectMessage aom(getId(), true, str);
1102 m_messages_out.push_back(aom);
1105 if(m_animations_sent == false){
1106 m_animations_sent = true;
1107 std::string str = gob_cmd_update_animations(m_animation_frames, m_animation_speed, m_animation_blend);
1108 // create message and add to list
1109 ActiveObjectMessage aom(getId(), true, str);
1110 m_messages_out.push_back(aom);
1113 if(m_animations_bone_sent == false){
1114 m_animations_bone_sent = true;
1115 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
1116 std::string str = gob_cmd_update_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y);
1117 // create message and add to list
1118 ActiveObjectMessage aom(getId(), true, str);
1119 m_messages_out.push_back(aom);
1123 if(m_attachment_sent == false){
1124 m_attachment_sent = true;
1125 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1126 // create message and add to list
1127 ActiveObjectMessage aom(getId(), true, str);
1128 m_messages_out.push_back(aom);
1132 void PlayerSAO::setBasePosition(const v3f &position)
1134 // This needs to be ran for attachments too
1135 ServerActiveObject::setBasePosition(position);
1136 m_position_not_sent = true;
1139 void PlayerSAO::setPos(v3f pos)
1141 if(m_parent != NULL)
1143 m_player->setPosition(pos);
1144 // Movement caused by this command is always valid
1145 m_last_good_position = pos;
1146 m_last_good_position_age = 0;
1147 // Force position change on client
1148 m_teleported = true;
1151 void PlayerSAO::moveTo(v3f pos, bool continuous)
1153 if(m_parent != NULL)
1155 m_player->setPosition(pos);
1156 // Movement caused by this command is always valid
1157 m_last_good_position = pos;
1158 m_last_good_position_age = 0;
1159 // Force position change on client
1160 m_teleported = true;
1163 int PlayerSAO::punch(v3f dir,
1164 const ToolCapabilities *toolcap,
1165 ServerActiveObject *puncher,
1166 float time_from_last_punch)
1168 // It's best that attachments cannot be punched
1169 if(m_parent != NULL)
1175 // No effect if PvP disabled
1176 if(g_settings->getBool("enable_pvp") == false){
1177 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1178 std::string str = gob_cmd_punched(0, getHP());
1179 // create message and add to list
1180 ActiveObjectMessage aom(getId(), true, str);
1181 m_messages_out.push_back(aom);
1186 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1187 time_from_last_punch);
1189 actionstream<<"Player "<<m_player->getName()<<" punched by "
1190 <<puncher->getDescription()<<", damage "<<hitparams.hp
1193 setHP(getHP() - hitparams.hp);
1195 if(hitparams.hp != 0)
1197 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1198 // create message and add to list
1199 ActiveObjectMessage aom(getId(), true, str);
1200 m_messages_out.push_back(aom);
1203 return hitparams.wear;
1206 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1210 s16 PlayerSAO::getHP() const
1212 return m_player->hp;
1215 void PlayerSAO::setHP(s16 hp)
1217 s16 oldhp = m_player->hp;
1221 else if(hp > PLAYER_MAX_HP)
1224 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1226 m_hp_not_sent = true; // fix wrong prediction on client
1233 m_hp_not_sent = true;
1235 // On death or reincarnation send an active object message
1236 if((hp == 0) != (oldhp == 0))
1238 // Will send new is_visible value based on (getHP()!=0)
1239 m_properties_sent = false;
1241 std::string str = gob_cmd_punched(0, getHP());
1242 ActiveObjectMessage aom(getId(), true, str);
1243 m_messages_out.push_back(aom);
1247 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1249 m_armor_groups = armor_groups;
1250 m_armor_groups_sent = false;
1253 void PlayerSAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
1255 // store these so they can be updated to clients
1256 m_animation_frames = frames;
1257 m_animation_speed = frame_speed;
1258 m_animation_blend = frame_blend;
1259 m_animations_sent = false;
1262 void PlayerSAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
1264 // store these so they can be updated to clients
1265 m_animation_bone[bone] = core::vector2d<v3f>(position, rotation);
1266 m_animations_bone_sent = false;
1269 void PlayerSAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
1271 // Attachments need to be handled on both the server and client.
1272 // If we just attach on the server, we can only copy the position of the parent. Attachments
1273 // are still sent to clients at an interval so players might see them lagging, plus we can't
1274 // read and attach to skeletal bones.
1275 // If we just attach on the client, the server still sees the child at its original location.
1276 // This breaks some things so we also give the server the most accurate representation
1277 // even if players only see the client changes.
1279 // Server attachment:
1282 // Client attachment:
1283 m_attachment_parent_id = parent->getId();
1284 m_attachment_bone = bone;
1285 m_attachment_position = position;
1286 m_attachment_rotation = rotation;
1287 m_attachment_sent = false;
1290 ObjectProperties* PlayerSAO::accessObjectProperties()
1295 void PlayerSAO::notifyObjectPropertiesModified()
1297 m_properties_sent = false;
1300 Inventory* PlayerSAO::getInventory()
1304 const Inventory* PlayerSAO::getInventory() const
1309 InventoryLocation PlayerSAO::getInventoryLocation() const
1311 InventoryLocation loc;
1312 loc.setPlayer(m_player->getName());
1316 void PlayerSAO::setInventoryModified()
1318 m_inventory_not_sent = true;
1321 std::string PlayerSAO::getWieldList() const
1326 int PlayerSAO::getWieldIndex() const
1328 return m_wield_index;
1331 void PlayerSAO::setWieldIndex(int i)
1333 if(i != m_wield_index)
1336 m_wielded_item_not_sent = true;
1340 void PlayerSAO::disconnected()
1344 if(m_player->getPlayerSAO() == this)
1346 m_player->setPlayerSAO(NULL);
1347 m_player->peer_id = 0;
1351 std::string PlayerSAO::getPropertyPacket()
1353 m_prop.is_visible = (getHP() != 0);
1354 return gob_cmd_set_properties(m_prop);