Full Version : QuestNPC + SkillCheck crash...
xmlspawner >>Troubleshooting >>QuestNPC + SkillCheck crash...
<< Prev | Next >>
Galfaroth- 01-27-2006
Today I've started to make quests on my shard... and according to http://www.ageofdarkstone.com/txt/questtut...st4Dummies.html this tutorial I'm stuck in place when I say: Hi to npc. When I do this, my server crashes issuing error:

CODE
Exception: System.NullReferenceException: Object reference not set to an instance of an object. at Server.Scripts.Commands.unattendedMacroGump..ctor(Mobile from, Mobile m) at Server.Scripts.Commands.JailSystem.macroTest(Mobile from, Mobile badBoy)
at Server.Misc.SkillCheck.CheckSkill(Mobile from, Skill skill, Object amObj, Double chance)
at Server.Misc.SkillCheck.Mobile_SkillCheckTarget(Mobile from, SkillName skillName, Object target, Double minSkill, Double maxSkill)
at Server.Mobile.CheckTargetSkill(SkillName skill, Object target, Double minSkill, Double maxSkill)
at Server.SkillHandlers.InternalTarget.OnTarget(Mobile from, Object targeted)
at Server.Targeting.Target.Invoke(Mobile from, Object targeted)
at Server.Network.PacketHandlers.TargetResponse(NetState state, PacketReader pvSrc)
at Server.Network.MessagePump.HandleReceive(NetState ns)
at Server.Network.MessagePump.Slice()
at Server.Core.Main(String[] args)

Here is part of my SkillCheck.cs script:

CODE
public static bool CheckSkill( Mobile from, Skill skill, object amObj, double chance ) { if ( from is PlayerMobile && AntiMacroCode && UseAntiMacro[skill.Info.SkillID] ) { switch (Utility.Random( 50 ))
{
case 0:
{
JailSystem.macroTest( from, from );
break;
}
case 1:
{ break; }
}
}
if ( from.Skills.Cap == 0 )
return false;
bool success = ( chance >= Utility.RandomDouble() );
double gc = (double)(from.Skills.Cap - from.Skills.Total) / from.Skills.Cap;
gc += ( skill.Cap - skill.Base ) / skill.Cap;
gc /= 10;//bylo 2
gc += ( 1.0 - chance ) * ( success ? 0.5 : 0.2 );
//gc /= 2; //Makes it twice as hard to gain
//gc /= 3; //3X as hard to gain
//gc /= 0.5; //Twice as easy to gain
gc *= skill.Info.GainFactor; //Pulls the Gainfactor info from above section
if ( skill.Base > 35.0 )
gc /= 1.5;
else if ( skill.Base > 50.0 )
gc /= 2;
else if ( skill.Base > 70.0 )
gc /= 3;
else if ( skill.Base > 85.0 )
gc /= 4;
else if ( skill.Base > 95.0 )
gc /= 5;
if ( gc < 0.01 )
gc = 0.01;
if ( from.Alive && ( ( gc >= Utility.RandomDouble() && AllowGain( from, skill, amObj ) ) || skill.Base < 10.0 ) )
Gain( from, skill );
return success;
}

What should I do to make XMLQuestNPC properly work?
ArteGordon- 01-27-2006
post the scripts that contain these
at Server.Scripts.Commands.unattendedMacroGump..ctor(Mobile from, Mobile m)
at Server.Scripts.Commands.JailSystem.macroTest(Mobile from, Mobile badBoy)
the macroTest method and the unattendedMacroGump constructor
Galfaroth- 01-27-2006
It's part of Cat's Jail system, very long script:
Here is link: http://www.runuo.com/forums/showthread.php...ht=cat%27s+jail
ArteGordon- 01-27-2006
I'll take a look
(edit)
ok, I'm guessing that the problem is here

CODE
((Account)m.Account).Comments.Add(new AccountComment(JailSystem.JSName + "-warning", from.Name + " checked to see if " + m.Name + " was macroing unattended on: " + DateTime.Now ));

If it gets a mobile that doesnt have an account, then this
((Account)m.Account)
will be null and so this
((Account)m.Account).Comments
will fail.
I would add checks before that line like this

QUOTE
if (m == null || m.Account == null || from == null) return; ((Account)m.Account).Comments.Add(new AccountComment(JailSystem.JSName + "-warning", from.Name + " checked to see if " + m.Name + " was macroing unattended on: " + DateTime.Now ));

If you ran the server with the "-debug" argument, it would give the precise line numbers where it crashed, but that is probably the problem.
(edit)
I have no idea what this has to do with the quest. Are you sure it is related?
Galfaroth- 01-27-2006
Still crash when saying sth related in XMLEdit to XMLQuestNpc. Tomorrow I will give you debugged crash report.
ArteGordon- 01-27-2006
the only other possibility for that error in that constructor is if the Comment list is also null. You could check for that with

QUOTE
if (m == null || m.Account == null || from == null || ((Account)m.Account).Comments == null) return; ((Account)m.Account).Comments.Add(new AccountComment(JailSystem.JSName + "-warning", from.Name + " checked to see if " + m.Name + " was macroing unattended on: " + DateTime.Now ));

When the npc is saying 'Hi', it is causing some script to activate targeting of some sort, and it is that targeting that is crashing. Are you using some macroing program like Razor or EasyUO perhaps?
The npcs generate actual speech, so that may be fooling those programs into thinking it is a player that is speaking.
Galfaroth- 01-28-2006

CODE
Exception: System.NullReferenceException: Object reference not set to an instance of an object. at Server.SpeechLogging.EventSink_Speech(SpeechEventArgs e) in d:\Ultima Online\Tools\WoG(Moj)\Scripts\WOG\Inne\WorldSpeechLogging.cs:line 160 at Server.SpeechEventHandler.Invoke(SpeechEventArgs e)
at Server.Mobile.DoSpeech(String text, Int32[] keywords, MessageType type, Int32 hue)
at Server.Engines.XmlSpawner2.XmlDialog.DelayedSpeech(Object state) in d:\Ultima Online\Tools\WoG(Moj)\Scripts\WOG\NPC\XMLSpawner\XmlAttachments\XmlDialog.cs:line 1139
at Server.DelayStateCallTimer.OnTick()
at Server.Timer.Slice()
at Server.Core.Main(String[] args)

Here is WordSpeechLogging.cs

CODE
/** World Speech Logging **/ /* Scripted on 3/3/2005 by Khaz * * For support, please e-mail me at dreki_herra@yahoo.com *
* This script is free to use and modify as you see fit.
* When sharing, please give credit where it is due.
*/
using System;
using System.IO;
using Server;
using Server.Accounting;
using Server.Mobiles;
using Server.Network;
namespace Server
{
public class SpeechLogging
{
private static StreamWriter m_toWrite;
private static bool m_Enabled = true;
private static bool m_ConsoleEnabled = true;
public static bool Enabled{ get{ return m_Enabled; } set{ m_Enabled = value; } }
public static bool ConsoleEnabled{ get{ return m_ConsoleEnabled; } set{ m_ConsoleEnabled = value; } }
public static StreamWriter toWrite{ get{ return m_toWrite; } }
public static void Initialize()
{
EventSink.Speech += new SpeechEventHandler( EventSink_Speech );
if( !Directory.Exists( "Logs" ) )
Directory.CreateDirectory( "Logs" );
string directory = "Logs/Speech";
if( !Directory.Exists( directory ) )
Directory.CreateDirectory( directory );
Console.Write( "Speech Logging: Loading..." );
try
{
m_toWrite = new StreamWriter( Path.Combine( directory, String.Format( "{0}.log", DateTime.Now.ToLongDateString() ) ), true );
m_toWrite.AutoFlush = true;
m_toWrite.WriteLine( "####################################" );
m_toWrite.WriteLine( "Logging started on {0}", DateTime.Now );
m_toWrite.WriteLine();
Console.WriteLine( "done" );
}
catch
{
Console.WriteLine( "failed" );
}
Commands.Register( "ConsoleListen", AccessLevel.Administrator, new CommandEventHandler( ConsoleListen_OnCommand ) );
}
public static object Format( object o )
{
if( o is Mobile )
{
Mobile m = (Mobile)o;
if( m.Account == null )
return String.Format( "{0} (no account)", m );
else
return String.Format( "{0} ('{1}')", m, ((Account)m.Account).Username );
}
else if( o is Item )
{
Item item = (Item)o;
return String.Format( "0x{0:X} ({1})", item.Serial.Value, item.GetType().Name );
}
return o;
}
public static void WriteLine( Mobile from, string format, params object[] args )
{
WriteLine( from, String.Format( format, args ) );
}
public static void WriteLine( Mobile from, string text )
{
if( !m_Enabled )
return;
try
{
m_toWrite.WriteLine( "{0}: {1}: {2}", DateTime.Now, from.NetState, text );
string path = Core.BaseDirectory;
Account acct = from.Account as Account;
string name = ( acct == null ? from.Name : acct.Username );
AppendPath( ref path, "Logs" );
AppendPath( ref path, "Speech" );
AppendPath( ref path, from.AccessLevel.ToString() );
path = Path.Combine( path, String.Format( "{0}.log", name ) );
using( StreamWriter sw = new StreamWriter( path, true ) )
sw.WriteLine( "{0}: {1}: {2}", DateTime.Now, from.NetState, text );
}
catch
{
}
}
private static char[] m_NotSafe = new char[]{ '\\', '/', ':', '<', '>', '|', '{', '}' };
public static void AppendPath( ref string path, string toAppend )
{
path = Path.Combine( path, toAppend );
if( !Directory.Exists( path ) )
Directory.CreateDirectory( path );
}
public static string Safe( string ip )
{
if ( ip == null )
return "null";
ip = ip.Trim();
if ( ip.Length == 0 )
return "empty";
bool isSafe = true;
for ( int i = 0; isSafe && i < m_NotSafe.Length; ++i )
isSafe = ( ip.IndexOf( m_NotSafe[i] ) == -1 );
if ( isSafe )
return ip;
System.Text.StringBuilder sb = new System.Text.StringBuilder( ip );
for ( int i = 0; i < m_NotSafe.Length; ++i )
sb.Replace( m_NotSafe[i], '_' );
return sb.ToString();
}
public static void EventSink_Speech( SpeechEventArgs e )
{
WriteLine( e.Mobile, "{0}: {1}", Format( e.Mobile ), e.Speech );
if( ConsoleEnabled )
Console.WriteLine( e.Mobile.Name + String.Format( " ({0}): ", ((Account)e.Mobile.Account).Username ) + e.Speech );
}
[Usage( "ConsoleListen <true | false>")]
[Description( "Enables or disables outputting speech to the console." )]
public static void ConsoleListen_OnCommand( CommandEventArgs e )
{
if( e.Length == 1 )
{
m_ConsoleEnabled = e.GetBoolean( 0 );
e.Mobile.SendMessage( "Console speech output has been {0}.", m_ConsoleEnabled ? "enabled" : "disabled" );
Console.WriteLine( "World listen has been {0}.", m_ConsoleEnabled ? "enabled" : "disabled" );
}
else
{
e.Mobile.SendMessage( "Format: ConsoleListen <true | false >" );
}
}
}
}

ArteGordon- 01-28-2006
you just need to check to see that the mobile is a playermobile before trying to process the speech

QUOTE
public static void EventSink_Speech( SpeechEventArgs e ) { if(!(e.Mobile is PlayerMobile)) return; WriteLine( e.Mobile, "{0}: {1}", Format( e.Mobile ), e.Speech ); if( ConsoleEnabled )
Console.WriteLine( e.Mobile.Name + String.Format( " ({0}): ", ((Account)e.Mobile.Account).Username ) + e.Speech );
}

DrussRob- 01-28-2006
I just had something similar happen (on my live server too eek!)
I created the xmlquestnpc and created a dialog on a range of 2, walked up to it and it did this:

CODE
Error: System.InvalidCastException: Specified cast is not valid. at Server.ClericCommands.Speech_Event(SpeechEventArgs e) in c:\DARKSTONE\Scri pts\CUSTOM\Spells\Monk_Spells\Speech.cs:line 20 at Server.SpeechEventHandler.Invoke(SpeechEventArgs e)
at Server.Mobile.DoSpeech(String text, Int32[] keywords, MessageType type, In
t32 hue)
at Server.Engines.XmlSpawner2.XmlDialog.DelayedSpeech(Object state) in c:\DAR
KSTONE\Scripts\XMLSpawner\XmlAttachments\XmlDialog.cs:line 1139
at Server.DelayStateCallTimer.OnTick()
at Server.Timer.Slice()
at Server.Core.Main(String[] args)
Crash: Backing up...done
Crash: Generating report...done
Crash: Restarting...done
Warning:
System.Threading.ThreadAbortException: Thread was being aborted.
at System.Threading.Thread.Sleep(Int32 millisecondsTimeout)
at Server.TimerThread.TimerMain()

Here's the part of the monk spell it's referring to:

CODE
public static void Speech_Event( SpeechEventArgs e ) { PlayerMobile mp = ((PlayerMobile )e.Mobile ); if ( e.Speech.ToLower().IndexOf( "i pray to the gods" ) != -1 ) {
if ( !mp.CheckAlive() )
{
mp.SendMessage( "You cannot pray while dead." );
}
else if ( ((PlayerMobile)mp).Level_Monk >= 1 || ((PlayerMobile)mp).JobType == JobType.God || mp.AccessLevel >= AccessLevel.GameMaster )
{
mp.CloseGump( typeof ( ClericGump ) );
mp.SendGump( new ClericGump( e.Mobile ) );
}
else
{
mp.SendMessage( "Only Monks Can Use These Powers." );
}
}
}

So I added the line you suggested above like this:

CODE
public static void Speech_Event( SpeechEventArgs e ) { PlayerMobile mp = ((PlayerMobile )e.Mobile ); if(!(e.Mobile is PlayerMobile)) return; if ( e.Speech.ToLower().IndexOf( "i pray to the gods" ) != -1 )

But it still crashed again:

CODE
Exception: System.InvalidCastException: Specified cast is not valid. at Server.ClericCommands.Speech_Event(SpeechEventArgs e) in c:\DARKSTONE\Scripts\CUSTOM\Spells\Monk_Spells\Speech.cs:line 21 at Server.SpeechEventHandler.Invoke(SpeechEventArgs e)
at Server.Mobile.DoSpeech(String text, Int32[] keywords, MessageType type, Int32 hue)
at Server.Engines.XmlSpawner2.XmlDialog.DelayedSpeech(Object state) in c:\DARKSTONE\Scripts\XMLSpawner\XmlAttachments\XmlDialog.cs:line 1139
at Server.DelayStateCallTimer.OnTick()
at Server.Timer.Slice()
at Server.Core.Main(String[] args)

So now I'm trying:

CODE
public static void Speech_Event( SpeechEventArgs e ) { PlayerMobile mp = ((PlayerMobile )e.Mobile ); if (!(e.Mobile is PlayerMobile)) return; if (mp == null || mp.Account == null) return;
if ( e.Speech.ToLower().IndexOf( "i pray to the gods" ) != -1 )

I'll tell ya how that pans out in a sec or two...
*Edit* Actually, I'll try next time I restart the server, probably tomorrow. I feel bad for my players having to crash twice now. I dont wanna do it to them again lol
DrussRob- 01-28-2006
I just wanted to add that I know these crashes aren't the fault of the spawner system itself, and wanted to thank you profusely for taking your free time out to help all of us. I wish I knew more as I would be in here helping as well. But I'm learning...
ArteGordon- 01-28-2006
right idea, but you have to make the test earlier so that it doesnt try to do the cast to a PlayerMobile which will fail (because the mobile isnt a PlayerMobile) and cause the crash.

CODE
public static void Speech_Event( SpeechEventArgs e ) { if(!(e.Mobile is PlayerMobile)) return; PlayerMobile mp = ((PlayerMobile )e.Mobile );

DrussRob- 01-28-2006
ahhhhh... I gotcha. cool deal, thx for the pointer.
You know, between godfood, ambak and myself asking all these questions I think we're developing a rather nice little knowledge base here
ArteGordon- 01-28-2006
and these are all questions that others have as well.
Galfaroth- 01-29-2006
Okay thanks Arte! Everything works exept finish of quest... I do everything like in tutorial - quest gives me questholder with description but when I kill Bobo, quest doesn't finish and doesn't give me a reward... It is impossible to complete it? Maybe I missed something at installation?
ArteGordon- 01-29-2006
yes, I think you missed installation step 2 that is needed to track creature kills.

QUOTE
STEP 2: (recommended but not required) To take advantage of the XmlQuestToken killtask keywords KILL and KILLNAMED, one line must be added to the OnDeath method in BaseCreature.cs as described below (note, you dont have to make this mod if you dont want to, the spawner and other items will work just fine without it, the KILL and KILLNAMED features simply wont do anything)
around line 3883 of basecreature.cs change
Titles.AwardKarma( ds.m_Mobile, totalKarma, true );
to
Titles.AwardKarma( ds.m_Mobile, totalKarma, true );
// modification to support XmlQuest Killtasks
XmlQuest.RegisterKill( this, ds.m_Mobile);