Pryaxis / TShock

☕️⚡️TShock provides Terraria servers with server-side characters, anti-cheat, and community management tools.
GNU General Public License v3.0
2.41k stars 377 forks source link

Error when using TSPlayer.Server.SpawnNPC() under the legendary difficulty map #2842

Closed ATFGK closed 1 year ago

ATFGK commented 1 year ago

System.NullReferenceException: Object reference not set to an instance of an object. at Terraria.NPC.NewNPC(IEntitySource source, Int32 X, Int32 Y, Int32 Type, Int32 Start, Single ai0, Single ai1, Single ai2, Single ai3, Int32 Target) at TShockAPI.TSServerPlayer.SpawnNPC(Int32 type, String name, Int32 amount, Int32 startTileX, Int32 startTileY, Int32 tileXRange, Int32 tileYRange)

sgkoishi commented 1 year ago

Both /spawnboss and /spawnmob are set to AllowServer = false, I guess you should not spawn it as the server player?

ATFGK commented 1 year ago

var sonnpc = TShock.Utils.GetNPCById(spawn.Key); if (sonnpc != null) if (sonnpc.type != 113 && sonnpc.type != 0 && sonnpc.type < Terraria.ID.NPCID.Count) TSPlayer.Server.SpawnNPC(sonnpc.type, sonnpc.FullName, amount, npc.position.ToTileCoordinates().X,npc.position.ToTileCoordinates().Y, 15, 15);

This is my code. I don't know what the problem is. It only reports errors on the Legend Map......

sgkoishi commented 1 year ago

Unable to reproduce, tested on journey, expert and master and getfixedboi seed master mode.

Commands.ChatCommands.Add(new Command((_) =>
{
    Console.WriteLine("Triggered");
    var npc = TShock.Utils.GetNPCById(1);
    TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, 1, npc.position.ToTileCoordinates().X, npc.position.ToTileCoordinates().Y, 15, 15);
}, "testcmd"));
ATFGK commented 1 year ago

Legend difficulty map is a new mode of version 144, which is actually Master+FTW. ` using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using TShockAPI; using Terraria; using TerrariaApi.Server; using Microsoft.Xna.Framework; using System.Timers;

namespace TestPlugin { [ApiVersion(2, 1)] public class TestPlugin : TerrariaPlugin { public override string Author => "aaa"; public override string Description => "Test"; public override string Name => "TestTestTest"; public override Version Version => new Version(1, 0, 0, 1);

    public TestPlugin(Main game) : base(game)
    {
        Order = 1000;
    }
    public override void Initialize()
    {
        Update.Elapsed += OnUpdate;
        Update.Start();
        ServerApi.Hooks.GameInitialize.Register(this, OnInitialize);
    }
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Update.Elapsed -= OnUpdate;
            Update.Stop();
            ServerApi.Hooks.GameInitialize.Deregister(this, OnInitialize);
        }
        base.Dispose(disposing);
    }
    private void OnInitialize(EventArgs args)
    {

        Commands.ChatCommands.Add(
           new Command("", CRP, "aaa")
           {
               HelpText = "/aaa"
           });
    }
    private void CRP(CommandArgs args)
    {

        x = args.Player.TPlayer.position.ToTileCoordinates().X;
        y = args.Player.TPlayer.position.ToTileCoordinates().Y;
        s = 535;

        //try
        //{
        //    var sonnpc = TShock.Utils.GetNPCById(535);
        //    if (sonnpc != null)
        //        if (sonnpc.type != 113 && sonnpc.type != 0 && sonnpc.type < Terraria.ID.NPCID.Count)
        //            TSPlayer.Server.SpawnNPC(sonnpc.type, sonnpc.FullName, 1, args.Player.TPlayer.position.ToTileCoordinates().X, args.Player.TPlayer.position.ToTileCoordinates().Y, 15, 15);

        //}
        //catch (Exception ex)
        //{

        //    TShock.Log.ConsoleError("A:" + ex.ToString());
        //}//No problem here

        args.Player.SendSuccessMessage("++++" );
    }

    public static bool ULock = false;
    public static int x = 0;
    public static int y = 0;
    public static int s = 0;

    static readonly System.Timers.Timer Update = new System.Timers.Timer(1000);
    public void OnUpdate(object sender, ElapsedEventArgs e)
    {
        if (ULock) return;
        ULock = true;
        if(s>0)
        {
            try
            {
                var sonnpc = TShock.Utils.GetNPCById(s);
                if (sonnpc != null)
                    if (sonnpc.type != 113 && sonnpc.type != 0 && sonnpc.type < Terraria.ID.NPCID.Count)
                        TSPlayer.Server.SpawnNPC(sonnpc.type, sonnpc.FullName, 1, x, y, 15, 15);
                s = 0;
            }
            catch (Exception ex)
            {

                TShock.Log.ConsoleError("A:" + ex.ToString());//
            }

        }
        ULock = false;
    }

}

}

` This code only reports errors on legendary difficulty.

sgkoishi commented 1 year ago

You are calling it from Timer.Elapsed and the Main.rand is null because it is thread-local (marked as [ThreadStatic]), which means its value does not share across threads.

You can simply fix it by adding this to the start of your OnUpdate:

if (Main.rand == null)
{
    Main.rand = new UnifiedRandom();
}

Or, I would suggest using the game's Update hook (which is 60 ticks/frames/updates per second) instead of a Timer

ATFGK commented 1 year ago

You are calling it from Timer.Elapsed and the Main.rand is null because it is thread-local (marked as [ThreadStatic]), which means its value does not share across threads.

You can simply fix it by adding this to the start of your OnUpdate:

if (Main.rand == null)
{
  Main.rand = new UnifiedRandom();
}

Wow, it finally works. Thank you for your help