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},
func = function(name, param)
toname = param
if not toname then
minetest.chat_send_player(toname, "Name field required")
if toname == "" then
minetest.chat_send_player(name, "Name field required")
return
end
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/
@ -694,6 +694,11 @@ image[<X>,<Y>;<W>,<H>;<texture name>]
^ Show an image
^ 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>]
^ 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
@ -1098,6 +1103,10 @@ methods:
- get_wielded_item() -> ItemStack
- set_wielded_item(item): replaces the wielded item, returns true if successful
- 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)
LuaEntitySAO-only: (no-op for other objects)
- setvelocity({x=num, y=num, z=num})
@ -1123,7 +1132,11 @@ Player-only: (no-op for other objects)
^ Redefine player's inventory form
^ Should usually be called in on_joinplayer
- 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
methods:
- is_empty(listname): return true if list is empty
@ -1222,9 +1235,11 @@ Object Properties
physical = true,
weight = 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},
mesh = "model",
textures = {}, -- number of required textures depends on visual
colors = {}, -- number of required colors depends on visual
spritediv = {x=1, y=1},
initial_sprite_basepos = {x=0, y=0},
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 "util/string.h"
#include "hex.h"
#include "IMeshCache.h"
static std::string getMediaCacheDir()
{
@ -821,7 +822,7 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
if(name != "")
{
verbosestream<<"Client: Attempting to load image "
<<"file \""<<filename<<"\""<<std::endl;
<<"file \""<<filename<<"\""<<std::endl;
io::IFileSystem *irrfs = m_device->getFileSystem();
video::IVideoDriver *vdrv = m_device->getVideoDriver();
@ -855,11 +856,33 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
if(name != "")
{
verbosestream<<"Client: Attempting to load sound "
<<"file \""<<filename<<"\""<<std::endl;
<<"file \""<<filename<<"\""<<std::endl;
m_sound->loadSoundData(name, data);
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 \""
<<filename<<"\""<<std::endl;
return false;
@ -1961,7 +1984,7 @@ void Client::sendPlayerPos()
v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
s32 pitch = myplayer->getPitch() * 100;
s32 yaw = myplayer->getYaw() * 100;
u32 keyPressed=myplayer->keyPressed;
/*
Format:
[0] u16 command
@ -1969,15 +1992,15 @@ void Client::sendPlayerPos()
[2+12] v3s32 speed*100
[2+12+12] s32 pitch*100
[2+12+12+4] s32 yaw*100
[2+12+12+4+4] u32 keyPressed
*/
SharedBuffer<u8> data(2+12+12+4+4);
SharedBuffer<u8> data(2+12+12+4+4+4);
writeU16(&data[0], TOSERVER_PLAYERPOS);
writeV3S32(&data[2], position);
writeV3S32(&data[2+12], speed);
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(0, data, false);
}

View File

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

View File

@ -49,13 +49,19 @@ public:
virtual void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
IrrlichtDevice *irr){}
virtual void removeFromScene(){}
virtual void removeFromScene(bool permanent){}
// 0 <= light_at_pos <= LIGHT_SUN
virtual void updateLight(u8 light_at_pos){}
virtual v3s16 getLightPosition(){return v3s16(0,0,0);}
virtual core::aabbox3d<f32>* getSelectionBox(){return NULL;}
virtual core::aabbox3d<f32>* getCollisionBox(){return NULL;}
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;}
// 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
PROTOCOL_VERSION 13:
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
@ -366,6 +372,7 @@ enum ToServerCommand
[2+12] v3s32 speed*100
[2+12+12] s32 pitch*100
[2+12+12+4] s32 yaw*100
[2+12+12+4+4] u32 keyPressed
*/
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/mathconstants.h"
#include "map.h"
#include "main.h" // g_settings
#include <IMeshManipulator.h>
#include <IAnimatedMeshSceneNode.h>
#include <IBoneSceneNode.h>
class Settings;
struct ToolCapabilities;
@ -552,7 +555,8 @@ private:
// Only set at initialization
std::string m_name;
bool m_is_player;
bool m_is_local_player; // determined locally
bool m_is_local_player;
int m_id;
// Property-ish things
ObjectProperties m_prop;
//
@ -560,6 +564,7 @@ private:
IrrlichtDevice *m_irr;
core::aabbox3d<f32> m_selection_box;
scene::IMeshSceneNode *m_meshnode;
scene::IAnimatedMeshSceneNode *m_animated_meshnode;
scene::IBillboardSceneNode *m_spritenode;
scene::ITextSceneNode* m_textnode;
v3f m_position;
@ -573,6 +578,14 @@ private:
v2s16 m_tx_basepos;
bool m_initial_tx_basepos_set;
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_num_frames;
float m_anim_framelength;
@ -582,6 +595,7 @@ private:
bool m_visuals_expired;
float m_step_distance_counter;
u8 m_last_light;
bool m_is_visible;
public:
GenericCAO(IGameDef *gamedef, ClientEnvironment *env):
@ -589,11 +603,13 @@ public:
//
m_is_player(false),
m_is_local_player(false),
m_id(0),
//
m_smgr(NULL),
m_irr(NULL),
m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
m_meshnode(NULL),
m_animated_meshnode(NULL),
m_spritenode(NULL),
m_textnode(NULL),
m_position(v3f(0,10*BS,0)),
@ -605,6 +621,14 @@ public:
m_tx_basepos(0,0),
m_initial_tx_basepos_set(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_num_frames(1),
m_anim_framelength(0.2),
@ -612,7 +636,8 @@ public:
m_reset_textures_timer(-1),
m_visuals_expired(false),
m_step_distance_counter(0),
m_last_light(255)
m_last_light(255),
m_is_visible(false)
{
if(gamedef == NULL)
ClientActiveObject::registerType(getType(), create);
@ -632,6 +657,7 @@ public:
}
m_name = deSerializeString(is);
m_is_player = readU8(is);
m_id = readS16(is);
m_position = readV3F1000(is);
m_yaw = readF1000(is);
m_hp = readS16(is);
@ -668,21 +694,113 @@ public:
}
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 &m_selection_box;
}
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;
}
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){
m_meshnode->remove();
m_meshnode = NULL;
}
if(m_animated_meshnode){
m_animated_meshnode->remove();
m_animated_meshnode = NULL;
}
if(m_spritenode){
m_spritenode->remove();
m_spritenode = NULL;
@ -695,7 +813,7 @@ public:
m_smgr = smgr;
m_irr = irr;
if(m_meshnode != NULL || m_spritenode != NULL)
if(m_meshnode != NULL || m_animated_meshnode != NULL || m_spritenode != NULL)
return;
m_visuals_expired = false;
@ -791,7 +909,24 @@ public:
m_prop.visual_size.X));
u8 li = m_last_light;
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<<"textures: "<<m_prop.textures.size()<<std::endl;
if(m_prop.textures.size() >= 1){
@ -823,6 +958,8 @@ public:
scene::ISceneNode *node = NULL;
if(m_spritenode)
node = m_spritenode;
else if(m_animated_meshnode)
node = m_animated_meshnode;
else if(m_meshnode)
node = m_meshnode;
if(node && m_is_player && !m_is_local_player){
@ -833,8 +970,11 @@ public:
wname.c_str(), video::SColor(255,255,255,255), node);
m_textnode->setPosition(v3f(0, BS*1.1, 0));
}
updateNodePos();
updateAnimation();
updateBonePosition();
updateAttachments();
}
void expireVisuals()
@ -844,19 +984,16 @@ public:
void updateLight(u8 light_at_pos)
{
bool is_visible = (m_hp != 0);
u8 li = decode_light(light_at_pos);
if(li != m_last_light){
m_last_light = li;
video::SColor color(255,li,li,li);
if(m_meshnode){
if(m_meshnode)
setMeshColor(m_meshnode->getMesh(), color);
m_meshnode->setVisible(is_visible);
}
if(m_spritenode){
if(m_animated_meshnode)
setMeshColor(m_animated_meshnode->getMesh(), color);
if(m_spritenode)
m_spritenode->setColor(color);
m_spritenode->setVisible(is_visible);
}
}
}
@ -867,12 +1004,21 @@ public:
void updateNodePos()
{
if(getParent() != NULL)
return;
if(m_meshnode){
m_meshnode->setPosition(pos_translator.vect_show);
v3f rot = m_meshnode->getRotation();
rot.Y = -m_yaw;
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){
m_spritenode->setPosition(pos_translator.vect_show);
}
@ -880,56 +1026,116 @@ public:
void step(float dtime, ClientEnvironment *env)
{
v3f lastpos = pos_translator.vect_show;
if(m_visuals_expired && m_smgr && m_irr){
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);
// 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){
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();
}
// Make sure m_is_visible is always applied
if(m_meshnode)
m_meshnode->setVisible(m_is_visible);
if(m_animated_meshnode)
m_animated_meshnode->setVisible(m_is_visible);
if(m_spritenode)
m_spritenode->setVisible(m_is_visible);
if(m_textnode)
m_textnode->setVisible(m_is_visible);
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());
if(getParent() != NULL) // Attachments should be glued to their parent by Irrlicht
{
// Set these for later
m_position = getPosition();
m_velocity = v3f(0,0,0);
m_acceleration = v3f(0,0,0);
pos_translator.vect_show = m_position;
if(m_is_local_player) // Update local player attachment position
{
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("");
}
}
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;
updateNodePos();
}
@ -1008,6 +1214,10 @@ public:
{
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_prop.visual == "sprite")
@ -1018,6 +1228,58 @@ public:
texturestring += mod;
m_spritenode->setMaterialTexture(0,
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)
@ -1044,6 +1306,20 @@ public:
material.setTexture(0, atlas);
material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.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")
@ -1057,6 +1333,20 @@ public:
scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
buf->getMaterial().setTexture(0,
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";
@ -1068,11 +1358,213 @@ public:
scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
buf->getMaterial().setTexture(0,
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)
{
//infostream<<"GenericCAO: Got message"<<std::endl;
@ -1099,6 +1591,8 @@ public:
}
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_velocity = readV3F1000(is);
m_acceleration = readV3F1000(is);
@ -1112,7 +1606,10 @@ public:
// the ground due to sucky collision detection...
if(m_prop.physical)
m_position += v3f(0,0.002,0);
if(getParent() != NULL) // Just in case
return;
if(do_interpolate){
if(!m_prop.physical)
pos_translator.update(m_position, is_end_position, update_interval);
@ -1140,6 +1637,41 @@ public:
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)
{
/*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_position_timer(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
if(env == NULL){
@ -429,6 +432,17 @@ ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
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)
{
if(!m_properties_sent)
@ -440,30 +454,52 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
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;
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.25; // Distance per iteration
f32 stepheight = 0; // Maximum climbable step height
v3f p_pos = m_base_position;
v3f p_velocity = m_velocity;
v3f p_acceleration = 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;
// 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())
{
v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
m_base_position = pos;
m_velocity = v3f(0,0,0);
m_acceleration = v3f(0,0,0);
}
else
{
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.25; // Distance per iteration
f32 stepheight = 0; // Maximum climbable step height
v3f p_pos = m_base_position;
v3f p_velocity = m_velocity;
v3f p_acceleration = 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){
@ -473,20 +509,23 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
if(send_recommended == false)
return;
// TODO: force send when acceleration changes enough?
float minchange = 0.2*BS;
if(m_last_sent_position_timer > 1.0){
minchange = 0.01*BS;
} else if(m_last_sent_position_timer > 0.2){
minchange = 0.05*BS;
}
float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
move_d += m_last_sent_move_precision;
float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
if(move_d > minchange || vel_d > minchange ||
fabs(m_yaw - m_last_sent_yaw) > 1.0){
sendPosition(true, false);
if(!isAttached())
{
// TODO: force send when acceleration changes enough?
float minchange = 0.2*BS;
if(m_last_sent_position_timer > 1.0){
minchange = 0.01*BS;
} else if(m_last_sent_position_timer > 0.2){
minchange = 0.05*BS;
}
float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
move_d += m_last_sent_move_precision;
float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
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){
@ -497,6 +536,32 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
ActiveObjectMessage aom(getId(), true, str);
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()
@ -504,13 +569,21 @@ std::string LuaEntitySAO::getClientInitializationData()
std::ostringstream os(std::ios::binary);
writeU8(os, 0); // version
os<<serializeString(""); // name
writeS16(os, getId()); //id
writeU8(os, 0); // is_player
writeV3F1000(os, m_base_position);
writeF1000(os, m_yaw);
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(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();
}
@ -550,6 +623,10 @@ int LuaEntitySAO::punch(v3f dir,
m_removed = true;
return 0;
}
// It's best that attachments cannot be punched
if(isAttached())
return 0;
ItemStack *punchitem = NULL;
ItemStack punchitem_static;
@ -594,18 +671,25 @@ void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
{
if(!m_registered)
return;
// It's best that attachments cannot be clicked
if(isAttached())
return;
lua_State *L = m_env->getLua();
scriptapi_luaentity_rightclick(L, m_id, clicker);
}
void LuaEntitySAO::setPos(v3f pos)
{
if(isAttached())
return;
m_base_position = pos;
sendPosition(false, true);
}
void LuaEntitySAO::moveTo(v3f pos, bool continuous)
{
if(isAttached())
return;
m_base_position = pos;
if(!continuous)
sendPosition(true, true);
@ -644,6 +728,37 @@ void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
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()
{
return &m_prop;
@ -718,6 +833,10 @@ std::string LuaEntitySAO::getPropertyPacket()
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_position);
m_last_sent_position_timer = 0;
@ -765,8 +884,11 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
m_properties_sent(true),
m_privs(privs),
m_is_singleplayer(is_singleplayer),
m_animation_sent(false),
m_bone_position_sent(false),
m_attachment_sent(false),
// public
m_teleported(false),
m_moved(false),
m_inventory_not_sent(false),
m_hp_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.weight = 75;
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_size = v2f(1, 2);
m_prop.textures.clear();
m_prop.textures.push_back("player.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.is_visible = (getHP() != 0);
// end of default appearance
m_prop.is_visible = true;
m_prop.makes_footstep_sound = true;
}
@ -842,12 +968,21 @@ std::string PlayerSAO::getClientInitializationData()
writeU8(os, 0); // version
os<<serializeString(m_player->getName()); // name
writeU8(os, 1); // is_player
writeS16(os, getId()); //id
writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
writeF1000(os, m_player->getYaw());
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(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();
}
@ -857,6 +992,17 @@ std::string PlayerSAO::getStaticData()
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)
{
if(!m_properties_sent)
@ -868,73 +1014,102 @@ void PlayerSAO::step(float dtime, bool send_recommended)
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_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_player->setPosition(pos);
}
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_teleported = true;
}
if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
{
m_last_good_position = m_player->getPosition();
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)
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;
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(
m_player->getPosition() + v3f(0,BS*1,0),
pos,
v3f(0,0,0),
v3f(0,0,0),
m_player->getYaw(),
@ -961,32 +1136,63 @@ void PlayerSAO::step(float dtime, bool send_recommended)
ActiveObjectMessage aom(getId(), true, str);
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)
{
// This needs to be ran for attachments too
ServerActiveObject::setBasePosition(position);
m_position_not_sent = true;
}
void PlayerSAO::setPos(v3f pos)
{
if(isAttached())
return;
m_player->setPosition(pos);
// Movement caused by this command is always valid
m_last_good_position = pos;
m_last_good_position_age = 0;
// Force position change on client
m_teleported = true;
m_moved = true;
}
void PlayerSAO::moveTo(v3f pos, bool continuous)
{
if(isAttached())
return;
m_player->setPosition(pos);
// Movement caused by this command is always valid
m_last_good_position = pos;
m_last_good_position_age = 0;
// Force position change on client
m_teleported = true;
m_moved = true;
}
int PlayerSAO::punch(v3f dir,
@ -994,6 +1200,10 @@ int PlayerSAO::punch(v3f dir,
ServerActiveObject *puncher,
float time_from_last_punch)
{
// It's best that attachments cannot be punched
if(isAttached())
return 0;
if(!toolcap)
return 0;
@ -1075,6 +1285,39 @@ void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
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()
{
return &m_prop;
@ -1138,7 +1381,7 @@ void PlayerSAO::disconnected()
std::string PlayerSAO::getPropertyPacket()
{
m_prop.is_visible = (getHP() != 0);
m_prop.is_visible = (true);
return gob_cmd_set_properties(m_prop);
}

View File

@ -46,6 +46,7 @@ public:
virtual void addedToEnvironment(u32 dtime_s);
static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
const std::string &data);
bool isAttached();
void step(float dtime, bool send_recommended);
std::string getClientInitializationData();
std::string getStaticData();
@ -61,6 +62,9 @@ public:
void setHP(s16 hp);
s16 getHP() const;
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();
void notifyObjectPropertiesModified();
/* LuaEntitySAO-specific */
@ -96,6 +100,20 @@ private:
float m_last_sent_position_timer;
float m_last_sent_move_precision;
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;
std::string getClientInitializationData();
std::string getStaticData();
bool isAttached();
void step(float dtime, bool send_recommended);
void setBasePosition(const v3f &position);
void setPos(v3f pos);
@ -142,6 +161,9 @@ public:
void setHP(s16 hp);
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();
void notifyObjectPropertiesModified();
@ -229,15 +251,32 @@ private:
bool m_position_not_sent;
ItemGroupList m_armor_groups;
bool m_armor_groups_sent;
bool m_properties_sent;
struct ObjectProperties m_prop;
// Cached privileges for enforcement
std::set<std::string> m_privs;
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:
// Some flags used by Server
bool m_teleported;
bool m_moved;
bool m_inventory_not_sent;
bool m_hp_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;
return;
}
obj->removeFromScene();
obj->removeFromScene(true);
delete obj;
m_active_objects.remove(id);
}

View File

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

View File

@ -1881,6 +1881,8 @@ void the_game(
bool a_jump,
bool a_superspeed,
bool a_sneak,
bool a_LMB,
bool a_RMB,
float a_pitch,
float a_yaw*/
PlayerControl control(
@ -1891,10 +1893,24 @@ void the_game(
input->isKeyDown(getKeySetting("keymap_jump")),
input->isKeyDown(getKeySetting("keymap_special1")),
input->isKeyDown(getKeySetting("keymap_sneak")),
input->getLeftState(),
input->getRightState(),
camera_pitch,
camera_yaw
);
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();
}
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::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_SET_TEXTURE_MOD 2
#define GENERIC_CMD_SET_SPRITE 3
#define GENERIC_CMD_PUNCHED 4
#define GENERIC_CMD_UPDATE_ARMOR_GROUPS 5
#define GENERIC_CMD_SET_ANIMATION 4
#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"
std::string gob_cmd_set_properties(const ObjectProperties &prop);
@ -54,6 +57,12 @@ std::string gob_cmd_set_sprite(
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);
#include "itemgroup.h"

View File

@ -199,6 +199,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
m_inventorylists.clear();
m_images.clear();
m_backgrounds.clear();
m_fields.clear();
Strfnd f(m_formspec_string);
@ -278,9 +279,26 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
<<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl;
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));
}
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")
{
std::string fname = f.next(";");
@ -458,6 +476,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
video::ITexture *texture = m_gamedef->tsrc()->getTextureRaw(fimage);
gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
e->setUseAlphaChannel(true);
e->setImage(texture);
e->setPressedImage(texture);
e->setScaleImage(true);
@ -691,6 +710,26 @@ void GUIFormSpecMenu::drawMenu()
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
*/
@ -715,8 +754,11 @@ void GUIFormSpecMenu::drawMenu()
Draw items
Phase 0: Item slot rectangles
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++)
{
drawList(m_inventorylists[i], phase);

View File

@ -206,6 +206,7 @@ protected:
TextDest *m_text_dst;
core::array<ListDrawSpec> m_inventorylists;
core::array<ImageDrawSpec> m_backgrounds;
core::array<ImageDrawSpec> m_images;
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):
Player(gamedef),
isAttached(false),
overridePosition(v3f(0,0,0)),
m_sneak_node(32767,32767,32767),
m_sneak_node_exists(false),
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;
// Copy parent position if local player is attached
if(isAttached)
{
setPosition(overridePosition);
return;
}
// Skip collision detection if a special movement mode is used
bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
bool free_move = fly_allowed && g_settings->getBool("free_move");
@ -352,7 +361,14 @@ void LocalPlayer::applyControl(float dtime)
setPitch(control.pitch);
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);
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"
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
{
public:
@ -79,6 +32,10 @@ public:
{
return true;
}
bool isAttached;
v3f overridePosition;
void move(f32 dtime, Map &map, f32 pos_max_d,
core::list<CollisionInfo> *collision_info);
@ -87,9 +44,6 @@ public:
void applyControl(float dtime);
v3s16 getStandingNodePos();
PlayerControl control;
private:
// This is used for determining the sneaking range
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++)
{
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++;
}

View File

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

View File

@ -217,7 +217,7 @@ void ContentFeatures::reset()
void ContentFeatures::serialize(std::ostream &os)
{
writeU8(os, 5); // version
writeU8(os, 6); // version
os<<serializeString(name);
writeU16(os, groups.size());
for(ItemGroupList::const_iterator
@ -254,6 +254,7 @@ void ContentFeatures::serialize(std::ostream &os)
os<<serializeString(liquid_alternative_flowing);
os<<serializeString(liquid_alternative_source);
writeU8(os, liquid_viscosity);
writeU8(os, liquid_renewable);
writeU8(os, light_source);
writeU32(os, damage_per_second);
node_box.serialize(os);
@ -265,13 +266,12 @@ void ContentFeatures::serialize(std::ostream &os)
serializeSimpleSoundSpec(sound_dug, os);
// Stuff below should be moved to correct place in a version that otherwise changes
// the protocol version
writeU8(os, liquid_renewable);
}
void ContentFeatures::deSerialize(std::istream &is)
{
int version = readU8(is);
if(version != 5)
if(version != 6)
throw SerializationError("unsupported ContentFeatures version");
name = deSerializeString(is);
groups.clear();
@ -311,6 +311,7 @@ void ContentFeatures::deSerialize(std::istream &is)
liquid_alternative_flowing = deSerializeString(is);
liquid_alternative_source = deSerializeString(is);
liquid_viscosity = readU8(is);
liquid_renewable = readU8(is);
light_source = readU8(is);
damage_per_second = readU32(is);
node_box.deSerialize(is);
@ -325,7 +326,6 @@ void ContentFeatures::deSerialize(std::istream &is)
try{
// Stuff below should be moved to correct place in a version that
// otherwise changes the protocol version
liquid_renewable = readU8(is);
}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 "irrlichttypes_bloated.h"
#include "util/serialize.h"
#include <sstream>
#include <map>
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
#define PP2(x) "("<<(x).X<<","<<(x).Y<<")"
@ -30,6 +32,7 @@ ObjectProperties::ObjectProperties():
weight(5),
collisionbox(-0.5,-0.5,-0.5, 0.5,0.5,0.5),
visual("sprite"),
mesh(""),
visual_size(1,1),
spritediv(1,1),
initial_sprite_basepos(0,0),
@ -38,6 +41,7 @@ ObjectProperties::ObjectProperties():
automatic_rotate(0)
{
textures.push_back("unknown_object.png");
colors.push_back(video::SColor(255,255,255,255));
}
std::string ObjectProperties::dump()
@ -48,12 +52,18 @@ std::string ObjectProperties::dump()
os<<", weight="<<weight;
os<<", collisionbox="<<PP(collisionbox.MinEdge)<<","<<PP(collisionbox.MaxEdge);
os<<", visual="<<visual;
os<<", mesh="<<mesh;
os<<", visual_size="<<PP2(visual_size);
os<<", textures=[";
for(u32 i=0; i<textures.size(); i++){
os<<"\""<<textures[i]<<"\" ";
}
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<<", initial_sprite_basepos="<<PP2(initial_sprite_basepos);
os<<", is_visible="<<is_visible;
@ -71,11 +81,16 @@ void ObjectProperties::serialize(std::ostream &os) const
writeV3F1000(os, collisionbox.MinEdge);
writeV3F1000(os, collisionbox.MaxEdge);
os<<serializeString(visual);
os<<serializeString(mesh);
writeV2F1000(os, visual_size);
writeU16(os, textures.size());
for(u32 i=0; i<textures.size(); 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, initial_sprite_basepos);
writeU8(os, is_visible);
@ -94,12 +109,17 @@ void ObjectProperties::deSerialize(std::istream &is)
collisionbox.MinEdge = readV3F1000(is);
collisionbox.MaxEdge = readV3F1000(is);
visual = deSerializeString(is);
mesh = deSerializeString(is);
visual_size = readV2F1000(is);
textures.clear();
u32 texture_count = readU16(is);
for(u32 i=0; i<texture_count; i++){
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);
initial_sprite_basepos = readV2S16(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 "irrlichttypes_bloated.h"
#include <iostream>
#include <map>
struct ObjectProperties
{
@ -32,14 +33,17 @@ struct ObjectProperties
float weight;
core::aabbox3d<f32> collisionbox;
std::string visual;
std::string mesh;
v2f visual_size;
core::array<std::string> textures;
core::array<video::SColor> colors;
v2s16 spritediv;
v2s16 initial_sprite_basepos;
bool is_visible;
bool makes_footstep_sound;
float automatic_rotate;
ObjectProperties();
std::string dump();
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-_"
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 IGameDef;
struct CollisionInfo;
@ -155,9 +210,17 @@ public:
u16 hp;
u16 peer_id;
std::string inventory_formspec;
PlayerControl control;
PlayerControl getPlayerControl()
{
return control;
}
u32 keyPressed;
protected:
IGameDef *m_gamedef;
@ -182,7 +245,7 @@ public:
void setPlayerSAO(PlayerSAO *sao)
{ m_sao = sao; }
void setPosition(const v3f &position);
private:
PlayerSAO *m_sao;
};

View File

@ -936,6 +936,8 @@ static void read_object_properties(lua_State *L, int index,
lua_pop(L, 1);
getstringfield(L, -1, "visual", prop->visual);
getstringfield(L, -1, "mesh", prop->mesh);
lua_getfield(L, -1, "visual_size");
if(lua_istable(L, -1))
@ -958,6 +960,23 @@ static void read_object_properties(lua_State *L, int index,
}
}
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");
if(lua_istable(L, -1))
@ -2697,6 +2716,80 @@ private:
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)
static int l_set_properties(lua_State *L)
{
@ -2932,7 +3025,54 @@ private:
lua_pushlstring(L, formspec.c_str(), formspec.size());
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:
ObjectRef(ServerActiveObject *object):
m_object(object)
@ -3011,6 +3151,10 @@ const luaL_reg ObjectRef::methods[] = {
method(ObjectRef, get_wielded_item),
method(ObjectRef, set_wielded_item),
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),
// LuaEntitySAO-only
method(ObjectRef, setvelocity),
@ -3031,6 +3175,8 @@ const luaL_reg ObjectRef::methods[] = {
method(ObjectRef, get_look_yaw),
method(ObjectRef, set_inventory_formspec),
method(ObjectRef, get_inventory_formspec),
method(ObjectRef, get_player_control),
method(ObjectRef, get_player_control_bits),
{0,0}
};
@ -6588,6 +6734,8 @@ void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
lua_pop(L, 1);
getstringfield(L, -1, "visual", prop->visual);
getstringfield(L, -1, "mesh", prop->mesh);
// Deprecated: read object properties directly
read_object_properties(L, -1, prop);

View File

@ -1371,9 +1371,9 @@ void Server::AsyncRunStep()
/*
Send player inventories and HPs if necessary
*/
if(playersao->m_teleported){
if(playersao->m_moved){
SendMovePlayer(client->peer_id);
playersao->m_teleported = false;
playersao->m_moved = false;
}
if(playersao->m_inventory_not_sent){
UpdateCrafting(client->peer_id);
@ -2369,7 +2369,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
if(command == TOSERVER_PLAYERPOS)
{
if(datasize < 2+12+12+4+4)
if(datasize < 2+12+12+4+4+4)
return;
u32 start = 0;
@ -2377,6 +2377,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
v3s32 ss = readV3S32(&data[start+2+12]);
f32 pitch = (f32)readS32(&data[2+12+12]) / 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 speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
pitch = wrapDegrees(pitch);
@ -2386,6 +2387,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
player->setSpeed(speed);
player->setPitch(pitch);
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 "
<<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
@ -3166,6 +3177,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
} // action == 4
/*
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 + "sounds");
paths.push_back(mod.path + DIR_DELIM + "media");
paths.push_back(mod.path + DIR_DELIM + "models");
}
std::string path_all = "textures";
paths.push_back(path_all + DIR_DELIM + "all");
@ -4054,6 +4067,7 @@ void Server::fillMediaCache()
".png", ".jpg", ".bmp", ".tga",
".pcx", ".ppm", ".psd", ".wal", ".rgb",
".ogg",
".x", ".b3d", ".md2", ".obj",
NULL
};
if(removeStringEnd(filename, supported_ext) == ""){

View File

@ -152,6 +152,12 @@ public:
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()
{ return NULL; }
virtual void notifyObjectPropertiesModified()

View File

@ -518,15 +518,6 @@ core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<
// Apply transform to image data
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".
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 from resulting image
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)
@ -816,7 +809,7 @@ void TextureSource::rebuildImagesAndTextures()
// Create texture from resulting image
video::ITexture *t = NULL;
if(img)
t = register_texture(driver, sap->name, img);
t = driver->addTexture(sap->name.c_str(), img);
// Replace texture
sap->a.atlas = t;
@ -1051,7 +1044,7 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
/*
Make texture
*/
video::ITexture *t = register_texture(driver, "__main_atlas__", atlas_img);
video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
assert(t);
/*
@ -1142,15 +1135,6 @@ video::IImage* generate_image_from_scratch(std::string name,
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,
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);
// Create textures from images
video::ITexture *texture_top = register_texture(driver,
imagename_top + "__temp1__", img_top);
video::ITexture *texture_left = register_texture(driver,
imagename_left + "__temp2__", img_left);
video::ITexture *texture_right = register_texture(driver,
imagename_right + "__temp3__", img_right);
video::ITexture *texture_top = driver->addTexture(
(imagename_top + "__temp__").c_str(), img_top);
video::ITexture *texture_left = driver->addTexture(
(imagename_left + "__temp__").c_str(), img_left);
video::ITexture *texture_right = driver->addTexture(
(imagename_right + "__temp__").c_str(), img_right);
assert(texture_top && texture_left && texture_right);
// Drop images

View File

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define UTIL_SERIALIZE_HEADER
#include "../irrlichttypes.h"
#include "../irrlichttypes_bloated.h"
#include "../irr_v2d.h"
#include "../irr_v3d.h"
#include <iostream>
@ -197,6 +198,24 @@ inline v3s16 readV3S16(u8 *data)
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
*/
@ -344,6 +363,20 @@ inline v3s16 readV3S16(std::istream &is)
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
*/