Skip to content

Commit

Permalink
Only send changed object properties
Browse files Browse the repository at this point in the history
  • Loading branch information
cx384 committed Jan 11, 2025
1 parent e5542e5 commit 467ba28
Show file tree
Hide file tree
Showing 12 changed files with 309 additions and 20 deletions.
3 changes: 2 additions & 1 deletion src/activeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ enum ActiveObjectCommand {
AO_CMD_OBSOLETE1,
// ^ UPDATE_NAMETAG_ATTRIBUTES deprecated since 0.4.14, removed in 5.3.0
AO_CMD_SPAWN_INFANT,
AO_CMD_SET_ANIMATION_SPEED
AO_CMD_SET_ANIMATION_SPEED,
AO_CMD_UPDATE_PROPERTIES
};

struct BoneOverride
Expand Down
12 changes: 8 additions & 4 deletions src/client/content_cao.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1583,11 +1583,15 @@ void GenericCAO::processMessage(const std::string &data)
std::istringstream is(data, std::ios::binary);
// command
u8 cmd = readU8(is);
if (cmd == AO_CMD_SET_PROPERTIES) {
if (cmd == AO_CMD_SET_PROPERTIES || cmd == AO_CMD_UPDATE_PROPERTIES) {
ObjectProperties newprops;
newprops.show_on_minimap = m_is_player; // default

newprops.deSerialize(is);
if (cmd == AO_CMD_UPDATE_PROPERTIES) {
newprops = m_prop;
newprops.deSerializeChanges(is);
} else {
newprops.show_on_minimap = m_is_player; // default
newprops.deSerialize(is);
}

// Check what exactly changed
bool expire_visuals = visualExpiryRequired(newprops);
Expand Down
246 changes: 246 additions & 0 deletions src/object_properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,249 @@ void ObjectProperties::deSerialize(std::istream &is)
rotate_selectionbox = tmp;
} catch (SerializationError &e) {}
}

ObjectProperties::ChangedProperties ObjectProperties::getChange(const ObjectProperties &other)
{
ChangedProperties change;

change[0] = textures != other.textures;
change[1] = colors != other.colors;
change[2] = collisionbox != other.collisionbox;
change[3] = selectionbox != other.selectionbox;
change[4] = visual != other.visual;
change[5] = mesh != other.mesh;
change[6] = damage_texture_modifier != other.damage_texture_modifier;
change[7] = nametag != other.nametag;
change[8] = infotext != other.infotext;
change[9] = wield_item != other.wield_item;
change[10] = visual_size != other.visual_size;
change[11] = nametag_color != other.nametag_color;
change[12] = nametag_bgcolor != other.nametag_bgcolor;
change[13] = spritediv != other.spritediv;
change[14] = initial_sprite_basepos != other.initial_sprite_basepos;
change[15] = stepheight != other.stepheight;
change[16] = automatic_rotate != other.automatic_rotate;
change[17] = automatic_face_movement_dir_offset != other.automatic_face_movement_dir_offset;
change[18] = automatic_face_movement_max_rotation_per_sec !=
other.automatic_face_movement_max_rotation_per_sec;
change[19] = eye_height != other.eye_height;
change[20] = zoom_fov != other.zoom_fov;
change[21] = hp_max != other.hp_max;
change[22] = breath_max != other.breath_max;
change[23] = glow != other.glow;
change[24] = pointable != other.pointable;
change[25] = physical != other.physical ||
collideWithObjects != other.collideWithObjects ||
rotate_selectionbox != other.rotate_selectionbox ||
is_visible != other.is_visible ||
makes_footstep_sound != other.makes_footstep_sound ||
automatic_face_movement_dir != other.automatic_face_movement_dir ||
backface_culling != other.backface_culling ||
use_texture_alpha != other.use_texture_alpha;
change[26] = shaded != other.shaded || show_on_minimap != other.show_on_minimap;

return change;
}

void ObjectProperties::serializeChanges(std::ostream &os, const ChangedProperties &fields) const
{
writeU8(os, 0); // Version 0 uses 27 bits (actually only a quarter of the last bit :-)

writeU32(os, fields.to_ulong());

if (fields[0]) {
writeU16(os, textures.size());
for (const std::string &texture : textures) {
os << serializeString16(texture);
}
}
if (fields[1]) {
writeU16(os, colors.size());
for (video::SColor color : colors) {
writeARGB8(os, color);
}
}
if (fields[2]) {
writeV3F32(os, collisionbox.MinEdge);
writeV3F32(os, collisionbox.MaxEdge);
}
if (fields[3]) {
writeV3F32(os, selectionbox.MinEdge);
writeV3F32(os, selectionbox.MaxEdge);
}
if (fields[4])
os << serializeString16(visual);
if (fields[5])
os << serializeString16(mesh);
if (fields[6])
os << serializeString16(damage_texture_modifier);
if (fields[7])
os << serializeString16(nametag);
if (fields[8])
os << serializeString16(infotext);
if (fields[9])
os << serializeString16(wield_item);
if (fields[10])
writeV3F32(os, visual_size);
if (fields[11])
writeARGB8(os, nametag_color);
if (fields[12]) {
if (!nametag_bgcolor)
writeARGB8(os, NULL_BGCOLOR);
else if (nametag_bgcolor.value().getAlpha() == 0)
writeARGB8(os, video::SColor(0, 0, 0, 0));
else
writeARGB8(os, nametag_bgcolor.value());
}
if (fields[13])
writeV2S16(os, spritediv);
if (fields[14])
writeV2S16(os, initial_sprite_basepos);
if (fields[15])
writeF32(os, stepheight);
if (fields[16])
writeF32(os, automatic_rotate);
if (fields[17])
writeF32(os, automatic_face_movement_dir_offset);
if (fields[18])
writeF32(os, automatic_face_movement_max_rotation_per_sec);
if (fields[19])
writeF32(os, eye_height);
if (fields[20])
writeF32(os, zoom_fov);
if (fields[21])
writeU16(os, hp_max);
if (fields[22])
writeU16(os, breath_max);
if (fields[23])
writeS8(os, glow);
if (fields[24])
Pointabilities::serializePointabilityType(os, pointable);

if (fields[25]) {
u8 bits = 0;
if (physical)
bits |= 0b00000001;
if (collideWithObjects)
bits |= 0b00000010;
if (rotate_selectionbox)
bits |= 0b00000100;
if (is_visible)
bits |= 0b00001000;
if (makes_footstep_sound)
bits |= 0b00010000;
if (automatic_face_movement_dir)
bits |= 0b00100000;
if (backface_culling)
bits |= 0b01000000;
if (use_texture_alpha)
bits |= 0b10000000;
writeU8(os, bits);
}

if (fields[26]) {
u8 bits = 0;
if (shaded)
bits |= 0b00000001;
if (show_on_minimap)
bits |= 0b00000010;
writeU8(os, bits);
}
}

void ObjectProperties::deSerializeChanges(std::istream &is)
{
u8 version = readU8(is);
if (version != 0)
throw SerializationError("unsupported ObjectProperties serializeChanges version");

ChangedProperties fields(readU32(is));

if (fields[0]) {
textures.clear();
u16 texture_count = readU16(is);
for (u16 i = 0; i < texture_count; i++){
textures.push_back(deSerializeString16(is));
}
}
if (fields[1]) {
colors.clear();
u16 color_count = readU16(is);
for (u16 i = 0; i < color_count; i++){
colors.push_back(readARGB8(is));
}
}
if (fields[2]) {
collisionbox.MinEdge = readV3F32(is);
collisionbox.MaxEdge = readV3F32(is);
}
if (fields[3]) {
selectionbox.MinEdge = readV3F32(is);
selectionbox.MaxEdge = readV3F32(is);
}
if (fields[4])
visual = deSerializeString16(is);
if (fields[5])
mesh = deSerializeString16(is);
if (fields[6])
damage_texture_modifier = deSerializeString16(is);
if (fields[7])
nametag = deSerializeString16(is);
if (fields[8])
infotext = deSerializeString16(is);
if (fields[9])
wield_item = deSerializeString16(is);
if (fields[10])
visual_size = readV3F32(is);
if (fields[11])
nametag_color = readARGB8(is);
if (fields[12]) {
auto bgcolor = readARGB8(is);
if (bgcolor != NULL_BGCOLOR)
nametag_bgcolor = bgcolor;
else
nametag_bgcolor = std::nullopt;
}
if (fields[13])
spritediv = readV2S16(is);
if (fields[14])
initial_sprite_basepos = readV2S16(is);
if (fields[15])
stepheight = readF32(is);
if (fields[16])
automatic_rotate = readF32(is);
if (fields[17])
automatic_face_movement_dir_offset = readF32(is);
if (fields[18])
automatic_face_movement_max_rotation_per_sec = readF32(is);
if (fields[19])
eye_height = readF32(is);
if (fields[20])
zoom_fov = readF32(is);
if (fields[21])
hp_max = readU16(is);
if (fields[22])
breath_max = readU16(is);
if (fields[23])
glow = readS8(is);
if (fields[24])
pointable = Pointabilities::deSerializePointabilityType(is);

if (fields[25]) {
u8 bits = readU8(is);
physical = bits & 0b00000001;
collideWithObjects = bits & 0b00000010;
rotate_selectionbox = bits & 0b00000100;
is_visible = bits & 0b00001000;
makes_footstep_sound = bits & 0b00010000;
automatic_face_movement_dir = bits & 0b00100000;
backface_culling = bits & 0b01000000;
use_texture_alpha = bits & 0b10000000;
}

if (fields[26]) {
u8 bits = readU8(is);
shaded = bits & 0b00000001;
show_on_minimap = bits & 0b00000010;
}
}
12 changes: 12 additions & 0 deletions src/object_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <iostream>
#include <vector>
#include "util/pointabilities.h"
#include <bitset>

struct ObjectProperties
{
Expand Down Expand Up @@ -74,4 +75,15 @@ struct ObjectProperties

void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);

using ChangedProperties = std::bitset<27>;
ChangedProperties getChange(const ObjectProperties &other);
static constexpr ChangedProperties nametag_change{0b1100010000000ul};
static constexpr ChangedProperties full_change{~0ul};

// Those only change values if the corresponding bit is set,
// presumably a little slower to parse, but much more space efficient.
// (Even if all bits are set, they are not compatible with serialize and deSerialize.)
void serializeChanges(std::ostream &os, const ChangedProperties &fields) const;
void deSerializeChanges(std::istream &is);
};
4 changes: 2 additions & 2 deletions src/script/lua_api/l_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,7 @@ int ObjectRef::l_set_properties(lua_State *L)
read_object_properties(L, 2, sao, prop, getServer(L)->idef());
if (*prop != old) {
prop->validate();
sao->notifyObjectPropertiesModified();
sao->notifyObjectPropertiesModified(prop->getChange(old));
}
return 0;
}
Expand Down Expand Up @@ -950,7 +950,7 @@ int ObjectRef::l_set_nametag_attributes(lua_State *L)
prop->nametag = getstringfield_default(L, 2, "text", prop->nametag);

prop->validate();
sao->notifyObjectPropertiesModified();
sao->notifyObjectPropertiesModified(ObjectProperties::nametag_change);
return 0;
}

Expand Down
11 changes: 8 additions & 3 deletions src/server/luaentity_sao.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ void LuaEntitySAO::dispatchScriptDeactivate(bool removal)

void LuaEntitySAO::step(float dtime, bool send_recommended)
{
if (!m_properties_sent) {
m_properties_sent = true;
std::string str = getPropertyPacket();
if (m_properties_to_send.any()) {
std::string str = getPropertyPacket(m_properties_to_send);
m_properties_to_send.reset();
// create message and add to list
m_messages_out.emplace(getId(), true, std::move(str));
}
Expand Down Expand Up @@ -494,6 +494,11 @@ std::string LuaEntitySAO::getPropertyPacket()
return generateSetPropertiesCommand(m_prop);
}

std::string LuaEntitySAO::getPropertyPacket(const ObjectProperties::ChangedProperties &change)
{
return generateUpdatePropertiesCommand(m_prop, change);
}

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
Expand Down
1 change: 1 addition & 0 deletions src/server/luaentity_sao.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class LuaEntitySAO : public UnitSAO

private:
std::string getPropertyPacket();
std::string getPropertyPacket(const ObjectProperties::ChangedProperties &change);
void sendPosition(bool do_interpolate, bool is_movement_end);
std::string generateSetTextureModCommand() const;
static std::string generateSetSpriteCommand(v2s16 p, u16 num_frames,
Expand Down
13 changes: 9 additions & 4 deletions src/server/player_sao.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,9 @@ void PlayerSAO::step(float dtime, bool send_recommended)
}
}

if (!m_properties_sent) {
m_properties_sent = true;
std::string str = getPropertyPacket();
if (m_properties_to_send.any()) {
std::string str = getPropertyPacket(m_properties_to_send);
m_properties_to_send.reset();
// create message and add to list
m_messages_out.emplace(getId(), true, str);
m_env->getScriptIface()->player_event(this, "properties_changed");
Expand Down Expand Up @@ -524,7 +524,7 @@ void PlayerSAO::setHP(s32 target_hp, const PlayerHPChangeReason &reason, bool fr

// Update properties on death
if ((hp == 0) != (m_hp == 0))
m_properties_sent = false;
m_properties_to_send = ObjectProperties::full_change;

if (hp != m_hp) {
m_hp = hp;
Expand Down Expand Up @@ -616,6 +616,11 @@ std::string PlayerSAO::getPropertyPacket()
return generateSetPropertiesCommand(m_prop);
}

std::string PlayerSAO::getPropertyPacket(const ObjectProperties::ChangedProperties &change)
{
return generateUpdatePropertiesCommand(m_prop, change);
}

void PlayerSAO::setMaxSpeedOverride(const v3f &vel)
{
if (m_max_speed_override_time == 0.0f)
Expand Down
1 change: 1 addition & 0 deletions src/server/player_sao.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ class PlayerSAO : public UnitSAO

private:
std::string getPropertyPacket();
std::string getPropertyPacket(const ObjectProperties::ChangedProperties &change);
void unlinkPlayerSessionAndSave();
std::string generateUpdatePhysicsOverrideCommand() const;

Expand Down
Loading

0 comments on commit 467ba28

Please sign in to comment.