Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - RFCsDriveMeNuts

Pages: [1]
1
I understood what you meant by inherited, and achieved what you asked:

My object is actually a series of rigidbodies all connected together.  Previously, I used your auto sync rigid body on each of the most critical parts, but now in my root object, which did all the network communication anyway, I send the positions, rotations, velocities myself, very similarly to sync rigid body, except all grouped together in one packet, which is more efficiency UDP-wise anyway!  Wins all around!

Thanks!

2
TNet 3 Support / Re: 3 questions about TNSyncRigidBody
« on: March 11, 2014, 07:12:54 PM »
Call me crazy, but this seems like crazy overkill for rolling dice.  To me, this is the equivalent to: I want to start a fire, instead of getting a match, I am going to build a nuclear weapon, detonate it, and go find a flaming sheet of paper from a destroyed office building. :-P

In gaming, perception > reality.  Games lie to you all the time, but you never would realize it.

Simply use an RFC to send the rolled dice value from the host/authority, then simulate the dice roll (without physics) to match the result on each client.  This is more secure, saves you a ton of physics madness, and is far more straight forward.

How you achieve the simulation?  There are many ways.  If you're more of an editory person (I generally avoid Unity's editors), you could use the animation tool to create a dice roll, and then manipulate the dice cube orientation before you run the animation ensure you get the desired output, since it will be rolling the same way each time.

An intermediate solution: technically, if you drop the dice from the same orientation, velocity, angular velocity, position, etc. you should get the same outcome.  You could combine that with my previous suggestion of sending the outcome first and manipulating the throw variables to ensure you get the same result, or, simply send those variables in an RFC call and hopefully they should all end up the same on each client.

Long story short, in networking, you try to remove all randomness (make everything deterministic), so that you can simply send the start state, and all clients end up with the same result.  This is how real-time strategies work.  You cannot send 10,000 units worth of data each frame.  So, you simply send the few things that do change, each player's orders, and each client runs the exact same simulation.

I am not sure if Unity's physics (uses PhysX for 3D), is deterministic.  I would imagine it is.  But who knows, there is a ton of floats, which could introduce rounding errors.

3
Interestingly, the child UID is zero.  It doesn't even match the parent.

I am actually already very close to having all of the RFCs and such in the parent.  That child is simply a TNRigidBody autosync, everything else I've done is in the parent already!  I will manually sync my rigidbody through the parent script.

Cheers.

4
I have found that when using TNManager.CreateEX/RCCs to create game objects, it does not register (give UIDs) to all TNObjects inside of it.  For instance, my prefab looks kinda like this:

Root (with TNObject)
   Body (without TNObject)
   Head (with TNObject)

Only root is getting a TNObject UID.

I have more or less copied your example exactly, but maybe I still made a mistake?

  1.     public enum ERCCMessageType : int
  2.     {
  3.         createPlayer = 10,
  4.     }
  5.  
  6.     internal void SpawnPlayer(Vector3 position, Quaternion rotation, string faction, int hitPoints)
  7.     {
  8.         TNManager.CreateEx((int)ERCCMessageType.createPlayer, false, player, position, rotation, faction, hitPoints);
  9.  
  10.     }
  11.  
  12.     [RCC((int)ERCCMessageType.createPlayer)]
  13.     private GameObject OnSpawnPlayer(GameObject prefab, Vector3 position, Quaternion rotation, string faction, int hitPoints)
  14.     {
  15.         GameObject go = (GameObject)Instantiate(prefab, position, rotation);
  16.         CPhysicsPlayer player = go.GetComponent<CPhysicsPlayer>();
  17.         player.HitPoints = hitPoints;
  18. #if UNITY_EDITOR
  19.         Debug.Log("this is working? " + player.name + " with " + hitPoints);
  20. #endif
  21.         return go;
  22.     }
  23.  

I have been wandering your code, trying to find where the returned GameObject is taken and is assigned its TNObject UID, but I haven't been successful.  Are you not using Register to assign its UID?

5
I understand.

But TNBehaviour does do one thing that is very important besides give access to TNObject: set rebuildMethodList to true.  Without that, your RFCs will not work reliably (it seems to dependent on the component execution order?).  By checking the inheritance hierarchy, only on errors related to missing RFCs (obviously you don't want to always check for optimization reasons), you can educate your customer: use TNBehaviour or set rebuildMethodList to true manually to ensure RFCs work.  The customer can then fix their own bugs quickly, and learn about your library in the process.  It never hurts to give a programmer too much information on how they screwed up!

I hope you're not offended by my suggestions.  I have felt inspired since you implemented one of my first suggestions.  I am not trying to redesign your library, as it is very good and clearly I love your work.  I am simply suggesting additions I've put in for myself, that I think other people may enjoy and find useful.  Most of my suggestions merely guide the customer when they make mistakes, like this suggestion.

6
I was "homebrewing" TNBehavior, before I learned that TNBehavior existed and ran into it not finding RFC calls, even though it previously had (order issue I presume).

If you expect or recommend users to use TNBehavior, when a RFC not found error occurs, I'd suggest doing a quick iteration down the inheritance chain to see if it contains a TNBehavior.  If not found, throw a warning or error, stating that you force/recommend the user to use TNBehavior.  If you want to leave it as a choice, mention in the warning to flip the TNObject.rebuildMethodList bool to true.

It should alleviate some of the questions before they come to the forums begging for help, like me :-).

7
This is something I did on my own, and later found you had suggested to another user in the forums:

  1.                                
  2. Debug.LogError("Unable to execute function with ID of '" + funcID + "'. Make sure there is a script alongside TNObject ID " + objID + " that can receive this call.\nGameObject: " + GetHierarchy(obj.gameObject), obj.gameObject);
  3.  

This will make tracing RFC ID mistakes easier to track down when you have complex objects or similar objects.

8
Physics changes a lot, especially if you have a physics controlled player (a car), or other gameplay sensitive objects.

I've simply added a member variable: public bool sendQuickly = false; to the TNSyncRigidbody script, and then called SendQuickly instead of Send if the sendQuickly member variable is true. 

This way the user can choose per rigidbody: is this object getting moved a lot by physics?  Perhaps I should check the sendQuickly bool.

9
TNet 3 Support / TNAutoCreate doubling spawns.
« on: February 23, 2014, 01:17:28 PM »
Pretty straightforward question: I am using TNAutoCreate, but it always creates two instead of one, which is kinda goofy.

I've looked at TNAutoCreate itself... it seems no different than how I am spawning my avatars.  Simply using TNManager.Create(...).  It's as if TNAutoCreate is awaking twice before its destruction.

10
Thank for the reply, that clears up some questions.

Yes, I fixed several of the parameters don't match by checking signatures, but I was running around in circles with them, apparently because I screwed up the RCC.  I'd fix one, and another would break.  I even had your rigidbody auto sync script throwing it at one point, something I had never touched.

Do RFC IDs need to be unique per script, per project, per TNObject, per what?  I am pretty sure it's the latter, but just making sure I'm not entirely crazy (just mostly :-P).

TNBehavior, awesome shortcut!

My issue with the RCC was that I can't use 0, 1, and 2, correct?  If I use 3, everything should work as I expected above?  I was starting to think that all creation went through one function call, and it was up to you in the parameters to determine which is which.

Thank for taking time out of your Sunday to assist me (and others).  Lots of confusion in learning the library, but having written my fair share of my own networking libraries over the years, and I know yours is saving me an incredible amount of time!


11
I'd recommend making Tash's exception logger print out the inner exception.  I made a quick change to track down one of these bugs just a few minutes ago. 

Go to TNUnityTools's PrintException function, and print out the inner exception, if there is one.  You can use the StackTrace property on the exception to get the line numbers too.

My not-so-pretty-but-works Exception dumper:

  1.        
  2.         public static string FullDump(Exception exception, bool stacktrace)
  3.         {
  4.             string ret = "";
  5.             string tab = "";
  6.  
  7.             Exception current = exception;
  8.             while (current != null && ret.Length < 10000000)
  9.             {
  10.                 ret += tab + current.Message + "\r\n";
  11.                 if (stacktrace)
  12.                 {
  13.                     if (current.StackTrace != null)
  14.                     {
  15.                         string[] lines = current.StackTrace.Split('\n');
  16.                         foreach (string l in lines)
  17.                         {
  18.                             ret += tab + l + "\r\n";
  19.                         }
  20.                         ret += "\r\n";
  21.                     }
  22.                 }
  23.                 tab += "   ";
  24.  
  25.                 current = current.InnerException;
  26.             }
  27.  
  28.             return ret;
  29.         }
  30.  

12
TNet 3 Support / I'm in RFC id hell right now. (Parameters don't match.)
« on: February 23, 2014, 02:15:34 AM »
-What are the rules of RFC numbers? 
-I would assume they need to be unique per script, but what about across the project? 
-What about RCCs?  Do they share the same pool?  If so, why are RFCs bytes and RCCs ints?

My first remote function anything was an RCC:

   
  1.     public enum EMessageType : byte
  2.     {
  3.         //1 I think is for syncing rigid bodies.
  4.         createPlayer = 243, //me now trying random values
  5.         playerUpdate = 3
  6.     }
  7.  
  8.     internal void SpawnPlayer(Vector3 position, Quaternion rotation, string faction)
  9.     {
  10.         TNManager.CreateEx((int)EMessageType.createPlayer, false, player, position, rotation, faction);
  11.        
  12.     }
  13.    
  14.     [RCC((int)EMessageType.createPlayer)]
  15.     private void OnSpawnPlayer(GameObject prefab, Vector3 position, Quaternion rotation, string faction)
  16.     {
  17.         GameObject go = (GameObject)Instantiate(prefab, position, rotation);
  18.         CPhysicsPlayer player = go.GetComponent<CPhysicsPlayer>();
  19.         Debug.Log("this is working? " + player.name);
  20.     }

Which seemed to work fine for a while, until I added my first RFC in a different script:

To call it:

       
  1.         if (myTNObject.isMine)
  2.         {
  3.             myTNObject.SendQuickly((byte)CNetworkManager.EMessageType.playerUpdate, TNet.Target.OthersSaved, forwardInput, strafeInput, primaryInput, secondaryInput, useInput);
  4.         }

The function it is calling:

   
  1.     [RFC((byte)CNetworkManager.EMessageType.playerUpdate)]
  2.     private void Receive_Update(float forwardInput, float strafeInput, bool primaryInput, bool secondaryInput, bool useInput)
  3.     {
  4.         this.forwardInput = forwardInput;
  5.         this.strafeInput = strafeInput;
  6.         this.primaryInput = primaryInput;
  7.         this.secondaryInput = secondaryInput;
  8.         this.useInput = useInput;
  9.     }
  10.  
Now I have been swimming in "parameters don't match" Hell for an hour (it's like Windows 9X all over again with DLLs!).  I assumed the worst case scenario and made all IDs unique across all my scripts. 

I have deleted the server.dat I found, and I have yet to see another one.

I have restarted the stand-alone server multiple times.

Once I switched my SpawnPlayer to a random number, it resumed working without any parameter issues, except now none of my objects synchronize movement anymore.

What in the world is going on?!?!  All I am trying to do is spawn one player prefab per player and have them move, but this is like pulling teeth.

Thanks in advance.  I am sure it's something I'm doing wrong, but I feel like documentation is rather scant on the particulars.

EDIT: Well, I gave up on the RCCs and my objects' movement is synchronizing again.  I have been wandering the forums and it was hinted that the RCCid is actually the ID given to the TNObject upon instantiation?  If that's the case, how does it now which RCC function to call?  Eventually I will need to do RCCs again, but at least I can make some progress in other areas, such as proving my primitive prediction works beautifully.

Pages: [1]