Author Topic: Enum Issue  (Read 178 times)

Kerozard

  • Newbie
  • *
  • Thank You
  • -Given: 2
  • -Receive: 0
  • Posts: 17
    • View Profile
Enum Issue
« on: September 07, 2017, 05:28:28 AM »
I am experiencing an issue with enums and I can't seem to pinpoint the cause.

My data flow is this
-> PlayerManager confirms a move (BattlePlan) for a unit
-> UnitController sends this BattlePlan to the MatchManager on the host
-> MatchManager sets an order index (and performs a few local calculations) and sends the final BattlePlan data back to the client
-> UnitController updates the BattlePlan with the new values

  1. public enum AbilityLocation : int
  2. {
  3.     Default = 0,
  4.     Hand = 1
  5. }
  6.  

PlayerManager:
  1. Unit.tno.Send("HostRfcSetBattlePlan", Target.Host, location, index, x, z);
  2.  

UnitController:
  1. [RFC]
  2. public void RfcSetBattlePlan(int orderIndex, AbilityLocation location, int index, int targetX, int targetZ)
  3. {
  4.     Debug.Log($"Client battle plan from {tno.uid}: {location} {index} {targetX} {targetZ} (Order Index: {orderIndex})");
  5.     var battlePlan = new BattlePlan(tno.uid, location, index, targetX, targetZ) { OrderIndex = orderIndex } ;
  6.  
  7.     Main.Match.UpdateBattlePlan(battlePlan);
  8. }
  9.  
  10. [RFC]
  11. public void HostRfcSetBattlePlan(AbilityLocation location, int index, int targetX, int targetZ)
  12. {
  13.     var battlePlan = new BattlePlan(tno.uid, location, index, targetX, targetZ);
  14.     Debug.Log($"New    battle plan from {tno.uid}: {battlePlan.Location} {battlePlan.Index} {battlePlan.TargetX} {battlePlan.TargetZ} (Order Index: {battlePlan.OrderIndex})");
  15.     Main.Match.HostUpdateBattlePlan(battlePlan);
  16. }
  17.  

MatchManager:
  1. public void HostUpdateBattlePlan(BattlePlan battlePlan)
  2. {
  3.     UpdateBattlePlan(battlePlan);
  4.     Debug.Log($"Host   battle plan from {battlePlan.Creator.tno.uid}: {battlePlan.Location} {battlePlan.Index} {battlePlan.TargetX} {battlePlan.TargetZ} (Order Index: {battlePlan.OrderIndex})");
  5.  
  6.     battlePlan.Creator.tno.Send("RfcSetBattlePlan", Target.AllSaved, battlePlan.OrderIndex, battlePlan.Location, battlePlan.Index, battlePlan.TargetX, battlePlan.TargetZ);
  7. }
  8.  

The debug output:
  1. New    battle plan from 16777214: Hand 0 1 1 (Order Index: -1)
  2. Host   battle plan from 16777214: Hand 0 1 1 (Order Index: 0) // (order index set by the host)
  3. Client battle plan from 16777214: Default 0 1 1 (Order Index: 0)
  4.  

So for some reason the enum value "Hand" reaches the server just fine, but turns into "Default" when it gets send back to the clients. If I, however, cast my enum to an integer when sending it to the clients and back to an enum on the client, it does work and the output becomes this:

  1. New    battle plan from 16777214: Hand 0 1 1 (Order Index: -1)
  2. Host   battle plan from 16777214: Hand 0 1 1 (Order Index: 0)
  3. Client battle plan from 16777214: Hand 0 1 1 (Order Index: 0)
  4.  

I don't understand why it would work in one direction and not the other. The fact that manual casts to int seem to fix the issue makes me think it might not be my code.

I'm happy for any input on the matter.

cmifwdll

  • Global Moderator
  • Full Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 132
  • Posts: 241
  • TNet Alchemist
    • View Profile
Re: Enum Issue
« Reply #1 on: September 07, 2017, 07:02:26 AM »
The common practice is to cast to int (or byte if possible) in the tno.Send call:
  1. tno.Send(37, Target.Host, (int)test);
  2.  
  3. [RFC(37)]
  4. void RecvInitialEnumParam(AbilityLocation test)
  5. {
  6.         Debug.LogError("Host got " + test);
  7.         tno.Send(38, Target.All, (int)test);
  8. }
  9.  

This, as you've seen already, solves your problem, and it also reduces packet size and CPU usage. Note that the function still takes the AbilityLocation enum type - the casting is done automatically.

Kerozard

  • Newbie
  • *
  • Thank You
  • -Given: 2
  • -Receive: 0
  • Posts: 17
    • View Profile
Re: Enum Issue
« Reply #2 on: September 07, 2017, 08:00:39 AM »
In that case, I feel that this information should at be added to the "Example 1 - RFCs.pdf". Silent bugs can be very dangerous. :-)

cmifwdll

  • Global Moderator
  • Full Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 132
  • Posts: 241
  • TNet Alchemist
    • View Profile
Re: Enum Issue
« Reply #3 on: September 07, 2017, 08:11:31 AM »
I wasn't able to replicate your problem, so it doesn't seem to be a bug in TNet:
  1. public class EnumAsRFCParamTest : TNBehaviour
  2. {
  3.     public enum AbilityLocation : int
  4.     {
  5.         Default = 0,
  6.         Hand = 1
  7.     };
  8.  
  9.     void Update()
  10.     {
  11.         if (Input.GetKeyDown(KeyCode.E))
  12.         {
  13.             Debug.LogError("Sending initial RFC");
  14.             AbilityLocation test = AbilityLocation.Hand;
  15.             tno.Send(37, Target.Host, test);
  16.         }
  17.     }
  18.  
  19.     [RFC(38)]
  20.     void RecvFinalEnumParam(AbilityLocation test)
  21.     {
  22.         Debug.LogError("Client got " + test);
  23.     }
  24.  
  25.     [RFC(37)]
  26.     void RecvInitialEnumParam(AbilityLocation test)
  27.     {
  28.         Debug.LogError("Host got " + test);
  29.         tno.Send(38, Target.All, test);
  30.     }
  31. }
  32.  
Outputs Hand on both the host and client.
The enum param gets serialized with type prefix 254 and both its name and value are written using reflection. If REFLECTION_SUPPORT isn't defined it'd be prefix 255 and use BinaryFormatter.

Either way, casting to int or byte should always be preferred, but as far as I can tell enum's are supported by TNet (just as well as any other unknown type).

You could implement the IBinarySerializable interface on your BattlePlan class and handle the serialization directly. This would be faster, you'd have more control over bugs, and you'd be able to pass the BattlePlan instance as the param instead of its parts.

Kerozard

  • Newbie
  • *
  • Thank You
  • -Given: 2
  • -Receive: 0
  • Posts: 17
    • View Profile
Re: Enum Issue
« Reply #4 on: September 07, 2017, 08:55:14 AM »
I will see if I can build a tiny repro case once I am done with the prototype. At this point we are just trying out a few concepts and I made the shift from UNet to TNet about 2 days before the first testing session was scheduled. UNet was driving me crazy and I remembered TNet 2 fondly. So there was not a lot of time to read up on all the docs. I kinda threw things in and saw that it works. It is not supposed to be pretty right now. :-)

But I am grateful for your input and will keep that model in mind if we decide to go forward with the project and start implementing the final version.