Merge remote-tracking branch 'upstream/master'

This commit is contained in:
sfan5 2012-11-25 20:59:08 +01:00
commit 6c43e29c54
29 changed files with 1481 additions and 269 deletions

View File

@ -215,8 +215,8 @@ minetest.register_chatcommand("clearpassword", {
privs = {password=true}, privs = {password=true},
func = function(name, param) func = function(name, param)
toname = param toname = param
if not toname then if toname == "" then
minetest.chat_send_player(toname, "Name field required") minetest.chat_send_player(name, "Name field required")
return return
end end
minetest.set_player_password(toname, '') minetest.set_player_password(toname, '')

View File

@ -1,4 +1,4 @@
Minetest Lua Modding API Reference 0.4.0 Minetest Lua Modding API Reference 0.4.3
========================================== ==========================================
More information at http://c55.me/minetest/ More information at http://c55.me/minetest/
@ -694,6 +694,11 @@ image[<X>,<Y>;<W>,<H>;<texture name>]
^ Show an image ^ Show an image
^ Position and size units are inventory slots ^ Position and size units are inventory slots
background[<X>,<Y>;<W>,<H>;<texture name>]
^ Use a background. Inventory rectangles are not drawn then.
^ Position and size units are inventory slots
^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px
field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>] field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]
^ Textual field; will be sent to server when a button is clicked ^ Textual field; will be sent to server when a button is clicked
^ x and y position the field relative to the top left of the menu ^ x and y position the field relative to the top left of the menu
@ -1098,6 +1103,10 @@ methods:
- get_wielded_item() -> ItemStack - get_wielded_item() -> ItemStack
- set_wielded_item(item): replaces the wielded item, returns true if successful - set_wielded_item(item): replaces the wielded item, returns true if successful
- set_armor_groups({group1=rating, group2=rating, ...}) - set_armor_groups({group1=rating, group2=rating, ...})
- set_animation({x=1,y=1}, frame_speed=15, frame_blend=0)
- set_attach(parent, "", {x=0,y=0,z=0}, {x=0,y=0,z=0})
- set_detach()
- set_bone_position("", {x=0,y=0,z=0}, {x=0,y=0,z=0})
- set_properties(object property table) - set_properties(object property table)
LuaEntitySAO-only: (no-op for other objects) LuaEntitySAO-only: (no-op for other objects)
- setvelocity({x=num, y=num, z=num}) - setvelocity({x=num, y=num, z=num})
@ -1123,7 +1132,11 @@ Player-only: (no-op for other objects)
^ Redefine player's inventory form ^ Redefine player's inventory form
^ Should usually be called in on_joinplayer ^ Should usually be called in on_joinplayer
- get_inventory_formspec() -> formspec string - get_inventory_formspec() -> formspec string
- get_player_control(): returns table with player pressed keys
{jump=bool,right=bool,left=bool,LMB=bool,RMB=bool,sneak=bool,aux1=bool,down=bool,up=bool}
- get_player_control_bits(): returns integer with bit packed player pressed keys
bit nr/meaning: 0/up ,1/down ,2/left ,3/right ,4/jump ,5/aux1 ,6/sneak ,7/LMB ,8/RMB
InvRef: Reference to an inventory InvRef: Reference to an inventory
methods: methods:
- is_empty(listname): return true if list is empty - is_empty(listname): return true if list is empty
@ -1222,9 +1235,11 @@ Object Properties
physical = true, physical = true,
weight = 5, weight = 5,
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5}, collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
visual = "cube"/"sprite"/"upright_sprite", visual = "cube"/"sprite"/"upright_sprite"/"mesh",
visual_size = {x=1, y=1}, visual_size = {x=1, y=1},
mesh = "model",
textures = {}, -- number of required textures depends on visual textures = {}, -- number of required textures depends on visual
colors = {}, -- number of required colors depends on visual
spritediv = {x=1, y=1}, spritediv = {x=1, y=1},
initial_sprite_basepos = {x=0, y=0}, initial_sprite_basepos = {x=0, y=0},
is_visible = true, is_visible = true,

View File

@ -41,6 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "sound.h" #include "sound.h"
#include "util/string.h" #include "util/string.h"
#include "hex.h" #include "hex.h"
#include "IMeshCache.h"
static std::string getMediaCacheDir() static std::string getMediaCacheDir()
{ {
@ -821,7 +822,7 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
if(name != "") if(name != "")
{ {
verbosestream<<"Client: Attempting to load image " verbosestream<<"Client: Attempting to load image "
<<"file \""<<filename<<"\""<<std::endl; <<"file \""<<filename<<"\""<<std::endl;
io::IFileSystem *irrfs = m_device->getFileSystem(); io::IFileSystem *irrfs = m_device->getFileSystem();
video::IVideoDriver *vdrv = m_device->getVideoDriver(); video::IVideoDriver *vdrv = m_device->getVideoDriver();
@ -855,11 +856,33 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
if(name != "") if(name != "")
{ {
verbosestream<<"Client: Attempting to load sound " verbosestream<<"Client: Attempting to load sound "
<<"file \""<<filename<<"\""<<std::endl; <<"file \""<<filename<<"\""<<std::endl;
m_sound->loadSoundData(name, data); m_sound->loadSoundData(name, data);
return true; return true;
} }
const char *model_ext[] = {
".x", ".b3d", ".md2", ".obj",
NULL
};
name = removeStringEnd(filename, model_ext);
if(name != "")
{
verbosestream<<"Client: Storing model into Irrlicht: "
<<"\""<<filename<<"\""<<std::endl;
io::IFileSystem *irrfs = m_device->getFileSystem();
io::IReadFile *rfile = irrfs->createMemoryReadFile(
*data_rw, data_rw.getSize(), filename.c_str());
assert(rfile);
scene::ISceneManager *smgr = m_device->getSceneManager();
scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
return true;
}
errorstream<<"Client: Don't know how to load file \"" errorstream<<"Client: Don't know how to load file \""
<<filename<<"\""<<std::endl; <<filename<<"\""<<std::endl;
return false; return false;
@ -1961,7 +1984,7 @@ void Client::sendPlayerPos()
v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100); v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
s32 pitch = myplayer->getPitch() * 100; s32 pitch = myplayer->getPitch() * 100;
s32 yaw = myplayer->getYaw() * 100; s32 yaw = myplayer->getYaw() * 100;
u32 keyPressed=myplayer->keyPressed;
/* /*
Format: Format:
[0] u16 command [0] u16 command
@ -1969,15 +1992,15 @@ void Client::sendPlayerPos()
[2+12] v3s32 speed*100 [2+12] v3s32 speed*100
[2+12+12] s32 pitch*100 [2+12+12] s32 pitch*100
[2+12+12+4] s32 yaw*100 [2+12+12+4] s32 yaw*100
[2+12+12+4+4] u32 keyPressed
*/ */
SharedBuffer<u8> data(2+12+12+4+4+4);
SharedBuffer<u8> data(2+12+12+4+4);
writeU16(&data[0], TOSERVER_PLAYERPOS); writeU16(&data[0], TOSERVER_PLAYERPOS);
writeV3S32(&data[2], position); writeV3S32(&data[2], position);
writeV3S32(&data[2+12], speed); writeV3S32(&data[2+12], speed);
writeS32(&data[2+12+12], pitch); writeS32(&data[2+12+12], pitch);
writeS32(&data[2+12+12+4], yaw); writeS32(&data[2+12+12+4], yaw);
writeU32(&data[2+12+12+4+4], keyPressed);
// Send as unreliable // Send as unreliable
Send(0, data, false); Send(0, data, false);
} }

View File

@ -36,7 +36,7 @@ ClientActiveObject::ClientActiveObject(u16 id, IGameDef *gamedef,
ClientActiveObject::~ClientActiveObject() ClientActiveObject::~ClientActiveObject()
{ {
removeFromScene(); removeFromScene(true);
} }
ClientActiveObject* ClientActiveObject::create(u8 type, IGameDef *gamedef, ClientActiveObject* ClientActiveObject::create(u8 type, IGameDef *gamedef,

View File

@ -49,13 +49,19 @@ public:
virtual void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, virtual void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
IrrlichtDevice *irr){} IrrlichtDevice *irr){}
virtual void removeFromScene(){} virtual void removeFromScene(bool permanent){}
// 0 <= light_at_pos <= LIGHT_SUN // 0 <= light_at_pos <= LIGHT_SUN
virtual void updateLight(u8 light_at_pos){} virtual void updateLight(u8 light_at_pos){}
virtual v3s16 getLightPosition(){return v3s16(0,0,0);} virtual v3s16 getLightPosition(){return v3s16(0,0,0);}
virtual core::aabbox3d<f32>* getSelectionBox(){return NULL;} virtual core::aabbox3d<f32>* getSelectionBox(){return NULL;}
virtual core::aabbox3d<f32>* getCollisionBox(){return NULL;} virtual core::aabbox3d<f32>* getCollisionBox(){return NULL;}
virtual v3f getPosition(){return v3f(0,0,0);} virtual v3f getPosition(){return v3f(0,0,0);}
virtual scene::IMeshSceneNode *getMeshSceneNode(){return NULL;}
virtual scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode(){return NULL;}
virtual scene::IBillboardSceneNode *getSpriteSceneNode(){return NULL;}
virtual bool isPlayer(){return false;}
virtual bool isLocalPlayer(){return false;}
virtual void setAttachments(){}
virtual bool doShowSelectionBox(){return true;} virtual bool doShowSelectionBox(){return true;}
// Step object in time // Step object in time

View File

@ -67,9 +67,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
TOCLIENT_DETACHED_INVENTORY TOCLIENT_DETACHED_INVENTORY
PROTOCOL_VERSION 13: PROTOCOL_VERSION 13:
InventoryList field "Width" (deserialization fails with old versions) InventoryList field "Width" (deserialization fails with old versions)
PROTOCOL_VERSION 14:
Added transfer of player pressed keys to the server
Added new messages for mesh and bone animation, as well as attachments
GENERIC_CMD_SET_ANIMATION
GENERIC_CMD_SET_BONE_POSITION
GENERIC_CMD_SET_ATTACHMENT
*/ */
#define PROTOCOL_VERSION 13 #define PROTOCOL_VERSION 14
#define PROTOCOL_ID 0x4f457403 #define PROTOCOL_ID 0x4f457403
@ -366,6 +372,7 @@ enum ToServerCommand
[2+12] v3s32 speed*100 [2+12] v3s32 speed*100
[2+12+12] s32 pitch*100 [2+12+12] s32 pitch*100
[2+12+12+4] s32 yaw*100 [2+12+12+4] s32 yaw*100
[2+12+12+4+4] u32 keyPressed
*/ */
TOSERVER_GOTBLOCKS = 0x24, TOSERVER_GOTBLOCKS = 0x24,

View File

@ -40,7 +40,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/serialize.h" #include "util/serialize.h"
#include "util/mathconstants.h" #include "util/mathconstants.h"
#include "map.h" #include "map.h"
#include "main.h" // g_settings
#include <IMeshManipulator.h> #include <IMeshManipulator.h>
#include <IAnimatedMeshSceneNode.h>
#include <IBoneSceneNode.h>
class Settings; class Settings;
struct ToolCapabilities; struct ToolCapabilities;
@ -552,7 +555,8 @@ private:
// Only set at initialization // Only set at initialization
std::string m_name; std::string m_name;
bool m_is_player; bool m_is_player;
bool m_is_local_player; // determined locally bool m_is_local_player;
int m_id;
// Property-ish things // Property-ish things
ObjectProperties m_prop; ObjectProperties m_prop;
// //
@ -560,6 +564,7 @@ private:
IrrlichtDevice *m_irr; IrrlichtDevice *m_irr;
core::aabbox3d<f32> m_selection_box; core::aabbox3d<f32> m_selection_box;
scene::IMeshSceneNode *m_meshnode; scene::IMeshSceneNode *m_meshnode;
scene::IAnimatedMeshSceneNode *m_animated_meshnode;
scene::IBillboardSceneNode *m_spritenode; scene::IBillboardSceneNode *m_spritenode;
scene::ITextSceneNode* m_textnode; scene::ITextSceneNode* m_textnode;
v3f m_position; v3f m_position;
@ -573,6 +578,14 @@ private:
v2s16 m_tx_basepos; v2s16 m_tx_basepos;
bool m_initial_tx_basepos_set; bool m_initial_tx_basepos_set;
bool m_tx_select_horiz_by_yawpitch; bool m_tx_select_horiz_by_yawpitch;
v2f m_animation_range;
int m_animation_speed;
int m_animation_blend;
std::map<std::string, core::vector2d<v3f> > m_bone_position; // stores position and rotation for each bone name
std::string m_attachment_bone;
v3f m_attachment_position;
v3f m_attachment_rotation;
bool m_attached_to_local;
int m_anim_frame; int m_anim_frame;
int m_anim_num_frames; int m_anim_num_frames;
float m_anim_framelength; float m_anim_framelength;
@ -582,6 +595,7 @@ private:
bool m_visuals_expired; bool m_visuals_expired;
float m_step_distance_counter; float m_step_distance_counter;
u8 m_last_light; u8 m_last_light;
bool m_is_visible;
public: public:
GenericCAO(IGameDef *gamedef, ClientEnvironment *env): GenericCAO(IGameDef *gamedef, ClientEnvironment *env):
@ -589,11 +603,13 @@ public:
// //
m_is_player(false), m_is_player(false),
m_is_local_player(false), m_is_local_player(false),
m_id(0),
// //
m_smgr(NULL), m_smgr(NULL),
m_irr(NULL), m_irr(NULL),
m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.), m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
m_meshnode(NULL), m_meshnode(NULL),
m_animated_meshnode(NULL),
m_spritenode(NULL), m_spritenode(NULL),
m_textnode(NULL), m_textnode(NULL),
m_position(v3f(0,10*BS,0)), m_position(v3f(0,10*BS,0)),
@ -605,6 +621,14 @@ public:
m_tx_basepos(0,0), m_tx_basepos(0,0),
m_initial_tx_basepos_set(false), m_initial_tx_basepos_set(false),
m_tx_select_horiz_by_yawpitch(false), m_tx_select_horiz_by_yawpitch(false),
m_animation_range(v2f(0,0)),
m_animation_speed(15),
m_animation_blend(0),
m_bone_position(std::map<std::string, core::vector2d<v3f> >()),
m_attachment_bone(""),
m_attachment_position(v3f(0,0,0)),
m_attachment_rotation(v3f(0,0,0)),
m_attached_to_local(false),
m_anim_frame(0), m_anim_frame(0),
m_anim_num_frames(1), m_anim_num_frames(1),
m_anim_framelength(0.2), m_anim_framelength(0.2),
@ -612,7 +636,8 @@ public:
m_reset_textures_timer(-1), m_reset_textures_timer(-1),
m_visuals_expired(false), m_visuals_expired(false),
m_step_distance_counter(0), m_step_distance_counter(0),
m_last_light(255) m_last_light(255),
m_is_visible(false)
{ {
if(gamedef == NULL) if(gamedef == NULL)
ClientActiveObject::registerType(getType(), create); ClientActiveObject::registerType(getType(), create);
@ -632,6 +657,7 @@ public:
} }
m_name = deSerializeString(is); m_name = deSerializeString(is);
m_is_player = readU8(is); m_is_player = readU8(is);
m_id = readS16(is);
m_position = readV3F1000(is); m_position = readV3F1000(is);
m_yaw = readF1000(is); m_yaw = readF1000(is);
m_hp = readS16(is); m_hp = readS16(is);
@ -668,21 +694,113 @@ public:
} }
core::aabbox3d<f32>* getSelectionBox() core::aabbox3d<f32>* getSelectionBox()
{ {
if(!m_prop.is_visible || m_is_local_player) if(!m_prop.is_visible || !m_is_visible || m_is_local_player || getParent() != NULL)
return NULL; return NULL;
return &m_selection_box; return &m_selection_box;
} }
v3f getPosition() v3f getPosition()
{ {
if(getParent() != NULL){
if(m_meshnode)
return m_meshnode->getAbsolutePosition();
if(m_animated_meshnode)
return m_animated_meshnode->getAbsolutePosition();
if(m_spritenode)
return m_spritenode->getAbsolutePosition();
return m_position;
}
return pos_translator.vect_show; return pos_translator.vect_show;
} }
void removeFromScene() scene::IMeshSceneNode *getMeshSceneNode()
{ {
if(m_meshnode)
return m_meshnode;
return NULL;
}
scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode()
{
if(m_animated_meshnode)
return m_animated_meshnode;
return NULL;
}
scene::IBillboardSceneNode *getSpriteSceneNode()
{
if(m_spritenode)
return m_spritenode;
return NULL;
}
bool isPlayer()
{
return m_is_player;
}
bool isLocalPlayer()
{
return m_is_local_player;
}
void setAttachments()
{
updateAttachments();
}
ClientActiveObject *getParent()
{
ClientActiveObject *obj = NULL;
for(std::vector<core::vector2d<int> >::const_iterator cii = m_env->attachment_list.begin(); cii != m_env->attachment_list.end(); cii++)
{
if(cii->X == getId()){ // This ID is our child
if(cii->Y > 0){ // A parent ID exists for our child
if(cii->X != cii->Y){ // The parent and child ID are not the same
obj = m_env->getActiveObject(cii->Y);
}
}
break;
}
}
if(obj)
return obj;
return NULL;
}
void removeFromScene(bool permanent)
{
if(permanent) // Should be true when removing the object permanently and false when refreshing (eg: updating visuals)
{
// Detach this object's children
for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
{
if(ii->Y == getId()) // Is a child of our object
{
ii->Y = 0;
ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
if(obj)
obj->setAttachments();
}
}
// Delete this object from the attachments list
for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
{
if(ii->X == getId()) // Is our object
{
m_env->attachment_list.erase(ii);
break;
}
}
}
if(m_meshnode){ if(m_meshnode){
m_meshnode->remove(); m_meshnode->remove();
m_meshnode = NULL; m_meshnode = NULL;
} }
if(m_animated_meshnode){
m_animated_meshnode->remove();
m_animated_meshnode = NULL;
}
if(m_spritenode){ if(m_spritenode){
m_spritenode->remove(); m_spritenode->remove();
m_spritenode = NULL; m_spritenode = NULL;
@ -695,7 +813,7 @@ public:
m_smgr = smgr; m_smgr = smgr;
m_irr = irr; m_irr = irr;
if(m_meshnode != NULL || m_spritenode != NULL) if(m_meshnode != NULL || m_animated_meshnode != NULL || m_spritenode != NULL)
return; return;
m_visuals_expired = false; m_visuals_expired = false;
@ -791,7 +909,24 @@ public:
m_prop.visual_size.X)); m_prop.visual_size.X));
u8 li = m_last_light; u8 li = m_last_light;
setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li)); setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li));
} else if(m_prop.visual == "wielditem"){ }
else if(m_prop.visual == "mesh"){
infostream<<"GenericCAO::addToScene(): mesh"<<std::endl;
scene::IAnimatedMesh *mesh = smgr->getMesh(m_prop.mesh.c_str());
if(mesh)
{
m_animated_meshnode = smgr->addAnimatedMeshSceneNode(mesh, NULL);
m_animated_meshnode->animateJoints(); // Needed for some animations
m_animated_meshnode->setScale(v3f(m_prop.visual_size.X,
m_prop.visual_size.Y,
m_prop.visual_size.X));
u8 li = m_last_light;
setMeshColor(m_animated_meshnode->getMesh(), video::SColor(255,li,li,li));
}
else
errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl;
}
else if(m_prop.visual == "wielditem"){
infostream<<"GenericCAO::addToScene(): node"<<std::endl; infostream<<"GenericCAO::addToScene(): node"<<std::endl;
infostream<<"textures: "<<m_prop.textures.size()<<std::endl; infostream<<"textures: "<<m_prop.textures.size()<<std::endl;
if(m_prop.textures.size() >= 1){ if(m_prop.textures.size() >= 1){
@ -823,6 +958,8 @@ public:
scene::ISceneNode *node = NULL; scene::ISceneNode *node = NULL;
if(m_spritenode) if(m_spritenode)
node = m_spritenode; node = m_spritenode;
else if(m_animated_meshnode)
node = m_animated_meshnode;
else if(m_meshnode) else if(m_meshnode)
node = m_meshnode; node = m_meshnode;
if(node && m_is_player && !m_is_local_player){ if(node && m_is_player && !m_is_local_player){
@ -833,8 +970,11 @@ public:
wname.c_str(), video::SColor(255,255,255,255), node); wname.c_str(), video::SColor(255,255,255,255), node);
m_textnode->setPosition(v3f(0, BS*1.1, 0)); m_textnode->setPosition(v3f(0, BS*1.1, 0));
} }
updateNodePos(); updateNodePos();
updateAnimation();
updateBonePosition();
updateAttachments();
} }
void expireVisuals() void expireVisuals()
@ -844,19 +984,16 @@ public:
void updateLight(u8 light_at_pos) void updateLight(u8 light_at_pos)
{ {
bool is_visible = (m_hp != 0);
u8 li = decode_light(light_at_pos); u8 li = decode_light(light_at_pos);
if(li != m_last_light){ if(li != m_last_light){
m_last_light = li; m_last_light = li;
video::SColor color(255,li,li,li); video::SColor color(255,li,li,li);
if(m_meshnode){ if(m_meshnode)
setMeshColor(m_meshnode->getMesh(), color); setMeshColor(m_meshnode->getMesh(), color);
m_meshnode->setVisible(is_visible); if(m_animated_meshnode)
} setMeshColor(m_animated_meshnode->getMesh(), color);
if(m_spritenode){ if(m_spritenode)
m_spritenode->setColor(color); m_spritenode->setColor(color);
m_spritenode->setVisible(is_visible);
}
} }
} }
@ -867,12 +1004,21 @@ public:
void updateNodePos() void updateNodePos()
{ {
if(getParent() != NULL)
return;
if(m_meshnode){ if(m_meshnode){
m_meshnode->setPosition(pos_translator.vect_show); m_meshnode->setPosition(pos_translator.vect_show);
v3f rot = m_meshnode->getRotation(); v3f rot = m_meshnode->getRotation();
rot.Y = -m_yaw; rot.Y = -m_yaw;
m_meshnode->setRotation(rot); m_meshnode->setRotation(rot);
} }
if(m_animated_meshnode){
m_animated_meshnode->setPosition(pos_translator.vect_show);
v3f rot = m_animated_meshnode->getRotation();
rot.Y = -m_yaw;
m_animated_meshnode->setRotation(rot);
}
if(m_spritenode){ if(m_spritenode){
m_spritenode->setPosition(pos_translator.vect_show); m_spritenode->setPosition(pos_translator.vect_show);
} }
@ -880,56 +1026,116 @@ public:
void step(float dtime, ClientEnvironment *env) void step(float dtime, ClientEnvironment *env)
{ {
v3f lastpos = pos_translator.vect_show;
if(m_visuals_expired && m_smgr && m_irr){ if(m_visuals_expired && m_smgr && m_irr){
m_visuals_expired = false; m_visuals_expired = false;
removeFromScene();
// Attachments, part 1: All attached objects must be unparented first, or Irrlicht causes a segmentation fault
for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
{
if(ii->Y == getId()) // This is a child of our parent
{
ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
if(obj)
{
scene::IMeshSceneNode *m_child_meshnode = obj->getMeshSceneNode();
scene::IAnimatedMeshSceneNode *m_child_animated_meshnode = obj->getAnimatedMeshSceneNode();
scene::IBillboardSceneNode *m_child_spritenode = obj->getSpriteSceneNode();
if(m_child_meshnode)
m_child_meshnode->setParent(m_smgr->getRootSceneNode());
if(m_child_animated_meshnode)
m_child_animated_meshnode->setParent(m_smgr->getRootSceneNode());
if(m_child_spritenode)
m_child_spritenode->setParent(m_smgr->getRootSceneNode());
}
}
}
removeFromScene(false);
addToScene(m_smgr, m_gamedef->tsrc(), m_irr); addToScene(m_smgr, m_gamedef->tsrc(), m_irr);
// Attachments, part 2: Now that the parent has been refreshed, put its attachments back
for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
{
if(ii->Y == getId()) // This is a child of our parent
{
ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
if(obj)
obj->setAttachments();
}
}
} }
if(m_prop.physical){ // Make sure m_is_visible is always applied
core::aabbox3d<f32> box = m_prop.collisionbox; if(m_meshnode)
box.MinEdge *= BS; m_meshnode->setVisible(m_is_visible);
box.MaxEdge *= BS; if(m_animated_meshnode)
collisionMoveResult moveresult; m_animated_meshnode->setVisible(m_is_visible);
f32 pos_max_d = BS*0.125; // Distance per iteration if(m_spritenode)
f32 stepheight = 0; m_spritenode->setVisible(m_is_visible);
v3f p_pos = m_position; if(m_textnode)
v3f p_velocity = m_velocity; m_textnode->setVisible(m_is_visible);
v3f p_acceleration = m_acceleration;
IGameDef *gamedef = env->getGameDef();
moveresult = collisionMoveSimple(&env->getMap(), gamedef,
pos_max_d, box, stepheight, dtime,
p_pos, p_velocity, p_acceleration);
// Apply results
m_position = p_pos;
m_velocity = p_velocity;
m_acceleration = p_acceleration;
bool is_end_position = moveresult.collides;
pos_translator.update(m_position, is_end_position, dtime);
pos_translator.translate(dtime);
updateNodePos();
} else {
m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
m_velocity += dtime * m_acceleration;
pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
pos_translator.translate(dtime);
updateNodePos();
}
float moved = lastpos.getDistanceFrom(pos_translator.vect_show); if(getParent() != NULL) // Attachments should be glued to their parent by Irrlicht
m_step_distance_counter += moved; {
if(m_step_distance_counter > 1.5*BS){ // Set these for later
m_step_distance_counter = 0; m_position = getPosition();
if(!m_is_local_player && m_prop.makes_footstep_sound){ m_velocity = v3f(0,0,0);
INodeDefManager *ndef = m_gamedef->ndef(); m_acceleration = v3f(0,0,0);
v3s16 p = floatToInt(getPosition() + v3f(0, pos_translator.vect_show = m_position;
(m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
MapNode n = m_env->getMap().getNodeNoEx(p); if(m_is_local_player) // Update local player attachment position
SimpleSoundSpec spec = ndef->get(n).sound_footstep; {
m_gamedef->sound()->playSoundAt(spec, false, getPosition()); LocalPlayer *player = m_env->getLocalPlayer();
player->overridePosition = getParent()->getPosition();
}
}
else
{
v3f lastpos = pos_translator.vect_show;
if(m_prop.physical){
core::aabbox3d<f32> box = m_prop.collisionbox;
box.MinEdge *= BS;
box.MaxEdge *= BS;
collisionMoveResult moveresult;
f32 pos_max_d = BS*0.125; // Distance per iteration
f32 stepheight = 0;
v3f p_pos = m_position;
v3f p_velocity = m_velocity;
v3f p_acceleration = m_acceleration;
IGameDef *gamedef = env->getGameDef();
moveresult = collisionMoveSimple(&env->getMap(), gamedef,
pos_max_d, box, stepheight, dtime,
p_pos, p_velocity, p_acceleration);
// Apply results
m_position = p_pos;
m_velocity = p_velocity;
m_acceleration = p_acceleration;
bool is_end_position = moveresult.collides;
pos_translator.update(m_position, is_end_position, dtime);
pos_translator.translate(dtime);
updateNodePos();
} else {
m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
m_velocity += dtime * m_acceleration;
pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
pos_translator.translate(dtime);
updateNodePos();
}
float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
m_step_distance_counter += moved;
if(m_step_distance_counter > 1.5*BS){
m_step_distance_counter = 0;
if(!m_is_local_player && m_prop.makes_footstep_sound){
INodeDefManager *ndef = m_gamedef->ndef();
v3s16 p = floatToInt(getPosition() + v3f(0,
(m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
MapNode n = m_env->getMap().getNodeNoEx(p);
SimpleSoundSpec spec = ndef->get(n).sound_footstep;
m_gamedef->sound()->playSoundAt(spec, false, getPosition());
}
} }
} }
@ -950,7 +1156,7 @@ public:
updateTextures(""); updateTextures("");
} }
} }
if(fabs(m_prop.automatic_rotate) > 0.001){ if(getParent() == NULL && fabs(m_prop.automatic_rotate) > 0.001){
m_yaw += dtime * m_prop.automatic_rotate * 180 / M_PI; m_yaw += dtime * m_prop.automatic_rotate * 180 / M_PI;
updateNodePos(); updateNodePos();
} }
@ -1008,6 +1214,10 @@ public:
{ {
ITextureSource *tsrc = m_gamedef->tsrc(); ITextureSource *tsrc = m_gamedef->tsrc();
bool use_trilinear_filter = g_settings->getBool("trilinear_filter");
bool use_bilinear_filter = g_settings->getBool("bilinear_filter");
bool use_anisotropic_filter = g_settings->getBool("anisotropic_filter");
if(m_spritenode) if(m_spritenode)
{ {
if(m_prop.visual == "sprite") if(m_prop.visual == "sprite")
@ -1018,6 +1228,58 @@ public:
texturestring += mod; texturestring += mod;
m_spritenode->setMaterialTexture(0, m_spritenode->setMaterialTexture(0,
tsrc->getTextureRaw(texturestring)); tsrc->getTextureRaw(texturestring));
// This allows setting per-material colors. However, until a real lighting
// system is added, the code below will have no effect. Once MineTest
// has directional lighting, it should work automatically.
if(m_prop.colors.size() >= 1)
{
m_spritenode->getMaterial(0).AmbientColor = m_prop.colors[0];
m_spritenode->getMaterial(0).DiffuseColor = m_prop.colors[0];
m_spritenode->getMaterial(0).SpecularColor = m_prop.colors[0];
}
m_spritenode->getMaterial(0).setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
m_spritenode->getMaterial(0).setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
m_spritenode->getMaterial(0).setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
}
}
if(m_animated_meshnode)
{
if(m_prop.visual == "mesh")
{
for (u32 i = 0; i < m_prop.textures.size() && i < m_animated_meshnode->getMaterialCount(); ++i)
{
std::string texturestring = m_prop.textures[i];
if(texturestring == "")
continue; // Empty texture string means don't modify that material
texturestring += mod;
video::ITexture* texture = tsrc->getTextureRaw(texturestring);
if(!texture)
{
errorstream<<"GenericCAO::updateTextures(): Could not load texture "<<texturestring<<std::endl;
continue;
}
// Set material flags and texture
m_animated_meshnode->setMaterialTexture(i, texture);
video::SMaterial& material = m_animated_meshnode->getMaterial(i);
material.setFlag(video::EMF_LIGHTING, false);
material.setFlag(video::EMF_BILINEAR_FILTER, false);
m_animated_meshnode->getMaterial(i).setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
m_animated_meshnode->getMaterial(i).setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
m_animated_meshnode->getMaterial(i).setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
}
for (u32 i = 0; i < m_prop.colors.size() && i < m_animated_meshnode->getMaterialCount(); ++i)
{
// This allows setting per-material colors. However, until a real lighting
// system is added, the code below will have no effect. Once MineTest
// has directional lighting, it should work automatically.
m_animated_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
m_animated_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
m_animated_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
}
} }
} }
if(m_meshnode) if(m_meshnode)
@ -1044,6 +1306,20 @@ public:
material.setTexture(0, atlas); material.setTexture(0, atlas);
material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y); material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
material.getTextureMatrix(0).setTextureScale(size.X, size.Y); material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
// This allows setting per-material colors. However, until a real lighting
// system is added, the code below will have no effect. Once MineTest
// has directional lighting, it should work automatically.
if(m_prop.colors.size() > i)
{
m_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
m_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
m_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
}
m_meshnode->getMaterial(i).setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
m_meshnode->getMaterial(i).setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
m_meshnode->getMaterial(i).setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
} }
} }
else if(m_prop.visual == "upright_sprite") else if(m_prop.visual == "upright_sprite")
@ -1057,6 +1333,20 @@ public:
scene::IMeshBuffer *buf = mesh->getMeshBuffer(0); scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
buf->getMaterial().setTexture(0, buf->getMaterial().setTexture(0,
tsrc->getTextureRaw(tname)); tsrc->getTextureRaw(tname));
// This allows setting per-material colors. However, until a real lighting
// system is added, the code below will have no effect. Once MineTest
// has directional lighting, it should work automatically.
if(m_prop.colors.size() >= 1)
{
buf->getMaterial().AmbientColor = m_prop.colors[0];
buf->getMaterial().DiffuseColor = m_prop.colors[0];
buf->getMaterial().SpecularColor = m_prop.colors[0];
}
buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
} }
{ {
std::string tname = "unknown_object.png"; std::string tname = "unknown_object.png";
@ -1068,11 +1358,213 @@ public:
scene::IMeshBuffer *buf = mesh->getMeshBuffer(1); scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
buf->getMaterial().setTexture(0, buf->getMaterial().setTexture(0,
tsrc->getTextureRaw(tname)); tsrc->getTextureRaw(tname));
// This allows setting per-material colors. However, until a real lighting
// system is added, the code below will have no effect. Once MineTest
// has directional lighting, it should work automatically.
if(m_prop.colors.size() >= 2)
{
buf->getMaterial().AmbientColor = m_prop.colors[1];
buf->getMaterial().DiffuseColor = m_prop.colors[1];
buf->getMaterial().SpecularColor = m_prop.colors[1];
}
else if(m_prop.colors.size() >= 1)
{
buf->getMaterial().AmbientColor = m_prop.colors[0];
buf->getMaterial().DiffuseColor = m_prop.colors[0];
buf->getMaterial().SpecularColor = m_prop.colors[0];
}
buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
} }
} }
} }
} }
void updateAnimation()
{
if(m_animated_meshnode == NULL)
return;
m_animated_meshnode->setFrameLoop((int)m_animation_range.X, (int)m_animation_range.Y);
m_animated_meshnode->setAnimationSpeed(m_animation_speed);
m_animated_meshnode->setTransitionTime(m_animation_blend);
}
void updateBonePosition()
{
if(!m_bone_position.size() || m_animated_meshnode == NULL)
return;
m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render
for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
std::string bone_name = (*ii).first;
v3f bone_pos = (*ii).second.X;
v3f bone_rot = (*ii).second.Y;
irr::scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
if(bone)
{
bone->setPosition(bone_pos);
bone->setRotation(bone_rot);
}
}
}
void updateAttachments()
{
m_attached_to_local = getParent() != NULL && getParent()->isLocalPlayer();
m_is_visible = !m_attached_to_local; // Objects attached to the local player should always be hidden
if(getParent() == NULL || m_attached_to_local) // Detach or don't attach
{
if(m_meshnode)
{
v3f old_position = m_meshnode->getAbsolutePosition();
v3f old_rotation = m_meshnode->getRotation();
m_meshnode->setParent(m_smgr->getRootSceneNode());
m_meshnode->setPosition(old_position);
m_meshnode->setRotation(old_rotation);
m_meshnode->updateAbsolutePosition();
}
if(m_animated_meshnode)
{
v3f old_position = m_animated_meshnode->getAbsolutePosition();
v3f old_rotation = m_animated_meshnode->getRotation();
m_animated_meshnode->setParent(m_smgr->getRootSceneNode());
m_animated_meshnode->setPosition(old_position);
m_animated_meshnode->setRotation(old_rotation);
m_animated_meshnode->updateAbsolutePosition();
}
if(m_spritenode)
{
v3f old_position = m_spritenode->getAbsolutePosition();
v3f old_rotation = m_spritenode->getRotation();
m_spritenode->setParent(m_smgr->getRootSceneNode());
m_spritenode->setPosition(old_position);
m_spritenode->setRotation(old_rotation);
m_spritenode->updateAbsolutePosition();
}
if(m_is_local_player)
{
LocalPlayer *player = m_env->getLocalPlayer();
player->isAttached = false;
}
}
else // Attach
{
scene::IMeshSceneNode *parent_mesh = NULL;
if(getParent()->getMeshSceneNode())
parent_mesh = getParent()->getMeshSceneNode();
scene::IAnimatedMeshSceneNode *parent_animated_mesh = NULL;
if(getParent()->getAnimatedMeshSceneNode())
parent_animated_mesh = getParent()->getAnimatedMeshSceneNode();
scene::IBillboardSceneNode *parent_sprite = NULL;
if(getParent()->getSpriteSceneNode())
parent_sprite = getParent()->getSpriteSceneNode();
scene::IBoneSceneNode *parent_bone = NULL;
if(parent_animated_mesh && m_attachment_bone != "")
parent_bone = parent_animated_mesh->getJointNode(m_attachment_bone.c_str());
// The spaghetti code below makes sure attaching works if either the parent or child is a spritenode, meshnode, or animatedmeshnode
// TODO: Perhaps use polymorphism here to save code duplication
if(m_meshnode){
if(parent_bone){
m_meshnode->setParent(parent_bone);
m_meshnode->setPosition(m_attachment_position);
m_meshnode->setRotation(m_attachment_rotation);
m_meshnode->updateAbsolutePosition();
}
else
{
if(parent_mesh){
m_meshnode->setParent(parent_mesh);
m_meshnode->setPosition(m_attachment_position);
m_meshnode->setRotation(m_attachment_rotation);
m_meshnode->updateAbsolutePosition();
}
else if(parent_animated_mesh){
m_meshnode->setParent(parent_animated_mesh);
m_meshnode->setPosition(m_attachment_position);
m_meshnode->setRotation(m_attachment_rotation);
m_meshnode->updateAbsolutePosition();
}
else if(parent_sprite){
m_meshnode->setParent(parent_sprite);
m_meshnode->setPosition(m_attachment_position);
m_meshnode->setRotation(m_attachment_rotation);
m_meshnode->updateAbsolutePosition();
}
}
}
if(m_animated_meshnode){
if(parent_bone){
m_animated_meshnode->setParent(parent_bone);
m_animated_meshnode->setPosition(m_attachment_position);
m_animated_meshnode->setRotation(m_attachment_rotation);
m_animated_meshnode->updateAbsolutePosition();
}
else
{
if(parent_mesh){
m_animated_meshnode->setParent(parent_mesh);
m_animated_meshnode->setPosition(m_attachment_position);
m_animated_meshnode->setRotation(m_attachment_rotation);
m_animated_meshnode->updateAbsolutePosition();
}
else if(parent_animated_mesh){
m_animated_meshnode->setParent(parent_animated_mesh);
m_animated_meshnode->setPosition(m_attachment_position);
m_animated_meshnode->setRotation(m_attachment_rotation);
m_animated_meshnode->updateAbsolutePosition();
}
else if(parent_sprite){
m_animated_meshnode->setParent(parent_sprite);
m_animated_meshnode->setPosition(m_attachment_position);
m_animated_meshnode->setRotation(m_attachment_rotation);
m_animated_meshnode->updateAbsolutePosition();
}
}
}
if(m_spritenode){
if(parent_bone){
m_spritenode->setParent(parent_bone);
m_spritenode->setPosition(m_attachment_position);
m_spritenode->setRotation(m_attachment_rotation);
m_spritenode->updateAbsolutePosition();
}
else
{
if(parent_mesh){
m_spritenode->setParent(parent_mesh);
m_spritenode->setPosition(m_attachment_position);
m_spritenode->setRotation(m_attachment_rotation);
m_spritenode->updateAbsolutePosition();
}
else if(parent_animated_mesh){
m_spritenode->setParent(parent_animated_mesh);
m_spritenode->setPosition(m_attachment_position);
m_spritenode->setRotation(m_attachment_rotation);
m_spritenode->updateAbsolutePosition();
}
else if(parent_sprite){
m_spritenode->setParent(parent_sprite);
m_spritenode->setPosition(m_attachment_position);
m_spritenode->setRotation(m_attachment_rotation);
m_spritenode->updateAbsolutePosition();
}
}
}
if(m_is_local_player)
{
LocalPlayer *player = m_env->getLocalPlayer();
player->isAttached = true;
}
}
}
void processMessage(const std::string &data) void processMessage(const std::string &data)
{ {
//infostream<<"GenericCAO: Got message"<<std::endl; //infostream<<"GenericCAO: Got message"<<std::endl;
@ -1099,6 +1591,8 @@ public:
} }
else if(cmd == GENERIC_CMD_UPDATE_POSITION) else if(cmd == GENERIC_CMD_UPDATE_POSITION)
{ {
// Not sent by the server if this object is an attachment.
// We might however get here if the server notices the object being detached before the client.
m_position = readV3F1000(is); m_position = readV3F1000(is);
m_velocity = readV3F1000(is); m_velocity = readV3F1000(is);
m_acceleration = readV3F1000(is); m_acceleration = readV3F1000(is);
@ -1112,7 +1606,10 @@ public:
// the ground due to sucky collision detection... // the ground due to sucky collision detection...
if(m_prop.physical) if(m_prop.physical)
m_position += v3f(0,0.002,0); m_position += v3f(0,0.002,0);
if(getParent() != NULL) // Just in case
return;
if(do_interpolate){ if(do_interpolate){
if(!m_prop.physical) if(!m_prop.physical)
pos_translator.update(m_position, is_end_position, update_interval); pos_translator.update(m_position, is_end_position, update_interval);
@ -1140,6 +1637,41 @@ public:
updateTexturePos(); updateTexturePos();
} }
else if(cmd == GENERIC_CMD_SET_ANIMATION)
{
m_animation_range = readV2F1000(is);
m_animation_speed = readF1000(is);
m_animation_blend = readF1000(is);
updateAnimation();
}
else if(cmd == GENERIC_CMD_SET_BONE_POSITION)
{
std::string bone = deSerializeString(is);
v3f position = readV3F1000(is);
v3f rotation = readV3F1000(is);
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
updateBonePosition();
}
else if(cmd == GENERIC_CMD_SET_ATTACHMENT)
{
// If an entry already exists for this object, delete it first to avoid duplicates
for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
{
if(ii->X == getId()) // This is the ID of our object
{
m_env->attachment_list.erase(ii);
break;
}
}
m_env->attachment_list.push_back(core::vector2d<int>(getId(), readS16(is)));
m_attachment_bone = deSerializeString(is);
m_attachment_position = readV3F1000(is);
m_attachment_rotation = readV3F1000(is);
updateAttachments();
}
else if(cmd == GENERIC_CMD_PUNCHED) else if(cmd == GENERIC_CMD_PUNCHED)
{ {
/*s16 damage =*/ readS16(is); /*s16 damage =*/ readS16(is);

View File

@ -355,7 +355,10 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
m_last_sent_velocity(0,0,0), m_last_sent_velocity(0,0,0),
m_last_sent_position_timer(0), m_last_sent_position_timer(0),
m_last_sent_move_precision(0), m_last_sent_move_precision(0),
m_armor_groups_sent(false) m_armor_groups_sent(false),
m_animation_sent(false),
m_bone_position_sent(false),
m_attachment_sent(false)
{ {
// Only register type if no environment supplied // Only register type if no environment supplied
if(env == NULL){ if(env == NULL){
@ -429,6 +432,17 @@ ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
return sao; return sao;
} }
bool LuaEntitySAO::isAttached()
{
if(!m_attachment_parent_id)
return false;
// Check if the parent still exists
ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
if(obj)
return true;
return false;
}
void LuaEntitySAO::step(float dtime, bool send_recommended) void LuaEntitySAO::step(float dtime, bool send_recommended)
{ {
if(!m_properties_sent) if(!m_properties_sent)
@ -440,30 +454,52 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
m_messages_out.push_back(aom); m_messages_out.push_back(aom);
} }
// If attached, check that our parent is still there. If it isn't, detach.
if(m_attachment_parent_id && !isAttached())
{
m_attachment_parent_id = 0;
m_attachment_bone = "";
m_attachment_position = v3f(0,0,0);
m_attachment_rotation = v3f(0,0,0);
sendPosition(false, true);
}
m_last_sent_position_timer += dtime; m_last_sent_position_timer += dtime;
if(m_prop.physical){ // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
core::aabbox3d<f32> box = m_prop.collisionbox; // If the object gets detached this comes into effect automatically from the last known origin
box.MinEdge *= BS; if(isAttached())
box.MaxEdge *= BS; {
collisionMoveResult moveresult; v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
f32 pos_max_d = BS*0.25; // Distance per iteration m_base_position = pos;
f32 stepheight = 0; // Maximum climbable step height m_velocity = v3f(0,0,0);
v3f p_pos = m_base_position; m_acceleration = v3f(0,0,0);
v3f p_velocity = m_velocity; }
v3f p_acceleration = m_acceleration; else
IGameDef *gamedef = m_env->getGameDef(); {
moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, if(m_prop.physical){
pos_max_d, box, stepheight, dtime, core::aabbox3d<f32> box = m_prop.collisionbox;
p_pos, p_velocity, p_acceleration); box.MinEdge *= BS;
// Apply results box.MaxEdge *= BS;
m_base_position = p_pos; collisionMoveResult moveresult;
m_velocity = p_velocity; f32 pos_max_d = BS*0.25; // Distance per iteration
m_acceleration = p_acceleration; f32 stepheight = 0; // Maximum climbable step height
} else { v3f p_pos = m_base_position;
m_base_position += dtime * m_velocity + 0.5 * dtime v3f p_velocity = m_velocity;
* dtime * m_acceleration; v3f p_acceleration = m_acceleration;
m_velocity += dtime * m_acceleration; IGameDef *gamedef = m_env->getGameDef();
moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
pos_max_d, box, stepheight, dtime,
p_pos, p_velocity, p_acceleration);
// Apply results
m_base_position = p_pos;
m_velocity = p_velocity;
m_acceleration = p_acceleration;
} else {
m_base_position += dtime * m_velocity + 0.5 * dtime
* dtime * m_acceleration;
m_velocity += dtime * m_acceleration;
}
} }
if(m_registered){ if(m_registered){
@ -473,20 +509,23 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
if(send_recommended == false) if(send_recommended == false)
return; return;
// TODO: force send when acceleration changes enough? if(!isAttached())
float minchange = 0.2*BS; {
if(m_last_sent_position_timer > 1.0){ // TODO: force send when acceleration changes enough?
minchange = 0.01*BS; float minchange = 0.2*BS;
} else if(m_last_sent_position_timer > 0.2){ if(m_last_sent_position_timer > 1.0){
minchange = 0.05*BS; minchange = 0.01*BS;
} } else if(m_last_sent_position_timer > 0.2){
float move_d = m_base_position.getDistanceFrom(m_last_sent_position); minchange = 0.05*BS;
move_d += m_last_sent_move_precision; }
float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity); float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
if(move_d > minchange || vel_d > minchange || move_d += m_last_sent_move_precision;
fabs(m_yaw - m_last_sent_yaw) > 1.0){ float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
sendPosition(true, false); if(move_d > minchange || vel_d > minchange ||
fabs(m_yaw - m_last_sent_yaw) > 1.0){
sendPosition(true, false);
}
} }
if(m_armor_groups_sent == false){ if(m_armor_groups_sent == false){
@ -497,6 +536,32 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
ActiveObjectMessage aom(getId(), true, str); ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom); m_messages_out.push_back(aom);
} }
if(m_animation_sent == false){
m_animation_sent = true;
std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
if(m_bone_position_sent == false){
m_bone_position_sent = true;
for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
}
if(m_attachment_sent == false){
m_attachment_sent = true;
std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
} }
std::string LuaEntitySAO::getClientInitializationData() std::string LuaEntitySAO::getClientInitializationData()
@ -504,13 +569,21 @@ std::string LuaEntitySAO::getClientInitializationData()
std::ostringstream os(std::ios::binary); std::ostringstream os(std::ios::binary);
writeU8(os, 0); // version writeU8(os, 0); // version
os<<serializeString(""); // name os<<serializeString(""); // name
writeS16(os, getId()); //id
writeU8(os, 0); // is_player writeU8(os, 0); // is_player
writeV3F1000(os, m_base_position); writeV3F1000(os, m_base_position);
writeF1000(os, m_yaw); writeF1000(os, m_yaw);
writeS16(os, m_hp); writeS16(os, m_hp);
writeU8(os, 2); // number of messages stuffed in here
writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
os<<serializeLongString(getPropertyPacket()); // message 1 os<<serializeLongString(getPropertyPacket()); // message 1
os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
}
os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
// return result // return result
return os.str(); return os.str();
} }
@ -550,6 +623,10 @@ int LuaEntitySAO::punch(v3f dir,
m_removed = true; m_removed = true;
return 0; return 0;
} }
// It's best that attachments cannot be punched
if(isAttached())
return 0;
ItemStack *punchitem = NULL; ItemStack *punchitem = NULL;
ItemStack punchitem_static; ItemStack punchitem_static;
@ -594,18 +671,25 @@ void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
{ {
if(!m_registered) if(!m_registered)
return; return;
// It's best that attachments cannot be clicked
if(isAttached())
return;
lua_State *L = m_env->getLua(); lua_State *L = m_env->getLua();
scriptapi_luaentity_rightclick(L, m_id, clicker); scriptapi_luaentity_rightclick(L, m_id, clicker);
} }
void LuaEntitySAO::setPos(v3f pos) void LuaEntitySAO::setPos(v3f pos)
{ {
if(isAttached())
return;
m_base_position = pos; m_base_position = pos;
sendPosition(false, true); sendPosition(false, true);
} }
void LuaEntitySAO::moveTo(v3f pos, bool continuous) void LuaEntitySAO::moveTo(v3f pos, bool continuous)
{ {
if(isAttached())
return;
m_base_position = pos; m_base_position = pos;
if(!continuous) if(!continuous)
sendPosition(true, true); sendPosition(true, true);
@ -644,6 +728,37 @@ void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
m_armor_groups_sent = false; m_armor_groups_sent = false;
} }
void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
{
m_animation_range = frame_range;
m_animation_speed = frame_speed;
m_animation_blend = frame_blend;
m_animation_sent = false;
}
void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
{
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
m_bone_position_sent = false;
}
void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
{
// Attachments need to be handled on both the server and client.
// If we just attach on the server, we can only copy the position of the parent. Attachments
// are still sent to clients at an interval so players might see them lagging, plus we can't
// read and attach to skeletal bones.
// If we just attach on the client, the server still sees the child at its original location.
// This breaks some things so we also give the server the most accurate representation
// even if players only see the client changes.
m_attachment_parent_id = parent_id;
m_attachment_bone = bone;
m_attachment_position = position;
m_attachment_rotation = rotation;
m_attachment_sent = false;
}
ObjectProperties* LuaEntitySAO::accessObjectProperties() ObjectProperties* LuaEntitySAO::accessObjectProperties()
{ {
return &m_prop; return &m_prop;
@ -718,6 +833,10 @@ std::string LuaEntitySAO::getPropertyPacket()
void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
{ {
// If the object is attached client-side, don't waste bandwidth sending its position to clients
if(isAttached())
return;
m_last_sent_move_precision = m_base_position.getDistanceFrom( m_last_sent_move_precision = m_base_position.getDistanceFrom(
m_last_sent_position); m_last_sent_position);
m_last_sent_position_timer = 0; m_last_sent_position_timer = 0;
@ -765,8 +884,11 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
m_properties_sent(true), m_properties_sent(true),
m_privs(privs), m_privs(privs),
m_is_singleplayer(is_singleplayer), m_is_singleplayer(is_singleplayer),
m_animation_sent(false),
m_bone_position_sent(false),
m_attachment_sent(false),
// public // public
m_teleported(false), m_moved(false),
m_inventory_not_sent(false), m_inventory_not_sent(false),
m_hp_not_sent(false), m_hp_not_sent(false),
m_wielded_item_not_sent(false) m_wielded_item_not_sent(false)
@ -782,13 +904,17 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
m_prop.physical = false; m_prop.physical = false;
m_prop.weight = 75; m_prop.weight = 75;
m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.); m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
// start of default appearance, this should be overwritten by LUA
m_prop.visual = "upright_sprite"; m_prop.visual = "upright_sprite";
m_prop.visual_size = v2f(1, 2); m_prop.visual_size = v2f(1, 2);
m_prop.textures.clear(); m_prop.textures.clear();
m_prop.textures.push_back("player.png"); m_prop.textures.push_back("player.png");
m_prop.textures.push_back("player_back.png"); m_prop.textures.push_back("player_back.png");
m_prop.colors.clear();
m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
m_prop.spritediv = v2s16(1,1); m_prop.spritediv = v2s16(1,1);
m_prop.is_visible = (getHP() != 0); // end of default appearance
m_prop.is_visible = true;
m_prop.makes_footstep_sound = true; m_prop.makes_footstep_sound = true;
} }
@ -842,12 +968,21 @@ std::string PlayerSAO::getClientInitializationData()
writeU8(os, 0); // version writeU8(os, 0); // version
os<<serializeString(m_player->getName()); // name os<<serializeString(m_player->getName()); // name
writeU8(os, 1); // is_player writeU8(os, 1); // is_player
writeS16(os, getId()); //id
writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0)); writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
writeF1000(os, m_player->getYaw()); writeF1000(os, m_player->getYaw());
writeS16(os, getHP()); writeS16(os, getHP());
writeU8(os, 2); // number of messages stuffed in here
writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
os<<serializeLongString(getPropertyPacket()); // message 1 os<<serializeLongString(getPropertyPacket()); // message 1
os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
}
os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
// return result
return os.str(); return os.str();
} }
@ -857,6 +992,17 @@ std::string PlayerSAO::getStaticData()
return ""; return "";
} }
bool PlayerSAO::isAttached()
{
if(!m_attachment_parent_id)
return false;
// Check if the parent still exists
ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
if(obj)
return true;
return false;
}
void PlayerSAO::step(float dtime, bool send_recommended) void PlayerSAO::step(float dtime, bool send_recommended)
{ {
if(!m_properties_sent) if(!m_properties_sent)
@ -868,73 +1014,102 @@ void PlayerSAO::step(float dtime, bool send_recommended)
m_messages_out.push_back(aom); m_messages_out.push_back(aom);
} }
// If attached, check that our parent is still there. If it isn't, detach.
if(m_attachment_parent_id && !isAttached())
{
m_attachment_parent_id = 0;
m_attachment_bone = "";
m_attachment_position = v3f(0,0,0);
m_attachment_rotation = v3f(0,0,0);
m_player->setPosition(m_last_good_position);
m_moved = true;
}
m_time_from_last_punch += dtime; m_time_from_last_punch += dtime;
m_nocheat_dig_time += dtime; m_nocheat_dig_time += dtime;
if(m_is_singleplayer || g_settings->getBool("disable_anticheat")) // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
// If the object gets detached this comes into effect automatically from the last known origin
if(isAttached())
{ {
m_last_good_position = m_player->getPosition(); v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
m_last_good_position = pos;
m_last_good_position_age = 0; m_last_good_position_age = 0;
m_player->setPosition(pos);
} }
else else
{ {
/* if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
Check player movements {
m_last_good_position = m_player->getPosition();
NOTE: Actually the server should handle player physics like the
client does and compare player's position to what is calculated
on our side. This is required when eg. players fly due to an
explosion. Altough a node-based alternative might be possible
too, and much more lightweight.
*/
float player_max_speed = 0;
float player_max_speed_up = 0;
if(m_privs.count("fast") != 0){
// Fast speed
player_max_speed = BS * 20;
player_max_speed_up = BS * 20;
} else {
// Normal speed
player_max_speed = BS * 4.0;
player_max_speed_up = BS * 4.0;
}
// Tolerance
player_max_speed *= 2.5;
player_max_speed_up *= 2.5;
m_last_good_position_age += dtime;
if(m_last_good_position_age >= 1.0){
float age = m_last_good_position_age;
v3f diff = (m_player->getPosition() - m_last_good_position);
float d_vert = diff.Y;
diff.Y = 0;
float d_horiz = diff.getLength();
/*infostream<<m_player->getName()<<"'s horizontal speed is "
<<(d_horiz/age)<<std::endl;*/
if(d_horiz <= age * player_max_speed &&
(d_vert < 0 || d_vert < age * player_max_speed_up)){
m_last_good_position = m_player->getPosition();
} else {
actionstream<<"Player "<<m_player->getName()
<<" moved too fast; resetting position"
<<std::endl;
m_player->setPosition(m_last_good_position);
m_teleported = true;
}
m_last_good_position_age = 0; m_last_good_position_age = 0;
} }
else
{
/*
Check player movements
NOTE: Actually the server should handle player physics like the
client does and compare player's position to what is calculated
on our side. This is required when eg. players fly due to an
explosion. Altough a node-based alternative might be possible
too, and much more lightweight.
*/
float player_max_speed = 0;
float player_max_speed_up = 0;
if(m_privs.count("fast") != 0){
// Fast speed
player_max_speed = BS * 20;
player_max_speed_up = BS * 20;
} else {
// Normal speed
player_max_speed = BS * 4.0;
player_max_speed_up = BS * 4.0;
}
// Tolerance
player_max_speed *= 2.5;
player_max_speed_up *= 2.5;
m_last_good_position_age += dtime;
if(m_last_good_position_age >= 1.0){
float age = m_last_good_position_age;
v3f diff = (m_player->getPosition() - m_last_good_position);
float d_vert = diff.Y;
diff.Y = 0;
float d_horiz = diff.getLength();
/*infostream<<m_player->getName()<<"'s horizontal speed is "
<<(d_horiz/age)<<std::endl;*/
if(d_horiz <= age * player_max_speed &&
(d_vert < 0 || d_vert < age * player_max_speed_up)){
m_last_good_position = m_player->getPosition();
} else {
actionstream<<"Player "<<m_player->getName()
<<" moved too fast; resetting position"
<<std::endl;
m_player->setPosition(m_last_good_position);
m_moved = true;
}
m_last_good_position_age = 0;
}
}
} }
if(send_recommended == false) if(send_recommended == false)
return; return;
if(m_position_not_sent) // If the object is attached client-side, don't waste bandwidth sending its position to clients
if(m_position_not_sent && !isAttached())
{ {
m_position_not_sent = false; m_position_not_sent = false;
float update_interval = m_env->getSendRecommendedInterval(); float update_interval = m_env->getSendRecommendedInterval();
v3f pos;
if(isAttached()) // Just in case we ever do send attachment position too
pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
else
pos = m_player->getPosition() + v3f(0,BS*1,0);
std::string str = gob_cmd_update_position( std::string str = gob_cmd_update_position(
m_player->getPosition() + v3f(0,BS*1,0), pos,
v3f(0,0,0), v3f(0,0,0),
v3f(0,0,0), v3f(0,0,0),
m_player->getYaw(), m_player->getYaw(),
@ -961,32 +1136,63 @@ void PlayerSAO::step(float dtime, bool send_recommended)
ActiveObjectMessage aom(getId(), true, str); ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom); m_messages_out.push_back(aom);
} }
if(m_animation_sent == false){
m_animation_sent = true;
std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
if(m_bone_position_sent == false){
m_bone_position_sent = true;
for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
}
if(m_attachment_sent == false){
m_attachment_sent = true;
std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
} }
void PlayerSAO::setBasePosition(const v3f &position) void PlayerSAO::setBasePosition(const v3f &position)
{ {
// This needs to be ran for attachments too
ServerActiveObject::setBasePosition(position); ServerActiveObject::setBasePosition(position);
m_position_not_sent = true; m_position_not_sent = true;
} }
void PlayerSAO::setPos(v3f pos) void PlayerSAO::setPos(v3f pos)
{ {
if(isAttached())
return;
m_player->setPosition(pos); m_player->setPosition(pos);
// Movement caused by this command is always valid // Movement caused by this command is always valid
m_last_good_position = pos; m_last_good_position = pos;
m_last_good_position_age = 0; m_last_good_position_age = 0;
// Force position change on client // Force position change on client
m_teleported = true; m_moved = true;
} }
void PlayerSAO::moveTo(v3f pos, bool continuous) void PlayerSAO::moveTo(v3f pos, bool continuous)
{ {
if(isAttached())
return;
m_player->setPosition(pos); m_player->setPosition(pos);
// Movement caused by this command is always valid // Movement caused by this command is always valid
m_last_good_position = pos; m_last_good_position = pos;
m_last_good_position_age = 0; m_last_good_position_age = 0;
// Force position change on client // Force position change on client
m_teleported = true; m_moved = true;
} }
int PlayerSAO::punch(v3f dir, int PlayerSAO::punch(v3f dir,
@ -994,6 +1200,10 @@ int PlayerSAO::punch(v3f dir,
ServerActiveObject *puncher, ServerActiveObject *puncher,
float time_from_last_punch) float time_from_last_punch)
{ {
// It's best that attachments cannot be punched
if(isAttached())
return 0;
if(!toolcap) if(!toolcap)
return 0; return 0;
@ -1075,6 +1285,39 @@ void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
m_armor_groups_sent = false; m_armor_groups_sent = false;
} }
void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
{
// store these so they can be updated to clients
m_animation_range = frame_range;
m_animation_speed = frame_speed;
m_animation_blend = frame_blend;
m_animation_sent = false;
}
void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
{
// store these so they can be updated to clients
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
m_bone_position_sent = false;
}
void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
{
// Attachments need to be handled on both the server and client.
// If we just attach on the server, we can only copy the position of the parent. Attachments
// are still sent to clients at an interval so players might see them lagging, plus we can't
// read and attach to skeletal bones.
// If we just attach on the client, the server still sees the child at its original location.
// This breaks some things so we also give the server the most accurate representation
// even if players only see the client changes.
m_attachment_parent_id = parent_id;
m_attachment_bone = bone;
m_attachment_position = position;
m_attachment_rotation = rotation;
m_attachment_sent = false;
}
ObjectProperties* PlayerSAO::accessObjectProperties() ObjectProperties* PlayerSAO::accessObjectProperties()
{ {
return &m_prop; return &m_prop;
@ -1138,7 +1381,7 @@ void PlayerSAO::disconnected()
std::string PlayerSAO::getPropertyPacket() std::string PlayerSAO::getPropertyPacket()
{ {
m_prop.is_visible = (getHP() != 0); m_prop.is_visible = (true);
return gob_cmd_set_properties(m_prop); return gob_cmd_set_properties(m_prop);
} }

View File

@ -46,6 +46,7 @@ public:
virtual void addedToEnvironment(u32 dtime_s); virtual void addedToEnvironment(u32 dtime_s);
static ServerActiveObject* create(ServerEnvironment *env, v3f pos, static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
const std::string &data); const std::string &data);
bool isAttached();
void step(float dtime, bool send_recommended); void step(float dtime, bool send_recommended);
std::string getClientInitializationData(); std::string getClientInitializationData();
std::string getStaticData(); std::string getStaticData();
@ -61,6 +62,9 @@ public:
void setHP(s16 hp); void setHP(s16 hp);
s16 getHP() const; s16 getHP() const;
void setArmorGroups(const ItemGroupList &armor_groups); void setArmorGroups(const ItemGroupList &armor_groups);
void setAnimation(v2f frame_range, float frame_speed, float frame_blend);
void setBonePosition(std::string bone, v3f position, v3f rotation);
void setAttachment(int parent_id, std::string bone, v3f position, v3f rotation);
ObjectProperties* accessObjectProperties(); ObjectProperties* accessObjectProperties();
void notifyObjectPropertiesModified(); void notifyObjectPropertiesModified();
/* LuaEntitySAO-specific */ /* LuaEntitySAO-specific */
@ -96,6 +100,20 @@ private:
float m_last_sent_position_timer; float m_last_sent_position_timer;
float m_last_sent_move_precision; float m_last_sent_move_precision;
bool m_armor_groups_sent; bool m_armor_groups_sent;
v2f m_animation_range;
float m_animation_speed;
float m_animation_blend;
bool m_animation_sent;
std::map<std::string, core::vector2d<v3f> > m_bone_position;
bool m_bone_position_sent;
int m_attachment_parent_id;
std::string m_attachment_bone;
v3f m_attachment_position;
v3f m_attachment_rotation;
bool m_attachment_sent;
}; };
/* /*
@ -124,6 +142,7 @@ public:
bool unlimitedTransferDistance() const; bool unlimitedTransferDistance() const;
std::string getClientInitializationData(); std::string getClientInitializationData();
std::string getStaticData(); std::string getStaticData();
bool isAttached();
void step(float dtime, bool send_recommended); void step(float dtime, bool send_recommended);
void setBasePosition(const v3f &position); void setBasePosition(const v3f &position);
void setPos(v3f pos); void setPos(v3f pos);
@ -142,6 +161,9 @@ public:
void setHP(s16 hp); void setHP(s16 hp);
void setArmorGroups(const ItemGroupList &armor_groups); void setArmorGroups(const ItemGroupList &armor_groups);
void setAnimation(v2f frame_range, float frame_speed, float frame_blend);
void setBonePosition(std::string bone, v3f position, v3f rotation);
void setAttachment(int parent_id, std::string bone, v3f position, v3f rotation);
ObjectProperties* accessObjectProperties(); ObjectProperties* accessObjectProperties();
void notifyObjectPropertiesModified(); void notifyObjectPropertiesModified();
@ -229,15 +251,32 @@ private:
bool m_position_not_sent; bool m_position_not_sent;
ItemGroupList m_armor_groups; ItemGroupList m_armor_groups;
bool m_armor_groups_sent; bool m_armor_groups_sent;
bool m_properties_sent; bool m_properties_sent;
struct ObjectProperties m_prop; struct ObjectProperties m_prop;
// Cached privileges for enforcement // Cached privileges for enforcement
std::set<std::string> m_privs; std::set<std::string> m_privs;
bool m_is_singleplayer; bool m_is_singleplayer;
v2f m_animation_range;
float m_animation_speed;
float m_animation_blend;
bool m_animation_sent;
std::map<std::string, core::vector2d<v3f> > m_bone_position; // Stores position and rotation for each bone name
bool m_bone_position_sent;
int m_attachment_parent_id;
std::string m_attachment_bone;
v3f m_attachment_position;
v3f m_attachment_rotation;
bool m_attachment_sent;
public: public:
// Some flags used by Server // Some flags used by Server
bool m_teleported; bool m_moved;
bool m_inventory_not_sent; bool m_inventory_not_sent;
bool m_hp_not_sent; bool m_hp_not_sent;
bool m_wielded_item_not_sent; bool m_wielded_item_not_sent;

View File

@ -2315,7 +2315,7 @@ void ClientEnvironment::removeActiveObject(u16 id)
<<"id="<<id<<" not found"<<std::endl; <<"id="<<id<<" not found"<<std::endl;
return; return;
} }
obj->removeFromScene(); obj->removeFromScene(true);
delete obj; delete obj;
m_active_objects.remove(id); m_active_objects.remove(id);
} }

View File

@ -463,6 +463,8 @@ public:
// Get event from queue. CEE_NONE is returned if queue is empty. // Get event from queue. CEE_NONE is returned if queue is empty.
ClientEnvEvent getClientEvent(); ClientEnvEvent getClientEvent();
std::vector<core::vector2d<int> > attachment_list; // X is child ID, Y is parent ID
private: private:
ClientMap *m_map; ClientMap *m_map;

View File

@ -1881,6 +1881,8 @@ void the_game(
bool a_jump, bool a_jump,
bool a_superspeed, bool a_superspeed,
bool a_sneak, bool a_sneak,
bool a_LMB,
bool a_RMB,
float a_pitch, float a_pitch,
float a_yaw*/ float a_yaw*/
PlayerControl control( PlayerControl control(
@ -1891,10 +1893,24 @@ void the_game(
input->isKeyDown(getKeySetting("keymap_jump")), input->isKeyDown(getKeySetting("keymap_jump")),
input->isKeyDown(getKeySetting("keymap_special1")), input->isKeyDown(getKeySetting("keymap_special1")),
input->isKeyDown(getKeySetting("keymap_sneak")), input->isKeyDown(getKeySetting("keymap_sneak")),
input->getLeftState(),
input->getRightState(),
camera_pitch, camera_pitch,
camera_yaw camera_yaw
); );
client.setPlayerControl(control); client.setPlayerControl(control);
u32 keyPressed=
1*(int)input->isKeyDown(getKeySetting("keymap_forward"))+
2*(int)input->isKeyDown(getKeySetting("keymap_backward"))+
4*(int)input->isKeyDown(getKeySetting("keymap_left"))+
8*(int)input->isKeyDown(getKeySetting("keymap_right"))+
16*(int)input->isKeyDown(getKeySetting("keymap_jump"))+
32*(int)input->isKeyDown(getKeySetting("keymap_special1"))+
64*(int)input->isKeyDown(getKeySetting("keymap_sneak"))+
128*(int)input->getLeftState()+
256*(int)input->getRightState();
LocalPlayer* player = client.getEnv().getLocalPlayer();
player->keyPressed=keyPressed;
} }
/* /*

View File

@ -92,6 +92,43 @@ std::string gob_cmd_set_sprite(
return os.str(); return os.str();
} }
std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend)
{
std::ostringstream os(std::ios::binary);
// command
writeU8(os, GENERIC_CMD_SET_ANIMATION);
// parameters
writeV2F1000(os, frames);
writeF1000(os, frame_speed);
writeF1000(os, frame_blend);
return os.str();
}
std::string gob_cmd_update_bone_position(std::string bone, v3f position, v3f rotation)
{
std::ostringstream os(std::ios::binary);
// command
writeU8(os, GENERIC_CMD_SET_BONE_POSITION);
// parameters
os<<serializeString(bone);
writeV3F1000(os, position);
writeV3F1000(os, rotation);
return os.str();
}
std::string gob_cmd_update_attachment(int parent_id, std::string bone, v3f position, v3f rotation)
{
std::ostringstream os(std::ios::binary);
// command
writeU8(os, GENERIC_CMD_SET_ATTACHMENT);
// parameters
writeS16(os, parent_id);
os<<serializeString(bone);
writeV3F1000(os, position);
writeV3F1000(os, rotation);
return os.str();
}
std::string gob_cmd_punched(s16 damage, s16 result_hp) std::string gob_cmd_punched(s16 damage, s16 result_hp)
{ {
std::ostringstream os(std::ios::binary); std::ostringstream os(std::ios::binary);

View File

@ -28,8 +28,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define GENERIC_CMD_UPDATE_POSITION 1 #define GENERIC_CMD_UPDATE_POSITION 1
#define GENERIC_CMD_SET_TEXTURE_MOD 2 #define GENERIC_CMD_SET_TEXTURE_MOD 2
#define GENERIC_CMD_SET_SPRITE 3 #define GENERIC_CMD_SET_SPRITE 3
#define GENERIC_CMD_PUNCHED 4 #define GENERIC_CMD_SET_ANIMATION 4
#define GENERIC_CMD_UPDATE_ARMOR_GROUPS 5 #define GENERIC_CMD_SET_BONE_POSITION 5
#define GENERIC_CMD_SET_ATTACHMENT 6
#define GENERIC_CMD_PUNCHED 7
#define GENERIC_CMD_UPDATE_ARMOR_GROUPS 8
#include "object_properties.h" #include "object_properties.h"
std::string gob_cmd_set_properties(const ObjectProperties &prop); std::string gob_cmd_set_properties(const ObjectProperties &prop);
@ -54,6 +57,12 @@ std::string gob_cmd_set_sprite(
bool select_horiz_by_yawpitch bool select_horiz_by_yawpitch
); );
std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend);
std::string gob_cmd_update_bone_position(std::string bone, v3f position, v3f rotation);
std::string gob_cmd_update_attachment(int parent_id, std::string bone, v3f position, v3f rotation);
std::string gob_cmd_punched(s16 damage, s16 result_hp); std::string gob_cmd_punched(s16 damage, s16 result_hp);
#include "itemgroup.h" #include "itemgroup.h"

View File

@ -199,6 +199,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
m_inventorylists.clear(); m_inventorylists.clear();
m_images.clear(); m_images.clear();
m_backgrounds.clear();
m_fields.clear(); m_fields.clear();
Strfnd f(m_formspec_string); Strfnd f(m_formspec_string);
@ -278,9 +279,26 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
<<", geom=("<<geom.X<<","<<geom.Y<<")" <<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl; <<std::endl;
if(bp_set != 2) if(bp_set != 2)
errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl; errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
m_images.push_back(ImageDrawSpec(name, pos, geom)); m_images.push_back(ImageDrawSpec(name, pos, geom));
} }
else if(type == "background")
{
v2s32 pos = basepos;
pos.X += stof(f.next(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
pos.Y += stof(f.next(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
v2s32 geom;
geom.X = stof(f.next(",")) * (float)spacing.X;
geom.Y = stof(f.next(";")) * (float)spacing.Y;
std::string name = f.next("]");
infostream<<"image name="<<name
<<", pos=("<<pos.X<<","<<pos.Y<<")"
<<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl;
if(bp_set != 2)
errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
}
else if(type == "field") else if(type == "field")
{ {
std::string fname = f.next(";"); std::string fname = f.next(";");
@ -458,6 +476,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
video::ITexture *texture = m_gamedef->tsrc()->getTextureRaw(fimage); video::ITexture *texture = m_gamedef->tsrc()->getTextureRaw(fimage);
gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str()); gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
e->setUseAlphaChannel(true);
e->setImage(texture); e->setImage(texture);
e->setPressedImage(texture); e->setPressedImage(texture);
e->setScaleImage(true); e->setScaleImage(true);
@ -691,6 +710,26 @@ void GUIFormSpecMenu::drawMenu()
m_tooltip_element->setVisible(false); m_tooltip_element->setVisible(false);
/*
Draw backgrounds
*/
for(u32 i=0; i<m_backgrounds.size(); i++)
{
const ImageDrawSpec &spec = m_backgrounds[i];
video::ITexture *texture =
m_gamedef->tsrc()->getTextureRaw(spec.name);
// Image size on screen
core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
// Image rectangle on screen
core::rect<s32> rect = imgrect + spec.pos;
const video::SColor color(255,255,255,255);
const video::SColor colors[] = {color,color,color,color};
driver->draw2DImage(texture, rect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getOriginalSize())),
NULL/*&AbsoluteClippingRect*/, colors, true);
}
/* /*
Draw images Draw images
*/ */
@ -715,8 +754,11 @@ void GUIFormSpecMenu::drawMenu()
Draw items Draw items
Phase 0: Item slot rectangles Phase 0: Item slot rectangles
Phase 1: Item images; prepare tooltip Phase 1: Item images; prepare tooltip
If backgrounds used, do not draw Item slot rectangles
*/ */
for(int phase=0; phase<=1; phase++) int start_phase=0;
if (m_backgrounds.size() > 0) start_phase=1;
for(int phase=start_phase; phase<=1; phase++)
for(u32 i=0; i<m_inventorylists.size(); i++) for(u32 i=0; i<m_inventorylists.size(); i++)
{ {
drawList(m_inventorylists[i], phase); drawList(m_inventorylists[i], phase);

View File

@ -206,6 +206,7 @@ protected:
TextDest *m_text_dst; TextDest *m_text_dst;
core::array<ListDrawSpec> m_inventorylists; core::array<ListDrawSpec> m_inventorylists;
core::array<ImageDrawSpec> m_backgrounds;
core::array<ImageDrawSpec> m_images; core::array<ImageDrawSpec> m_images;
core::array<FieldSpec> m_fields; core::array<FieldSpec> m_fields;

View File

@ -34,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
LocalPlayer::LocalPlayer(IGameDef *gamedef): LocalPlayer::LocalPlayer(IGameDef *gamedef):
Player(gamedef), Player(gamedef),
isAttached(false),
overridePosition(v3f(0,0,0)),
m_sneak_node(32767,32767,32767), m_sneak_node(32767,32767,32767),
m_sneak_node_exists(false), m_sneak_node_exists(false),
m_old_node_below(32767,32767,32767), m_old_node_below(32767,32767,32767),
@ -59,6 +61,13 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
v3f old_speed = m_speed; v3f old_speed = m_speed;
// Copy parent position if local player is attached
if(isAttached)
{
setPosition(overridePosition);
return;
}
// Skip collision detection if a special movement mode is used // Skip collision detection if a special movement mode is used
bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
bool free_move = fly_allowed && g_settings->getBool("free_move"); bool free_move = fly_allowed && g_settings->getBool("free_move");
@ -352,7 +361,14 @@ void LocalPlayer::applyControl(float dtime)
setPitch(control.pitch); setPitch(control.pitch);
setYaw(control.yaw); setYaw(control.yaw);
// Nullify speed and don't run positioning code if the player is attached
if(isAttached)
{
setSpeed(v3f(0,0,0));
return;
}
v3f move_direction = v3f(0,0,1); v3f move_direction = v3f(0,0,1);
move_direction.rotateXZBy(getYaw()); move_direction.rotateXZBy(getYaw());

View File

@ -22,53 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "player.h" #include "player.h"
struct PlayerControl
{
PlayerControl()
{
up = false;
down = false;
left = false;
right = false;
jump = false;
aux1 = false;
sneak = false;
pitch = 0;
yaw = 0;
}
PlayerControl(
bool a_up,
bool a_down,
bool a_left,
bool a_right,
bool a_jump,
bool a_aux1,
bool a_sneak,
float a_pitch,
float a_yaw
)
{
up = a_up;
down = a_down;
left = a_left;
right = a_right;
jump = a_jump;
aux1 = a_aux1;
sneak = a_sneak;
pitch = a_pitch;
yaw = a_yaw;
}
bool up;
bool down;
bool left;
bool right;
bool jump;
bool aux1;
bool sneak;
float pitch;
float yaw;
};
class LocalPlayer : public Player class LocalPlayer : public Player
{ {
public: public:
@ -79,6 +32,10 @@ public:
{ {
return true; return true;
} }
bool isAttached;
v3f overridePosition;
void move(f32 dtime, Map &map, f32 pos_max_d, void move(f32 dtime, Map &map, f32 pos_max_d,
core::list<CollisionInfo> *collision_info); core::list<CollisionInfo> *collision_info);
@ -87,9 +44,6 @@ public:
void applyControl(float dtime); void applyControl(float dtime);
v3s16 getStandingNodePos(); v3s16 getStandingNodePos();
PlayerControl control;
private: private:
// This is used for determining the sneaking range // This is used for determining the sneaking range
v3s16 m_sneak_node; v3s16 m_sneak_node;

View File

@ -132,7 +132,8 @@ void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
for(s16 ii=0; ii<trunk_h; ii++) for(s16 ii=0; ii<trunk_h; ii++)
{ {
if(vmanip.m_area.contains(p1)) if(vmanip.m_area.contains(p1))
vmanip.m_data[vmanip.m_area.index(p1)] = treenode; if(ii == 0 || vmanip.getNodeNoExNoEmerge(p1).getContent() == CONTENT_AIR)
vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
p1.Y++; p1.Y++;
} }

View File

@ -433,9 +433,6 @@ video::ITexture *generateTextureFromMesh(scene::IMesh *mesh,
} }
// Create render target texture // Create render target texture
video::ITexture *oldtexture = driver->findTexture(texture_name.c_str());
if(oldtexture)
driver->removeTexture(oldtexture);
video::ITexture *rtt = driver->addRenderTargetTexture( video::ITexture *rtt = driver->addRenderTargetTexture(
dim, texture_name.c_str(), video::ECF_A8R8G8B8); dim, texture_name.c_str(), video::ECF_A8R8G8B8);
if(rtt == NULL) if(rtt == NULL)

View File

@ -217,7 +217,7 @@ void ContentFeatures::reset()
void ContentFeatures::serialize(std::ostream &os) void ContentFeatures::serialize(std::ostream &os)
{ {
writeU8(os, 5); // version writeU8(os, 6); // version
os<<serializeString(name); os<<serializeString(name);
writeU16(os, groups.size()); writeU16(os, groups.size());
for(ItemGroupList::const_iterator for(ItemGroupList::const_iterator
@ -254,6 +254,7 @@ void ContentFeatures::serialize(std::ostream &os)
os<<serializeString(liquid_alternative_flowing); os<<serializeString(liquid_alternative_flowing);
os<<serializeString(liquid_alternative_source); os<<serializeString(liquid_alternative_source);
writeU8(os, liquid_viscosity); writeU8(os, liquid_viscosity);
writeU8(os, liquid_renewable);
writeU8(os, light_source); writeU8(os, light_source);
writeU32(os, damage_per_second); writeU32(os, damage_per_second);
node_box.serialize(os); node_box.serialize(os);
@ -265,13 +266,12 @@ void ContentFeatures::serialize(std::ostream &os)
serializeSimpleSoundSpec(sound_dug, os); serializeSimpleSoundSpec(sound_dug, os);
// Stuff below should be moved to correct place in a version that otherwise changes // Stuff below should be moved to correct place in a version that otherwise changes
// the protocol version // the protocol version
writeU8(os, liquid_renewable);
} }
void ContentFeatures::deSerialize(std::istream &is) void ContentFeatures::deSerialize(std::istream &is)
{ {
int version = readU8(is); int version = readU8(is);
if(version != 5) if(version != 6)
throw SerializationError("unsupported ContentFeatures version"); throw SerializationError("unsupported ContentFeatures version");
name = deSerializeString(is); name = deSerializeString(is);
groups.clear(); groups.clear();
@ -311,6 +311,7 @@ void ContentFeatures::deSerialize(std::istream &is)
liquid_alternative_flowing = deSerializeString(is); liquid_alternative_flowing = deSerializeString(is);
liquid_alternative_source = deSerializeString(is); liquid_alternative_source = deSerializeString(is);
liquid_viscosity = readU8(is); liquid_viscosity = readU8(is);
liquid_renewable = readU8(is);
light_source = readU8(is); light_source = readU8(is);
damage_per_second = readU32(is); damage_per_second = readU32(is);
node_box.deSerialize(is); node_box.deSerialize(is);
@ -325,7 +326,6 @@ void ContentFeatures::deSerialize(std::istream &is)
try{ try{
// Stuff below should be moved to correct place in a version that // Stuff below should be moved to correct place in a version that
// otherwise changes the protocol version // otherwise changes the protocol version
liquid_renewable = readU8(is);
}catch(SerializationError &e) {}; }catch(SerializationError &e) {};
} }

View File

@ -18,8 +18,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
#include "object_properties.h" #include "object_properties.h"
#include "irrlichttypes_bloated.h"
#include "util/serialize.h" #include "util/serialize.h"
#include <sstream> #include <sstream>
#include <map>
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
#define PP2(x) "("<<(x).X<<","<<(x).Y<<")" #define PP2(x) "("<<(x).X<<","<<(x).Y<<")"
@ -30,6 +32,7 @@ ObjectProperties::ObjectProperties():
weight(5), weight(5),
collisionbox(-0.5,-0.5,-0.5, 0.5,0.5,0.5), collisionbox(-0.5,-0.5,-0.5, 0.5,0.5,0.5),
visual("sprite"), visual("sprite"),
mesh(""),
visual_size(1,1), visual_size(1,1),
spritediv(1,1), spritediv(1,1),
initial_sprite_basepos(0,0), initial_sprite_basepos(0,0),
@ -38,6 +41,7 @@ ObjectProperties::ObjectProperties():
automatic_rotate(0) automatic_rotate(0)
{ {
textures.push_back("unknown_object.png"); textures.push_back("unknown_object.png");
colors.push_back(video::SColor(255,255,255,255));
} }
std::string ObjectProperties::dump() std::string ObjectProperties::dump()
@ -48,12 +52,18 @@ std::string ObjectProperties::dump()
os<<", weight="<<weight; os<<", weight="<<weight;
os<<", collisionbox="<<PP(collisionbox.MinEdge)<<","<<PP(collisionbox.MaxEdge); os<<", collisionbox="<<PP(collisionbox.MinEdge)<<","<<PP(collisionbox.MaxEdge);
os<<", visual="<<visual; os<<", visual="<<visual;
os<<", mesh="<<mesh;
os<<", visual_size="<<PP2(visual_size); os<<", visual_size="<<PP2(visual_size);
os<<", textures=["; os<<", textures=[";
for(u32 i=0; i<textures.size(); i++){ for(u32 i=0; i<textures.size(); i++){
os<<"\""<<textures[i]<<"\" "; os<<"\""<<textures[i]<<"\" ";
} }
os<<"]"; os<<"]";
os<<", colors=[";
for(u32 i=0; i<colors.size(); i++){
os<<"\""<<colors[i].getAlpha()<<","<<colors[i].getRed()<<","<<colors[i].getGreen()<<","<<colors[i].getBlue()<<"\" ";
}
os<<"]";
os<<", spritediv="<<PP2(spritediv); os<<", spritediv="<<PP2(spritediv);
os<<", initial_sprite_basepos="<<PP2(initial_sprite_basepos); os<<", initial_sprite_basepos="<<PP2(initial_sprite_basepos);
os<<", is_visible="<<is_visible; os<<", is_visible="<<is_visible;
@ -71,11 +81,16 @@ void ObjectProperties::serialize(std::ostream &os) const
writeV3F1000(os, collisionbox.MinEdge); writeV3F1000(os, collisionbox.MinEdge);
writeV3F1000(os, collisionbox.MaxEdge); writeV3F1000(os, collisionbox.MaxEdge);
os<<serializeString(visual); os<<serializeString(visual);
os<<serializeString(mesh);
writeV2F1000(os, visual_size); writeV2F1000(os, visual_size);
writeU16(os, textures.size()); writeU16(os, textures.size());
for(u32 i=0; i<textures.size(); i++){ for(u32 i=0; i<textures.size(); i++){
os<<serializeString(textures[i]); os<<serializeString(textures[i]);
} }
writeU16(os, colors.size());
for(u32 i=0; i<colors.size(); i++){
writeARGB8(os, colors[i]);
}
writeV2S16(os, spritediv); writeV2S16(os, spritediv);
writeV2S16(os, initial_sprite_basepos); writeV2S16(os, initial_sprite_basepos);
writeU8(os, is_visible); writeU8(os, is_visible);
@ -94,12 +109,17 @@ void ObjectProperties::deSerialize(std::istream &is)
collisionbox.MinEdge = readV3F1000(is); collisionbox.MinEdge = readV3F1000(is);
collisionbox.MaxEdge = readV3F1000(is); collisionbox.MaxEdge = readV3F1000(is);
visual = deSerializeString(is); visual = deSerializeString(is);
mesh = deSerializeString(is);
visual_size = readV2F1000(is); visual_size = readV2F1000(is);
textures.clear(); textures.clear();
u32 texture_count = readU16(is); u32 texture_count = readU16(is);
for(u32 i=0; i<texture_count; i++){ for(u32 i=0; i<texture_count; i++){
textures.push_back(deSerializeString(is)); textures.push_back(deSerializeString(is));
} }
u32 color_count = readU16(is);
for(u32 i=0; i<color_count; i++){
colors.push_back(readARGB8(is));
}
spritediv = readV2S16(is); spritediv = readV2S16(is);
initial_sprite_basepos = readV2S16(is); initial_sprite_basepos = readV2S16(is);
is_visible = readU8(is); is_visible = readU8(is);

View File

@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string> #include <string>
#include "irrlichttypes_bloated.h" #include "irrlichttypes_bloated.h"
#include <iostream> #include <iostream>
#include <map>
struct ObjectProperties struct ObjectProperties
{ {
@ -32,14 +33,17 @@ struct ObjectProperties
float weight; float weight;
core::aabbox3d<f32> collisionbox; core::aabbox3d<f32> collisionbox;
std::string visual; std::string visual;
std::string mesh;
v2f visual_size; v2f visual_size;
core::array<std::string> textures; core::array<std::string> textures;
core::array<video::SColor> colors;
v2s16 spritediv; v2s16 spritediv;
v2s16 initial_sprite_basepos; v2s16 initial_sprite_basepos;
bool is_visible; bool is_visible;
bool makes_footstep_sound; bool makes_footstep_sound;
float automatic_rotate; float automatic_rotate;
ObjectProperties(); ObjectProperties();
std::string dump(); std::string dump();
void serialize(std::ostream &os) const; void serialize(std::ostream &os) const;

View File

@ -28,6 +28,61 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" #define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
struct PlayerControl
{
PlayerControl()
{
up = false;
down = false;
left = false;
right = false;
jump = false;
aux1 = false;
sneak = false;
LMB = false;
RMB = false;
pitch = 0;
yaw = 0;
}
PlayerControl(
bool a_up,
bool a_down,
bool a_left,
bool a_right,
bool a_jump,
bool a_aux1,
bool a_sneak,
bool a_LMB,
bool a_RMB,
float a_pitch,
float a_yaw
)
{
up = a_up;
down = a_down;
left = a_left;
right = a_right;
jump = a_jump;
aux1 = a_aux1;
sneak = a_sneak;
LMB = a_LMB;
RMB = a_RMB;
pitch = a_pitch;
yaw = a_yaw;
}
bool up;
bool down;
bool left;
bool right;
bool jump;
bool aux1;
bool sneak;
bool LMB;
bool RMB;
float pitch;
float yaw;
};
class Map; class Map;
class IGameDef; class IGameDef;
struct CollisionInfo; struct CollisionInfo;
@ -155,9 +210,17 @@ public:
u16 hp; u16 hp;
u16 peer_id; u16 peer_id;
std::string inventory_formspec; std::string inventory_formspec;
PlayerControl control;
PlayerControl getPlayerControl()
{
return control;
}
u32 keyPressed;
protected: protected:
IGameDef *m_gamedef; IGameDef *m_gamedef;
@ -182,7 +245,7 @@ public:
void setPlayerSAO(PlayerSAO *sao) void setPlayerSAO(PlayerSAO *sao)
{ m_sao = sao; } { m_sao = sao; }
void setPosition(const v3f &position); void setPosition(const v3f &position);
private: private:
PlayerSAO *m_sao; PlayerSAO *m_sao;
}; };

View File

@ -936,6 +936,8 @@ static void read_object_properties(lua_State *L, int index,
lua_pop(L, 1); lua_pop(L, 1);
getstringfield(L, -1, "visual", prop->visual); getstringfield(L, -1, "visual", prop->visual);
getstringfield(L, -1, "mesh", prop->mesh);
lua_getfield(L, -1, "visual_size"); lua_getfield(L, -1, "visual_size");
if(lua_istable(L, -1)) if(lua_istable(L, -1))
@ -958,6 +960,23 @@ static void read_object_properties(lua_State *L, int index,
} }
} }
lua_pop(L, 1); lua_pop(L, 1);
lua_getfield(L, -1, "colors");
if(lua_istable(L, -1)){
prop->colors.clear();
int table = lua_gettop(L);
lua_pushnil(L);
while(lua_next(L, table) != 0){
// key at index -2 and value at index -1
if(lua_isstring(L, -1))
prop->colors.push_back(readARGB8(L, -1));
else
prop->colors.push_back(video::SColor(255, 255, 255, 255));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
}
lua_pop(L, 1);
lua_getfield(L, -1, "spritediv"); lua_getfield(L, -1, "spritediv");
if(lua_istable(L, -1)) if(lua_istable(L, -1))
@ -2697,6 +2716,80 @@ private:
return 0; return 0;
} }
// set_animation(self, frame_range, frame_speed, frame_blend)
static int l_set_animation(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
if(co == NULL) return 0;
// Do it
v2f frames = v2f(1, 1);
if(!lua_isnil(L, 2))
frames = read_v2f(L, 2);
float frame_speed = 15;
if(!lua_isnil(L, 3))
frame_speed = lua_tonumber(L, 3);
float frame_blend = 0;
if(!lua_isnil(L, 4))
frame_blend = lua_tonumber(L, 4);
co->setAnimation(frames, frame_speed, frame_blend);
return 0;
}
// set_bone_position(self, std::string bone, v3f position, v3f rotation)
static int l_set_bone_position(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
if(co == NULL) return 0;
// Do it
std::string bone = "";
if(!lua_isnil(L, 2))
bone = lua_tostring(L, 2);
v3f position = v3f(0, 0, 0);
if(!lua_isnil(L, 3))
position = read_v3f(L, 3);
v3f rotation = v3f(0, 0, 0);
if(!lua_isnil(L, 4))
rotation = read_v3f(L, 4);
co->setBonePosition(bone, position, rotation);
return 0;
}
// set_attach(self, parent, bone, position, rotation)
static int l_set_attach(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
ObjectRef *parent_ref = checkobject(L, 2);
ServerActiveObject *co = getobject(ref);
ServerActiveObject *parent = getobject(parent_ref);
if(co == NULL) return 0;
if(parent == NULL) return 0;
// Do it
std::string bone = "";
if(!lua_isnil(L, 3))
bone = lua_tostring(L, 3);
v3f position = v3f(0, 0, 0);
if(!lua_isnil(L, 4))
position = read_v3f(L, 4);
v3f rotation = v3f(0, 0, 0);
if(!lua_isnil(L, 5))
rotation = read_v3f(L, 5);
co->setAttachment(parent->getId(), bone, position, rotation);
return 0;
}
// set_detach(self)
static int l_set_detach(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
if(co == NULL) return 0;
// Do it
co->setAttachment(0, "", v3f(0,0,0), v3f(0,0,0));
return 0;
}
// set_properties(self, properties) // set_properties(self, properties)
static int l_set_properties(lua_State *L) static int l_set_properties(lua_State *L)
{ {
@ -2932,7 +3025,54 @@ private:
lua_pushlstring(L, formspec.c_str(), formspec.size()); lua_pushlstring(L, formspec.c_str(), formspec.size());
return 1; return 1;
} }
// get_player_control(self)
static int l_get_player_control(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if(player == NULL){
lua_pushlstring(L, "", 0);
return 1;
}
// Do it
PlayerControl control = player->getPlayerControl();
lua_newtable(L);
lua_pushboolean(L, control.up);
lua_setfield(L, -2, "up");
lua_pushboolean(L, control.down);
lua_setfield(L, -2, "down");
lua_pushboolean(L, control.left);
lua_setfield(L, -2, "left");
lua_pushboolean(L, control.right);
lua_setfield(L, -2, "right");
lua_pushboolean(L, control.jump);
lua_setfield(L, -2, "jump");
lua_pushboolean(L, control.aux1);
lua_setfield(L, -2, "aux1");
lua_pushboolean(L, control.sneak);
lua_setfield(L, -2, "sneak");
lua_pushboolean(L, control.LMB);
lua_setfield(L, -2, "LMB");
lua_pushboolean(L, control.RMB);
lua_setfield(L, -2, "RMB");
return 1;
}
// get_player_control_bits(self)
static int l_get_player_control_bits(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if(player == NULL){
lua_pushlstring(L, "", 0);
return 1;
}
// Do it
lua_pushnumber(L, player->keyPressed);
return 1;
}
public: public:
ObjectRef(ServerActiveObject *object): ObjectRef(ServerActiveObject *object):
m_object(object) m_object(object)
@ -3011,6 +3151,10 @@ const luaL_reg ObjectRef::methods[] = {
method(ObjectRef, get_wielded_item), method(ObjectRef, get_wielded_item),
method(ObjectRef, set_wielded_item), method(ObjectRef, set_wielded_item),
method(ObjectRef, set_armor_groups), method(ObjectRef, set_armor_groups),
method(ObjectRef, set_animation),
method(ObjectRef, set_bone_position),
method(ObjectRef, set_attach),
method(ObjectRef, set_detach),
method(ObjectRef, set_properties), method(ObjectRef, set_properties),
// LuaEntitySAO-only // LuaEntitySAO-only
method(ObjectRef, setvelocity), method(ObjectRef, setvelocity),
@ -3031,6 +3175,8 @@ const luaL_reg ObjectRef::methods[] = {
method(ObjectRef, get_look_yaw), method(ObjectRef, get_look_yaw),
method(ObjectRef, set_inventory_formspec), method(ObjectRef, set_inventory_formspec),
method(ObjectRef, get_inventory_formspec), method(ObjectRef, get_inventory_formspec),
method(ObjectRef, get_player_control),
method(ObjectRef, get_player_control_bits),
{0,0} {0,0}
}; };
@ -6588,6 +6734,8 @@ void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
lua_pop(L, 1); lua_pop(L, 1);
getstringfield(L, -1, "visual", prop->visual); getstringfield(L, -1, "visual", prop->visual);
getstringfield(L, -1, "mesh", prop->mesh);
// Deprecated: read object properties directly // Deprecated: read object properties directly
read_object_properties(L, -1, prop); read_object_properties(L, -1, prop);

View File

@ -1371,9 +1371,9 @@ void Server::AsyncRunStep()
/* /*
Send player inventories and HPs if necessary Send player inventories and HPs if necessary
*/ */
if(playersao->m_teleported){ if(playersao->m_moved){
SendMovePlayer(client->peer_id); SendMovePlayer(client->peer_id);
playersao->m_teleported = false; playersao->m_moved = false;
} }
if(playersao->m_inventory_not_sent){ if(playersao->m_inventory_not_sent){
UpdateCrafting(client->peer_id); UpdateCrafting(client->peer_id);
@ -2369,7 +2369,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
if(command == TOSERVER_PLAYERPOS) if(command == TOSERVER_PLAYERPOS)
{ {
if(datasize < 2+12+12+4+4) if(datasize < 2+12+12+4+4+4)
return; return;
u32 start = 0; u32 start = 0;
@ -2377,6 +2377,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
v3s32 ss = readV3S32(&data[start+2+12]); v3s32 ss = readV3S32(&data[start+2+12]);
f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0; f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0; f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
u32 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.); v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.); v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
pitch = wrapDegrees(pitch); pitch = wrapDegrees(pitch);
@ -2386,6 +2387,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
player->setSpeed(speed); player->setSpeed(speed);
player->setPitch(pitch); player->setPitch(pitch);
player->setYaw(yaw); player->setYaw(yaw);
player->keyPressed=keyPressed;
player->control.up = (bool)(keyPressed&1);
player->control.down = (bool)(keyPressed&2);
player->control.left = (bool)(keyPressed&4);
player->control.right = (bool)(keyPressed&8);
player->control.jump = (bool)(keyPressed&16);
player->control.aux1 = (bool)(keyPressed&32);
player->control.sneak = (bool)(keyPressed&64);
player->control.LMB = (bool)(keyPressed&128);
player->control.RMB = (bool)(keyPressed&256);
/*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to " /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
<<"("<<position.X<<","<<position.Y<<","<<position.Z<<")" <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
@ -3166,6 +3177,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
} }
} // action == 4 } // action == 4
/* /*
Catch invalid actions Catch invalid actions
@ -4029,6 +4041,7 @@ void Server::fillMediaCache()
paths.push_back(mod.path + DIR_DELIM + "textures"); paths.push_back(mod.path + DIR_DELIM + "textures");
paths.push_back(mod.path + DIR_DELIM + "sounds"); paths.push_back(mod.path + DIR_DELIM + "sounds");
paths.push_back(mod.path + DIR_DELIM + "media"); paths.push_back(mod.path + DIR_DELIM + "media");
paths.push_back(mod.path + DIR_DELIM + "models");
} }
std::string path_all = "textures"; std::string path_all = "textures";
paths.push_back(path_all + DIR_DELIM + "all"); paths.push_back(path_all + DIR_DELIM + "all");
@ -4054,6 +4067,7 @@ void Server::fillMediaCache()
".png", ".jpg", ".bmp", ".tga", ".png", ".jpg", ".bmp", ".tga",
".pcx", ".ppm", ".psd", ".wal", ".rgb", ".pcx", ".ppm", ".psd", ".wal", ".rgb",
".ogg", ".ogg",
".x", ".b3d", ".md2", ".obj",
NULL NULL
}; };
if(removeStringEnd(filename, supported_ext) == ""){ if(removeStringEnd(filename, supported_ext) == ""){

View File

@ -152,6 +152,12 @@ public:
virtual void setArmorGroups(const ItemGroupList &armor_groups) virtual void setArmorGroups(const ItemGroupList &armor_groups)
{} {}
virtual void setAnimation(v2f frames, float frame_speed, float frame_blend)
{}
virtual void setBonePosition(std::string bone, v3f position, v3f rotation)
{}
virtual void setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
{}
virtual ObjectProperties* accessObjectProperties() virtual ObjectProperties* accessObjectProperties()
{ return NULL; } { return NULL; }
virtual void notifyObjectPropertiesModified() virtual void notifyObjectPropertiesModified()

View File

@ -518,15 +518,6 @@ core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<
// Apply transform to image data // Apply transform to image data
void imageTransform(u32 transform, video::IImage *src, video::IImage *dst); void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
/*
Adds a new texture to the video driver and returns a pointer to it.
This pointer should not be dropped. Any texture that was registered
with that name before is removed (this may invalidate some ITexture
pointers).
*/
video::ITexture* register_texture(video::IVideoDriver *driver,
std::string name, video::IImage *img);
/* /*
Generate image based on a string like "stone.png" or "[crack0". Generate image based on a string like "stone.png" or "[crack0".
if baseimg is NULL, it is created. Otherwise stuff is made on it. if baseimg is NULL, it is created. Otherwise stuff is made on it.
@ -695,9 +686,11 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
" create texture \""<<name<<"\""<<std::endl; " create texture \""<<name<<"\""<<std::endl;
} }
// Create texture from resulting image
if(baseimg != NULL) if(baseimg != NULL)
t = register_texture(driver, name, baseimg); {
// Create texture from resulting image
t = driver->addTexture(name.c_str(), baseimg);
}
/* /*
Add texture to caches (add NULL textures too) Add texture to caches (add NULL textures too)
@ -816,7 +809,7 @@ void TextureSource::rebuildImagesAndTextures()
// Create texture from resulting image // Create texture from resulting image
video::ITexture *t = NULL; video::ITexture *t = NULL;
if(img) if(img)
t = register_texture(driver, sap->name, img); t = driver->addTexture(sap->name.c_str(), img);
// Replace texture // Replace texture
sap->a.atlas = t; sap->a.atlas = t;
@ -1051,7 +1044,7 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
/* /*
Make texture Make texture
*/ */
video::ITexture *t = register_texture(driver, "__main_atlas__", atlas_img); video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
assert(t); assert(t);
/* /*
@ -1142,15 +1135,6 @@ video::IImage* generate_image_from_scratch(std::string name,
return baseimg; return baseimg;
} }
video::ITexture* register_texture(video::IVideoDriver *driver,
std::string name, video::IImage *img)
{
video::ITexture *old_texture = driver->findTexture(name.c_str());
if(old_texture)
driver->removeTexture(old_texture);
return driver->addTexture(name.c_str(), img);
}
bool generate_image(std::string part_of_name, video::IImage *& baseimg, bool generate_image(std::string part_of_name, video::IImage *& baseimg,
IrrlichtDevice *device, SourceImageCache *sourcecache) IrrlichtDevice *device, SourceImageCache *sourcecache)
{ {
@ -1557,12 +1541,12 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
assert(img_top && img_left && img_right); assert(img_top && img_left && img_right);
// Create textures from images // Create textures from images
video::ITexture *texture_top = register_texture(driver, video::ITexture *texture_top = driver->addTexture(
imagename_top + "__temp1__", img_top); (imagename_top + "__temp__").c_str(), img_top);
video::ITexture *texture_left = register_texture(driver, video::ITexture *texture_left = driver->addTexture(
imagename_left + "__temp2__", img_left); (imagename_left + "__temp__").c_str(), img_left);
video::ITexture *texture_right = register_texture(driver, video::ITexture *texture_right = driver->addTexture(
imagename_right + "__temp3__", img_right); (imagename_right + "__temp__").c_str(), img_right);
assert(texture_top && texture_left && texture_right); assert(texture_top && texture_left && texture_right);
// Drop images // Drop images

View File

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define UTIL_SERIALIZE_HEADER #define UTIL_SERIALIZE_HEADER
#include "../irrlichttypes.h" #include "../irrlichttypes.h"
#include "../irrlichttypes_bloated.h"
#include "../irr_v2d.h" #include "../irr_v2d.h"
#include "../irr_v3d.h" #include "../irr_v3d.h"
#include <iostream> #include <iostream>
@ -197,6 +198,24 @@ inline v3s16 readV3S16(u8 *data)
return p; return p;
} }
inline void writeARGB8(u8 *data, video::SColor p)
{
writeU8(&data[0], p.getAlpha());
writeU8(&data[1], p.getRed());
writeU8(&data[2], p.getGreen());
writeU8(&data[3], p.getBlue());
}
inline video::SColor readARGB8(u8 *data)
{
video::SColor p;
p.setAlpha(readU8(&data[0]));
p.setRed(readU8(&data[1]));
p.setGreen(readU8(&data[2]));
p.setBlue(readU8(&data[3]));
return p;
}
/* /*
The above stuff directly interfaced to iostream The above stuff directly interfaced to iostream
*/ */
@ -344,6 +363,20 @@ inline v3s16 readV3S16(std::istream &is)
return readV3S16((u8*)buf); return readV3S16((u8*)buf);
} }
inline void writeARGB8(std::ostream &os, video::SColor p)
{
char buf[4] = {0};
writeARGB8((u8*)buf, p);
os.write(buf, 4);
}
inline video::SColor readARGB8(std::istream &is)
{
char buf[4] = {0};
is.read(buf, 4);
return readARGB8((u8*)buf);
}
/* /*
More serialization stuff More serialization stuff
*/ */