Defining Awesome — coding black magic
  • Status Updates

  • coding black magic

    Written by . Posted at 6:08 am on September 17th, 2010

    Back to news for coding geeks. I made 2 interesting things this week. It would seem like black magic to me a year ago but now I have uber c++ skills.


    First I made a class called BadassPointer.
    It works like a smart pointer but additionally all objects that are pointed by a BadassPointer keep lists of them. So when the the object gets deleted it NULLs all these BadassPointers. This is extremely useful because of a frequent error I would get that’s hard to debug and workaround. Let’s say I have a pointer to a *grenade on the client. Now the actual grenade object gets deleted by a request from server. The grenade gets deleted but all the pointers to the grenade remain, because they don’t know the grenade got deleted! This results in Access Violation errors. BadassPointers solve this problem elegantly.

    It’s very easy to use them. Just declare:
    CBadassPointer<CGrenade> grenadeptr;
    Assign to the actual object:
    grenadeptr = grenade;

    And voila! You can use grenadeptr just as you would a normal pointer.

    Another feature which works but I don’t like the way I wrote it, so it needs refactoring, is that objects keep track of lists they are in. So not only they NULL pointers pointing at them but they remove themselves from all lists holding them! Awesomness.


    Second thing is I made a network variable. I wanted to have something that you declare in one line of code and you never have to think about it again, whether it gets synced across network or not. You just declare it and it sends itself.

    There are two problems with this.
    1. How does the remote client know what actual variable the network variable points at? Since memory addresses on each machine are different.
    2. How do I have just one way of declaring a variable for all types?
    3. How do I control the sending rate so the network isn’t flooded (for example when a variable changes every tick)?

    For the first problem I decided to use ID’s which are hardcoded. I thought about this long but didn’t find a better solution. It seems to be the only way to do this. So before declaring a netvariable I have to assign it a unique ID (this isn’t so much of a problem, I keep them all in an enum; could this be generated automatically?).
    The second problem is solved of course by using templates. The third problem is solved by directly declaring the send threshold in the template settings. So the declaration of a network variable looks like this:

    CNetVariable< int, unique_id, 10 > time;

    From then on time can be used just the same as a normal int due to operator overloads. 10 – is the threshold, so it sends the variable only if it differs by 10 from the old sent value.

    I can also use String variables:
    CNetVariable< String, unique_id,  0 > name;

    0 means the string will be always sent whenever any change occurs on it.

    Using templates added another problem, which is, I have no idea how to hold template classes in a list (anyone knows?). I don’t think it is possible, so what I did is I just hold a list of void* pointers. And when I send or receive these variables I read the class byte by byte and determine by some bytes, what type the variable is and where it actually points to. This is why I like c/c++ cause I have access to the smallest bits of anything and I can work on that. A class is not an abstract object, if you know how computers and compilers work, you see the underlying bit structure of them. Moving/reading/writing that, is the black magic.

    Be Sociable, Share!

    16 comments.

    1. >I have no idea how to hold template classes in a list (anyone knows?)

      Inherit from a base class, create a storage container with pointers to the base class.


    2. Toumaz: example? And will it preserve the functionality I got now?


    3. That’s an awesome pointer class :)

      The second part sounds very similar to a networking library I used a while back called Zoidcom. It could do full class replication as well. So if you made a new object on the server it automatically tells all the clients to also create that object.

      Look at this page of Zoidcom’s documentation: http://www.zoidcom.com/docs/SyncReplicators.html

      Keep up the good work. Link-Dead is going to be awesome!


    4. This is more or less how I handle network variables; I’ve enumerated most from memory as I don’t have actual examples available to me.

      http://pastie.org/1164711


    5. Toumaz: did you forget deriving the interface
      class CNetVar : public CAbstractNetVar ?


    6. Yes. 😛


    7. Damn I forgot entirely about using interfaces. That will work and is much cleaner. I’ll rewrite it some day.

      What about ID’s how does the client know what actual variable it should update?


    8. From the top down, all network entities in my network code are inheriting from a base class with these characteristics:

      – A SendTable member
      – A Serialize method (public access to sendtable)
      – A Deserialize method (public access to sendtable)
      – An entity ID

      To begin with, I use an entity dictionary (“edict”) to properly identify and construct specific network classes across the network. I’ve implemented this using self-registering template factories (if you want a code sample, I’ll cook something up later), which in the end serve to identify classes by an 8-bit integer. (I.e., I can do something like CPlayer::GetEdict() and receive “0”, and do CEdict::CreateInstance(0) and receive a new CPlayer instance.) This is mainly used for construction across the network.

      To uniquely identify different entities, they all have a entity/instance ID – all instances have a globally unique 16-bit integer.

      Since all network entities have a sendtable class, I can define certain variables for which to provide network serialization in the appropriate constructor.

      Upon serialization of the CSendTable, only variables which needs to be sent are serialized – this allows for delta updates, updates ever X seconds. It could all be specialized and customized thanks to the fact that there is a base network variable class.

      A short example:
      http://pastie.org/private/4mids0n7qcl9hgv2qszg


    9. It’s been a while since I’ve looked at Source SDK; but I think they used normal int’s etc and had within a constructor a ‘NetworkVariable( , precision ,…) method that was invoked on each variable they wanted to keep updated over the network.

      Maybe check out how Source SDK does it?


    10. Blacksheepboy

      I’m going to do some shameless advertising here of a product that isn’t mine…

      Although I lack all means, all skills to use this product, it still fascinates me. What I did do that introduced me to the project was help design the simple icons. If ever you are curious, and have the spare time (maybe during a lull in the Link Dead production), look into the product behind http//www.lrstar.org. You did mention something about compilers, and this fella behind lrstar has been working solo on this project for the last 32 years.

      Once again, this stuff is way beyond me, but from what I’ve seen, this guy is dedicated to making it “the best in the world”. You might be interested.


    11. Blacksheepboy

      ugh, link fail. Seems like he took his webpage down. That’s too bad. All of my big talk for nothing o_o


    12. Blacksheepboy

      Apologies for the triple post…

      Evidence of website existence (I swear, it was up yesterday! lol):
      http://webcache.googleusercontent.com/search?q=cache:PHju54k8-4QJ:www.lrstar.org/+lrstar&cd=2&hl=en&ct=clnk&gl=us

      Softpedia and sourceforge nabbed copies of his latest version:
      http://www.softpedia.com/get/Programming/Other-Programming-Files/LALR-LR-Parser-Generator.shtml


    13. I’m lost, not big surprise.


    14. Toumaz: I do it very similarly. Object factories are great. I use the code from Enginuity for my object factory.

      But I wanted to have network variables in a much simpler class, where I can use ints and floats the same way I would normally do. So I can use ++ and -= and assign/comparison signs on them. That’s why I made it separate from my usual network object class.

      Andrew: Ah of course, I remember Source having that. I’ll check it out.

      Blacksheepboy: I don’t know what I would use it for, in-game c++ parser for scripts?


    15. Blacksheepboy

      I am not really sure. I got ahead of myself with that suggestion. Too far ahead of myself.


    16. Nice post.

      Wouldn’t it be more flexible if you allowed both a number *or* a function pointer as the third template parameter to CNetVariable? This way, you could have fine control over how/when updates to the variable get propagated over the network.


    Post a comment.

    Links