ZAP Questions
by Fabian Holmberg · in Torque Game Engine · 03/10/2005 (3:56 am) · 9 replies
I wanted to post this thread in the ZAP Discussion forum but there is no link that allows me to do so.
In the
function, I would like to know that the purpose of the
function. Within it "packs" and "unpacks" the move. But whats the point? It writes all the details of the movement structure to a Bitstream class and reads it right after, without doing anything with it...
It would make sense if:
within the pack (or unpack) function was called, but such is not allways the case as the pack function is usually called with a false third argument which makes it ignore the abovementioned function...
In the
void ClientGame::idle(U32 timeDelta)
function, I would like to know that the purpose of the
theMove->prepare();
function. Within it "packs" and "unpacks" the move. But whats the point? It writes all the details of the movement structure to a Bitstream class and reads it right after, without doing anything with it...
It would make sense if:
stream->writeRangedU32(time, 0, MaxMoveTime);
within the pack (or unpack) function was called, but such is not allways the case as the pack function is usually called with a false third argument which makes it ignore the abovementioned function...
#2
So this would mean that the values read into the bitstream class could potentially be subject to modification (due to truncating) once they are read into it - correct?
I would also assume that this "client side prediction" ties in with ghosting aka dead reckoning. Correct me if I am wrong on this one. Thanks.
03/10/2005 (4:15 pm)
Quote:basically taking all floating point values and truncating the precision to whatever is specified in the move's pack and unpack functions
So this would mean that the values read into the bitstream class could potentially be subject to modification (due to truncating) once they are read into it - correct?
I would also assume that this "client side prediction" ties in with ghosting aka dead reckoning. Correct me if I am wrong on this one. Thanks.
#3
Client-side prediction does tie in with the ghosting system - basically it's a way of having a controllable object on the client that has zero latency for control input, yet still allows the server to be totally authoritative. The client object simulates using client input immediately, which the server then "checks" with the client input data as soon as it is available from the client. See the Zap ControlObjectConnection class for a sample implementation.
03/11/2005 (8:17 am)
Floating point values written into the BitStream, particularly with the writeFloat(value, bitCount) and writeSignedFloat(value, bitCount) calls write a limited number of bits of precision, so yes the values read will quite likely be different than the values written.Client-side prediction does tie in with the ghosting system - basically it's a way of having a controllable object on the client that has zero latency for control input, yet still allows the server to be totally authoritative. The client object simulates using client input immediately, which the server then "checks" with the client input data as soon as it is available from the client. See the Zap ControlObjectConnection class for a sample implementation.
#4
I have also noticed that both the ClientGame and ServerGame have their proprietary mGameObjects array which is inherited from the Game class. This represents all the objects within the game, which brings me to my next question: After thorough searching, I was unable to find out where each Client (or player) connected to the game, recieves updates from other Clients (or players).
In the:
function, the ClientGame cycles thru its mGameObjects array, but I cannot figure out where these game objects get updated on the client side. This array must recieve updates from other clients at some point. Would you be able to indicate where this happens?
03/15/2005 (1:40 am)
Yes, I have been looking into ControlObjectConnection which seems to centralize its functionality on the sending of Moves back and forth accross the wire. I have also noticed that both the ClientGame and ServerGame have their proprietary mGameObjects array which is inherited from the Game class. This represents all the objects within the game, which brings me to my next question: After thorough searching, I was unable to find out where each Client (or player) connected to the game, recieves updates from other Clients (or players).
In the:
void ClientGame::idle(U32 timeDelta)
function, the ClientGame cycles thru its mGameObjects array, but I cannot figure out where these game objects get updated on the client side. This array must recieve updates from other clients at some point. Would you be able to indicate where this happens?
#5
When the client object is created by being ghosted from the server, the onGhostAdd is called by TNL:
03/15/2005 (7:54 am)
Zap is a client/server game, so each client receives updates about the objects in the world from the server. This magic is performed by the GhostConnection class from which ControlObjectConnection derives.When the client object is created by being ghosted from the server, the onGhostAdd is called by TNL:
bool GameObject::onGhostAdd(GhostConnection *theConnection)
{
addToGame(gClientGame);
return true;
}
#6
called from:
respectively, seem to be responsible for updating the positions of all the ships in the game. I have noticed however, that all the RPC functions you have ever implemented are more of the broadcasting sort. They are not called on the same frequency as the abovementioned functions which are called at every game loop.
I am asking this as I would like to implement an RPC based movement update system for every object in the game within the main client / server loops. Just wanted to know if this is a feasable functional alternative and that performance will not suffer as a result...
03/28/2005 (1:21 am)
Would it be feasable to use the RPC functionality to send player/ship movement updates in a continuous loop? I am asking this because in your ZAP implementation the following functions:// Called from GhostConnection Ship::packUpdate(GhostConnection *connection, U32 updateMask, BitStream *stream) // Called from GhostConnection Ship::unpackUpdate(GhostConnection *connection, BitStream *stream)
called from:
void GhostConnection::readPacket(BitStream *bstream) void GhostConnection::writePacket(BitStream *bstream, PacketNotify *pnotify)
respectively, seem to be responsible for updating the positions of all the ships in the game. I have noticed however, that all the RPC functions you have ever implemented are more of the broadcasting sort. They are not called on the same frequency as the abovementioned functions which are called at every game loop.
I am asking this as I would like to implement an RPC based movement update system for every object in the game within the main client / server loops. Just wanted to know if this is a feasable functional alternative and that performance will not suffer as a result...
#7
http://opentnl.sourceforge.net/doxydocs/fundamentals.html
http://opentnl.sourceforge.net/doxydocs/archoverview.html
I think you will see some major performance degradation if you switch to an RPC-based scheme from the ghosting system. GhostConnection handles: most-recent state data guarantee, update prioritization, partial state updates, etc. None of these can adequately be performed in a simple RPC scheme.
- Mark
03/29/2005 (1:12 pm)
You could use RPCs to send object updates, but then you would lose all of the benefits of the object replication functionality (ghosting) within TNL. For an overview of these features, read the relevant sections in the docs:http://opentnl.sourceforge.net/doxydocs/fundamentals.html
http://opentnl.sourceforge.net/doxydocs/archoverview.html
I think you will see some major performance degradation if you switch to an RPC-based scheme from the ghosting system. GhostConnection handles: most-recent state data guarantee, update prioritization, partial state updates, etc. None of these can adequately be performed in a simple RPC scheme.
- Mark
#8
So what I'll be doing instead of using RPC functions, is the plain ol' inheritence from NetConnection, whilst overriding the writePacket/readPacket functions in my GameConnection classes. I cannot use ghosting now as we prefer to have more control over aspects like deadreckoning and whatnot. So in this instance I will not be inheriting from GhostConnection as I have been instructed to avoid making my code too TNL dependant, despite the fact that we will be using your library.
As for the update prioritization, we are developing a sports game where all players will allways be in "scope" and require updates accordingly. So information will be allways relevant at all times. So the ghosting would probably not come in much use here anyway. There will never be more then 22 players within a game at any given moment, so hopefully we will not suffer to much lag...
Thanks again for the info though.
- Fabian
03/29/2005 (11:06 pm)
Like I mentioned, I never saw you implement an RPC object update system so what you said pretty much confirmed my beliefs... So what I'll be doing instead of using RPC functions, is the plain ol' inheritence from NetConnection, whilst overriding the writePacket/readPacket functions in my GameConnection classes. I cannot use ghosting now as we prefer to have more control over aspects like deadreckoning and whatnot. So in this instance I will not be inheriting from GhostConnection as I have been instructed to avoid making my code too TNL dependant, despite the fact that we will be using your library.
As for the update prioritization, we are developing a sports game where all players will allways be in "scope" and require updates accordingly. So information will be allways relevant at all times. So the ghosting would probably not come in much use here anyway. There will never be more then 22 players within a game at any given moment, so hopefully we will not suffer to much lag...
Thanks again for the info though.
- Fabian
#9
I see that read/writePacket is used as a basis to "transparently" cause Move transmissions and receptions to occur, but Ship::packUpdate() also sends position data for the ship from server to (non-controlling) clients. And I also see Ship::packUpdate() writes Moves in (without time information). I don't recall seeing an explanation of the design, really, apart from that old document about Tribes networking.
I feel I'd give my right arm for a good solid explanation, represented in a document and not in a sprinkling of answers to ad hoc questions, of how the TGE/Zap movement code actually works. The question/answer format fails me too often as many answers only reveal a more fundamental aspect that I've not have explained to me, such as renderPosition vs actualPosition, or what happens on a tick versus what happens every frame, or what units is "F32 time" in?
tone
01/09/2007 (8:19 am)
What I have not grasped is where writePacket/readPacket lies between the worlds of Ghosting and RPCs.I see that read/writePacket is used as a basis to "transparently" cause Move transmissions and receptions to occur, but Ship::packUpdate() also sends position data for the ship from server to (non-controlling) clients. And I also see Ship::packUpdate() writes Moves in (without time information). I don't recall seeing an explanation of the design, really, apart from that old document about Tribes networking.
I feel I'd give my right arm for a good solid explanation, represented in a document and not in a sprinkling of answers to ad hoc questions, of how the TGE/Zap movement code actually works. The question/answer format fails me too often as many answers only reveal a more fundamental aspect that I've not have explained to me, such as renderPosition vs actualPosition, or what happens on a tick versus what happens every frame, or what units is "F32 time" in?
tone
Torque Owner Mark Frohnmayer