Page 3 of 4 FirstFirst 1234 LastLast
Results 21 to 30 of 33
  1. #21
    Join Date
    Nov 2006
    Location
    Vancouver, WA
    Posts
    236
    What I am going to do is redesign the lobby system into a system that allows eight people make a group in game.

    And as for the master server and region servers I have yet to decide what I am going to do with that right now I am doing research on the Vector2,3 and 4 so I can write those files and start testing avatar movement .

  2. #22
    Join Date
    Aug 2010
    Posts
    160
    The VectorX classes are just a data structure to hold X float values. Vector3 can then be used to hold X, Y, and Z position. Vector4 can be used to model a Quaternion for rotation.

    I think what nelson was doing is using a Vector4 to hold X,Y, and Z positions and then the W value to hold the Euler Angle of the rotation around the Y axis. Just rewatched this part and he is using a Vector3 for position and just a float for rotation angle. Same as Vector4 just not wrapped.

    Instead of doing this, I created a NetworkTransform object to hold this data. It maintains 7 float values, an int Id, and a byte flag for changes.

    In its current iteration it will always send the Id, and the changed flags (4 + 1 bytes = 6 bytes) minimum to (4 + 1 + 7*4 bytes = 33 bytes) at most. That is just for this though.

    Then we have to wrap it in our SimpleSerializer. Non value types are appended with a boolean to declare if they are null or not. (6 + 1 bytes = 7 bytes) minimum to (33 + 1 bytes = 34 bytes) at most

    Then it has to be wrapped by our MappedMethod system and in this case I am using a method on a system that only has 1 argument. (Move(NetworkTransform networkTransform)) so we have to add the the MethodId byte, the methodId, and the ArgumentBytes byte. (7 + 1 + 1 + 1 bytes = 11 bytes) minimum to (34 + 1 + 1 + 1 bytes = 37 bytes) at most.

    Then it is wrapped by our MappedComponent system which adds the Component Id and the Dictionary from the MappedMethod step. Photon looks to serialize Dictionaries with (3 + size(keys) + size(values)) bytes. We have already factored in the size of keys and values from the previous step so we just need to add 3. (11 + 3 bytes = 14 bytes) minimum to (37 + 3 bytes = 40 bytes) at most.

    After that we still have to wrap this in Photons OpCustom which sends a custom operation code (MappedComponent Id) and the Dictionary of parameters above. I don't know what this adds but nelson can't get away from it either.

    This is why nelson just wanted to use binary readers and writers in the movement code. He still can't get rid of the overhead of photons serialization which needs an OperationCode and a Dictionary of Parameters. He is using a Vector3 for position and it looks like the serialization part converts it to an array of floats so we have to add a length value. This could be a byte, short, or int. There is a float for rotation, byte for ActorType.Id, and what ever Id is. Then in the AdditionalFullState it adds some other fields like PlayerId (int), MaxHealth (int), and Health (int) but that gets away from the point of what I am doing here so lets just keep PlayerId so we know which unit we are updating.

    Position (12 + 4 bytes), Rotation (4 bytes), ActorType.Id (1 byte), PlayerId (4 bytes), and Id?? (1 or 4 bytes??) = 26 - 29 bytes.

    This will still need to be wrapped in a OpCustom call which will add the OperationCode byte and a dictionary which will map the data to an operation. That dictionary will also need to map out what the arguments are and what the method is. I can't tell well in the code where this is all wrapped, but at minimum we need 1 byte for the OpCode, 3 bytes for Dictionary Overhead, 1 byte for Dictionary Key, and 26-29 bytes for the Dictionary Value.

    1 + 3 + 1 + (26-29) = 31-34 bytes

    Since my NetworkTransform has the changed flags built in we can use Pos X, Y, and Z, and Rotation Y without touching the other rotation floats. This means that I would be adding 4 floats to my minimum value or 16 bytes which would be 14 + 16 = 30 bytes. This doesn't have the ActorType.Id or the Id fields but they could be added to the wrapping caller and would add the same data as Nelson's at 2-5 bytes respectively. Which would put this system at 32-35 bytes. We could also move out the Id to the wrapping caller list Nelson did.

    We could go further and add compression or delta logic and use shorts or even bytes to say how far a player has moved in a single direction if the distance will be short enough.

    The slowdown isn't from the data being sent. The slowdown is from the SimpleSerializer in which it has to create an instance activation of a type through reflection and then set its values. Since Nelson is using the Vector3 class he made which serializes as a float[] and a float and then is able to call his float[] Extension Method ToVector() on it he can use a direct call to new Vector3 while setting the values to avoid the reflection overhead. We could replace this with compiled expressions which I think are not IL2CPP friendly

    I am going to stick with Systems for this for now and not prematurely optimize it. I will probably pull out the Id field from this class and move it to who ever wants to send the message as an additional parameter.


    Here is the NetworkTransform Class. It is basically just a replacement for the Vector3 class plus float rotation. I should honestly have this defined as a float[7] so that the memory is all contiguous as well. All that would do is map the index of the array to [PosX, PosY, PosZ, RotX, RotY, RotZ, RotW] and use the properties to get or set these index values instead of 7 randomly placed objects in memory. After all, these values will be read and written together all the time.

    Code:
    using MMO.Base.Extensions;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    
    namespace MMO.Base.Components
    {
        public class NetworkTransform : ICustomSerializer
        {
            [Flags]
            public enum Fields : byte
            {
                PositionX = 1,  // 00000001
                PositionY = 2,  // 00000010
                PositionZ = 4,  // 00000100
                RotationX = 8,  // 00001000
                RotationY = 16, // 00010000
                RotationZ = 32, // 00100000
                RotationW = 64  // 01000000
            }
    
            private float _positionX;
            private float _positionY;
            private float _positionZ;
    
            private float _rotationX;
            private float _rotationY;
            private float _rotationZ;
            private float _rotationW;
    
    
            public Fields Changed { get; private set; }
    
            public int Id { get; set; }
    
            // Positions
            public float PositionX
            {
                get { return _positionX; }
                set
                {
                    if (value == _positionX)
                        return;
    
                    _positionX = value;
                    Changed |= Fields.PositionX;
                }
            }
            public float PositionY
            {
                get { return _positionY; }
                set
                {
                    if (value == _positionY)
                        return;
    
                    _positionY = value;
                    Changed |= Fields.PositionY;
                }
            }
            public float PositionZ
            {
                get { return _positionZ; }
                set
                {
                    if (value == _positionZ)
                        return;
    
                    _positionZ = value;
                    Changed |= Fields.PositionZ;
                }
            }
    
            // Rotations
            public float RotationX
            {
                get { return _rotationX; }
                set
                {
                    if (value == _rotationX)
                        return;
    
                    _rotationX = value;
                    Changed |= Fields.RotationX;
                }
            }
            public float RotationY
            {
                get { return _rotationY; }
                set
                {
                    if (value == _rotationY)
                        return;
    
                    _rotationY = value;
                    Changed |= Fields.RotationY;
                }
            }
            public float RotationZ
            {
                get { return _rotationZ; }
                set
                {
                    if (value == _rotationZ)
                        return;
    
                    _rotationZ = value;
                    Changed |= Fields.RotationZ;
                }
            }
            public float RotationW
            {
                get { return _rotationW; }
                set
                {
                    if (value == _rotationW)
                        return;
    
                    _rotationW = value;
                    Changed |= Fields.RotationW;
                }
            }
    
            public void ResetChange()
            {
                Changed = 0;
            }
    
            #region ICustomSerializer Methods
            public void Save(BinaryWriter writer, ISerializer serializer)
            {
                writer.Write(Id);
                writer.Write((byte)Changed);
    
                if (Changed.ContainsFlag(Fields.PositionX))
                    writer.Write(_positionX);
    
                if (Changed.ContainsFlag(Fields.PositionY))
                    writer.Write(_positionY);
    
                if (Changed.ContainsFlag(Fields.PositionZ))
                    writer.Write(_positionZ);
    
                if (Changed.ContainsFlag(Fields.RotationX))
                    writer.Write(_rotationX);
    
                if (Changed.ContainsFlag(Fields.RotationY))
                    writer.Write(_rotationY);
    
                if (Changed.ContainsFlag(Fields.RotationZ))
                    writer.Write(_rotationZ);
    
                if (Changed.ContainsFlag(Fields.RotationW))
                    writer.Write(_rotationW);
            }
    
            public void Load(BinaryReader reader, ISerializer serializer)
            {
                Id = reader.ReadInt32();
                Changed = (Fields)reader.ReadByte();
    
                if (Changed.ContainsFlag(Fields.PositionX))
                    _positionX = reader.ReadSingle();
    
                if (Changed.ContainsFlag(Fields.PositionY))
                    _positionY = reader.ReadSingle();
    
                if (Changed.ContainsFlag(Fields.PositionZ))
                    _positionZ = reader.ReadSingle();
    
                if (Changed.ContainsFlag(Fields.RotationX))
                    _rotationX = reader.ReadSingle();
    
                if (Changed.ContainsFlag(Fields.RotationY))
                    _rotationY = reader.ReadSingle();
    
                if (Changed.ContainsFlag(Fields.RotationZ))
                    _rotationZ = reader.ReadSingle();
    
                if (Changed.ContainsFlag(Fields.RotationW))
                    _rotationW = reader.ReadSingle();
            }
            #endregion
    
            public override string ToString()
            {
                return $"(networkTransform: Position - [X:{_positionX}, Y:{_positionY}, Z:{_positionZ}], Rotation - [X:{_rotationX}, Y:{_rotationY}, Z:{_rotationZ}, W:{_rotationW}]))";
            }
        }
    }

  3. #23
    Join Date
    Aug 2010
    Posts
    160
    Quickly refactored this class to use a backing array of float instead of 7 individual floats. Also removed the PlayerId field.

    Code:
    using MMO.Base.Extensions;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    
    namespace MMO.Base.Components
    {
        public class NetworkTransform : ICustomSerializer
        {
            [Flags]
            public enum Fields : byte
            {
                PositionX = 1,  // 00000001
                PositionY = 2,  // 00000010
                PositionZ = 4,  // 00000100
                RotationX = 8,  // 00001000
                RotationY = 16, // 00010000
                RotationZ = 32, // 00100000
                RotationW = 64  // 01000000
            }
    
            private float[] _fields;
    
    
            public Fields Changed { get; private set; }
    
            // Positions
            public float PositionX
            {
                get { return _fields[0]; }
                set
                {
                    if (value == _fields[0])
                        return;
    
                    _fields[0] = value;
                    Changed |= Fields.PositionX;
                }
            }
            public float PositionY
            {
                get { return _fields[1]; }
                set
                {
                    if (value == _fields[1])
                        return;
    
                    _fields[1] = value;
                    Changed |= Fields.PositionY;
                }
            }
            public float PositionZ
            {
                get { return _fields[2]; }
                set
                {
                    if (value == _fields[2])
                        return;
    
                    _fields[2] = value;
                    Changed |= Fields.PositionZ;
                }
            }
    
            // Rotations
            public float RotationX
            {
                get { return _fields[3]; }
                set
                {
                    if (value == _fields[3])
                        return;
    
                    _fields[3] = value;
                    Changed |= Fields.RotationX;
                }
            }
            public float RotationY
            {
                get { return _fields[4]; }
                set
                {
                    if (value == _fields[4])
                        return;
    
                    _fields[4] = value;
                    Changed |= Fields.RotationY;
                }
            }
            public float RotationZ
            {
                get { return _fields[5]; }
                set
                {
                    if (value == _fields[5])
                        return;
    
                    _fields[5] = value;
                    Changed |= Fields.RotationZ;
                }
            }
            public float RotationW
            {
                get { return _fields[6]; }
                set
                {
                    if (value == _fields[6])
                        return;
    
                    _fields[6] = value;
                    Changed |= Fields.RotationW;
                }
            }
    
            public NetworkTransform()
            {
                _fields = new float[7];
            }
    
            public void ResetChange()
            {
                Changed = 0;
            }
    
            #region ICustomSerializer Methods
            public void Save(BinaryWriter writer, ISerializer serializer)
            {
                writer.Write((byte)Changed);
    
                if (Changed.ContainsFlag(Fields.PositionX))
                    writer.Write(_fields[0]);
    
                if (Changed.ContainsFlag(Fields.PositionY))
                    writer.Write(_fields[1]);
    
                if (Changed.ContainsFlag(Fields.PositionZ))
                    writer.Write(_fields[2]);
    
                if (Changed.ContainsFlag(Fields.RotationX))
                    writer.Write(_fields[3]);
    
                if (Changed.ContainsFlag(Fields.RotationY))
                    writer.Write(_fields[4]);
    
                if (Changed.ContainsFlag(Fields.RotationZ))
                    writer.Write(_fields[5]);
    
                if (Changed.ContainsFlag(Fields.RotationW))
                    writer.Write(_fields[6]);
            }
    
            public void Load(BinaryReader reader, ISerializer serializer)
            {
                Changed = (Fields)reader.ReadByte();
    
                if (Changed.ContainsFlag(Fields.PositionX))
                    _fields[0] = reader.ReadSingle();
    
                if (Changed.ContainsFlag(Fields.PositionY))
                    _fields[1] = reader.ReadSingle();
    
                if (Changed.ContainsFlag(Fields.PositionZ))
                    _fields[2] = reader.ReadSingle();
    
                if (Changed.ContainsFlag(Fields.RotationX))
                    _fields[3] = reader.ReadSingle();
    
                if (Changed.ContainsFlag(Fields.RotationY))
                    _fields[4] = reader.ReadSingle();
    
                if (Changed.ContainsFlag(Fields.RotationZ))
                    _fields[5] = reader.ReadSingle();
    
                if (Changed.ContainsFlag(Fields.RotationW))
                    _fields[6] = reader.ReadSingle();
            }
            #endregion
    
            public override string ToString()
            {
                return $"(networkTransform: Position - [X:{_fields[0]}, Y:{_fields[1]}, Z:{_fields[2]}], Rotation - [X:{_fields[3]}, Y:{_fields[4]}, Z:{_fields[5]}, W:{_fields[6]}]))";
            }
        }
    }

  4. #24
    Join Date
    Nov 2006
    Location
    Vancouver, WA
    Posts
    236
    am385, Thank you for sharing your solution to the Vector x issue and the very good explanation for me to study. I am embarrassed for my lack the skills to complete the framework on my own, I have watched all of the videos many many times as Nelson explains what he is doing and enjoy reading the code and follow along to learn as much as I can in the hope to be able to make a MMO.
    The only way to fail is to give up or die...

  5. #25
    Join Date
    Feb 2014
    Posts
    277
    I've asked Steve 2 times about Nelson's development code. I don't want to nag him again.
    Of course, you are all free to ask him too!
    Last edited by oldngrey; 05-15-2017 at 10:11 PM.

  6. #26
    Join Date
    Nov 2006
    Location
    Vancouver, WA
    Posts
    236
    I don't think that will be helpful as Nelson is no longer obligated to 3DBuzz but like I said before I have written what was shown in the chapter 12 intro and I don't see no reason why I can't post it here? Unless someone at 3DBuzz says no...

    Then we can work it out and see if it is possible to complete chapter 12, but the Intro does have missing code having said that some of it might be useful.

    Although I would still rather follow Am385, as he is moving in a MMO direction not MOBA like Nelson was.
    Last edited by Matroblend; 05-15-2017 at 10:20 AM.

  7. #27
    Join Date
    Nov 2006
    Location
    Vancouver, WA
    Posts
    236
    Quote Originally Posted by am385 View Post
    That's what I am starting this week. This wasn't a MOBA class but that's what happened. I am removing the master and region and just creating a login server and cell servers / Zone servers. Granted I am creating a new branch for this so I am not just deleting the work. That being said, I don't want to go down the MOBA path.
    Do you still have plans to share your changes and direction to move the MMO project forward?

  8. #28
    Join Date
    Nov 2006
    Location
    Vancouver, WA
    Posts
    236
    In ActorId is a ToString and I am not sure how it should close the var.


    public override string ToString()
    {
    var kindText = Kind == ActorKind.Persistant ? "P" : Kind == ActorKind.Scene ? "X" : Kind == ActorKind.ServerSide ? "S" :
    Kind == ActorKind.Transient ? "T" : Kind == ActorKind.Transient ? "N"

    return $"{kindText}{Id}";
    }
    The only way to fail is to give up or die...

  9. #29
    Join Date
    Feb 2014
    Posts
    277
    That's really not a ton of information to go on with

    Do you know what the ActorKind enum contains? That might help point to other options for "N" or how on earth it manages to equate a string with an int.
    That code kinda baffles me too. It seems contradictory and even resharper throws in the towel. Like.... where does {Id} come from?

  10. #30
    Join Date
    Aug 2010
    Posts
    160
    So 'Id' is pascal case so we can assume that it is a property on this class. Also remember that string interpolation is just like string format in that it calls the ToString method on any argument that is present.

    The ToString() method is just a set of ternary operations that would basically be this but note the logic errors. You cant get to "N".

    Code:
    public int Id { get; set; }
    public ActorKind Kind { get; set; }
    
    public override string ToString()
    {
        string kindText;
        if (Kind == ActorKind.Persistant)
            kindText = "P";
        else if (Kind == ActorKind.Scene)
            kindText = "X";
        else if (Kind == ActorKind.ServerSide)
            kindText = "S";
        else if (Kind == ActorKind.Transient)
            kindText = "T";
        else if (Kind == ActorKind.Transient)
            kindText = "N"; // Will never get hit. bag logic
        else
            // No else. Will not compile
    
        return $"{kindText}{Id}";    
    }
    To end this we would need to know what the other ActorKind options are for a hint, or so see where this info gets parsed so we can see what kind of characters he is looking for.
    Last edited by am385; 05-27-2017 at 11:53 AM.

Page 3 of 4 FirstFirst 1234 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •