Game Development Community

Sometimes ghost objs not being created?

by Dan Chao · in RTS Starter Kit · 01/15/2006 (8:33 pm) · 7 replies

So, I created a new class called RTSSpell. Each RTSUnit carries 4 of them. the server side seems to work fine with all the spells getting set properly. however sporadically, 1 in 4 don't create a ghost, or at the very least can't find the appopriate ghostID. when I send them over in packUpdate(), sometimes NetConnection->getGhostIndex() returns a -1 for some of them. this is pretty strange, because most of them work.

my RTSSpells are set to Ghostable and ScopeAlways. any reason you can think of why this might be happeneing or where to look?


thanks,
dan


in gameConnection->RTSConnection::createPlayer()
{
...
%spell = new RTSSpell()
{
dataBlock = nukeSpellBlock;
};
%player.setSpell(0, %spell);
%spell = new RTSSpell()
{
dataBlock = nukeSpellBlock;
};
%player.setSpell(1, %spell);
...and so on for up to 4 spells
}

RTSUnit::packUpdate()
{
...
// spells
if(stream->writeFlag(mask & InitialUpdateMask))
{
if (mSpellsSet >= 4)
{
stream->writeFlag(true);
for (S32 i=0; i<4; i++) {
S32 ghostId = con->getGhostIndex(mSpells[i]);
if (stream->writeFlag(ghostId != -1))
stream->writeInt(ghostId, NetConnection::GhostIdBitSize);
}
}
else
stream->writeFlag(false);
}
...
}

RTSUnit::unpackUpdate()
{
...
// spells
if(stream->readFlag())
{
if(stream->readFlag())
{
mSpellsSet = 4;
for (S32 i=0; i<4; i++) {
if(stream->readFlag()) {
S32 ObjId = stream->readInt(NetConnection::GhostIdBitSize);
mSpells[i] = (RTSSpell*)con->resolveGhost(ObjId);
}
}
}
}
...
}

#1
01/16/2006 (1:27 am)
Ghosts aren't guaranteed to happen. It takes time, affected by bandwidth, etc. If you must get info use an event.
#2
01/16/2006 (5:53 pm)
I have to admit I'm not very familiar with events, are you saying that I write an event to set the RTSSpell pointers on the client side? or are you saying that if I need any information that's contained in my RTSSpell to write an event for that? I did the former and basically encountered the same problems as before-- ghost objects having not been created yet.

is there a way to wait for the objects to be ghosted, and then send the ghostIDs from the server to the client?
#3
01/17/2006 (10:35 am)
In RTSConnection.cc you wil see code for the RTSUnitAttackEvent. What Ben is suggesting is that you use this type of communication between your server and client for information like this, since NetEvents can be set to guaranteed delivery, ensuring that the information is available on the client.

However, your main issue is your immediate attempt to do a con->resolveGhost(ObjId) within the ::unpackUpdate() for an object that isn't necessarily ghosted yet.

In addition, I don't see any code where you actually ghost the spells themselves. Your changes to RTSUnit::pack/unpack send the object ID information, but you'll need an RTSSpell::pack/unpack to actually ghost those particular objects.

However, let's talk a bit of design/theory here for a minute: what actually does the client need for the capability to cast spells, and have that capability networked to all of your clients from the server?

For a very large portion of your gameplay functionality, all you want the client to be able to do is to request to perform an action. This is mostly to alleviate hacking/cheating, but in general it also saves on bandwidth, etc. as well, and makes sure that the right clients receive the right information at the right times.

For this to happen properly, you need something like this type of sequence:

  • Transmit to the client the minimum information necessary for knowing which spells he can cast. Normally, this will be just identifier information, like Spell Name, mana cost, description, etc. This can actually be networked simply be sending a (unique per spell) index for the spells avail to that client. You do -not- need to be using ghostID/ObjectID for this, but some form of indentifier within your spell's datablock is good.

  • Player requests casting a spell. Send this information (spell requested, location of cast, target info if appropriate, etc.). This can be done in script easily via the cmdToServer functionality--look at how sendAttackEvent() is implemented, or the issue commands (issueAttack is a good one to search for).

  • Server processes the request, and performs actions if necessary. This will normally be done by modifying values on objects that are already ghosted, but may include something like creating a new projectile (that then gets ghosted), maybe causing an environmental change (rain spell?) or something else. In most every case I can think of however, you will be performing the changes on the server, and then using ghosting to network those changes to the simulation.

  • Appropriate clients (in scope for the effects of the spell) receive the network updates, and therefore "observe" the spell.

  • As you can see, nowhere really do you need to be passing object/ghostID's of the spell objects themselves here in another object. And while you can network the RTSSpell itself, it's not really necessary, and it's probably best not to, since it gives your clients (and therefore cheaters/hackers) a bit of info they can use to affect your gameplay adversely.

    Edit: BTW, I'm guessing here that you are testing with a co-located executable, meaning that you have the client and server running in the same process (only starting up one Torque application, etc.). This actually can be very misleading, because since you are basically having your server and your client sharing the same memory space, many objects and other data elements are available to the client that normally would not be--and when you move to a "real" networked test platform (two different computers, one hosting multiplayer, one joining as a multiplayer client), much of what you thought was working won't be.
    #4
    01/18/2006 (10:33 am)
    Ah thanks very much. I actually did write a pack and unpack for my RTSSpell and did set it to ghostable/scopealways. they were getting ghosted but not quickly enough.

    you are absolutely right about not needing all the spell info on the client. I mostly need it to show the icon of the spell which is contained in the rtsspelldata and the cooldown time which I need to display as a faded out clock timer on the spell icon. so it's mostly HUD related. the request to cast a spell is already server side.

    thanks again for that huge amount of info. you've been a great help.
    #5
    01/18/2006 (11:35 am)
    For hud display purposes, you don't even need the image on the server side (in the datablock) for your spell at all. Spells after all in their "spellbook" form aren't going to be objects within your world space (well, probably not at least!), so that information can be kept client side only. Take a look at how the stock RTS-SK handles gui images for the selection display icons for your units, or how the World Domination mod expands on that idea for action icons as well.

    Quite honestly, while it does demonstrate that you've studies uses of Torque well, you don't really even need an RTSSpell class. Nothing wrong with having one, and it can be quite useful for keeping track of creating them, managing them, etc., but everything that you seem to want to do with spells can be handled with script and a simply script object set/array. In this particular case, at least for prototyping purposes, creating your RTSSpell class in c++ is almost overkill.
    #6
    01/18/2006 (1:39 pm)
    I'm beginning to see that I can probably do all this in script. I saw the selection portraits and how they were done when I started doing this, but I thought it'd be nice to have them all in one spot. ie- the RTSSpellData block.

    the reason I originally thought I needed the RTSSpell class on the engine side was because I thought the spell management would be easier on the engine side; but now I see I think I can probably do that all in script. originally all the spell mechanics were on the engine side but I quickly moved that over to the .cs side.
    #7
    01/18/2006 (1:55 pm)
    It's really quite flexible to be honest. There isn't any reason not to use source code, but there isn't any demanding/overriding reason from what I've seen you describe so far that dictates it either, and in general source can be more difficult than script.

    I'd say prototype it in script, see how it works out for you, and then possibly see if it makes sense to refactor with a more code based solution...at least when getting started.