Reputation: 281
So the situation is I am making an api for a game that controls networking and to keep down the bandwidth needed I need to know what properties change. I am using an entity component system so the programmer working on gameplay would be making code like this
public class PosComponent{
public int x {get; set;}
public int y {get; set;}
}
So what my goal is, is for the programmer to mark what they want synced and how it should be synced at which point the networking systems will take over and handle detecting changes and actually sending the data to the clients and what not. So the programmer would do something like this.
public class PosComponent{
[KeepSynced(Reliability.Loose, SyncDirection.ToClient)]
public int x {get; set;}
[KeepSynced(Reliability.Loose, SyncDirection.ToClient)]
public int y {get; set;}
}
Everything I find about similar problems is people saying well just add code to the properties. I do not like this solution because it would make the programmer add a bunch of code to track their own changes and I do not want that because it adds ability to introduce bugs that are really hard to track down but also handleing it all in another part of the program lets me optimize all everything all together with out the need to refactor tons and tons of code.
So I don't want to just do something like :
public class PosComponent{
public int _x;
public bool _xChange;
[KeepSynced(Reliability.Loose, SyncDirection.ToClient, ref _xChange)]
public int x {get{return _x;} set{
if(value!=_x) _yChange = true;
_x=value;
}}
public int _y;
public bool _yChange;
[KeepSynced(Reliability.Loose, SyncDirection.ToClient, ref _yChange)]
public int y {get{return _y;} set{
if(value!=_y) _yChange = true;
_y=value;
}}
}
Now that is not to say I am opposed to that being the effective code if anyone can tell me how to inject some code into a property or something but the main thing is I want to maintain that nice clean simple signature in the source code that is super easy to read.
I have accepted the fact that I may need to just keep a copy of all of the values that have the KeepSynced
attribute but I am not sure how to do that with reflection I am currently trying to get it to a point where I can just loop through all the entity components and if they have the attribute then I will check it ageist a huge table (dictionary) that contains some kind of identifier for the instance of the property and then holds a copy of the last value the property was and then it will go and if the match it will not do anything if they do not match then I will mark another table of bools indexed with what ever identifier i use (which I do not yet know how to get) this will tell me what is dirty and needs to be sent out to the clients.
Upvotes: 0
Views: 398
Reputation: 11
To simplify: What you're attempting to do is "raise an event" (of sorts) when a value changes, without modifying the component itself. You have two (simple) options:
With a data-oriented ECS, you generally store components as collections, which makes both options trivially easy. If I were you, I'd make writing to the component auto-set a flag. E.g. (simplified)
// Example ECS component storage. Simple, and pretty damn fast.
public class ComponentStorage<T> where T : struct
{
private T[] m_data;
public BitArray m_replacedThisFrame;
...
// Ideally, we'd inline this.
public void ReplaceComponent(int entityId, T newVal)
{
if(m_data[entityId] == newVal) return;
m_data[entityId] = newVal;
m_replacedThisFrame[entityId] = true;
}
}
// Use case.
componentStorageInstance.ReplaceComponent(entityId, new PosComponent { ... });
m_replacedThisFrame
effectively gives you a query of all changed entities this frame. Iterate over the collection to generate a serializable (networkable) collection of changes, using whatever strategy you want.
Personally, for network code, I'd strongly advise you look into PURE entity component systems. Keep all your data in one format, apply rules on how you read/write to them. Networking becomes much more trivial.
Attributes and Reflection "magic" (a.k.a. unknown stuff to make the code work) is a pain to work with, and is usually extremely bad for performance and memory. Keep it simple, build a for loop that aggregates changes from the BitArray as a strategy.
Upvotes: 1