问题描述
|
我试图编写一些基础结构来促进服务器和客户端之间的对象更新。这可能会用在游戏中,但是,我觉得这个问题根本不是游戏所特有的(因此我在这里已经问过了)。
出于安全和效率的原因,我希望服务器选择性地更新对象属性。例如,对象的特定属性可能仅对控制该对象的客户端有用,因为这样服务器将仅使用此信息来更新“所有者”。或者,可能需要将某些属性发送给所有客户端。为了实现这一点,我定义了一个自定义属性,该属性指定网络处理属性的方式:
[AttributeUsage(AttributeTargets.Property)]
public class NetworkParameterattribute : System.Attribute
{
public enum NetworkParameterType
{
ServerToOwner,ServerToAll,ServerToOwnerView,OwnerToServer
}
private NetworkParameterType type;
public NetworkParameterType Type
{
get
{
return type;
}
}
public NetworkParameterattribute(NetworkParameterType Type)
{
this.type = Type;
}
}
现在,在对象类中,我可以定义如下属性:
public class TestObject
{
[NetworkParameter(NetworkParameterattribute.NetworkParameterType.ServerToAll)]
public int ID { get; set; }
[NetworkParameter(NetworkParameterattribute.NetworkParameterType.ServerToOwner)]
public string Name { get; set; }
}
然后,我可以编写一个简单的函数,该函数自动从对象中获取一组特定的属性:
public byte[] GetBytes(NetworkParameterattribute.NetworkParameterType type)
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
foreach (PropertyInfo info in this.GetType().GetProperties())
{
foreach (object attribute in info.GetCustomAttributes(true))
{
if (attribute is NetworkParameterattribute &&
((NetworkParameterattribute)attribute).Type == type)
{
formatter.Serialize(stream,info.GetValue(this,null));
}
}
}
byte[] buf = new byte[stream.Length];
Array.copy(stream.GetBuffer(),buf,stream.Length);
return buf;
}
类似的功能可以将对象放回接收侧。我遇到的问题是序列化在使用空间方面非常低效。例如,从TestObject获取ServerToAll属性将产生54个字节(而该字节可能少至4个字节)。
那么问题来了:是否有一种更有效的方法将对象序列化为可以满足我预期目的的字节流?请注意,我不希望编写很多与序列化相关的代码。
谢谢!
解决方法
NetworkParameterAttribute应该具有附加字段,该字段将相应的属性表示为\“ dirty \”。对属性的每次更改都应有效地设置此标志,并且在序列化期间应重置该标志。实际上只有脏属性应该被序列化。
此外,由于对象仅被部分序列化,因此在序列化期间,您需要提供有关要序列化哪些属性的信息。在填充流时,请保留一个脏属性的位向量,并将该位向量放入返回的字节数组的开头。
编辑:我们可以拥有最后序列化的实际值,而不是在属性中包含标志。好处是我们不需要每个属性都需要附加代码来使标志与属性保持同步。在序列化过程中,我们比较两个值,如果值不相等,则对属性进行序列化。