Hello everyone, new member here!
I've recently fallen in love with ServUO, after having decided to play some UO after years, since i missed it.
I feel the freedom and mood are still the best around, even when compared to modern soulless themepark mmorpgs.

Long story short, i'm working on some scripts for a custom personal server, and already dug around quite a number of Server and Script files.
Even though i started scripting today (played around with Admin Commands, exploring Props, UOFiddler and other tools for a couple days though),
i think i kind of got the most basic stuff down, including the EventSink (didn't find any tutorials on that one, so i peeked into other scripts
to figure it out).

There is one thing that's giving me a hard time though.


Basically, i'm trying to create a simple script that every x amount of time will check a file, read from it and use this information inside the game.

Ideally, this script would check with real-time accuracy, accurate to the second sounds about right.
Since the script also executes some custom Python code, it takes about 2 seconds for it to finish, and while it's running it completely freezes the Game - very short amount of time, but running this with a one second accuracy with the Timer seems impossible at the moment.

I think the answer is in a separate thread, so i can run it in the background?

Having started today with c#, i'm very much a newbie, so even though i grasp the concept of background workers in c# and multi-threading, i still though it would be much better to ask here, since it's something that has to do with a game, and specifically ServUO.

You guys probably know how to handle a background process so that it doesn't block the Main Program execution and hence freezes it.


I already set up the Timer Checks and stuff, so all that's left is really just to figure out how to have this python code work in the background, most likely in a separate thread.

I'm also already running a separate process, a new instance of Process specifically.
Which is the reason i think the only answer could be setting up some kind of separate thread.



So, would you guys know how to best handle this situation, so that i can execute this custom code in the background without freezing the game?


Here is my current code:

Code:
using System;
using System.IO;
using System.Diagnostics;

namespace Scripts.Custom
{
    
    class PythonBridge
    {
        public static void Main(string args)
        {
            // full path of python interpreter 
            string python = @"D:\Python\Python35\python.exe";

            // python app to call 
            string myPythonApp = @"D:\Python\My_Script.py";

            // dummy parameters to send Python script 
            string tag = args;

            // Create new process start info 
            ProcessStartInfo myProcessStartInfo = new ProcessStartInfo(python);

            // make sure we can read the output from stdout 
            myProcessStartInfo.UseShellExecute = true;
            myProcessStartInfo.RedirectStandardOutput = false;
            myProcessStartInfo.CreateNoWindow = true;
            myProcessStartInfo.WindowStyle = ProcessWindowStyle.Hidden;


            // start python app with 3 arguments  
            // 1st arguments is pointer to itself,  
            // 2nd and 3rd are actual arguments we want to send 
            myProcessStartInfo.Arguments = String.Format("\"{0}\" {1}", myPythonApp, tag);

            Process myProcess = new Process();
            // assign start information to the process 
            myProcess.StartInfo = myProcessStartInfo;

            Console.WriteLine("Calling Python script with tag {0}", tag);
            // start the process 
            myProcess.Start();

            // Read the standard output of the app we called.  
            // in order to avoid deadlock we will read output first 
            // and then wait for process terminate: 
            //StreamReader myStreamReader = myProcess.StandardOutput;
            //string myString = myStreamReader.ReadLine();

            // wait exit signal from the app called and then close it. 
            myProcess.WaitForExit();
            myProcess.Close();

            // write the output we got from python app 
            //Console.WriteLine("Value received from script: " + myString);

            //return myString;

        }
    }
}



As you can also see, there is some commented out code that i'm not using at the moment.

It was an attempt to read the stdoutput from the python process, but i wasn't able to make it work because in order to redirect the output i can't use a Shell, and when the same Shell ServUO uses is used, it completely freezes everything until i close it - as if it were waiting for the ServUO process to end to continue - but if it ends, well.. there is no continuing xD

Also, i already tried removing the WaitForExit(), to solve both the reading stdout problem and the background process problem, to no avail.


I ended up solving the reading stdout problem by simply making the python script dump the output in a txt file and then reading it with the aforementioned checks.



Thank you very much for your time!
 
I haven't been using any forums for a very long time so i don't know how you guys look at double posting, but i think it's important to add that i solved it.

In case anyone googling stumbles on this (if there aren't any other questions like this one, that is), here is how i solved it:
I simply used the ThreadPool Queue.
Make sure you add a System.Threading reference at the start, so you can use the ThreadPool Queue.

Code:
[*]using System;
[*]using System.IO;
[*]using System.Diagnostics;
[*]using System.Threading;
[*]

[*]namespace Scripts.Custom
[*]{
[*]
[*]   class PythonBridge
[*]   {
[*]       public static void Main(string args)
[*]       {
[*]           // full path of python interpreter
[*]           string python = @"D:\Python\Python35\python.exe";
[*]

[*]           // python app to call
[*]           string myPythonApp = @"D:\Python\My_Script.py";
[*]

[*]           // dummy parameters to send Python script
[*]           string tag = args;
[*]

[*]           // Create new process start info
[*]            ProcessStartInfo myProcessStartInfo = new ProcessStartInfo(python);
[*]

[*]           // make sure we can read the output from stdout
[*]            myProcessStartInfo.UseShellExecute = true;
[*]            myProcessStartInfo.RedirectStandardOutput = false;
[*]            myProcessStartInfo.CreateNoWindow = true;
[*]            myProcessStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
[*]

[*]

[*]           // start python app with 3 arguments
[*]           // 1st arguments is pointer to itself,
[*]           // 2nd and 3rd are actual arguments we want to send
[*]            myProcessStartInfo.Arguments = String.Format("\"{0}\" {1}", myPythonApp, tag);
[*]

[*]            Process myProcess = Process();
[*]           // assign start information to the process
[*]            myProcess.StartInfo = myProcessStartInfo;
[*]

[*]            Console.WriteLine("Calling Python script with tag {0}", tag);
[*]           // start the process
[*]            ThreadPool.QueueUserWorkItem(delegate {myProcess = Process.Start(myProcessStartInfo);
[*]

[*]           // Read the standard output of the app we called.
[*]           // in order to avoid deadlock we will read output first
[*]           // and then wait for process terminate:
[*]           //StreamReader myStreamReader = myProcess.StandardOutput;
[*]           //string myString = myStreamReader.ReadLine();
[*]

[*]           // wait exit signal from the app called and then close it.
[*]            myProcess.WaitForExit();
[*]            myProcess.Close();
[*]

[*]           // write the output we got from python app
[*]           //Console.WriteLine("Value received from script: " + myString);
[*]

[*]           //return myString;
[*]	 		});

[*]       }
[*]   }
[*]}


The code is a little messed up because reasons but you should get the idea.


If you guys have a solution that better fits ServUO or is kind of a standard practice in these situations, please let me know!
Especially if the method i used could result in some unforeseen pitfalls and bad stuff xD

Also, if you figure out a way to read from the stdout i'm all eyes as well :)
 
Last edited:
There are a few ways to achieve running blocking code in a non-blocking context and you're very close.

Code:
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace Scripts.Custom
{
	public static class PythonBridge
	{
		public delegate void ResultCallback(string result);

		#region Ex 1
		// Create a delegate with the Execute(string args) method as the target
		private static readonly Func<string, string> _Execute = Execute;

		// Async method wrapper, uses the standard Begin* End* async patterns
		// to run blocking code in a non-blocking context
		public static IAsyncResult ExecuteAsync(string args, ResultCallback resultCallback)
		{
			return _Execute.BeginInvoke(
				args,
				r =>
				{
					var result = _Execute.EndInvoke(r);

					resultCallback(result);
				},
				null);
		}
		#endregion

		#region Ex 2
		public static Thread ExecuteThread(string args, ResultCallback resultCallback)
		{
			// explicit creation of a new thread
			var thread = new Thread(
				() =>
				{
					var result = Execute(args);

					resultCallback(result);
				});

			// start the thread
			thread.Start(args);

			// block until the thread initializes
			while (!thread.IsAlive)
			{
				// save cpu
				Thread.Sleep(10);
			}

			return thread;
		}
		#endregion

		#region Ex 3
		public static Task ExecuteTask(string args, ResultCallback resultCallback)
		{
			// start a new threading task
			return Task.Factory.StartNew(
				() =>
				{
					var result = Execute(args);

					resultCallback(result);
				});
		}
		#endregion

		public static string Execute(string args)
		{
			return InternalExecute(@"D:\Python\My_Script.py", args);
		}

		private static string InternalExecute(string app, string args)
		{
			try
			{
				// full path of python interpreter
				const string py = @"D:\Python\Python35\python.exe";

				// Create new process start info
				var start = new ProcessStartInfo(py)
				{
					UseShellExecute = true,
					RedirectStandardOutput = false,
					CreateNoWindow = true,
					WindowStyle = ProcessWindowStyle.Hidden,
					Arguments = String.Format("\"{0}\" {1}", app, args),
					//Verb = "runas" // request admin elevation
				};

				Console.WriteLine("Execute Python script with args: {0}", args);

				using (var process = Process.Start(start))
				{
					if (process == null)
					{
						Console.WriteLine("Could not start Python process.");

						return null;
					}

					process.WaitForExit();

					return process.StandardOutput.ReadToEnd();
				}
			}
			catch (Exception e)
			{
				Console.WriteLine(e);

				return null;
			}
		}
	}
}

I have not tested this code, but it will require the use of a callback:

Code:
// non-blocking
PythonBridge.ExecuteAsync("arguments", OnComplete);
PythonBridge.ExecuteThread("arguments", OnComplete);
PythonBridge.ExecuteTask("arguments", OnComplete);

// blocking
OnComplete(PythonBridge.Execute("arguments"));

// ...

private static void OnComplete(string result)
{
    // do
}

Of course, this can all be optimized and expanded to include calling multiple scripts - and if the ResultCallback delegate's arguments are modified slightly, you can also pass the originating script's path to the callback, which would be essential if you're calling multiple scripts at once and need to associate the results with the original request.

Also to note that the thread safety of the provided ResultCallback delegate is inconsistent here for the sake of a simple example.
The callback is thread-safe in the ExecuteAsync context because the End* part of Begin* End* is called on the originating thread.
Thread safety would need to be taken into consideration with the ExecuteThread and ExecuteTask methods.
 
Thank you very much Voxpire!

I seem to recall having seen the System.Threading and System.Threading.Tasks used in the Main.cs, and the overall structure of this code looks .. similar?
Of course to a newbie probably everything looks similar, but i think i'm kinda starting to see a pattern is what i'm trying to say xD

Since this is a bit overwhelming (still trying to digest all the technical terms, such as Callbacks, Thread-Safe, Delegate and so on) compared to my original script, could you please tell me the main differences between the two approaches?

Obviously the way the thread is put to sleep and the detail mean this is more optimized, but how much of a difference are we talking about?
Is there something the ThreadingPool can't achieve, for example?

Once again thank you very much, this has been very informative and i will try my best to study and assimilate your Reply!



Also, you bring up a very interesting point about calling multiple scripts.

I was considering and wondering about the limits of such an approach, when executing python scripts from the game.

More specifically, assuming the python script has an average cpu cost to be executed, is it feasible to run multiple instances of said code, if for instance i wanted to call a separate thread for every npc around?

I don't mean every NPC on the map, just the closest ones, sort of a Levels-Of-Detail technique but with A.I, since my python script can handle some interesting aspects that would require quite a lot of work to be reproduced in C# - if at all.

Now, just for the record, i have about 5 thousand NPCs wandering in New Haven, to make it more lively (and the mood they create is amazing!), but that doesn't seem to cause any issue at all.

If i were to restrict the range and thus the number of NPCs on which this "Advanced" Script for AI and other stuff is applied, do you think it would be feasible to have a good number of threads called for every NPC that's in range?

I have no idea on how many threads can be called on average without having any problems, but then again considering the game has to call the A.I scripts on each and every one of the 5 thousand NPCs makes me think that maybe it's possible to call the python script on many many of them at least?

The main idea here is to use this game as a platform to create advanced stuff and an advanced game, also taking advantage of the fact that it's 2D and way less heavy than modern 3D games, and that i don't care one iota because i still prefer it xD

I think there's heaps of potential here, if i can figure out how to best handle things and scripts.


Thank you very much
 
Last edited:
It looks like you're getting a grasp on things very fast, which is amazing.

I seem to recall having seen the System.Threading and System.Threading.Tasks used in the Main.cs, and the overall structure of this code looks .. similar?
Of course to a newbie probably everything looks similar, but i think i'm kinda starting to see a pattern is what i'm trying to say xD

It's all about the patterns! When a technique works, roll with it!
The 'Thread' way of doing things is used in the Core class for starting the Timer thread.

Since this is a bit overwhelming (still trying to digest all the technical terms, such as Callbacks, Thread-Safe, Delegate and so on) compared to my original script, could you please tell me the main differences between the two approaches?
Callback is a term used to describe the usage or context of a Delegate - when a Delegate is passed as an argument, it's usually for use as a "callback", meaning when you've made a request to process some code, the code can call you back by executing the Callback Delegate when it is completed.
A delegate is simply a dynamic variable that represents a method signature and body, wrapping the target method.

Thread Safety describes the act of ensuring that all code is synchronised between threads. One thread calling a method on an object that was create on a different thread, can yield unexpected results, though it is extremely rare. Thread safety also prevents race conditions, where two or more threads halt each-other to wait for each-other to complete, resulting in "deadlock".

The main difference between your original script and the one I posted is not much to be fair, your original "Main" method was more or less renamed to InternalExecute - after which, it was just a case of writing wrapping methods to handle asynchronous context.

All of the example methods I provided use the "fire and forget" pattern - a pattern in which you create an synchronous context for the code you wish to run, then expect with some certainty that the code will be run without needing to be inspected. Hence 'fire and forget'.

Obviously the way the thread is put to sleep and the detail mean this is more optimized, but how much of a difference are we talking about?

If the Thread was not slept during the while loop (which blocks the calling thread until the new Thread is alive), it would consume 100% CPU and the context would never switch in order to allow the new thread to initialize. Sleeping the thread allows the CPU to change context and process other waiting threads.

Is there something the ThreadingPool can't achieve, for example?

ThreadPool is just a factory for Threads, it produces new Thread objects similarly to how the Thread example works.
The bonus to using ThreadPool is that it allows you to preemptively create N amount of threads, caching them for future use.
ThreadPool also limits the number of Threads that can be created. I've never used ThreadPool, having not had any issues using the 'fire and forget' pattern for most tasks, but I'd assume ThreadPool would be more suitable when you want to spin up a few thousands threads at once to save on resources creating them on the fly when needed (like my example does).

The number of Threads you can create is not limited by software, it is scaled to hardware.
Depending on the O/S, a single Thread usually takes up a few MB of memory (approx 30mb IIRC).

I was considering and wondering about the limits of such an approach, when executing python scripts from the game.

More specifically, assuming the python script has an average cpu cost to be executed, is it feasible to run multiple instances of said code, if for instance i wanted to call a separate thread for every npc around?

I don't mean every NPC on the map, just the closest ones, sort of a Levels-Of-Detail technique but with A.I, since my python script can handle some interesting aspects that would require quite a lot of work to be reproduced in C# - if at all.

Now, just for the record, i have about 5 thousand NPCs wandering in New Haven, to make it more lively (and the mood they create is amazing!), but that doesn't seem to cause any issue at all.

If i were to restrict the range and thus the number of NPCs on which this "Advanced" Script for AI and other stuff is applied, do you think it would be feasible to have a good number of threads called for every NPC that's in range?

I have no idea on how many threads can be called on average without having any problems, but then again considering the game has to call the A.I scripts on each and every one of the 5 thousand NPCs makes me think that maybe it's possible to call the python script on many many of them at least?

This is where you could consider having a single dedicated Thread solely for processing Python script execution requests.
RunUO solved this issue with Timers by using a separate, single thread to process them. All Timers are constantly monitored for changes and if they are ready to 'tick', they are queued for call. The thread safety in Timers is achieved by calling each timer's OnTick method from the Core thread (the main server thread) using an O(n) operation over a queue that stores all the changed timers.

You could do something similar, in which you queue each script execution request and have a separate thread that de-queues these requests and processes them (via InternalExecute). The supplied ResultCallback delegate can then be queued to a different queue, in which the Core thread dequeues and calls each delegate.

Code:
public class PythonRequest
{
    public object State;
    public string Path;
    public string Args;

    public string Data;

    public Action<PythonRequest> Callback;
}

private static Timer _Timer;
private static Thread _Thread;

private static Queue<PythonRequest> _Requests = new Queue<PythonRequest>();
private static Queue<PythonRequest> _Responses = new Queue<PythonRequest>();

public static void BeginExecute(object state, string path, string args, Action<PythonRequest> callback)
{
     lock(_Requests)
        _Requests.Enqueue(new PythonRequest{ State = state, Path = path, Args = args, Callback = callback });
}

private static void EndExecute(PythonRequest req)
{
    req.Callback(req);
}

private static void Execute(PythonRequest req)
{
    req.Data = InternalExecute(req.Path, req.Args);

    lock(_Responses)
        _Responses.Enqueue(req);
}

private static void Start()
{
    _Thread = new Thread(ThreadStart)
    {
        Name = "Python Processor"
    };

    _Thread.Start();

    while(!_Thread.IsAlive)
    {
        Thread.Sleep(10);
    }

  _Timer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromSeconds(1.0), ProcessResponses);
}

[STAThread]
private static void ThreadStart()
{
    while(!Core.Closing) // Run this thread for as long as the core is running
    {
        lock(_Requests)
        {
            while(_Requests.Count > 0)
            {
                Execute(_Requests.Dequeue());
            }
        }

        Thread.Sleep(10);
    }
}

private static void ProcessResponses()
{
    lock(_Responses)
    {
        while(_Responses.Count > 0)
        {
            EndExecute(_Responses.Dequeue());
        }
    }
}

        private static string InternalExecute(string app, string args)
        {
            try
            {
                // full path of python interpreter
                const string py = @"D:\Python\Python35\python.exe";

                // Create new process start info
                var start = new ProcessStartInfo(py)
                {
                    UseShellExecute = true,
                    RedirectStandardOutput = false,
                    CreateNoWindow = true,
                    WindowStyle = ProcessWindowStyle.Hidden,
                    Arguments = String.Format("\"{0}\" {1}", app, args),
                    //Verb = "runas" // request admin elevation
                };

                Console.WriteLine("Execute Python script with args: {0}", args);

                using (var process = Process.Start(start))
                {
                    if (process == null)
                    {
                        Console.WriteLine("Could not start Python process.");

                        return null;
                    }

                    process.WaitForExit();

                    return process.StandardOutput.ReadToEnd();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);

                return e.ToString();
            }
        }


Code:
object state = mobile; // item, cheese, foo, "happy holidays"

PythonBridge.BeginExecute(state, "path/to/script.py", "-arguments", OnComplete);

// ...

private static void OnComplete(PythonRequest req)
{
    Console.WriteLine("Python: {0}, {1}", req.State, req.Data);
}

You could even make it event-driven, so you don't have to supply an Action<PythonRequest> callback with every request;
Code:
public static event Action<PythonRequest> OnResponse;

private static void EndExecute(PythonRequest req)
{
    if(req.Callback != null)
    {
        req.Callback(req);
    }

    if(OnResponse != null)
    {
        OnResponse(req);
    }
}

Adding an overload for BeginExecute that passes a null callback;
Code:
public static void BeginExecute(object state, string path, string args)
{
    BeginExecute(state, path, args, null);
}

And finally, the usage;
Code:
// Initialize the event (usually done in a Configure() or Initialize() method in ServUO)
PythonBridge.OnResponse += OnComplete;

// ..

private static void OnComplete(PythonRequest req)
{
    Console.WriteLine("Python: {0}, {1}", req.State, req.Data);
}

// ...

object state = mobile; // item, cheese, foo, "happy holidays"

PythonBridge.BeginExecute(state, "path/to/script.py", "-arguments");

Disclaimer: written entirely in the forum editor, no guarantees :p
 
Thank you so very much once again Voxpire!

Ohh yes, when yesterday i saw the way Timer created threads i had an "intuition" about using the same system to handle the script calls - after all, considering how many timers there are to keep track of every single item and mobiles, that must be one heck of an efficient implementation!
Thank you very much for confirming this could actually be a good way to handle things.


Callback is a term used to describe the usage or context of a Delegate - when a Delegate is passed as an argument, it's usually for use as a "callback", meaning when you've made a request to process some code, the code can call you back by executing the Callback Delegate when it is completed.
A delegate is simply a dynamic variable that represents a method signature and body, wrapping the target method.

Thread Safety describes the act of ensuring that all code is synchronised between threads. One thread calling a method on an object that was create on a different thread, can yield unexpected results, though it is extremely rare. Thread safety also prevents race conditions, where two or more threads halt each-other to wait for each-other to complete, resulting in "deadlock".

The main difference between your original script and the one I posted is not much to be fair, your original "Main" method was more or less renamed to InternalExecute - after which, it was just a case of writing wrapping methods to handle asynchronous context.

So we could define the Callback, as the name implies, as getting a ticket to reserve a seat at some event, then going about doing other stuff while you wait for the day of the event, and finally when the time comes you go there and you can enter because you reserved the seat?
There is no need to wait there for days to keep your seat and be stuck there - you just buy a ticket, a reservation and you are free to keep going on with the other stuff.
When the time comes, the reserved seat will be occupied by the snippet of code passed to the method as an argument, which given the context makes the argument (actually a delegate method) become something more specific - a Callback, if i got the "context of a Delegate" part correctly understood.

Which would be the way the Timer method works when asking for a Callback to execute once the time is up.

As for the delegate, i don't know what signature, body and wrapping mean in a coding context; but let's see if i can somehow get a glimpse of what they could be.
Could we say that a delegate is a method which keeps the same exact substance of the original method, but simply changes the name used to call it?
Like a delegate role, where the person taking up the role is different, but authorization wise the person has the exact same ones of the original person delegating the role - just the person changes, which is irrelevant in a context of authorizations and ranks.
If i correctly understood this, then the signature and body could be the "substance" of the method, what the code in the method does.
Subsequently, wrapping means to simply change the name of the method, or in other words the "outside".

If so, i think the way the EventSink seems to work in the code might leverage this "wrapping" concept, so that it becomes possible to "intercept" the data caught by the original method, redirecting it to the wrapped method so the original code can be further developed with some additions, if needed, without ever touching the "core" method, and of course while retaining the caught data.

Which would explain why, in order to use the EventSink, it seems a line such as this one is required before using it:
EventSink.Movement += OnMovement;
In which the original core method is wrapped by the OnMovement in this example, so that then by defining the OnMovement for instance in a static void it becomes possible to use this method and the caught data/arguments in way that's meaningful to the purpose at hand.

And the dumbed down description of Thread Safety would be "avoiding entanglement", which has more and more relevance as the number of threads increases and hence the likelihood of the entanglement happening? xD
Which would also explain why you mentioned in your first reply the Thread-Safety being relevant to the ExecuteThread and ExecuteTasks, since those create more threads and could result in entanglement.
And also why your Code is Thread-Safe in the ExecuteAsync context: because it is called on the originating thread, and hence there is no risk on entanglement with other threads.

Especially with Callbacks, if i got this right, the delayed execution of code could mix up the threads very quickly and cause a mess.

If the Thread was not slept during the while loop (which blocks the calling thread until the new Thread is alive), it would consume 100% CPU and the context would never switch in order to allow the new thread to initialize. Sleeping the thread allows the CPU to change context and process other waiting threads.

Ah yes, that makes sense.
So it would basically "block the traffic" because it is standing there doing nothing but getting in the way of the other threads, soaking up all the available cpu resources.
I guess the ThreadPool is specifically engineered to handle these things automagically, which would also further explain why you defined it as a Thread Factory; with the only catch being that you are limited in the number of threads you can create because, compared to your method, the ThreadPool doesn't create them on the fly as needed, but creates them all at once, so that they can be cached, which means more effective resource management because of the limited number of threads, but also of course that they are limited and have an extent that's well defined - otherwise they couldn't be cached and wouldn't be as effective in managing the threads, if they were created on the fly.


Sorry if i dumbed down your way better definitions, just making sure i got them right in more practical terms xD


Also, thank you so so very much for all the Code you took the time to write!

I think i get the concept and i kind of understand most of it, but i get the feeling this should be advanced stuff i should postpone a little whilst i at least get some more practice/knowledge about coding and c# xD

For this reason, i'll sink my teeth on this code shortly, but right now i'm working on something more "simple", since all this optimization and multi-threading stuff is something i'll need to consider when i scale up the number of script requests the game can handle.
So i'm digging around some more in the Code and giving a look at what's available - Utilities, methods supplied to handle the most basic needs and so on.



Which leads me to the next question, something i feel kinda ashamed to ask, because it seems quite "trivial" xD but i haven't found any way of doing it.

I can get the player character by using the EventSink alright, but how do i get it without any EventSink?
Let me elaborate.

My current code latches onto the Movement EventSink, and everything is awesome and dandy up to the point where i need to check if the mobile is in a specific range from the player.

Digging around in the code, i found the Utility.InRange() method, which seems to fit the bill perfectly.
Problem is, using the Movement event, i get only the mobile location, not the player's one, which would be a separate event.
But i need both, so they can be compared and checked.


It also seems odd that the only way to get the player character or any mobile is through the EventSink, so i guess i'm missing something simple here.

Here the snippet of code i'm using for the range check:
Code:
if (Utility.InRange(e.Mobile.Location, hereineedtheplayerone, 5))
                e.Mobile.Say(Utility.RandomList(WhatSay));


So the question basically is:
Is the EventSink the only way to get a Mobile's location/properties etc.?

If so, then maybe the only option is to get the player character using the OnLogin event and storing it for future reference in the code, such as the range check.

It just seems odd that the EventSink is the only way to get any mobile's properties, especially if i need to get a mobile but no specific event happens.


Once again, thank you so much for your time Voxpire, i just hope what i wrote makes sense and i correctly understood all the awesome information you gave me.
Both because english isn't my main language and because all these technical terms and definitions used for coding and the c# stuff are overwhelming for me xD

And Happy Christmas!



[doublepost=1482739582][/doublepost]This morning finally had some more time to analyze some more ServUO code, and i think i found a way to solve, at least momentarily, my last problem about checking whether the player was in a specific range from the mobile.

After looking at Mobile.cs and Base Creature.cs, here is how i decided to solve the problem:

Code:
foreach (Mobile m in e.Mobile.GetMobilesInRange(5))
                if (ChanceTalk <= 5 && m is PlayerMobile)
                    e.Mobile.Say(Utility.RandomList(WhatSay));

Which is working as intended, but as always if there's a better way of doing things instead of the way i'm clumsily cobbling them up, i'd love to hear it :)

Also, the original question still stands: how can i get any mobile/player without relying on the EventSink, so i could get them even if no specific event were to happen?
Is it possible at all?
 
Last edited:
Back