Author Topic: Static TNObject IDs  (Read 10469 times)

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Static TNObject IDs
« on: September 13, 2017, 06:55:03 AM »
Must admit this is a bit confusing.
In our project (which used to use TNet2) we have managers set up inheriting from TNBehaviour. These have RFCs etc. implemented and will respond to network events. These managers are not instantiated at load time and the level is not loaded using TNet.
Does this mean that we now (having upgraded to TNet3) have to handle these managers in a different way? Perhaps they do not even have to inherit from TNBehaviour to respond to network calls? Is there a better way?

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: Static TNObject IDs
« Reply #1 on: September 13, 2017, 07:05:02 AM »
Nope, nothing about that has changed from TNet2 to TNet3. There needs to be a TNObject on the gameobject and this TNObject needs to have a non-zero ID.

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: Static TNObject IDs
« Reply #2 on: September 13, 2017, 07:24:14 AM »
My deepest apologies. I replied in the wrong thread. :(
My reply was meant as a reply to this thread:
http://www.tasharen.com/forum/index.php?topic=14171.0

Can you move my reply or should I post in that thread as well...? Again, sorry and thanks in advance for any assistance.

PS. The forum security settings appear to be overly harsh. I could not post this reply for quite some time due to repeated notices that “my previous reply was less than 180 seconds ago”. And it seems each attempt pushed that timer along. We host forums ourselves and I would advise you to fight spam with another mechanic.

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: Static TNObject IDs
« Reply #3 on: September 13, 2017, 08:07:46 AM »
Ah, no problem. I can't move your topic (I'm not an actual mod - was just given mod to bypass those notices).

I just tried it though. Manager exists in scene 'world' with TNObject (id set to 1). On my main menu scene I connect to the server then JoinChannel (channelID 3, empty string for level name). Then on keypress I load 'world' via Unity's SceneManager. The Manager immediately starts firing off its RFCs. Its TNObject gets its channelID set to 3. On keypress again I load my main menu scene. The Manager object persists (via Unity's DontDestroyOnLoad - not TNet) and continues to fire its RFCs in the main menu.

The only issue I can see arising from this is that you *have* to stay in whatever channel you joined last before loading into the scene with your manager. It doesn't matter how you load the scene. In TNObject's Awake() it'll set its channelID to TNManager.lastChannelID.

I'm not sure this is good design though. Maybe your managers should just keep references to whatever objects they manage and send RFCs via their TNObjects (either GetComponent or expose as public property).

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: Static TNObject IDs
« Reply #4 on: September 13, 2017, 08:26:20 AM »
Thank you!
Maybe I misunderstand then. A game session for us flows like this:
1. Enters menu screen. The menu screen contains global managers (central game management, UI management, etc.).
2. Player either hosts a game (starts server instance) or joins.
3. Loads main game scene (not using TNet, normal async load).
4. Main game scene contains game runtime managers (environment, terrain, etc.) that need to communicate with other clients.

These managers in step 4 only reside in the game scene and inherit from TNbehaviour. I interpreted that thread as Aron stating that game objects that inherit from TNBehaviour (or simply have TNOs attached) MUST either pop into existence via:
- residing in a scene loaded via TNet (remember, ours isn't)
- being instantiated via TNet (our already exist in the scene).

So, our case would run afoul of the requirements in TNet3. Did I understand this correctly or totally miss the point? :)

Thanks!

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: Static TNObject IDs
« Reply #5 on: September 13, 2017, 08:39:25 AM »
I think ArenMook's saying that a static scene object can't be networked post-load. So, in your flow, none of your global managers from the menu screen could be networked (the central game management, UI management, etc).

But, I think, if the following conditions hold true *before* loading your main game scene then all should be well:
1. Your environment and terrain managers have a TNObject component with a non-zero, unique ID
2. You've connected to a server
3. You've joined a channel

And remember: if you leave the channel that you joined prior to loading the scene, then those static scene objects will be destroyed by TNet. So I'd suggest joining just one channel before loading the main game scene. That way it'll be easier for you to keep track of which channel you need to remain in.

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: Static TNObject IDs
« Reply #6 on: September 13, 2017, 09:17:47 AM »
Thanks.

But this really makes networking of higher functions in managers a pain in the proverbial a**...!?
Ideally, I do want the game manager and other entities to be able to communicate even though they are persistent (singletons using dontdestroy). The same goes for the in-game managers. The loading flow would be:
1. player joins channel 1.
2. loads game scene
3. procedural stuff is generated, if host data is sent to joining players (terrain info etc.)
4. player joins channel 2.
5. stuff is instantiated.

From point 2 onwards I want to utilise the in-game managers for a lot of things using RFCs primarily. And they cannot be destroyed at any point until the player leaves the game session.

How would we accomplish this without running into problems? It has to be achievable I suppose, since if not I do not see TNet as a viable solution.. :-/

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: Static TNObject IDs
« Reply #7 on: September 13, 2017, 10:36:33 AM »
And just to clarify:
The managers only need to communicate via RFCs or some other remote function execution mechanic. They do not need automatic syncing etc.
I understand why instantiated objects and other run-time game components might need to have these requirements (being instantiated or loaded via TNet), but I do not really understand why higher level components cannot use networking if scenes are loaded the traditional way....?

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: Static TNObject IDs
« Reply #8 on: September 13, 2017, 06:51:58 PM »
Every new object from point 2 onwards will be networked.

It's really not a limitation. It's basically just this: if you're not connected to a network, don't expect networking to work. Furthermore, if you connect to a network you'll need to re-initialize existing objects for them to be networked.

Again, I'd urge you to re-think your design. ArenMook's dealing with a lot of procedural content in his new game and we'd be getting any changes he makes to TNet. There haven't been many, so TNet definitely supports what you want to do.

However, as an immediate solution, you could just copy the two lines in TNObject's (not TNBehaviour's) Awake function and put it in a new public Initialize function. Add a bool IsInitialized so it doesn't get called twice. Now, in OnJoinChannel just call your TNObject's Initialize function. They should function as normal static tno's now.

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: Static TNObject IDs
« Reply #9 on: September 14, 2017, 12:41:27 AM »
Every new object from point 2 onwards will be networked.

It's really not a limitation. It's basically just this: if you're not connected to a network, don't expect networking to work. Furthermore, if you connect to a network you'll need to re-initialize existing objects for them to be networked.

But it is a limitation. If I have a master game control object for example., that exists upon launch. There are situations where it would be fully feasible for that object to want to communicate to other clients, without it being poor design.
Requiring that object to be deleted/instantiated or using a workaround suggests limitations in how TNet is implemented. It seems to have a very rigid design philosophy which is contrary to what we would like in a networking solution.
Quote
Again, I'd urge you to re-think your design. ArenMook's dealing with a lot of procedural content in his new game and we'd be getting any changes he makes to TNet. There haven't been many, so TNet definitely supports what you want to do.
I would then be very interested in how he does things in his new game. Or if you could point to an example or documentation that describes how to allow persistent manager objects to always be aware of the networking (if available).

Why is it a good feature having "old" objects not being able too hook into a new channel? I cannot see the point of the restriction.
Please not that I am not being argumentative for argument's sake. I am merely offering constructive criticism and trying to find a solution for us that does not require a switch of networking solution.

Thanks in advance.

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: Static TNObject IDs
« Reply #10 on: September 14, 2017, 08:27:12 AM »
Still no clear reply?

Also, TNet is a few years old at this point. Is there still no proper documentation that explains the general recommended strategy for successfully using Tnet for more complex projects? There seems to be a lot of requirements and little tricks that one is supposed to know, but are not really explained anywhere...?

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: Static TNObject IDs
« Reply #11 on: September 14, 2017, 07:06:00 PM »
Why does your GameManager and UIManager need to communicate via RFC? Maybe if you explained more specifically what you're trying to do I could provide some more direction.

Still waiting on ArenMook to show up :P

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: Static TNObject IDs
« Reply #12 on: September 15, 2017, 01:32:04 AM »
The UIManager does not need to communicate. However, the scene that loads on game start contains several managers that do need to communicate.
I am under the impression that they will not be able to do so unless instantiated after scene load? Which is contrary to the Unity UI philosophy (ie. use the inspector to set up dependencies that do not need dynamic allocation).

The one hope I have is that the following is true:
Static objects already present in a scene *will* be able to communicate *if* the level is loaded via JoinChannel with the specified level.

If the above is true we would be able to set up player connections, join channel X with the main game scene specified, do procedural stuff (using managers which can now network since we loaded via join channel) then join channel Y (additively) to instantiate game objects and run the main game.

Do I finally understand this correctly...? :)

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: Static TNObject IDs
« Reply #13 on: September 15, 2017, 02:57:28 AM »
I think so? Any objects in the scene you load via JoinChannel will be networked. Objects from previous scenes (like your startup scene) won't automatically become networked.

You shouldn't have any objects in your startup scene that require RFCs because you aren't connected to a channel at startup. How could they be aware of something that didn't exist at their birth?
Well, the same way we do, I guess, by being told about them:
  1. public class GameManager : TNBehaviour
  2. {
  3.         void OnEnable()
  4.         {
  5.                 TNManager.onJoinChannel += OnJoinChannel;
  6.         }
  7.        
  8.         void OnDisable()
  9.         {
  10.                 TNManager.onJoinChannel -= OnJoinChannel;
  11.         }
  12.        
  13.         private void OnJoinChannel(int channelID, bool success, string message)
  14.         {
  15.                 if (!success)
  16.                 {
  17.                         Debug.LogError(name + " Saw failed JoinChannel (" + message + ")");
  18.                         return;
  19.                 }
  20.                 tno.Initialize();
  21.                 tno.Send(45, Target.All, TNManager.isHosting ? "HOST initialized" : "CLIENT initialized");
  22.         }
  23.  
  24.         [RFC(45)]
  25.         void TestRFC(string msg)
  26.         {
  27.                 Debug.Log("Message from: " + msg);
  28.         }
  29. }
  30.  
Add the following to TNObject.cs:
  1. public bool IsInitialized = false;
  2. public void Initialize()
  3. {
  4.         if (IsInitialized) return;
  5.         Unregister();
  6.         channelID = TNManager.lastChannelID;
  7.         Register();
  8.         if (mCallDataChanged && onDataChanged != null)
  9.         {
  10.                 mCallDataChanged = false;
  11.                 onDataChanged(mData);
  12.         }
  13.         rebuildMethodList = true;
  14.         IsInitialized = true;
  15. }
  16.  

It's super sloppy but I tested it and it works. You could probably hook TNObject itself up to the OnJoinChannel event but that'd require modifying some of its existing code. So for the sake of posting this I went with an approach that only adds new code.



Oh, I also just realized that maybe you don't need *RFCs* but don't know about the glory of custom packets... I probably should've told you about those in like the first post. My bad.
You can use custom packets by calling TNManager.BeginSend and TNManager.EndSend directly. Set the packet handler with TNManager.SetPacketHandler. You only get one packet handler per packetID which is perfectly fine for a singleton manager.

And to sort of explain why getting RFCs working on an un-networked object is such a hassle:
RFCs belong to objects which belong to channels. The benefits of an RFC are: ease of use (one line!), instanced, and the ability to save the call on the channel so it's automatically sent to new players.
So if your object doesn't belong to a channel then RFCs can't work. The code snippet above forcefully registers your object with the channel to get its RFCs working.
Maybe it'd help to think of RFC (Remote Function Call) as RCC (Remote *Channel* Call). But TNet uses RCC for something else (Remote Creation Call), so yeah :P

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Static TNObject IDs
« Reply #14 on: September 16, 2017, 11:13:44 AM »
With TNet 2, there was always only one channel that the player could belong to, so it was easy to determine when objects could send and when they couldn't. With TNet 3, you can be present in as many channels as you like, and objects have to be cleaned up properly when leaving channels. Furthermore, server will no longer send out RFCs for objects that have been destroyed (eliminating race conditions from TNet 2), so it has to have a better understanding of the objects' lifetimes. Due to this I strongly advise not using static IDs.

The way I do it in Sightseer is pretty simple.

1. Client joins a global communication channel (used for world chat and world-wide objects, such as player factions, outposts, managers, etc). This channel contains all objects that need to update regardless of where they player is in the world.

2. OnJoinChannel notification is only fired after all RCCs and RFCs have been called -- this means that it's 100% guaranteed to execute after the entire join process has finished. This means that this is the perfect place to check if your managers are present or not. In Sightseer, I check for the presence of the global chat communication object, for example. If it's not there, it gets instantiated at this point. All future players that join will get that objects' RCC before OnJoinChannel, so it's 100% guaranteed to already be there in the future.

So all you need to do is instantiate your managers via TNManager.Instantiate in your OnJoinChannel, provided they don't already exist. That's it.