Author Topic: TNManager.Create Asynchronous call  (Read 20696 times)

cayou

  • Jr. Member
  • **
  • Thank You
  • -Given: 3
  • -Receive: 3
  • Posts: 90
    • View Profile
TNManager.Create Asynchronous call
« on: February 18, 2013, 09:35:50 AM »
Hi everybody,
I'm currently stuck with the TNManager.Create function.
I already know that's not possible to retrieve directly the object created with the TNManager.Create function, and the solution is to put the code after the creation into the Awake function of the created object.

But in my case, I have some "vortex" that create monsters. I create a monster, and then I place it into a list. I think in networking the server will create the vortex and handle monsters.
I can't do it (adding the monster to a list) in the child because I'm not able to retrieve the vortex parent (I can't search by tags, or name because the vortex is not a singleton).

How can I resolve this?

I though about adding an optional parameter into the Create function which is a System.Action<GameObject> callback. This callback will be called after the creation of the object.
But It could create some issue, maybe (will it be called on clients too?).

Is there anyone here having the same problem as me?

Thanks,
cAyou.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #1 on: February 18, 2013, 05:44:39 PM »
Sounds like you need to have an RFC call on your vortex that will create objects inside, rather than using TNManager.Create. That's the easiest way to pass any number of parameters you want.

In all honesty though, I am not sure why you would want to do this though. Create your monsters -- that's fine. But why parent them? If worst comes to worst, you can use the instantiated monster's position to find the closest monster spawner.

cayou

  • Jr. Member
  • **
  • Thank You
  • -Given: 3
  • -Receive: 3
  • Posts: 90
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #2 on: February 18, 2013, 08:50:51 PM »
Creating objects with the RFC function sounds good enough.
I took only an example with vortex, but I'm doing this sometimes (adding to a list, changing parameters, etc...). I understand why you did this, I'll change for creating monsters into a RFC function, called everywhere, thanks.

cayou

  • Jr. Member
  • **
  • Thank You
  • -Given: 3
  • -Receive: 3
  • Posts: 90
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #3 on: February 20, 2013, 03:19:58 PM »
It seems I can't create monsters into the RFC function (using basic Instantiate), because it won't be synchronised. If I try to call some RFC function to these objects after their creation it won't work (like position).

Edit: Maybe it's because objects are not sharing the same uid.
Edit2: Yes, it was the uid that have to be the same on both server and client. Do you have to manage myself a uid manager or does Tnet provides functions to handle that?
« Last Edit: February 20, 2013, 03:57:13 PM by cayou »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #4 on: February 20, 2013, 07:35:53 PM »
TNet manages all the IDs, and if you use TNManager.Create, the UID will be set for you. If you don't use TNManager.Create, it's more complicated and I don't recommend using a TNObject on that game object. Instead you will want to communicate via some kind of a manager that has a TNObject attached to it.

broknecho

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 33
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #5 on: February 26, 2013, 10:53:51 AM »
Alright so I think I have run into a similar issue and just wanted to clarify the best practice here.

I too have some "spawners" that I am controlling with the host.  There is a spawner for each team so when the host decides to spawn something, the "Spawned" enemy will need a few more properties set. (i.e. Team Color, Target, etc.)

Given that all we can do with TNManager.Create is set position, rotation, velocity and angularVelocity, what do you recommend as the correct pattern to instantiate an object that needs more information? The Manager pattern you described above that does the UnityEngine.Object.Instantiate?

Here's maybe a sample that could illustrate a common pattern.  What if you have a projectile that needs to know what GameObject fired it off?  (like in a strategy game)  Or what team the projectile belongs to?

Thanks,
Scott

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #6 on: February 26, 2013, 07:25:01 PM »
Have a script attached to your object you're instantiating. In that script's Start() function check -- tno.isMine? If yes, make an RFC call to Target.AllSaved with whatever properties you want to set.

broknecho

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 33
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #7 on: February 26, 2013, 08:11:14 PM »
But doesn't that only really work when the object that is instantiated knows everything it needs to?

Like tno.isMine just gives you the client that created the object but what if it needed to know something more about the game state?

Without coupling too much, I have a game object that can exist for either team (Blue team or Red team) and my gameplay manager  determines what team it's supposed to go on based on a game event.  This is the information that I'm referring to that won't be available at the run of the objects Start method.

I don't mean for you to beat this tno.isMine topic to death (I see it lots on the forum).  I'm just thinking this is a different case where an object needs to be fed more information on initialization.

Please let me know if there is something I'm missing entirely here or my explanation is not clear.

Side thought: I could make some variations of a prefab that already have the new information set in the inspector but I don't think that is a good solution due to the possibility of many combinations.  I could see that option then working with the Start Method RPC.  ie. Blue team Prefab Object versus Red Team Prefab Object.

Scott

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #8 on: February 27, 2013, 08:54:24 AM »
You have to determine the information you are trying to feed to the instantiation somewhere, right? (where you're instantiating it)

...so why not delay this logic, move it to the Start() function instead? Worst comes to worst you can just send an RFC call with all the data you need.

broknecho

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 33
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #9 on: February 27, 2013, 10:39:33 AM »
Hmmm yeah it definitely makes sense what you saying.  I think i'm getting into a design issue of the way that I did it in Unity Networking.

In there I used to:

In GameplayManager:
1- Check if Spawn Timer is up for a particular team
2- Figure out some properties to initialize new object with
3- var newTransform = Network.Instantiate the object
4- Grab that newTransform's  NetworkView
5- Call a few RPC's to "initialize" that new object

So ALL of that was done in the Gameplay Manager and not that actual Objects initialization.  The object just waits for the RPC's.  Very similar to what you described except that it came from the GamePlayManager.

Quote
...so why not delay this logic, move it to the Start() function instead? Worst comes to worst you can just send an RFC call with all the data you need.

So if I move that logic to the GameObjects Start() method, I would then have to get the initialization information from somewhere outside of the object which then couples that object to the gameplay (or whatever) manager.

Anyhow it seems like I have to redesign this.  I really appreciate your input.  I'll probably just do:

  1. void Start()
  2. {
  3.     var initValues = GamplayManager.GetInitValues(this);
  4.  
  5.     //Send RFC for all the values to AllSaved
  6.     tno.Send(...)
  7. }
  8.  


Something like that.  Thanks!

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #10 on: February 27, 2013, 10:42:49 AM »
Yup, and it's also cleaner this way as it's all done in the class that needs the values rather than somewhere else. :)

Suvu

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 11
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #11 on: March 01, 2013, 04:17:44 AM »
Hey guys,

I'm running into the same problem as you guys, I think it's a design issue as I have come from using the built-in networking as well...

This is a simplified version of what i'm working on :
  • Each player controls a ship (the player avatar)
  • The player can choose 2 weapons to attach to their ship
  • Each weapon fires a specific type of ammo
The point that i'm stuck on is when a player fires a weapon,
Using the built in networking I did it this way :
  • The player sends their control input on the ship to the server (host)
  • The server applies the input to the ship, and sends out the new position to the clients
  • If the one of the fire buttons was pressed, the ship (server side) 'checks' with the corresponding weapon that it can be fired.
  • If true, the server sends an RPC to all clients (on some kind of GameManager script), to spawn the correct ammo and passes a new NetworkViewID and ownerID with it.
  • The ammo is instantiated on all clients and uses the NetworkViewID to receive updates from the server.
But this design pattern doesn't work using TNet :(
If the server creates the ammo using TNManager.Create(ammoPrefab) then the server is always the 'owner' of the ammo, and as far as I can tell the 'ownerID' is the only indentifier for objects created in this way (because we can't pass extra parameters on Instantiation). So there's no way I can determine who the real owner was.

Another approach is to have the client spawn the ammo (TNManager.Create). But if that's the case, I would want the server/host to verify that this was allowed to happen (otherwise the client could easily cheat, spawning any ammo they want or adjusting refire rates etc).
So the server would somehow need to determine which player fired this Ammo (that's easy enough, with the TNManager.objectOwnerID called OnAwake()), get that player's ship and which weapon it came from (all I have to go on here is the ammo prefab?) and check with the weapon if the shot was allowed (x time has passed since the last shot). If it's not allowed then the server destroy's the Ammo.
But even if that was possible, this way probably wouldn't work because of fluctuations in latency (if i'm comaparing the time that the weapon was last fired)

I'm stumped here and it's really frustrating because I know it's just that i'm thinking in the wrong way....

Am I being too paranoid wanting the server to check everything? (From what I've read, you should never trust the clients)
Thanks!

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #12 on: March 01, 2013, 09:39:38 AM »
Yeah this approach isn't going to scale very well with lag. I hate playing games that lag. Don't you? If the player owns and controls their own avatar, then the game will feel fast and responsive, regardless of where the server is at. Furthermore sending controls isn't a good idea anyway. Send the effect, not the cause. For example in Windward the input changed the ship's steering and acceleration values -- and those are the values I was synchronizing, not the input.

Don't worry about checking everything. First make a game that's worth hacking, then worry about hacking.

broknecho

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 33
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #13 on: March 01, 2013, 11:44:18 AM »
Definitely agree with the handle the hacking when it's a problem but sometimes that can cause a huge redesign if handled late in development.  I don't think you're being crazy paranoid but definitely don't stress about it now until it's a problem.

Given that, you are right to never trust the client but to let the client do what they want is very viable.   Let the client fire off their gun.  If the server determines it's not the right gun, you can either destroy it or just not allow that collision to actually do anything.  OR even harsher, disconnect the client for being out of sync.

Another example is that if a client tried to do a speed hack, they could definitely do that if they were allowed to move on their own.  Now when their position change is sent to the server, the host can then validate if it moved outside of some threshold and then once again disconnect them for being way out of sync.  This would then require you to start keeping track of where players are and where they want to move to.  A lot more work to stop cheaters.....filthy cheaters.  :)

The game I originally wrote in Unity Networking was design like you did Suvu, input was actually sent to the host and then the host ran the simulation that everyone else just got updates from.  In a LAN, you couldn't really notice this but I had some testers over the Internet that mentioned lag between their input and actually seeing something happen.  Not a good experience.

Suvu

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 11
    • View Profile
Re: TNManager.Create Asynchronous call
« Reply #14 on: March 01, 2013, 01:54:32 PM »
Thanks for the input both! that's definitely given me something to think about.


I don't see sending input is a bad idea? As far as I know, that's how most modern multiplayer games work... I already have it running like this even with TNet for the player controlled entities.
  • Player samples their input and sends to Host. But ALSO, applies the Input locally (They get immediate response from their input)
  • Player is constantly sampling the entity state (if they are the owner) to a buffer (mLocalStateBuffer). (in my case, the entity state is just a struct containing timestamp, position, rotation, velocity and angularVelocity data)
  • Host applies input to the entity
  • Host samples the entity state and sends to all.
  • Client receives sample, and adds it to another buffer (mNetStateBuffer). (Client sets the timestamp of the incoming state to currentTime - (averagePing * 0.5)
  • If the client doesn't own this entity, it interpolates between two states in mNetStateBuffer that are ~50-100ms old (to allow for smooth interpolation even if minor network problems arise, can also extrapolate from the two most recent states as a fall-back solution for more serious network issues eg. multiple dropped packets)
  • If the client does own this entity, they find in their local state buffer (mLocalStateBuffer) the state with the timestamp closest to the most recent state in mNetStateBuffer. If these states differ by more that a threshold amount (in my case I just check difference in position being greater than some value) the client gradually applies corrections to the local entity.
This way the server can verify input, and the client has (what seems like) responsive controls (unless they have serious network problems... or they try to cheat :P )
« Last Edit: March 01, 2013, 01:58:33 PM by Suvu »