What's new
  • Visit Rebornbuddy
  • Visit Panda Profiles
  • Visit LLamamMagic
  • Visit Resources
  • Visit Downloads
  • Visit Portal

Re: GetObjectsByType

Dgc2002

Member
Joined
Jan 15, 2010
Messages
197
Reaction score
0
I'm pretty new to C# and very new to dealing with any of the buddy bots in a development sense. I'm attempting to keep an updated list of the monsters using:

Code:
LokiPoe.ObjectManager.GetObjectsByType<Monster>().Where(m => m.IsActive).OrderBy(m => m.Distance).ToList();

My issue is that this list does seem to update the way I'm using it. The size of the list updates regularly as expected. However the information in the list does not seem to change for me. Specifically the location of the monster's in the list does not change.

Code:
      monsters = LokiPoe.ObjectManager.GetObjectsByType<Monster>().Where(m => m.IsActive).OrderBy(m => m.Distance).ToList();
            

      foreach (Monster test in monsters)
      {
          mobPos = Loki.Game.CoordinateExtensions.WorldToMap(test.WorldPosition);
         
          Log.Debug("X: " + mobPos.X + " Y: " + mobPos.Y);
      }

This is a snippet to show roughly how I'm attempting to use this. Every time I call this the x and y will be the same for each mob already in the list. Any input? Do I not understand lists?
 
Last edited:
You can use the .Position property rather than going though the CoordinateExtensions.WorldToMap function.

How are you calling that code, and from where? E.g., a plugin, a CR, a bot. Are you doing it from your own thread, or is it inside a Tick function.
 
Ty for the position tip. Atm I'm just digging through the object browser trying to find relevant things.

I'm calling this in a plugin. I'm currently creating a form then in the plugin's Tick method I'm calling a method I've called Poll inside the form. All this method does is update information such as this list. I'm away from my computer right now or I'd do some tests based on what you've said.

Thanks for the response :) I've been away from OOP for quite a while now so I would be surprised if that was the cause of my problem ;)
 
If you were calling API stuff from a thread, then you would see the problem you described, but everything should be working normally from inside a plugin's Tick function.

Here's a modified ExamplePlugin.cs file you can run with the bot that outputs all the mobs and their name/id/position every 5s. Might be a little annoying to read the debug output with all the other stuff being spammed, but you should be able to open the log file and check ids and stuff and see things are moving around.
Code:
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using log4net;
using System;
using System.Linq;
using Loki.Bot;
using Loki.Game;
using Loki.Utilities;
using System.Collections.Generic;
using System.Windows.Documents;
using System.Diagnostics;
using Loki.Game.Objects;

namespace ExamplePlugin
{
    internal class ExamplePlugin : IPlugin
    {
        private static readonly ILog Log = Logger.GetLoggerInstanceForType();

        // Do not implement a ctor and do stuff in it.

        #region Implementation of IBase

        /// <summary>Initializes this plugin.</summary>
        public void Initialize()
        {
            Log.DebugFormat("[ExamplePlugin] Initialize");

            // To see if there are specific arguments passed to the program, you can do something like this.
            if (CommandLine.Arguments.Exists("myarg1"))
            {
                var myarg1 = CommandLine.Arguments.Single("myarg1");
                Log.DebugFormat("myarg1: {0}", myarg1);
            }

            ScriptManager.Initialize(null, new List<string> {"Loki.Game"});
        }

        #endregion

        #region Implementation of IAuthored

        /// <summary> The name of the plugin. </summary>
        public string Name
        {
            get { return "ExamplePlugin"; }
        }

        /// <summary> The description of the plugin. </summary>
        public string Description
        {
            get { return "An example plugin for Exilebuddy."; }
        }

        /// <summary>The author of the plugin.</summary>
        public string Author
        {
            get { return "Bossland GmbH"; }
        }

        /// <summary>The version of the plugin.</summary>
        public Version Version
        {
            get { return new Version(0, 0, 1, 1); }
        }

        #endregion

        #region Implementation of IRunnable

        /// <summary> The plugin start callback. Do any initialization here. </summary>
        public void Start()
        {
            Log.DebugFormat("[ExamplePlugin] Start");
        }

		Stopwatch _displayStopwatch = Stopwatch.StartNew();
		
        /// <summary> The plugin tick callback. Do any update logic here. </summary>
        public void Tick()
        {
			if(_displayStopwatch.ElapsedMilliseconds > 5000)
			{
				foreach(var monster in LokiPoe.ObjectManager.GetObjectsByType<Monster>().Where(m=>m.IsActive).OrderBy(m=>m.Distance))
				{
					Log.DebugFormat("[{2}] {0} {1}", monster.Name, monster.Position, monster.Id);
				}
				_displayStopwatch.Restart();
			}
        }

        /// <summary> The plugin stop callback. Do any pre-dispose cleanup here. </summary>
        public void Stop()
        {
            Log.DebugFormat("[ExamplePlugin] OnStop");
        }

        #endregion

        #region Implementation of IConfigurable

        /// <summary> The routine's settings control. This will be added to the Exilebuddy Settings tab.</summary>
        public UserControl Control
        {
            get
            {
                using (var fs = new FileStream(@"Plugins\ExamplePlugin\SettingsGui.xaml", FileMode.Open))
                {
                    var root = (UserControl) XamlReader.Load(fs);

                    // TODO: Your settings binding here.

                    // TODO: Your settings event handlers here.

                    var configButton = Wpf.FindControlByName<Button>(root, "ConfigButton");
                    configButton.Click += ConfigButtonOnClick;

                    var executePythonButton = Wpf.FindControlByName<Button>(root, "ExecutePythonButton");
                    executePythonButton.Click += ExecutePythonButtonOnClick;

                    _pyInputRichTextBox = Wpf.FindControlByName<RichTextBox>(root, "PyInputRichTextBox");

                    return root;
                }
            }
        }

        /// <summary>The settings object. This will be registered in the current configuration.</summary>
        public JsonSettings Settings
        {
            get { return ExamplePluginSettings.Instance; }
        }

        #endregion

        private RichTextBox _pyInputRichTextBox;

        private void ExecutePythonButtonOnClick(object sender, RoutedEventArgs routedEventArgs)
        {
            if (_pyInputRichTextBox == null)
            {
                Log.ErrorFormat("[ExecutePythonButtonOnClick] _pyInputRichTextBox == null");
                return;
            }

            var myText =
                new TextRange(_pyInputRichTextBox.Document.ContentStart, _pyInputRichTextBox.Document.ContentEnd).Text;

            ScriptManager.GetStatement(myText)();
        }

        private void ConfigButtonOnClick(object sender, RoutedEventArgs routedEventArgs)
        {
            new WinForms().ShowDialog();
        }

        #region Implementation of IEnableable

        /// <summary> The plugin is being enabled.</summary>
        public void OnEnable()
        {
            Log.DebugFormat("[ExamplePlugin] OnEnable");

            // Hooks us up into the main bot gui.
            LokiPoe.OnGuiTick += OnGuiTick;
        }

        /// <summary> The plugin is being disabled.</summary>
        public void OnDisable()
        {
            Log.DebugFormat("[ExamplePlugin] OnDisable");

            // Removes us from the main bot gui.
            LokiPoe.OnGuiTick -= OnGuiTick;
        }

        #endregion

        #region Implementation of IDisposable

        /// <summary> </summary>
        public void Dispose()
        {
        }

        #endregion

        #region Override of Object

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return Name + ": " + Description;
        }

        #endregion

        //private bool _start = true;
        //private bool _stop;
        //private Stopwatch sw = new Stopwatch();

        private void OnGuiTick(object sender, GuiTickEventArgs guiTickEventArgs)
        {
            var window = guiTickEventArgs.Window;

            Log.DebugFormat("[ExamplePlugin] OnGuiTick | {0} | {1}", window.Title, Environment.TickCount);

            // This shows an example of switching routines from the pluign. The bot must be stopped first.
            /*if (!BotManager.IsRunning)
            {
                var nr = RoutineManager.Routines.FirstOrDefault(r => r.Name == "NullRoutine");
                RoutineManager.CurrentRoutine = nr;
            }*/

            // This shows an example of starting and stopping the bot on demand. This is similar to how
            // it was done before, except this is from the gui thread, and not the main bot thread.
            /*if (_start)
            {
                if (!BotManager.IsRunning)
                {
                    BotManager.Start();
                    _start = false;
                    _stop = true;
                    sw.Restart();
                }
            }*/

            /*if (_stop)
            {
                if (sw.IsRunning && sw.ElapsedMilliseconds > 10000)
                {
                    if (BotManager.IsRunning)
                    {
                        BotManager.Stop();
                        _stop = false;
                    }
                }
            }*/
        }
    }
}

When it comes to objects and caching, things work a little awkwardly with our API because of how volatile the memory in this game is. Typically, you want to store a monster's id, and then get the object by id from the object manager (check for null) before using it. If you store references to objects, it's very possible they are now invalid, or are accessing cached data.
 
Last edited:
Thanks for the help! After some messing around I was unable to get it to work as it was. It was strange because in a bunch of test everything else acted normally. I ended up just making a list of positions and referencing that instead which worked fine. This is what I was trying to do anyway:

[video=youtube;nzRE1BIuZgA]https://www.youtube.com/watch?v=nzRE1BIuZgA[/video]

I was accessing the list from within the paint method of my panel. Could that have something to do with it? Also, if you don't mind me asking, what framework/library did you use for the old map visualizer? I toyed around with some opengl libraries but couldn't find one that I felt comfortable committing to.

Thanks again pushedx.
 
I was accessing the list from within the paint method of my panel. Could that have something to do with it? Also, if you don't mind me asking, what framework/library did you use for the old map visualizer? I toyed around with some opengl libraries but couldn't find one that I felt comfortable committing to.

Yes, you cannot share objects outside of the main bot thread, and using the API from other threads will result in undefined behavior if it's outside a framelock. Copying the data you need and storing that is the correct thing to do. :)

Apoc actually wrote the map visualizer and he used HelixToolkit. Since it's included with the bot, you can use that library as well for your stuff by referencing the assembly we use. The actual map visualizer was in need of some serious updates, so it's not made its way back into the project yet. The biggest problem with what it did, was use the API from another thread, which was causing all sorts of crashes and other issues.
 
Back
Top