Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

Welcome to our site

Take a moment to join our board

Sign in to follow this  
Xori.

Team System in Conquer Online

Recommended Posts

I'm working roughly on patch 5500-5517 for whatever reason it might matter.

Scenario: player 'a' creates team, player 'b' joins team. player 'a' has options invite/dismiss, player 'b' has exit/follow
if player 'a' dismisses their team and player 'b' creates a new one, when player 'a' joins, it still shows him to have options invite/dimiss(granted they don't work)
Packet 1023 (TeamAction), Sub-Type 15 (Leader) is what tells the client to offer options "invite/dismiss" for the party leader. So is there a subtype to tell the client to only offer party member options "exit/follow"? Or could this possibly just be a client bug.

Attached is an image of the issue.

Screenshot_9.png

Spoiler

 

- Packet Handler

#region 1023: TeamAction
                        case 1023:
                            {
                                TeamActionPacket taPacket = ptr;
                                switch (taPacket.Subtype)
                                {
                                    case TeamActionID.Create: { MyTeam.HandleCreate(user, taPacket); } break;
                                    case TeamActionID.RequestJoin: { Join.HandleRequestJoin(user, taPacket); } break;
                                    case TeamActionID.LeaveTeam: { Member.HandleLeave(user, taPacket); } break;
                                    case TeamActionID.AcceptInvite: { Join.HandleAcceptInvite(user, taPacket); } break;
                                    case TeamActionID.RequestInvite: { Join.HandleRequestInvite(user, taPacket); } break;
                                    case TeamActionID.AcceptJoin: { Join.HandleAcceptJoin(user, taPacket); } break;
                                    case TeamActionID.Dismiss: { MyTeam.HandleDismiss(user, taPacket); } break;
                                    case TeamActionID.Kick: { Member.HandleKick(user, taPacket); } break;
                                }
                                break;
                            }
                        #endregion

- MyTeam Class

public static class MyTeam
    {
        /// <summary>
        /// Handles the creation.
        /// </summary>
        /// <param name="player">The player.</param>
        /// <param name="packet">The packet.</param>
        public static bool HandleCreate(Player player, TeamActionPacket packet)
        {
            if (player.MyTeam != null)
            {
                return true;
            }

            if (Collections.TeamCollection.Create(player))
            {
                player.Send(new TeamActionPacket
                {
                    UID = player.UID,
                    Subtype = TeamActionID.Leader
                });
                player.Send(new TeamActionPacket
                {
                    UID = player.UID,
                    Subtype = TeamActionID.Create
                });
                player.AddEffect1(Effect1.TeamLeader, 0, false);
            }

            return true;
        }

        /// <summary>
		/// Handles the dismiss.
		/// </summary>
		/// <param name="player">The player.</param>
		/// <param name="packet">The packet.</param>
        public static bool HandleDismiss(Player player, TeamActionPacket packet)
		{
			if (player.MyTeam == null)
			{
				return true;
			}
			
			if (!player.MyTeam.IsLeader(player))
			{
				return true;
			}
			
			if (!player.Alive)
			{
				return true;
			}
			
			player.RemoveFlag1(Effect1.TeamLeader);
            var members = player.MyTeam.Delete();
			
			foreach (var member in members)
			{
				member.MyTeam = null;
				member.Send(packet);
			}
			
			return true;
		}
    }

 

- Join Class

public static class Join
    {
        /// <summary>
		/// Handles the request join.
		/// </summary>
		/// <param name="player">The player.</param>
		/// <param name="packet">The packet.</param>
        public static bool HandleRequestJoin(Player player, TeamActionPacket packet)
        {
            if (player.MyTeam != null)
            {
                return true;
            }

            var target = Kernel.Clients[packet.UID];
            if (target != null)
            {
                if (target.MyTeam != null && target.MyTeam.IsLeader(target))
                {
                    target.MyTeam.PendingJoin = player.UID;
                    packet.UID = player.UID;
                    target.Send(packet);
                }
            }

            return true;
        }

        /// <summary>
		/// Handles the request invite.
		/// </summary>
		/// <param name="player">The player.</param>
		/// <param name="packet">The packet.</param>
        public static bool HandleRequestInvite(Player player, TeamActionPacket packet)
        {
            if (player.MyTeam == null)
            {
                return true;
            }

            if (!player.MyTeam.IsLeader(player))
            {
                return true;
            }

            var newMember = Kernel.Clients[packet.UID];
            if (newMember != null && newMember.MyTeam == null)
            {
                player.MyTeam.PendingInvite = newMember.UID;
                packet.UID = player.UID;
                newMember.Send(packet);
            }

            return true;
        }

        /// <summary>
		/// Handles the accept join.
		/// </summary>
		/// <param name="player">The player.</param>
		/// <param name="packet">The packet.</param>
        public static bool HandleAcceptJoin(Player player, TeamActionPacket packet)
        {
            if (player.MyTeam == null)
            {
                return true;
            }

            if (!player.MyTeam.IsLeader(player))
            {
                return true;
            }
            
            if (packet.UID == player.MyTeam.PendingJoin)
            {
                var newMember = Kernel.Clients[packet.UID];
                if (newMember != null && newMember.MyTeam == null)
                {
                    if (player.MyTeam.Add(newMember))
                    {
                        var newTeamMemberPacket = new TeamMember
                        {
                            Name = newMember.Name,
                            PlayerID = newMember.UID,
                            PlayerMesh = newMember.Mesh,
                            MaxHealth = (ushort)newMember.MaxHealth,
                            CurrentHealth = (ushort)newMember.Health
                        };

                        foreach (var member in player.MyTeam.GetMembers())
                        {
                            if (member.UID != newMember.UID)
                            {
                                newMember.Send(new TeamMember
                                {
                                    Name = member.Name,
                                    PlayerID = member.UID,
                                    PlayerMesh = member.Mesh,
                                    MaxHealth = (ushort)member.MaxHealth,
                                    CurrentHealth = (ushort)member.Health
                                });
                                member.Send(newTeamMemberPacket);
                            }
                        }

                        newMember.Send(newTeamMemberPacket);

                        packet.UID = player.UID;
                        newMember.Send(packet);
                    }
                }
            }

            return true;
        }

        /// <summary>
		/// Handles the accept invite.
		/// </summary>
		/// <param name="player">The player.</param>
		/// <param name="packet">The packet.</param>
        public static bool HandleAcceptInvite(Player player, TeamActionPacket packet)
        {
            if (player.MyTeam != null)
            {
                return true;
            }

            var target = Kernel.Clients[packet.UID];
            if (target != null && target.MyTeam != null && target.MyTeam.IsLeader(target) &&
               player.UID == target.MyTeam.PendingInvite)
            {
                if (target.MyTeam.Add(player))
                {
                    var newTeamMemberPacket = new TeamMember
                    {
                        Name = player.Name,
                        PlayerID = player.UID,
                        PlayerMesh = player.Mesh,
                        MaxHealth = (ushort)player.MaxHealth,
                        CurrentHealth = (ushort)player.Health
                    };

                    foreach (var member in target.MyTeam.GetMembers())
                    {
                        if (member.UID != player.UID)
                        {
                            player.Send(new TeamMember
                            {
                                Name = member.Name,
                                PlayerID = member.UID,
                                PlayerMesh = member.Mesh,
                                MaxHealth = (ushort)member.MaxHealth,
                                CurrentHealth = (ushort)member.Health
                            });
                            member.Send(newTeamMemberPacket);
                        }
                    }

                    player.Send(newTeamMemberPacket);

                    packet.UID = target.UID;
                    player.Send(packet);
                }
            }

            return true;
        }
    }

- Member Class

 public static class Member
    {
        /// <summary>
		/// Handles the kick.
		/// </summary>
		/// <param name="player">The player.</param>
		/// <param name="packet">The packet.</param>
        public static bool HandleKick(Player player, TeamActionPacket packet)
        {
            if (player.MyTeam == null)
            {
                return true;
            }

            if (!player.MyTeam.IsLeader(player))
            {
                return true;
            }

            if (player.UID == packet.UID)
            {
                return true;
            }

            Player removedPlayer;
            if (player.MyTeam.Remove(packet.UID, out removedPlayer))
            {
                foreach (var member in player.MyTeam.GetMembers())
                {
                    member.Send(packet);
                }

                removedPlayer.Send(packet);

                removedPlayer.Team = null;
            }

            return true;
        }

        /// <summary>
		/// Handles the leave.
		/// </summary>
		/// <param name="player">The player.</param>
		/// <param name="packet">The packet.</param>
        public static bool HandleLeave(Player player, TeamActionPacket packet)
        {
            if (player.MyTeam == null)
            {
                return true;
            }

            if (player.MyTeam.IsLeader(player))
            {
                return true;
            }

            if (player.UID != packet.UID)
            {
                return true;
            }

            if (player.MyTeam.Remove(packet.UID, out Player removedPlayer))
            {
                foreach (var member in player.MyTeam.GetMembers())
                {
                    member.Send(packet);
                }

                player.Send(packet);

                player.MyTeam = null;
            }

            return true;
        }
    }

 

- Team Collection Class

/// <summary>
	/// A team collection.
	/// </summary>
    public sealed class TeamCollection
    {
        /// <summary>
        /// The team leader id.
        /// </summary>
        private uint _teamLeaderId;

        /// <summary>
		/// The collection of team members.
		/// </summary>
		private ConcurrentDictionary<uint, Player> _members;

        /// <summary>
        /// The pending join id for the team.
        /// </summary>
        public uint PendingJoin { get; set; }

        /// <summary>
        /// The pending invite id for the team.
        /// </summary>
        public uint PendingInvite { get; set; }

        /// <summary>
		/// Creates a new team collection.
		/// </summary>
		/// <param name="teamLeaderId">The team leader id.</param>
		private TeamCollection(uint teamLeaderId)
        {
            _members = new ConcurrentDictionary<uint, Player>();
            _teamLeaderId = teamLeaderId;
        }

        /// <summary>
		/// Checks whether a specific player is the leader of the team.
		/// </summary>
		/// <param name="player">The player to validate.</param>
		/// <returns>True if the player is the leader.</returns>
		public bool IsLeader(Player player)
        {
            return player.UID == _teamLeaderId;
        }

        /// <summary>
		/// Removes a specific team member.
		/// </summary>
		/// <param name="clientId">The team member's client id.</param>
		/// <param name="removedPlayer">The removed team member.</param>
		/// <returns>True if the team member was removed.</returns>
		public bool Remove(uint clientId, out Player removedPlayer)
        {
            return _members.TryRemove(clientId, out removedPlayer);
        }

        /// <summary>
		/// Deletes the team.
		/// </summary>
		/// <returns>An IEnumerable of all the members that were in the team.</returns>
		public IEnumerable<Player> Delete()
        {
            var membersArray = new Player[_members.Count];
            _members.Values.CopyTo(membersArray, 0);

            _teamLeaderId = 0;
            _members.Clear();

            return membersArray;
        }

        /// <summary>
		/// Adds a new player to the team.
		/// </summary>
		/// <param name="player">The player to add.</param>
		/// <returns>True if the player was added, false otherwise.</returns>
		public bool Add(Player player)
        {
            if (_members.Count >= 5)
            {
                return false;
            }

            if (_members.TryAdd(player.UID, player))
            {
                player.MyTeam = this;
                return true;
            }

            return false;
        }

        /// <summary>
		/// Gets all members in the team.
		/// </summary>
		/// <returns>The collection of members.</returns>
		public ICollection<Player> GetMembers()
        {
            return _members.Values;
        }

        /// <summary>
        /// Creates a new team.
        /// </summary>
        /// <param name="player">The team leader.</param>
        /// <returns>True if the team was created, false otherwise.</returns>
        public static bool Create(Player player)
        {
            player.MyTeam = new TeamCollection(player.UID);
            if (!player.MyTeam.Add(player))
            {
                player.Team = null;
                return false;
            }

            return true;
        }
    }

 

- Packet Structure

public unsafe struct TeamActionPacket
    {
        /// <summary>
        /// 4
        /// </summary>
        public uint UID;
        /// <summary>
        /// 8
        /// </summary>
        public TeamActionID Subtype;

        public static implicit operator TeamActionPacket(byte* ptr)
        {
            TeamActionPacket packet = new TeamActionPacket();
            packet.Subtype = (TeamActionID)(*(uint*)(ptr + 4));
            packet.UID = *(uint*)(ptr + 8);
            return packet;
        }

        public static implicit operator byte[](TeamActionPacket packet)
        {
            var buffer = new byte[20];
            fixed (byte* ptr = buffer)
            {
                PacketBuilder.AppendHeader(ptr, buffer.Length, 1023);
                *((uint*)(ptr + 4)) = (uint)packet.Subtype;     
                *((uint*)(ptr + 8)) = packet.UID;
            }
            return buffer;
        }
    }


 

 

By Smallxmac,

Added code into spoiler.

Share this post


Link to post
Share on other sites
On 8/30/2018 at 3:52 AM, bauss said:

Sorry for the late response. I just now implemented the way you handle teams with an exception to the "api controller" system. In the end, I get the same results..

 

Screenshot_1.png

Share this post


Link to post
Share on other sites

If you implemented it the same way, then it would have worked ;)

It looks to me as if you're sending the same packet for team creation and team accept. Those are two different packets.

When you create a team you send the TeamActionPacket with the action as Leader and also an additional TeamActionPacket with the action as Create (Same action that the request should be!)

When someone has accepted to join the team you send the TeamMemberPacket (of all members) to the one joining, but also the TeamMemberPacket of the one joining to all the members. You also have to send the TeamActionPacket back of course, but change the client id of the packet to the leader's client id. (This might be your issue.)

 

That's it.

Edited by bauss

Share this post


Link to post
Share on other sites
On 9/3/2018 at 3:57 PM, bauss said:

If you implemented it the same way, then it would have worked ;)

It looks to me as if you're sending the same packet for team creation and team accept. Those are two different packets.

When you create a team you send the TeamActionPacket with the action as Leader and also an additional TeamActionPacket with the action as Create (Same action that the request should be!)

When someone has accepted to join the team you send the TeamMemberPacket (of all members) to the one joining, but also the TeamMemberPacket of the one joining to all the members. You also have to send the TeamActionPacket back of course, but change the client id of the packet to the leader's client id. (This might be your issue.)

 

That's it.

He mentioned on Discord that after he replaced the client with a clean one, it worked. So I guess this can be closed?

Share this post


Link to post
Share on other sites
13 hours ago, Omicron said:

He mentioned on Discord that after he replaced the client with a clean one, it worked. So I guess this can be closed?

I don't like to close threads. I feel like the less involved I am in moderation, the better off people are.
Let's keep it open for now. I doubt anyone has anything to add to this, but you never know.

  • Like 1

Share this post


Link to post
Share on other sites
18 hours ago, Omicron said:

He mentioned on Discord that after he replaced the client with a clean one, it worked. So I guess this can be closed?

Well his implementation here was wrong though, but whatever floats his boat, as long as he resolved it.

Share this post


Link to post
Share on other sites

@bauss Please feel free to point out where the implementation is wrong. After moving some code around to clean it up I ran into the same issue. So with that being said I'm back to square one. This apparently isn't a client issue as much as I had hoped it was.

Share this post


Link to post
Share on other sites
12 hours ago, bauss said:

Already explained how to implement the whole team system in one of my posts.

Sorry I somehow over-looked said post when skimming through the thread yesterday. Will be adjusting the code shortly, I'll keep everyone posted.

Share this post


Link to post
Share on other sites
On 9/7/2018 at 7:48 PM, Xori. said:

Sorry I somehow over-looked said post when skimming through the thread yesterday. Will be adjusting the code shortly, I'll keep everyone posted.

Well if you have trouble implementing it just let me know, but it should be straightforward if you forllow my instructions.

Share this post


Link to post
Share on other sites
On 9/14/2018 at 9:19 AM, bauss said:

Well if you have trouble implementing it just let me know, but it should be straightforward if you forllow my instructions.

Resolved the issue with your previous post. Only issue I'm running into now is the displaying of the gold start showing team leader location on the mini-map.

#edit Resolved the issue regarding team leader location. For future reference packet 10010 General Data needs to be sent with a sub-type of 101 and values of Leader UID, Leader Map, Leader X, Leader Y.

 

Edited by Xori.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Similar Content

    • By Mugaru
      Hi guys, 
      i’m new here and also pretty dummy in programming in C#.
      Anyway, im working on a CO2 DMap editor. What i’ve done now; is reading out the binary of a dmap file. Got the portals and all other info about coords, if they are walkable or just not! 
      My question to you guys is, is this the right way to build such a thing? Or would you do it another way? 
       
      Kind regards, 
       
      Mugaru
×

Important Information

By using this site, you agree to our Terms of Use.