RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

See Hidden Party Members

The_Man

Sorceror
Ok, so just messing around I added this to PlayerMobile.cs CanSee method
Code:
Party p = Engines.PartySystem.Party.Get( m );
  if (p != null && p.Count > 0)
  {
      for (int k = 0; k < p.Members.Count; ++k)
      {
          PartyMemberInfo pmi = (PartyMemberInfo) p.Members[k];
          Mobile member = pmi.Mobile;
          if (member is PlayerMobile)
              return true;
      }
  }
so that members in party can see other members if they are hidden. Now, I realize this isn't the best way to go about it and plan on making it so that party members are added to each other's VisList. But I am curious how much overhead this really has.

What would be a good way to profile this (and other things in the future) to find out how it affects performance?
 

daat99

Moderator
Staff member
Considering party size is limited to 5 players the overhead isn't that high.

However, the CanSee method is called a lot of times so even a tiny overhead can easily turn into a massive performance issue on a large scale server with a lot of players in the same area.

If you do want to use your code I would suggest using the "foreach" loop (it'll be easier for the compiler to optimize it) as follows:


Code:
Party p = Engines.PartySystem.Party.Get( m );
if ( p != null )
{
foreach ( PartyMemberInfo pmi in p.Members )
{
if ( pmi.Mobile is PlayerMobile )
return true;
}
}


Please note that this code (as you wrote it and I just translated to a foreach loop) will means that a player in a party can see EVERYTHING! (not just his party members).

Without seeing your entire code I can just guess what you tried to do and how to fix it (and I don't like guessing).
 

Zycron

Squire
Ok so here is what i did in Party.cs

Code:
using System;
using System.Collections;
using Server;
using Server.Network;
using Server.Targeting;
using Server.Factions;
using Server.Commands;
using System.Collections.Generic;
using Server.Mobiles;
 
namespace Server.Engines.PartySystem
{
    public class Party : IParty
    {
        private Mobile m_Leader;
        private List<PartyMemberInfo> m_Members;
        private List<VisibilityList> m_Vis;
        private List<Mobile> m_Candidates;
        private List<Mobile> m_Listeners; // staff listening
 
        public const int Capacity = 10;
 
        public static void Initialize()
        {
            EventSink.Logout += new LogoutEventHandler( EventSink_Logout );
            EventSink.Login += new LoginEventHandler( EventSink_Login );
            EventSink.PlayerDeath += new PlayerDeathEventHandler( EventSink_PlayerDeath );
 
            CommandSystem.Register( "ListenToParty", AccessLevel.GameMaster, new CommandEventHandler( ListenToParty_OnCommand ) );
        }
 
        public static void ListenToParty_OnCommand( CommandEventArgs e )
        {
            e.Mobile.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( ListenToParty_OnTarget ) );
            e.Mobile.SendMessage( "Target a partied player." );
        }
 
        public static void ListenToParty_OnTarget( Mobile from, object obj )
        {
            if ( obj is Mobile )
            {
                Party p = Party.Get( (Mobile) obj );
 
                if ( p == null )
                {
                    from.SendMessage( "They are not in a party." );
                }
                else if ( p.m_Listeners.Contains( from ) )
                {
                    p.m_Listeners.Remove( from );
                    from.SendMessage( "You are no longer listening to that party." );
                }
                else
                {
                    p.m_Listeners.Add( from );
                    from.SendMessage( "You are now listening to that party." );
                }
            }
        }
 
        public static void EventSink_PlayerDeath( PlayerDeathEventArgs e )
        {
            Mobile from = e.Mobile;
            Party p = Party.Get( from );
 
            if ( p != null )
            {
                Mobile m = from.LastKiller;
 
                if ( m == from )
                    p.SendPublicMessage( from, "I killed myself !!" );
                else if ( m == null )
                    p.SendPublicMessage( from, "I was killed !!" );
                else
                    p.SendPublicMessage( from, String.Format( "I was killed by {0} !!", m.Name ) );
            }
        }
 
        private class RejoinTimer : Timer
        {
            private Mobile m_Mobile;
 
            public RejoinTimer( Mobile m ) : base( TimeSpan.FromSeconds( 1.0 ) )
            {
                m_Mobile = m;
            }
 
            protected override void OnTick()
            {
                Party p = Party.Get( m_Mobile );
 
                if ( p == null )
                    return;
 
                m_Mobile.SendLocalizedMessage( 1005437 ); // You have rejoined the party.
                m_Mobile.Send( new PartyMemberList( p ) );
 
                Packet message = Packet.Acquire( new MessageLocalizedAffix( Serial.MinusOne, -1, MessageType.Label, 0x3B2, 3, 1008087, "", AffixType.Prepend | AffixType.System, m_Mobile.Name, "" ) );
                Packet attrs  = Packet.Acquire( new MobileAttributesN( m_Mobile ) );
 
                foreach ( PartyMemberInfo mi in p.Members )
                {
                    Mobile m = mi.Mobile;
 
                    if ( m != m_Mobile )
                    {
                        m.Send( message );
                        m.Send( new MobileStatusCompact( m_Mobile.CanBeRenamedBy( m ), m_Mobile ) );
                        m.Send( attrs );
                        m_Mobile.Send( new MobileStatusCompact( m.CanBeRenamedBy( m_Mobile ), m ) );
                        m_Mobile.Send( new MobileAttributesN( m ) );
                    }
                }
 
                Packet.Release( message );
                Packet.Release( attrs );
            }
        }
 
        public static void EventSink_Login( LoginEventArgs e )
        {
            Mobile from = e.Mobile;
            Party p = Party.Get( from );
 
            if ( p != null )
                new RejoinTimer( from ).Start();
            else
                from.Party = null;
        }
 
        public static void EventSink_Logout( LogoutEventArgs e )
        {
            Mobile from = e.Mobile;
            Party p = Party.Get( from );
 
            if ( p != null )
                p.Remove( from );
 
            from.Party = null;
        }
 
        public static Party Get( Mobile m )
        {
            if ( m == null )
                return null;
 
            return m.Party as Party;
        }
 
        public Party( Mobile leader )
        {
            m_Leader = leader;
           
            m_Vis = new List<VisibilityList>();
            m_Members = new List<PartyMemberInfo>();
            m_Candidates = new List<Mobile>();
            m_Listeners = new List<Mobile>();
 
            m_Members.Add( new PartyMemberInfo( leader ) );
        }
 
        public void Add( Mobile m )
        {
            PartyMemberInfo mi = this[m];
 
            if ( mi == null )
            {
                m_Members.Add( new PartyMemberInfo ( m ) );
                m_Vis.Add( new VisibilityList( m ) );
                m.Party = this;
 
                Packet memberList = Packet.Acquire( new PartyMemberList( this ) );
                Packet attrs = Packet.Acquire( new MobileAttributesN( m ) );
 
                for ( int i = 0; i < m_Members.Count; ++i )
                {
                    Mobile f = ((PartyMemberInfo)m_Members[i]).Mobile;
 
                    f.Send( memberList );
 
                    if ( f != m )
                    {
                        f.Send( new MobileStatusCompact( m.CanBeRenamedBy( f ), m ) );
                        f.Send( attrs );
                        m.Send( new MobileStatusCompact( f.CanBeRenamedBy( m ), f ) );
                        m.Send( new MobileAttributesN( f ) );
                    }
                }
 
                Packet.Release( memberList );
                Packet.Release( attrs );
            }
        }
 
        public void OnAccept( Mobile from )
        {
            OnAccept( from, false );
        }
 
        public void OnAccept( Mobile from, bool force )
        {
            Faction ourFaction = Faction.Find( m_Leader );
            Faction theirFaction = Faction.Find( from );
 
            if ( !force && ourFaction != null && theirFaction != null && ourFaction != theirFaction )
                return;
 
            //  : joined the party.
            SendToAll( new MessageLocalizedAffix( Serial.MinusOne, -1, MessageType.Label, 0x3B2, 3, 1008094, "", AffixType.Prepend | AffixType.System, from.Name, "" ) );
 
            from.SendLocalizedMessage( 1005445 ); // You have been added to the party.
 
            m_Candidates.Remove( from );
            Add( from );
        }
 
        public void OnDecline( Mobile from, Mobile leader )
        {
            //  : Does not wish to join the party.
            leader.SendLocalizedMessage( 1008091, false, from.Name );
 
            from.SendLocalizedMessage( 1008092 ); // You notify them that you do not wish to join the party.
 
            m_Candidates.Remove( from );
            from.Send( new PartyEmptyList( from ) );
 
            if ( m_Candidates.Count == 0 && m_Members.Count <= 1 )
            {
                for ( int i = 0; i < m_Members.Count; ++i )
                {
                    this[i].Mobile.Send( new PartyEmptyList( this[i].Mobile ) );
                    this[i].Mobile.Party = null;
                }
 
                m_Members.Clear();
            }
        }
 
        public void Remove( Mobile m )
        {
            if ( m == m_Leader )
            {
                Disband();
            }
            else
            {
                for ( int i = 0; i < m_Members.Count; ++i )
                {
                    if ( ((PartyMemberInfo)m_Members[i]).Mobile == m )
                    {
                        m_Members.RemoveAt( i );
 
                        m.Party = null;
                        m.Send( new PartyEmptyList( m ) );
 
                        m.SendLocalizedMessage( 1005451 ); // You have been removed from the party.
 
                        SendToAll( new PartyRemoveMember( m, this ) );
                        SendToAll( 1005452 ); // A player has been removed from your party.
 
                        break;
                    }
                }
 
                if ( m_Members.Count == 1 )
                {
                    SendToAll( 1005450 ); // The last person has left the party...
                    Disband();
                }
            }
        }
 
        public bool Contains( Mobile m )
        {
            return ( this[m] != null );
        }
 
        public void Disband()
        {
            SendToAll( 1005449 ); // Your party has disbanded.
 
            for ( int i = 0; i < m_Members.Count; ++i )
            {
                this[i].Mobile.Send( new PartyEmptyList( this[i].Mobile ) );
                this[i].Mobile.Party = null;
            }
 
            m_Members.Clear();
        }
 
        public static void Invite( Mobile from, Mobile target )
        {
            Faction ourFaction = Faction.Find( from );
            Faction theirFaction = Faction.Find( target );
 
            if ( ourFaction != null && theirFaction != null && ourFaction != theirFaction )
            {
                from.SendLocalizedMessage( 1008088 ); // You cannot have players from opposing factions in the same party!
                target.SendLocalizedMessage( 1008093 ); // The party cannot have members from opposing factions.
                return;
            }
 
            Party p = Party.Get( from );
 
            if ( p == null )
                from.Party = p = new Party( from );
 
            if ( !p.Candidates.Contains( target ) )
                p.Candidates.Add( target );
 
            //  : You are invited to join the party. Type /accept to join or /decline to decline the offer.
            target.Send( new MessageLocalizedAffix( Serial.MinusOne, -1, MessageType.Label, 0x3B2, 3, 1008089, "", AffixType.Prepend | AffixType.System, from.Name, "" ) );
 
            from.SendLocalizedMessage( 1008090 ); // You have invited them to join the party.
 
            target.Send( new PartyInvitation( from ) );
            target.Party = from;
 
            DeclineTimer.Start( target, from );
        }
 
        public void SendToAll( int number )
        {
            SendToAll( number, "", 0x3B2 );
        }
 
        public void SendToAll( int number, string args )
        {
            SendToAll( number, args, 0x3B2 );
        }
 
        public void SendToAll( int number, string args, int hue )
        {
            SendToAll( new MessageLocalized( Serial.MinusOne, -1, MessageType.Regular, hue, 3, number, "System", args ) );
        }
 
        public void SendPublicMessage( Mobile from, string text )
        {
            SendToAll( new PartyTextMessage( true, from, text ) );
 
            for ( int i = 0; i < m_Listeners.Count; ++i )
            {
                Mobile mob = m_Listeners[i];
 
                if ( mob.Party != this )
                    m_Listeners[i].SendMessage( "[{0}]: {1}", from.Name, text );
            }
 
            SendToStaffMessage( from, "[Party]: {0}", text );
        }
 
        public void SendPrivateMessage( Mobile from, Mobile to, string text )
        {
            to.Send( new PartyTextMessage( false, from, text ) );
 
            for ( int i = 0; i < m_Listeners.Count; ++i )
            {
                Mobile mob = m_Listeners[i];
 
                if ( mob.Party != this )
                    m_Listeners[i].SendMessage( "[{0}]->[{1}]: {2}", from.Name, to.Name, text );
            }
 
            SendToStaffMessage( from, "[Party]->[{0}]: {1}", to.Name, text );
        }
 
        private void SendToStaffMessage( Mobile from, string text )
        {   
            Packet p = null;
 
            foreach( NetState ns in from.GetClientsInRange( 8 ) )
            {
                Mobile mob = ns.Mobile;
 
                if( mob != null && mob.AccessLevel >= AccessLevel.GameMaster && mob.AccessLevel > from.AccessLevel && mob.Party != this && !m_Listeners.Contains( mob ) )
                {
                    if( p == null )
                        p = Packet.Acquire( new UnicodeMessage( from.Serial, from.Body, MessageType.Regular, from.SpeechHue, 3, from.Language, from.Name, text ) );
 
                    ns.Send( p );
                }
            }
 
            Packet.Release( p );
        }
        private void SendToStaffMessage( Mobile from, string format, params object[] args )
        {
            SendToStaffMessage( from, String.Format( format, args ) );
        }
 
        public void SendToAll( Packet p )
        {
            p.Acquire();
 
            for ( int i = 0; i < m_Members.Count; ++i )
                m_Members[i].Mobile.Send( p );
 
            if ( p is MessageLocalized || p is MessageLocalizedAffix || p is UnicodeMessage || p is AsciiMessage )
            {
                for ( int i = 0; i < m_Listeners.Count; ++i )
                {
                    Mobile mob = m_Listeners[i];
 
                    if ( mob.Party != this )
                        mob.Send( p );
                }
            }
 
            p.Release();
        }
 
        public void OnStamChanged( Mobile m )
        {
            Packet p = null;
 
            for ( int i = 0; i < m_Members.Count; ++i )
            {
                Mobile c = m_Members[i].Mobile;
 
                if ( c != m && m.Map == c.Map && Utility.InUpdateRange( c, m ) && c.CanSee( m ) )
                {
                    if ( p == null )
                        p = Packet.Acquire( new MobileStamN( m ) );
 
                    c.Send( p );
                }
            }
 
            Packet.Release( p );
        }
 
        public void OnManaChanged( Mobile m )
        {
            Packet p = null;
 
            for ( int i = 0; i < m_Members.Count; ++i )
            {
                Mobile c = m_Members[i].Mobile;
 
                if ( c != m && m.Map == c.Map && Utility.InUpdateRange( c, m ) && c.CanSee( m ) )
                {
                    if ( p == null )
                        p = Packet.Acquire( new MobileManaN( m ) );
 
                    c.Send( p );
                }
            }
 
            Packet.Release( p );
        }
 
        public void OnStatsQuery( Mobile beholder, Mobile beheld )
        {
            if ( beholder != beheld && Contains( beholder ) && beholder.Map == beheld.Map && Utility.InUpdateRange( beholder, beheld ) )
            {
                if ( !beholder.CanSee( beheld ) )
                    beholder.Send( new MobileStatusCompact( beheld.CanBeRenamedBy( beholder ), beheld ) );
 
                beholder.Send( new MobileAttributesN( beheld ) );
            }
        }
 
        public int Count{ get{ return m_Members.Count; } }
        public bool Active{ get{ return m_Members.Count > 1; } }
        public Mobile Leader{ get{ return m_Leader; } }
        public List<PartyMemberInfo> Members{ get{ return m_Members; } }
        public List<Mobile> Candidates { get { return m_Candidates; } }
 
        public PartyMemberInfo this[int index]{ get{ return m_Members[index]; } }
        public PartyMemberInfo this[Mobile m]
        {
            get
            {
                for ( int i = 0; i < m_Members.Count; ++i )
                    if ( m_Members[i].Mobile == m )
                        return m_Members[i];
 
                return null;
            }
        }
    }
}

and here is the error i get

Code:
Errors:
+ Engines/Party/Party.cs:
    CS1501: Line 169: No overload for method 'VisibilityList' takes '1' argument
s

i have tried for hours i think im close though..
 

Zycron

Squire
so we can see eachother in party when invis for assasination grps or idoc stealthers think it would be fun.. why not? ;)
 

The_Man

Sorceror
Hi, thanks, I had just read up on using ForEach since Intellisense kept nagging at me about it.
And so:
Code:
Party p = Engines.PartySystem.Party.Get( m );
            if ( p != null )
            {
                foreach (PartyMemberInfo pmi in p.Members)
                {
                    if (pmi.Mobile is PlayerMobile && m is pmi.Mobile)
                        return true;
                }
            }

Should fix being able to see everything then...I think. Regardless, my buddy and I are trying to figure out a better way at the moment after failing at VisList and realizing there might be a better way. But... it's proving difficult so far.
 

daat99

Moderator
Staff member
Does it compile?

Code:
m is pmi.Mobile

Do you want to check:
if mobile A "is" (of type named) mobile B
or
if mobile A "equals to" (in values) mobile B
 

The_Man

Sorceror
That should have been:
Code:
Party p = Engines.PartySystem.Party.Get( m );
            if ( p != null )
            {
                foreach (PartyMemberInfo pmi in p.Members)
                {
                    if (pmi.Mobile is PlayerMobile && m is PlayerMobile)
                        return true;
                }
            }
 

Kraz

AssistUO Developer
Staff member
Why not something like:

PlayerMobile.cs
Code:
public override bool CanSee( Mobile m )
{
...
if ( m is PlayerMobile )
{
if ( ((PlayerMobile)m).m_VisList.Contains( this ) )
return true;
 
Party theirParty = m.Party as Party;
Party ourParty = this.Party as Party;
 
if ( ourParty != null && theirParty != null && ourParty == theirParty )
return true;
}
...
}
 

The_Man

Sorceror
Hi, that is great! Thanks for stopping by and sharing. It helps the readability to change it up like that as well and that is a great way to party check. I'll definitely use in the future. ^5
 

The_Man

Sorceror
There is still something I haven't quite figured out... When you join party while hidden or leave party while hidden you are not visible/are still visible. However, when using VisList it takes effect immediately.
 

Kraz

AssistUO Developer
Staff member
Why do you need this part in there?
Just adapted a statement from RunUO SVN so they have to replace from original CanSee method:

Code:
public override bool CanSee( Mobile m )
{
...
if ( m is PlayerMobile && ((PlayerMobile)m).m_VisList.Contains( this ) )
return true;
...
}

There is still something I haven't quite figured out... When you join party while hidden or leave party while hidden you are not visible/are still visible. However, when using VisList it takes effect immediately.
It happens because of VisibilityList.cs file, OnTarget method:
Code:
...
if ( Utility.InUpdateRange( targ, from ) )
{
NetState ns = targ.NetState;
 
if ( ns != null ) {
if ( targ.CanSee( from ) )
{
if ( ns.StygianAbyss )
ns.Send( new MobileIncoming( targ, from ) );
else
ns.Send( new MobileIncomingOld( targ, from ) );
 
if ( ObjectPropertyList.Enabled )
{
ns.Send( from.OPLPacket );
 
foreach ( Item item in from.Items )
ns.Send( item.OPLPacket );
}
}
else
{
ns.Send( from.RemovePacket );
}
}
}
 

Zycron

Squire
The problem we are having is NOT that big of a deal because basically you really only grp with people you would not mind seeing you anyways.. my question is. is this new method we have up more efficient for performance of the server then the old loop method that BOSS had?
 

Vorspire

Knight
Don't worry so much about efficiency until you actually have code that works.
How can one optimize code that doesn't even work to start with? :)

Kraz suggested a "Party" property equality comparison, but it can be simpler since Party is a reference type and we don't actually need the access it for anything other than equality comparison, there's no need to cast the property first.

Stock RunUO CanSee method from PlayerMobile.cs, with necessary edit (commented):
C#:
		public override bool CanSee( Mobile m )
		{
			if ( m is CharacterStatue )
				((CharacterStatue) m).OnRequestedAnimation( this );

			if ( m is PlayerMobile && ((PlayerMobile)m).m_VisList.Contains( this ) )
				return true;

			if ( m_DuelContext != null && m_DuelPlayer != null && !m_DuelContext.Finished && m_DuelContext.m_Tournament != null && !m_DuelPlayer.Eliminated )
			{
				Mobile owner = m;

				if ( owner is BaseCreature )
				{
					BaseCreature bc = (BaseCreature)owner;

					Mobile master = bc.GetMaster();

					if( master != null )
						owner = master;
				}

				if ( m.AccessLevel == AccessLevel.Player && owner is PlayerMobile && ((PlayerMobile)owner).DuelContext != m_DuelContext )
					return false;
			}

			#region Party Visibility

			// Check if they are not null, for safety.
			// Check if they have a party, then check it against our party
			// If the parties are the same reference, we can see them.
			// Only one of the two parties logically needs to be null-checked.

			if( m != null && m.Party != null && m.Party == this.Party )
				return true;

			#endregion

			return base.CanSee( m );
		}
 

RedDragonAdm

Wanderer
Don't worry so much about efficiency until you actually have code that works.
How can one optimize code that doesn't even work to start with? :)

Kraz suggested a "Party" property equality comparison, but it can be simpler since Party is a reference type and we don't actually need the access it for anything other than equality comparison, there's no need to cast the property first.

Stock RunUO CanSee method from PlayerMobile.cs, with necessary edit (commented):
C#:
public override bool CanSee( Mobile m )
{
if ( m is CharacterStatue )
((CharacterStatue) m).OnRequestedAnimation( this );
 
if ( m is PlayerMobile && ((PlayerMobile)m).m_VisList.Contains( this ) )
return true;
 
if ( m_DuelContext != null && m_DuelPlayer != null && !m_DuelContext.Finished && m_DuelContext.m_Tournament != null && !m_DuelPlayer.Eliminated )
{
Mobile owner = m;
 
if ( owner is BaseCreature )
{
BaseCreature bc = (BaseCreature)owner;
 
Mobile master = bc.GetMaster();
 
if( master != null )
owner = master;
}
 
if ( m.AccessLevel == AccessLevel.Player && owner is PlayerMobile && ((PlayerMobile)owner).DuelContext != m_DuelContext )
return false;
}
 
#region Party Visibility
 
// Check if they are not null, for safety.
// Check if they have a party, then check it against our party
// If the parties are the same reference, we can see them.
// Only one of the two parties logically needs to be null-checked.
 
if( m != null && m.Party != null && m.Party == this.Party )
return true;
 
#endregion
 
return base.CanSee( m );
}

This works, glorious.
Thanks you, Master Vorspire.
 
Top