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!

[RunUO 2.0 RC1] Knives' Chat 3.0

M_0_h

Page
Orbit Storm;839961 said:
Yes, I'd especially like to find out if there is any way to get this to function with 2.0 final.. I've tried tinkering with the scripts using some of the conversion FAQs, but I've gotten nowhere..

Strange, it worked for me well... maybe I simply can't remember I edited something... you could just post your errors, I'll try to help you then :)
 

Orbit Storm

Sorceror
M_0_h;839996 said:
Strange, it worked for me well... maybe I simply can't remember I edited something... you could just post your errors, I'll try to help you then :)


Well I certainly appreciate that! =)

Code:
Warnings:
 + Customs/Knives Chat 3.0 Beta 9/General/MultiConnection.cs:
    CS0168: Line 65: The variable 'e' is declared but never used
    CS0168: Line 134: The variable 'e' is declared but never used
    CS0168: Line 165: The variable 'e' is declared but never used
    CS0168: Line 184: The variable 'e' is declared but never used
    CS0168: Line 201: The variable 'e' is declared but never used
    CS0168: Line 252: The variable 'e' is declared but never used
 + Customs/Knives Chat 3.0 Beta 9/Gumps/Error Reporting/Errors.cs:
    CS0618: Line 91: 'System.Web.Mail.MailMessage' is obsolete: 'The recommended
 alternative is System.Net.Mail.MailMessage. http://go.microsoft.com/fwlink/?lin
kid=14202'
    CS0618: Line 91: 'System.Web.Mail.MailMessage' is obsolete: 'The recommended
 alternative is System.Net.Mail.MailMessage. http://go.microsoft.com/fwlink/?lin
kid=14202'
    CS0618: Line 102: 'System.Web.Mail.SmtpMail' is obsolete: 'The recommended a
lternative is System.Net.Mail.SmtpClient. http://go.microsoft.com/fwlink/?linkid
=14202'

This is just one part.. I was receiving a bunch of errors regarding a "config parser" even.. I'm thinking another script was causing the dysfunction.
 

M_0_h

Page
Orbit Storm;840377 said:
Any takers on the above warnings? =)

Okay, now please post the file in
Code:
-tags :) then we will see, there the error hides...

it may take some time until I answer, I'm on holiday right now ;)
 

Orbit Storm

Sorceror
Sure thing.. Here is MultiConnection.cs:

Code:
// Get name lists for online players not hidden

using System;
using System.IO;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Server;

namespace Knives.Chat3
{
    public class MultiConnection
    {
        private static MultiConnection s_Connection = new MultiConnection();
        public static MultiConnection Connection { get { return s_Connection; } }

        private Socket c_Master, c_Slave;
        private ArrayList c_Clients = new ArrayList();
        private Hashtable c_Names = new Hashtable();
        private bool c_Server;

        private bool c_Connecting, c_Connected;
        private Server.Timer c_ConnectTimer;

        public Hashtable Names { get { return c_Names; } }
        public bool Connecting { get { return c_Connecting; } }
        public bool Connected { get { return c_Connected; } }

        public MultiConnection()
        {
        }

        public void Block(string str)
        {
            foreach (Socket s in c_Clients)
                if (c_Names[s].ToString() == str)
                {
                    c_Names.Remove(s);
                    c_Clients.Remove(s);
                    s.Close();
                }
        }

        public void ConnectMaster()
        {
            if (c_Connected && c_Server)
                return;

            try
            {
                c_Server = true;
                //Console.WriteLine("CM Close Slave");
                CloseSlave();

                c_Master = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                c_Master.Bind(new IPEndPoint(IPAddress.Any, Data.MultiPort));
                c_Master.Listen(4);
                c_Master.BeginAccept(new AsyncCallback(OnClientConnect), null);
                //Console.WriteLine("Started");

                c_Connecting = false;
                c_Connected = true;
            }
            catch (Exception e)
            {
                c_Server = false;
                //Console.WriteLine(e.Message);
                //Console.WriteLine(e.StackTrace);
            }
        }

        public void CloseMaster()
        {
            if (c_Master != null)
                c_Master.Close();

            foreach (Socket sok in c_Clients)
                sok.Close();

            c_Clients.Clear();
            c_Names.Clear();

            c_Connecting = false;
            c_Connected = false;
        }

        public void ConnectSlave()
        {
            if (c_Connected && !c_Server)
                return;

            CloseMaster();
            new Thread(new ThreadStart(ConnectToMaster)).Start();
        }

        private void ConnectToMaster()
        {
            try
            {
                c_Slave = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
                c_Slave.Connect(new System.Net.IPEndPoint(System.Net.IPAddress.Parse(Data.MultiServer), Data.MultiPort));
            }
            catch(Exception e)
            {
                BroadcastSystem(General.Local(288));
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
                return;
            }

            BeginReceive();
        }

        private void BeginReceive()
        {
            try
            {
                BroadcastSystem(General.Local(289));

                byte[] msg = System.Text.Encoding.ASCII.GetBytes("    " + Server.Misc.ServerList.ServerName);

                c_Slave.Send(msg, 0, msg.Length, SocketFlags.None);

                c_Connected = true;
                c_Connecting = false;

                WaitForData(c_Slave);

                foreach (Data data in Data.Datas.Values)
                    if (data.Mobile.HasGump(typeof(MultiGump)))
                        GumpPlus.RefreshGump(data.Mobile, typeof(MultiGump));
            }
            catch (Exception e)
            {
                Errors.Report("Error opening stream for slave.");
                //Console.WriteLine(e.Message);
                //Console.WriteLine(e.StackTrace);
            }
        }

        private void HandleInput(string str)
        {
            Broadcast(str);
            //Console.WriteLine(str);
        }

        public void CloseSlave()
        {
            try
            {
                if (c_Connected)
                    BroadcastSystem(General.Local(290));

                c_Connected = false;
                c_Connecting = false;

                if (c_Slave != null)
                    c_Slave.Close();

                foreach (Data data in Data.Datas.Values)
                    if (data.Mobile.HasGump(typeof(MultiGump)))
                        GumpPlus.RefreshGump(data.Mobile, typeof(MultiGump));
            }
            catch (Exception e)
            {
                Errors.Report("Error disconnecting slave.");
                //Console.WriteLine(e.Message);
                //Console.WriteLine(e.Source);
                //Console.WriteLine(e.StackTrace);
            }
        }

        private void OnClientConnect(IAsyncResult asyn)
        {
            try
            {
                //Console.WriteLine("Connection Made");
                Socket sok = c_Master.EndAccept(asyn);
                c_Clients.Add(sok);
                //sok.Close();
                WaitForData(sok);
            }
            catch (Exception e)
            {
                //Console.WriteLine("Connection Died");
                //Console.WriteLine(e.Message);
                //Console.WriteLine(e.StackTrace);
            }
        }

        public void WaitForData(Socket sok)
        {
            try
            {
                MultiPacket pak = new MultiPacket();
                pak.Socket = sok;
                //Console.WriteLine("Waiting for input.");
                sok.BeginReceive(pak.Buffer, 0, pak.Buffer.Length, SocketFlags.None, new AsyncCallback(OnDataReceived), pak);
            }
            catch (Exception e)
            {
                if (c_Server)
                {
                    if(c_Names[sok] != null)
                        BroadcastSystem(c_Names[sok].ToString() + General.Local(291));
                    c_Clients.Remove(sok);
                    c_Names.Remove(sok);
                }
                else
                {
                    BroadcastSystem(General.Local(290));
                    CloseSlave();
                }
            }
        }

        public void OnDataReceived(IAsyncResult asyn)
        {
            try
            {
                MultiPacket pak = (MultiPacket)asyn.AsyncState;

                byte[] buffer = new byte[1024];
                int count = pak.Socket.Receive(buffer);
                char[] chars = new char[count];
                System.Text.Encoding.ASCII.GetDecoder().GetChars(buffer, 0, count, chars, 0);
                string input = new System.String(chars).Trim();

                //Console.WriteLine(pak.Socket + " " + input);

                if (c_Server)
                {
                    if (input.ToLower().IndexOf("<") != 0)
                    {
                        c_Names[pak.Socket] = input;
                        BroadcastSystem(input + General.Local(292));
                    }
                    else
                    {
                        Broadcast(input);
                        SendMaster(input);
                    }
                }
                else
                {
                    HandleInput(input);
                }

                WaitForData(pak.Socket);
            }
            catch (Exception e)
            {
                if (c_Server)
                    CloseMaster();
                else
                    CloseSlave();

                //Console.WriteLine(e.Message);
                //Console.WriteLine(e.StackTrace);
            }
        }

        public void Broadcast(string str)
        {
            if (Channel.GetByType(typeof(Multi)) == null)
                return;

            ((Multi)Channel.GetByType(typeof(Multi))).Broadcast(str);
        }

        public void SendMaster(string str)
        {
            if (!c_Server || c_Master == null)
                return;
                
            try
            {
                byte[] msg = System.Text.Encoding.ASCII.GetBytes("   " + str);

                foreach (Socket sok in c_Clients)
                    sok.Send(msg, 0, msg.Length, SocketFlags.None);
            }
            catch
            {
                Errors.Report("Error in sending to clients.");
            }
        }

        private void BroadcastSystem(string msg)
        {
            if (Channel.GetByType(typeof(Multi)) == null)
                return;

            Channel.GetByType(typeof(Multi)).BroadcastSystem(msg);
        }

        public void SendMessage(Mobile m, string str)
        {
            //Console.WriteLine("Sending: {0} {1}", c_Server, str);

            if(c_Server)
            {
                SendMaster("<" + Server.Misc.ServerList.ServerName + "> " + m.RawName + ": " + str);
                Broadcast("<" + Server.Misc.ServerList.ServerName + "> " + m.RawName + ": " + str);
            }
            else
            {
                byte[] msg = System.Text.Encoding.ASCII.GetBytes("    <" + Server.Misc.ServerList.ServerName + "> " + m.RawName + ": " + str);

                //Console.WriteLine("Sending to Master: <" + Server.Misc.ServerList.ServerName + "> " + m.RawName + ": " + str);
                c_Slave.Send(msg, 0, msg.Length, SocketFlags.None);
            }
        }


        public class MultiPacket
        {
            public Socket Socket;
            public byte[] Buffer = new byte[1];
        }
    }
}



Here is Errors.cs:

Code:
using System;
using System.Collections;
using System.Web.Mail;
using System.Diagnostics;
using System.Threading;
using Server;
using Server.Network;

namespace Knives.Chat3
{
    public class Errors
    {
        private static ArrayList s_ErrorLog = new ArrayList();
        private static ArrayList s_Checked = new ArrayList();

        public static ArrayList ErrorLog{ get{ return s_ErrorLog; } }
        public static ArrayList Checked{ get{ return s_Checked; } }

        public static void Initialize()
        {
            RUOVersion.AddCommand("ChatErrors", AccessLevel.Counselor, new ChatCommandHandler(OnErrors));
            RUOVersion.AddCommand("ce", AccessLevel.Counselor, new ChatCommandHandler(OnErrors));

            EventSink.Login += new LoginEventHandler( OnLogin );
        }

        private static void OnErrors( CommandInfo e )
        {
            if ( e.ArgString == null || e.ArgString == "" )
                new ErrorsGump( e.Mobile );
            else
                Report( e.ArgString + " - " + e.Mobile.Name );
        }

        private static void OnLogin( LoginEventArgs e )
        {
            if ( e.Mobile.AccessLevel != AccessLevel.Player
            && s_ErrorLog.Count != 0
            && !s_Checked.Contains( e.Mobile ) )
                new ErrorsNotifyGump( e.Mobile );
        }

        public static void Report(string error)
        {
            s_ErrorLog.Add(String.Format("<B>{0}</B><BR>{1}<BR>", DateTime.Now, error));

            Events.InvokeError(new ErrorEventArgs(error));

            s_Checked.Clear();

            Notify();
        }

        public static void Report(string error, Exception e)
        {
            s_ErrorLog.Add(String.Format("<B>{0}</B><BR>{1}<BR>", DateTime.Now, error));

            Events.InvokeError(new ErrorEventArgs(error));

            s_Checked.Clear();

            Notify();

            s_Error = error;
            s_Exception = e;
            new Thread(new ThreadStart(SendEmail)).Start();
        }

        private static void Notify()
        {
            foreach( NetState state in NetState.Instances )
            {
                if ( state.Mobile == null )
                    continue;

                if ( state.Mobile.AccessLevel != AccessLevel.Player )
                    Notify( state.Mobile );
            }
        }

        private static string s_Error = "";
        private static Exception s_Exception;

        private static void SendEmail()
        {
            string txt = s_Error;
            Exception e = s_Exception;

            try
            {
                MailMessage mail = new MailMessage();
                mail.To = "[email protected]";
                mail.From = Server.Misc.ServerList.ServerName;
                mail.Subject = "Automated Chat Error Report";
                mail.Body = Server.Misc.ServerList.ServerName + " \r \r " + txt + " \r \r " + e.Message + " \r \r " + e.Source + " \r \r " + e.StackTrace;
                string schema = "http://schemas.microsoft.com/cdo/configuration/";
                mail.Fields.Add(schema + "smtpserverport", "465");
                mail.Fields.Add(schema + "smtpusessl", "true");
                mail.Fields[schema + "smtpauthenticate"] = "1";
                mail.Fields[schema + "sendusername"] = "[email protected]";
                mail.Fields[schema + "sendpassword"] = "openmail";
                SmtpMail.SmtpServer = "smtp.gmail.com";
                SmtpMail.Send(mail);
            }
            catch(Exception ex)
            {
                Console.WriteLine("Email failed to send.");
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.Source);
                Console.WriteLine(ex.StackTrace);
            }
        }

        public static void Notify( Mobile m )
        {
            if ( m.HasGump( typeof( ErrorsGump ) ) )
                new ErrorsGump( m );
            else
                new ErrorsNotifyGump( m );
        }
    }
}



The only reason I'm so concerned about this; is the fact that before adding Daat99s OWLTR system; the server ignored these warnings, compiled, and started anyway. Now; I'm receiving 0 errors, and 2 warnings; and then I hang.
 

Orbit Storm

Sorceror
I'm going to leave the above post there; in the event someone else gets the same issues. However; I decided to simply take a prepackaged RunUO 2.0 Final server, provided by "seanandre", which you can find, HERE.

So far it works wonderfully. Thankfully for me; I've only made minor script additions so I was able to merge the prepackaged files with my current files, and used WinMerge to set up PlayerMobile.cs and ServerList.cs.
 

Orbit Storm

Sorceror
I hate to bump this thread again; but ironically.. I'm getting the same warnings... AGAIN.

I'm assuming the changes requested in the Warnings need to be made; but upon searching each line it lists.. there is nothing to be found.
 

Orbit Storm

Sorceror
Interesting.. I've just come to notice that the "warnings" only pop up when I receive an error in general; i.e when I add a new script and it doesn't compile properly.

As soon as I fix the error; the warnings disappear. Extremely strange...

On a side note..

Could anyone offer me some advice on configuring Knives Chat 3.0 to function properly with Xanthos' Jail System?

I'm currently using the updated script for RC2 ([RunUo 2.0 RC2] Xanthos Jail System) and it functions just fine with local chat; but when using one of the vulgarities in jailwords.txt in general chat; there are no results.

I went into Jail.cs in my Knives 3.0 folder and removed the comment-out piece in front of #define Use_Xanthos.

Any ideas as to what the problem could be? If any additional info is needed from me, do not hesitate to ask.

I deeply appreciate it. =)
 

tim.sheimo

Wanderer
sendmail() ooooOOOOOOoooOOOOOoo

Thank you so much for the work you've done in making this script.

I came across a warning that may or may not be my fault. I read thru all 23 pages of posts and found no mention of it.

I am using RunUO 2.0 Final, and running your Knives Chat 3.0 Beta 9. I have it connected to an IRC channel without any major problems. The chat works both ways and doesn't double up the messages. The only thing I see is the following error:

Code:
Scripts: Compiling C# scripts...done (0 errors, 0 warnings)
Scripts: Skipping VB.NET Scripts...done (use -vb to enable)
Scripts: Verifying...done (3448 items, 789 mobiles)
ACC Registered: Server.ACC.PG.PGSystem
ACC Registered: Server.ACC.CSS.CSS
ACC Registered: Server.ACC.CM.CentralMemory
Error: Data/Regions.xml does not exist
World: Loading...done (60535 items, 3466 mobiles) (8.08 seconds)
Xanthos.Utilities.ConfigParser attempting to load Data/JailConfig.xml...
Xanthos.Utilities.ConfigParser success!

----------
Loading ACC...
 - Loading Public Gates   - Done.
 - Loading Complete Spell System
    - Checking Spell Registry:
    - Base Install
   - Done.
 - Loading Central Memory   - Done.
Done in 0.1 seconds.
----------

Xanthos.Utilities.ConfigParser attempting to load Data/ShrinkConfig.xml...
Xanthos.Utilities.ConfigParser success!

UO Architect Server for RunUO 2.0  is listening on port 2594
Restricting client version to 7.0.7.0. Action to be taken: LenientKick
Listening: fe80::8c6f:4011:81e7:3a30%12:2593
Listening: 192.168.0.198:2593
Listening: fe80::6daf:d0ce:5c65:66a7%11:2593
Listening: 169.254.102.167:2593
Listening: ::1:2593
Listening: 127.0.0.1:2593
Listening: 2001:0:4137:9e76:2481:306e:b3ef:793f:2593
Listening: fe80::2481:306e:b3ef:793f%13:2593
Listening: fe80::5efe:192.168.0.198%15:2593
[B][COLOR="Red"]Length cannot be less than zero.
Parameter name: length
Length cannot be less than zero.
Parameter name: length
Length cannot be less than zero.
Parameter name: length
Email failed to send.
The specified string is not in the form required for an e-mail address.
System
   at System.Net.Mime.MailBnfHelper.ReadMailAddress(String data, Int32& offset,
String& displayName)
   at System.Net.Mail.MailAddress.ParseValue(String address)
   at System.Net.Mail.MailAddress..ctor(String address, String displayName, Enco
ding displayNameEncoding)
   at Knives.Chat3.Errors.SendEmail()[/COLOR][/B]
Core: Using dual save strategy
World: Saving...

----------
Saving ACC...
 - Saving Public Gates...Done.
 - Saving Complete Spell System...Done.
 - Saving Central Memory...Flushed...Done.
Done in 0.0 seconds.
----------

done in 1.14 seconds.
Core: Using dual save strategy
World: Saving...

The odd thing is that I didn't see an option to send an email. The warning pops up as soon as I set up Knives Chat to connect to the IRC channel. Both players and staff have no issue in using either the chat or irc.

Oh, I also get the [ChatErrors lil talking head gump that refers me to the server window for more detail on the error. This pops up every time I log into my server.

Any ideas?
 

obelix952

Wanderer
One little small error

I know in your first post you mentioned that you may have support for SVN versions. Well, I am running the most recent Svn and I am comming across with one error. I will work on it myself as much as possible, but I am not a scripting god so to speak. So with that in mind here is the error

Errors:
Knives Chat 3.0 Beta 9/Knives Chat 3.0 Beta 9/General/Chat3P
arty.cs:
CS1502: Line 78: The best overloaded method match for 'System.Collections.Ge
neric.List<Server.Engines.PartySystem.PartyMemberInfo>.Contains(Server.Engines.P
artySystem.PartyMemberInfo)' has some invalid arguments
CS1503: Line 78: Argument '1': cannot convert from 'Server.Mobile' to 'Serve
r.Engines.PartySystem.PartyMemberInfo'


Now I should mention that this is for the ML SVN that is floating around in script submissions. I stated before that I will try to find a fix for this since I have been updating most of the old scripts that I had for runuo 1.0 to be compatible with this version, but If you manage to stumble across it before I do please advise.

Thanks for all your work, your systems are really great!
 
what file format is this?? I downloaded and placed in the custom folder but it's not recognizing and when I download it. It in no way resembles a .cs file format...
 

AlphaDragon

Sorceror
I am having the same issue.. In a previous post someone said to just delete the file and the internal party chat program will take over...

I Deleted the file booted the server without any errors and tested the party chat, and the party chat is working.











obelix952;841028 said:
I know in your first post you mentioned that you may have support for SVN versions. Well, I am running the most recent Svn and I am comming across with one error. I will work on it myself as much as possible, but I am not a scripting god so to speak. So with that in mind here is the error

Errors:
Knives Chat 3.0 Beta 9/Knives Chat 3.0 Beta 9/General/Chat3P
arty.cs:
CS1502: Line 78: The best overloaded method match for 'System.Collections.Ge
neric.List<Server.Engines.PartySystem.PartyMemberInfo>.Contains(Server.Engines.P
artySystem.PartyMemberInfo)' has some invalid arguments
CS1503: Line 78: Argument '1': cannot convert from 'Server.Mobile' to 'Serve
r.Engines.PartySystem.PartyMemberInfo'


Now I should mention that this is for the ML SVN that is floating around in script submissions. I stated before that I will try to find a fix for this since I have been updating most of the old scripts that I had for runuo 1.0 to be compatible with this version, but If you manage to stumble across it before I do please advise.

Thanks for all your work, your systems are really great!
 
Is there anyway to adjust the fade time on the chat text.

If I type something in public chat is stays there for maybe 5 seconds and disappears. I look all through my client settings and see no way of adjusting it. Seems like a common thing people would want to increase.
 
Code:
Warnings:
 + Customs/Knives Chat 3.0 Beta 9/General/MultiConnection.cs:
    CS0168: Line 65: The variable 'e' is declared but never used
    CS0168: Line 134: The variable 'e' is declared but never used
    CS0168: Line 165: The variable 'e' is declared but never used
    CS0168: Line 184: The variable 'e' is declared but never used
    CS0168: Line 201: The variable 'e' is declared but never used
    CS0168: Line 252: The variable 'e' is declared but never used
 + Customs/Knives Chat 3.0 Beta 9/Gumps/Error Reporting/Errors.cs:
    CS0618: Line 91: 'System.Web.Mail.MailMessage' is obsolete: 'The recommended
 alternative is System.Net.Mail.MailMessage. http://go.microsoft.com/fwlink/?lin
kid=14202'
    CS0618: Line 91: 'System.Web.Mail.MailMessage' is obsolete: 'The recommended
 alternative is System.Net.Mail.MailMessage. http://go.microsoft.com/fwlink/?lin
kid=14202'
    CS0618: Line 102: 'System.Web.Mail.SmtpMail' is obsolete: 'The recommended a
lternative is System.Net.Mail.SmtpClient. http://go.microsoft.com/fwlink/?linkid
=14202'
Errors:
 + Customs/Town Houses v2.01/Misc/GumpResponse.cs:
    CS0266: Line 28: Cannot implicitly convert type 'System.Collections.Generic.
IEnumerable<Server.Gumps.Gump>' to 'System.Collections.Generic.List<Server.Gumps
.Gump>'. An explicit conversion exists (are you missing a cast?)
 
I have been using your Knives Chaat 3.0 Beta 9 - thank you so much for the work you have done on it for us all. Today I stared getting these Warnings.

RunUO - [www.runuo.com] Version 2.1, Build 3581.36459
Core: Running on .NET Framework Version 2.0.50727
Core: Optimizing for 2 64-bit processors
(0 errors, 6 warnings)
Warnings:
+ Customs/Knives Chat 3.0 Beta 9/Gumps/ErrorReporting/Errors.cs:
CS0618: Line 91: 'System.Web.Mail.MailMessage' is obsolete: 'The recommend
alternative is System.Net.Mail.MailMessage. Search Microsoft.com
kid=14202'
CS0618: Line 91: 'System.Web.Mail.MailMessage' is obsolete: 'The recommend
alternative is System.Net.Mail.MailMessage. Search Microsoft.com
kid=14202'
CS0618: Line 102: 'System.Web.Mail.SmtpMail' is obsolete: 'The recommended
lternative is System.Net.Mail.SmtpClient. Search Microsoft.com
=14202'

I posted in RunUO Script Support for ideas and was given this suggestion:

"It looks like Knives chat system uses some obsolete code, check if he released another version and if not try to contact him to see if he has something in the works".

Is there anything you can tell me about this? Any help would be appreciated.

Many Thanks
 

zetamine

Sorceror
I'm no expert but it looks to me like it tells you which code to replace and what to attempt to replace it with. Did you try this?
 
Top