Wait for commandToServer
by SimU Group · in RTS Starter Kit · 10/07/2007 (4:20 am) · 11 replies
I'm trying to create a new building without showing the building marker for the user. I do this on two steps: Create the marker, set it's transform, then invoke the GuiRTSTSCtrl::placeBuilding which invokes:
commandToServer('PlaceBuilding', $NewBuilding.getTransform(), $NewBuilding.thedatablock);
The commandToServer then invokes serverCmdPlaceBuilding(%conn, %transform, %data), which creates the building add add it to a simset called $Buildings.
The problem now is commandToServer happens asynchronosly. The serverCmdPlaceBuilding isn't invoked when I call the commandToServer.
What I want to do is wait for the serverCmdPlaceBuilding function to be invoked after i call the commandToServer.
commandToServer('PlaceBuilding', $NewBuilding.getTransform(), $NewBuilding.thedatablock);
The commandToServer then invokes serverCmdPlaceBuilding(%conn, %transform, %data), which creates the building add add it to a simset called $Buildings.
The problem now is commandToServer happens asynchronosly. The serverCmdPlaceBuilding isn't invoked when I call the commandToServer.
What I want to do is wait for the serverCmdPlaceBuilding function to be invoked after i call the commandToServer.
#2
When I call the placeBuilding the following gets executed:
Here's the full example:
That's the part that gets executed when commandToServer get executed.
now consider the following function:
One would imagine that the order of echoes on the console should be:
However, that's what's actually printed:
I hope that the problem is now clear..
10/08/2007 (1:54 am)
Thanks... but the problem is actually what happens inside the 'placeBuilding' function.When I call the placeBuilding the following gets executed:
commandToServer('PlaceBuilding', $NewBuilding.getTransform(), $NewBuilding.thedatablock);but this DOESN'T place the building right away.Here's the full example:
function serverCmdPlaceBuilding(%conn, %transform, %data)
{
echo("CONNECTION: " @ %conn);
%b = new ...;
.
.
.
%b.client = %conn;
%b.setTeam(%conn.getTeam());
%b.setControllingConnection(%conn);
echo("Placed a building at" SPC %b.getPosition());
%conn.buildings.add(%b);
%b.playRootAnimation();
MissionCleanup.add(%b);
[b]$Buildings.add(%b);[/b]
}That's the part that gets executed when commandToServer get executed.
now consider the following function:
function myFunction(%loc)
{
$Buildings.clear();
$NewBuilding = new RTSBuildingMarker()
{
scale = "1 1 1";
thedatablock = "TestBuildingBlock";
shapeName = "~/data/shapes/building/building.dts";
};
$NewBuilding.setTransform(%loc);
[b]$NewBuilding.setPosition("100 100 100");[/b]
echo("Trying to place building at:" SPC $NewBuilding.getPosition());
[b]GuiRTSTSCtrl::placeBuilding(PlayGui.getID());[/b]
echo("Current Buildings count:" SPC $Buildings.getCount());
$NewBuilding = new RTSBuildingMarker()
{
scale = "1 1 1";
thedatablock = "TestBuildingBlock";
shapeName = "~/data/shapes/building/building.dts";
};
$NewBuilding.setTransform(%loc);
[b]$NewBuilding.setPosition("50 50 50");[/b]
echo("Trying to place building at:" SPC $NewBuilding.getPosition());
[b]GuiRTSTSCtrl::placeBuilding(PlayGui.getID());[/b]
echo("Current Buildings count:" SPC $Buildings.getCount());
}One would imagine that the order of echoes on the console should be:
Trying to place building at: 50 50 50 Placed a building at: 50 50 50 Current Buildings count: 1 Trying to place building at: 100 100 100 Placed a building at: 100 100 100 Current Buildings count: 2
However, that's what's actually printed:
Trying to place building at: 50 50 50 Current Buildings count: 0 Trying to place building at: 100 100 100 Current Buildings count: 0 Placed a building at: 50 50 50 Placed a building at: 100 100 100
I hope that the problem is now clear..
#3
1) There is no such "$NewBuilding.setPosition("50 50 50");". You should use $NewBuilding.setTransform(%loc); where %loc has 7 values, the first 3 are the position (and you can pass only those 3)
2) As far as I can see, you are right, serverCmdPlaceBuilding is executed after the current function ending. (I didnt know that). Anyway, you should not repeat code, what take us to point 3:
3) To solve your problem (and write a cleaner code) you should isolate the block of code ordered of create buildings into a separate function, and call that function as many times as you need (instead of write many times the sames lines inside you function). That way you would have guaranteed the proper cicle across funtions and without repeat code.
Edit: I was trying to write some example code to paste here, but the same behaviour appeared. Came to conclusion that not only the current, but ANY client side function must finish before the commandToServer get executed. Some kind of shedule?
Ill be investigating this, but if there is anyone that can enlight us, would be great...
10/08/2007 (12:02 pm)
You have not one, but three problems/misunderstandings:1) There is no such "$NewBuilding.setPosition("50 50 50");". You should use $NewBuilding.setTransform(%loc); where %loc has 7 values, the first 3 are the position (and you can pass only those 3)
2) As far as I can see, you are right, serverCmdPlaceBuilding is executed after the current function ending. (I didnt know that). Anyway, you should not repeat code, what take us to point 3:
3) To solve your problem (and write a cleaner code) you should isolate the block of code ordered of create buildings into a separate function, and call that function as many times as you need (instead of write many times the sames lines inside you function). That way you would have guaranteed the proper cicle across funtions and without repeat code.
Edit: I was trying to write some example code to paste here, but the same behaviour appeared. Came to conclusion that not only the current, but ANY client side function must finish before the commandToServer get executed. Some kind of shedule?
Ill be investigating this, but if there is anyone that can enlight us, would be great...
#4
Here's the stock process that occurs with the kit:
Client enters the mode to place an RTSBuildingMarker (which is not an actual building).
--creates the RTSBuildingMarker, a client side only object.
--has the GUIRTSTSCtrl update the position of the RTSBuildingMarker (again, client side only), and accepts a click when the human likes the position. When the click happens, the commandToServer('PlaceBuilding', $NewBuilding.getTransform(), $NewBuilding.thedatablock); is executed.
--deletes the RTSBuildingMarker from the client simulation
On the server, nothing at all happens until the network event generated by the commandToServer is received. When it is received, it executes the code serverCmdPlaceBuilding, which actually:
--creates a real RTSUnit (the building)
--ties it to the owner (setTeam, etc)
Now that the building exists on the server, it is networked normally through the ghosting system, and appears (seemingly magically, although there is a lot involved as part of the ghosting process) on each client that has it visible.
You really need to follow this method, because client side objects are not meant to be created or placed in the simulation without the server being in charge (some exceptions apply, such as foilage replication, but they don't apply here), and the purpose of the RTSBuildingMarker is simply to give the client time to figure out where the building should go, and give a visual indicator of where that will be.
10/08/2007 (12:27 pm)
Actually, the root problem is that you are neglecting the very important interaction with the Server/Client relationship, and the authoritative nature of the server in that relationship.Here's the stock process that occurs with the kit:
Client enters the mode to place an RTSBuildingMarker (which is not an actual building).
--creates the RTSBuildingMarker, a client side only object.
--has the GUIRTSTSCtrl update the position of the RTSBuildingMarker (again, client side only), and accepts a click when the human likes the position. When the click happens, the commandToServer('PlaceBuilding', $NewBuilding.getTransform(), $NewBuilding.thedatablock); is executed.
--deletes the RTSBuildingMarker from the client simulation
On the server, nothing at all happens until the network event generated by the commandToServer is received. When it is received, it executes the code serverCmdPlaceBuilding, which actually:
--creates a real RTSUnit (the building)
--ties it to the owner (setTeam, etc)
Now that the building exists on the server, it is networked normally through the ghosting system, and appears (seemingly magically, although there is a lot involved as part of the ghosting process) on each client that has it visible.
You really need to follow this method, because client side objects are not meant to be created or placed in the simulation without the server being in charge (some exceptions apply, such as foilage replication, but they don't apply here), and the purpose of the RTSBuildingMarker is simply to give the client time to figure out where the building should go, and give a visual indicator of where that will be.
#5
But I think I understood that, my question is how/why
Is difficult for me to explain it, so just in case, I made this:
constructBuildings() calls middleFunction(%pos) as many times as buildings wanna construct.
Then middleFunction(%pos) calls myFunction(%loc) who calls GuiRTSTSCtrl::placeBuilding(%this)
And placeBuilding(%this) is who actually executes:
commandToServer(....)
BUT: commandToServer is apparently executed, but the debugging (and the actual console output), shows the execution only AFTER the last of the other client functions has finished. So again, it is a way of view it or actually happend this way?
10/08/2007 (12:47 pm)
Thanks Stephen!But I think I understood that, my question is how/why
commandToServer('PlaceBuilding', $NewBuilding.getTransform(), $NewBuilding.thedatablock);is executed only after ANY client side function is finished? Or is actually just a way of debugging (seeing it) when in fact it is simultaneous?Is difficult for me to explain it, so just in case, I made this:
constructBuildings() calls middleFunction(%pos) as many times as buildings wanna construct.
Then middleFunction(%pos) calls myFunction(%loc) who calls GuiRTSTSCtrl::placeBuilding(%this)
And placeBuilding(%this) is who actually executes:
commandToServer(....)
BUT: commandToServer is apparently executed, but the debugging (and the actual console output), shows the execution only AFTER the last of the other client functions has finished. So again, it is a way of view it or actually happend this way?
#6
--network packet frequency (stock is 100 milliseconds)
--latency in delivery (while in the single digit milliseconds for single player configurations, it still exists).
commandToServer simple is a script ConsoleFunction that creates and posts a NetEvent, which sits on the Net processing queue until the next network cycle is called, which is pretty fast, but guaranteed to not happen until the current process loop is finished--which includes not only TorqueScript within the scope block where the commandToServer statement was written, but any other objects and TorqueScript execution blocks still pending.
10/08/2007 (1:21 pm)
You are forgetting about:--network packet frequency (stock is 100 milliseconds)
--latency in delivery (while in the single digit milliseconds for single player configurations, it still exists).
commandToServer simple is a script ConsoleFunction that creates and posts a NetEvent, which sits on the Net processing queue until the next network cycle is called, which is pretty fast, but guaranteed to not happen until the current process loop is finished--which includes not only TorqueScript within the scope block where the commandToServer statement was written, but any other objects and TorqueScript execution blocks still pending.
#7
Now I get it, Stephen, thanks!
You are very often saving my ass on this conceptual non-understandings.
Lucky to have you ever overhere... :D
10/08/2007 (1:27 pm)
Ahhhh!Now I get it, Stephen, thanks!
You are very often saving my ass on this conceptual non-understandings.
Lucky to have you ever overhere... :D
#8
Thanks all for your help...
Btw.. the reason I wanted to create buildings is saving/loading scenarios... I save the RTSBuilding data to a file, in addition to my own data associated with this buildings.. then load it by first creating the building using the method we've been discussing to create the building then load the data associated...
Am I doing it the wrong way ??
10/08/2007 (11:33 pm)
I've solved the problem by breaking my code into a function that gets called many times (as Novack suggested) and then let this function schedule itself recursively each 50ms. However, I will still improve it by letting the serverCmdPlaceBuilding itself call this function if a certain flag is signaled.Thanks all for your help...
Btw.. the reason I wanted to create buildings is saving/loading scenarios... I save the RTSBuilding data to a file, in addition to my own data associated with this buildings.. then load it by first creating the building using the method we've been discussing to create the building then load the data associated...
Am I doing it the wrong way ??
#9
But if it is for loading, why you must wait for any building to be constructed before contruct the next? Beeing prior to the mision show, it doesnt matter if they appear one by one or all at the same time...
10/09/2007 (5:54 am)
I havent studied methods for saving/load, there is info on that matter on the general forums you should read it.But if it is for loading, why you must wait for any building to be constructed before contruct the next? Beeing prior to the mision show, it doesnt matter if they appear one by one or all at the same time...
#10
10/09/2007 (7:56 am)
Because I must wait for the actual object to be created before loading its other properties from the file (I added new member fields to the RTSBuilding).
#11
You should tell us something about you and your project!
I recommend this thread: Who's alive in this part of the world?
10/09/2007 (8:20 am)
Ah! ok, sounds like you are doing a fine job!You should tell us something about you and your project!
I recommend this thread: Who's alive in this part of the world?
Torque 3D Owner Novack
CyberianSoftware
Could you please explain again whats the problem, and what do you want to do?
For what I understood anyway, you wanna place buildings from script, without the GUI. If it is only that, try something like this:
function myFunction(%loc) { $NewBuilding = new RTSBuildingMarker() { scale = "1 1 1"; thedatablock = "TestBuildingBlock"; shapeName = "~/data/shapes/building/building.dts"; }; $NewBuilding.setTransform(%loc); GuiRTSTSCtrl::placeBuilding(PlayGui.getID()); }Then with: myFunction(transform); you must be able to place buildings from script without problem.
Edit: the part that activates the RTSBuildingMark visualization is PlayGui.startBuildingPlacement($NewBuilding);, as you can note, i just jump that part, but overall its the same functionality as stock one.