Quantcast

Jump to content

» «
Photo

Scripthook multiple timers

17 replies to this topic
pedro2555
  • pedro2555

    Open Sourcer

  • Members
  • Joined: 02 Sep 2012
  • Portugal

#1

Posted 27 April 2013 - 12:27 AM

The Scripthook Script class features an instance of GTA.Timer. However adding a new instance yourself to a derived class seems to crash the script with an exception.

On the constructor :
CODE

new_timer = new GTA.Timer(1000);
new_timer.Start();
new_timer.Tick += new EventHandler(new_timer_Tick);


new_timer_Tick :
CODE

void new_timer_Tick(object sender, EventArgs e)
{
       Game.DisplayText("It works!", 1100);
}


But it doesn't work, the line
CODE
new_timer.Tick += new EventHandler(new_timer_Tick);
raises an Exception 'Unable to determine the owning Script for this object' @ GTA.ScriptThread.LoadScriptNow(). I can't get this.

Anyway, I've tried System.Windows.Forms.Timer, with no luck.

Ended up using System.Timers.Timer, but it lags more than it should for some reason, only thing I'm doing is checking for a key press 'Game.isGameKeyPressed' and not doing anything on that event, happens every 100ms.

GTA.Timer does not produce the same lag as System.Timers.Timer so I would like to change to it.

As someone managed to get to GTA.Timer instances on the same script ?

Thanks in advance.

lindsayslorach
  • lindsayslorach

    Rat

  • Members
  • Joined: 26 Dec 2006

#2

Posted 27 April 2013 - 10:14 AM

Does your script compile? Would it be okay if I had a look at your code? I'm not quite sure I understand fully what you're saying.

I've written out a small script:

http://pastebin.com/1ZfX21Rc

Is this basically what you're trying to achieve? It compiles fine, though I havn't tried it in game (I will do so soon though).

NomeSkavinski
  • NomeSkavinski

    Player Hater

  • Members
  • Joined: 18 Feb 2013

#3

Posted 27 April 2013 - 02:07 PM

Try reordering the start up procedure to:
CODE

new_timer = new GTA.Timer(1000);
new_timer.Tick += new EventHandler(new_timer_Tick);
new_timer.Start();


Your telling the timer to start when it doesn't have anything to work on and then your giving it the method to use. Never have the timer running when you want to add or remove a tick event.

I hope that solves it for you.

lindsayslorach
  • lindsayslorach

    Rat

  • Members
  • Joined: 26 Dec 2006

#4

Posted 27 April 2013 - 03:02 PM

@Above, I don't think that makes a difference, because the GTA.Timer constructor has an overload to take a bool wether to start the timer when you create it: Timer(int Interval, bool StartNow);

pedro2555
  • pedro2555

    Open Sourcer

  • Members
  • Joined: 02 Sep 2012
  • Portugal

#5

Posted 27 April 2013 - 07:09 PM

QUOTE (NomeSkavinski @ Saturday, Apr 27 2013, 14:07)
Try reordering the start up procedure to:
CODE

new_timer = new GTA.Timer(1000);
new_timer.Tick += new EventHandler(new_timer_Tick);
new_timer.Start();


Your telling the timer to start when it doesn't have anything to work on and then your giving it the method to use. Never have the timer running when you want to add or remove a tick event.

I hope that solves it for you.

That is not really an issue, or at least it shouldn't be. My handler only gets added to the TimerTick event, you can do that when the timer is running for sure. I've done it before without any problems. Otherwise why would GTA.Timer have a constructor with the option to start immediately after creation ?

Thank you for you suggestion anyway.

pedro2555
  • pedro2555

    Open Sourcer

  • Members
  • Joined: 02 Sep 2012
  • Portugal

#6

Posted 27 April 2013 - 07:18 PM

QUOTE (lindsayslorach @ Saturday, Apr 27 2013, 10:14)
Does your script compile? Would it be okay if I had a look at your code? I'm not quite sure I understand fully what you're saying.

I've written out a small script:

http://pastebin.com/1ZfX21Rc

Is this basically what you're trying to achieve? It compiles fine, though I havn't tried it in game (I will do so soon though).

Yes the code does compile fine, I always use Visual Studio never plain hardcoded stuff. If you have any SVN client you can checkout the code, check you inbox for more info.

I haven't test your code yet, but I'm looking forward to it. I already see a problem, I want o check the return of function Game.isGameKeyPressed() on the Tick event of timer, and I doubt I can with your pastebin.

Prof_Farnsworth
  • Prof_Farnsworth

    Ambient Modder

  • Members
  • Joined: 25 Feb 2011

#7

Posted 02 May 2013 - 05:15 AM

QUOTE (pedro2555 @ Saturday, Apr 27 2013, 00:27)
~snip~
As someone managed to get to GTA.Timer instances on the same script ?

Thanks in advance.

Not sure if you have this solved, but here is how I use multiple timers:

CODE
public class example : Script
   {
      GTA.Timer test_tick, one_second, button_press;

public example()
       {
           test_tick = new GTA.Timer(500);
           one_second = new GTA.Timer(1000);
           button_press = new GTA.Timer(100);
           one_second.Tick += one_second_Tick;
           one_second.Start();
           test_tick.Tick += test_tick_Tick;
           test_tick.Start();
           button_press.Tick += button_press_Tick;
           button_press.Start();
       }
       void button_press_Tick(object sender, EventArgs e){}
       void one_second_Tick(object sender, EventArgs e){}
       void test_tick_Tick(object sender, EventArgs e){}
   }

You can probably start the timers when assigning them, this is what has worked for me though, hope it helps.

pedro2555
  • pedro2555

    Open Sourcer

  • Members
  • Joined: 02 Sep 2012
  • Portugal

#8

Posted 02 May 2013 - 01:50 PM

QUOTE (Prof_Farnsworth @ Thursday, May 2 2013, 05:15)
QUOTE (pedro2555 @ Saturday, Apr 27 2013, 00:27)
~snip~
As someone managed to get to GTA.Timer instances on the same script ?

Thanks in advance.

Not sure if you have this solved, but here is how I use multiple timers:

CODE
public class example : Script
   {
      GTA.Timer test_tick, one_second, button_press;

public example()
       {
           test_tick = new GTA.Timer(500);
           one_second = new GTA.Timer(1000);
           button_press = new GTA.Timer(100);
           one_second.Tick += one_second_Tick;
           one_second.Start();
           test_tick.Tick += test_tick_Tick;
           test_tick.Start();
           button_press.Tick += button_press_Tick;
           button_press.Start();
       }
       void button_press_Tick(object sender, EventArgs e){}
       void one_second_Tick(object sender, EventArgs e){}
       void test_tick_Tick(object sender, EventArgs e){}
   }

You can probably start the timers when assigning them, this is what has worked for me though, hope it helps.

thank you very much, I know that works for you, but for some reason it doens't for me.

I haven't solved this, I moved over and started coding other aspects.

I will most likely use a second thread and I will post any solution I end up using.

sextitsboobsyeah
  • sextitsboobsyeah

    Player Hater

  • BUSTED!
  • Joined: 01 May 2013

#9

Posted 02 May 2013 - 06:11 PM Edited by sextitsboobsyeah, 03 May 2013 - 12:31 AM.

here is my code, same as farnsworth... ur doing something wrong pedro, also u cant use multiple threads in the script thread... u can write multiple classes inheriting Script though

CODE
namespace MultipleTimers
{
   using System;
   using System.Windows.Forms;
   using GTA;

   public class Main : Script
   {
       GTA.Timer timer1, timer2;

       /*
       dont do this... not sure why but i think doing this gave me issues but not sure if i remember correctly

       GTA.Timer timer1 = new GTA.Timer();
       */

       public Main()
       {
           BindKey(Key.B, StartTimer1);
           BindKey(Key.N, StartTimer2);

           timer1 = new GTA.Timer(5000);
           timer1.Tick += timer1_Tick;

           timer2 = new GTA.Timer(10000);
           timer2.Tick += timer2_Tick;
       }

       private void StartTimer1()
       {
           if (!timer1.isRunning) timer1.Start();
           else timer1.Stop();
       }

       private void StartTimer2()
       {
           if (!timer2.isRunning) timer2.Start();
           else timer2.Stop();
       }

       private void timer1_Tick(object sender, EventArgs e)
       {
           Game.DisplayText("Timer1");
       }

       private void timer2_Tick(object sender, EventArgs e)
       {
           Game.DisplayText("Timer 2");
       }
   }
}

pedro2555
  • pedro2555

    Open Sourcer

  • Members
  • Joined: 02 Sep 2012
  • Portugal

#10

Posted 03 May 2013 - 12:22 AM

QUOTE
here is my code, same as farnsworth... ur doing something wrong pedro, also u cant use multiple threads in the script thread... u can write multiple classes inheriting Script though


Yes I was doing something wrong, in anycase, I have continued development and left this problem for a later session. I couldn't find the exact cause of it but I may take a look at older revisions and post a conclusion.

You can't start a thread in a thread, you can start a thread by calling Thread.Start() from an existenting/running thread, in fact that is the only way of doing it. Just to clarify, my approach will be, having a GTA.Timer (the one declared and initialized by the Script class) handling player position and key press (GameKey). Another thread will be running along side taking appropriate actions. Since the user interface mostly handles with displaying data and little data manipulation, separating data manipulation from data display will help performance a lot without that many 'cross-thread' problems (In fact I'm expecting only one cross-thread problem).

I will edit the very first post to feature the multiple timers approach and the multiple threads approach, and, hopefully, be of use in the future.

Thank you everyone for sharing their knowledge.

sextitsboobsyeah
  • sextitsboobsyeah

    Player Hater

  • BUSTED!
  • Joined: 01 May 2013

#11

Posted 03 May 2013 - 12:27 AM Edited by sextitsboobsyeah, 03 May 2013 - 12:30 AM.

QUOTE (pedro2555 @ Friday, May 3 2013, 00:22)
QUOTE
here is my code, same as farnsworth... ur doing something wrong pedro, also u cant use multiple threads in the script thread... u can write multiple classes inheriting Script though


Yes I was doing something wrong, in anycase, I have continued development and left this problem for a later session. I couldn't find the exact cause of it but I may take a look at older revisions and post a conclusion.

You can't start a thread in a thread, you can start a thread by calling Thread.Start() from an existenting/running thread, in fact that is the only way of doing it. Just to clarify, my approach will be, having a GTA.Timer (the one declared and initialized by the Script class) handling player position and key press (GameKey). Another thread will be running along side taking appropriate actions. Since the user interface mostly handles with displaying data and little data manipulation, separating data manipulation from data display will help performance a lot without that many 'cross-thread' problems (In fact I'm expecting only one cross-thread problem).

I will edit the very first post to feature the multiple timers approach and the multiple threads approach, and, hopefully, be of use in the future.

Thank you everyone for sharing their knowledge.

did u declare timer and initialize inside the namespace, i think this caused a bug for me in the past iirc but once i moved the calls to timer constructor from namespace to inside a method or constructor then it should be ok

edit: i put the possible bug in my example but commented out of actual code (above constructor)

sextitsboobsyeah
  • sextitsboobsyeah

    Player Hater

  • BUSTED!
  • Joined: 01 May 2013

#12

Posted 03 May 2013 - 12:41 AM Edited by sextitsboobsyeah, 03 May 2013 - 04:46 AM.

QUOTE (pedro2555 @ Friday, May 3 2013, 00:22)
QUOTE
here is my code, same as farnsworth... ur doing something wrong pedro, also u cant use multiple threads in the script thread... u can write multiple classes inheriting Script though


Yes I was doing something wrong, in anycase, I have continued development and left this problem for a later session. I couldn't find the exact cause of it but I may take a look at older revisions and post a conclusion.

You can't start a thread in a thread, you can start a thread by calling Thread.Start() from an existenting/running thread, in fact that is the only way of doing it. Just to clarify, my approach will be, having a GTA.Timer (the one declared and initialized by the Script class) handling player position and key press (GameKey). Another thread will be running along side taking appropriate actions. Since the user interface mostly handles with displaying data and little data manipulation, separating data manipulation from data display will help performance a lot without that many 'cross-thread' problems (In fact I'm expecting only one cross-thread problem).

I will edit the very first post to feature the multiple timers approach and the multiple threads approach, and, hopefully, be of use in the future.

Thank you everyone for sharing their knowledge.

this is how i "multi thread" in gta

CODE
namespace MultipleScriptExample
{
   public class Main : Script
   {
       private string _scriptName;
       internal string ScriptName
       {
           get { return _scriptName; }
       }

       internal static Main Instance;

       public Main()
       {
           _scriptName = "Mike";
           Tick += Main_Tick;
           Instance = this;
       }

       private void Main_Tick(object sender, EventArgs e)
       {
           Game.DisplayText("Hello " + Secondary.Instance.ScriptName, 100);
       }
   }
   public class Secondary : Script
   {
       private string _scriptName;
       internal string ScriptName
       {
           get { return _scriptName; }
       }

       internal static Secondary Instance;

       public Secondary()
       {
           _scriptName = "Habib";
           Tick += Secondary_Tick;
           Instance = this;
       }

       private void Secondary_Tick(object sender, EventArgs e)
       {
           Game.DisplayText(Environment.NewLine + "Well Hello " + Main.Instance.ScriptName + "!;D", 100);
       }
   }
}


so now u have two scripts running together at same time and each can access each other through an internal static instance of the other class

pedro2555
  • pedro2555

    Open Sourcer

  • Members
  • Joined: 02 Sep 2012
  • Portugal

#13

Posted 20 May 2013 - 10:23 PM

QUOTE (sextitsboobsyeah @ Friday, May 3 2013, 00:41)
QUOTE (pedro2555 @ Friday, May 3 2013, 00:22)
QUOTE
here is my code, same as farnsworth... ur doing something wrong pedro, also u cant use multiple threads in the script thread... u can write multiple classes inheriting Script though


Yes I was doing something wrong, in anycase, I have continued development and left this problem for a later session. I couldn't find the exact cause of it but I may take a look at older revisions and post a conclusion.

You can't start a thread in a thread, you can start a thread by calling Thread.Start() from an existenting/running thread, in fact that is the only way of doing it. Just to clarify, my approach will be, having a GTA.Timer (the one declared and initialized by the Script class) handling player position and key press (GameKey). Another thread will be running along side taking appropriate actions. Since the user interface mostly handles with displaying data and little data manipulation, separating data manipulation from data display will help performance a lot without that many 'cross-thread' problems (In fact I'm expecting only one cross-thread problem).

I will edit the very first post to feature the multiple timers approach and the multiple threads approach, and, hopefully, be of use in the future.

Thank you everyone for sharing their knowledge.

this is how i "multi thread" in gta

CODE
namespace MultipleScriptExample
{
   public class Main : Script
   {
       private string _scriptName;
       internal string ScriptName
       {
           get { return _scriptName; }
       }

       internal static Main Instance;

       public Main()
       {
           _scriptName = "Mike";
           Tick += Main_Tick;
           Instance = this;
       }

       private void Main_Tick(object sender, EventArgs e)
       {
           Game.DisplayText("Hello " + Secondary.Instance.ScriptName, 100);
       }
   }
   public class Secondary : Script
   {
       private string _scriptName;
       internal string ScriptName
       {
           get { return _scriptName; }
       }

       internal static Secondary Instance;

       public Secondary()
       {
           _scriptName = "Habib";
           Tick += Secondary_Tick;
           Instance = this;
       }

       private void Secondary_Tick(object sender, EventArgs e)
       {
           Game.DisplayText(Environment.NewLine + "Well Hello " + Main.Instance.ScriptName + "!;D", 100);
       }
   }
}


so now u have two scripts running together at same time and each can access each other through an internal static instance of the other class

Thanks for sharing your approach. I would like to point out some good to know things, about it

You are essentially using a .NET scripthook mechanic, where each Script runs in a different thread. Nothing wrong with that. But I would like to clarify a bit what is happening.

Since both classes are part of the same assembly file (.dll), they can access each other's internal static members. And given the fact that both classes inherit the Script class, they are running on different threads. But you can only read data that way.

And, by the way, if you declare only the internal variable, like below:
CODE

internal static string _scriptName;

instead of :
CODE

private string _scriptName;
internal string ScriptName
{
   get { return _scriptName; }
}
internal static Main Instance;

you still get the same output, with a lot less RAM usage. Just make sure to assign something to _scriptName, instead of pointing to 'this'.

But, be extremely carefull with this approach.
First, it works on the Scripthook environment, because scripthook always initializes all scripts ONCE. Remember that you are reading a static member initialized when the instance constructor is called.
Second, you will not 'see' changes to the value of _scriptName via Main.Instance.ScriptName, it will always return the value given when the constructor is called (In this case is a literal string, but imagine it isn't), you have to populate those changes yourself.
Third, it will break if the Main class constructor isn't called at least once (that is why it works okish in the scripthook environment, there is still the chance the script fails to load, but oh well,.. let that alone).
And more to the point, you are only reading a string. But you are reserving space for the entire instance, and that is cleary not correct. So instead of declaring a full class instance as static, you can have a static string member in the Main class, and still be accessible internally (assembly wise). That way you can neglect the instance member and use only the static member. Altought, this is not the intended use of a static member, it is ok to use it as described, since you are only expecting one instance of each derived Script class.

But the real problem is still there, cross-thread data access.

First we should notice that we are dealing with logical threads, and not actual CPU threads. Logical threads appear to run parallel, but they actually are running one at a time, alternating CPU usage. This happens extremely fast, fast enough for we to say they run parallel.

But they don't run parallel and the CPU has no idea what we are doing to data in each thread, it just does it. The programmer is responsible for assuring that threads that are manipulating the same data, don't race each other for changing data, endind up corrupting it.

Remember that a single C# line, can result in multiple CIL instructions, and that is the main core of the problem.

This a quick overview of the problem, I post this because I can already see some less experience coders, making the wrong use of the code quoted above. I will eventually update the top most post to provide an actual answer on how this is done. It is not easy to provide a good quality article in no time, so be patient.


A pratical, unrealistic example:

Assume a variable 'money' as a signed integer,
a function 'UpdateProfit' that changes money to be more 10%
a function 'Credit' that adds a literal value to money.

UpdateProfit has two phases:
calculate 10% of money
add calculated value to money

Credit has one phase:
add literal value to money

Both methods run on different and concurrent threads.
Now image that the OS decides that the thread running UpdateProfit, should pause after 'calculate 10% of money'.
Credit kicks in and changes 'money'.
When UpdateProfit resumes the work it will add a calculated value that is no longer valid (because 10% of money + x is only equal to 10% of money if x=0).

jitsuin666
  • jitsuin666

    Player Hater

  • BUSTED!
  • Joined: 21 May 2013

#14

Posted 21 May 2013 - 06:46 AM Edited by jitsuin666, 21 May 2013 - 07:08 AM.

QUOTE
But you can only read data that way.


no i dont think so... here is a little quote about static classes... "Static data is equivalent to global data. Everybody in the program sees the same data. If someone changes the data then everybody else will see the change as well. Static data is useful for sharing information across a program such as database connection strings or log files."

so in my example i have created properties for the script name and i only allow each class to return the other's name. maybe i want the classes to be able to set each other's name, then i just add a set accessor to the property that is simple. Yes static is loaded once but it can be updated by whoever has access. I can get or set wink.gif i chose not to allow setting. And yes I realize scripts run on seperate threads and as u cant multi thread yourself when inheriting script this was a good solution. Why not take advantage of a situation? smile.gif

also encapsulating properties is what i prefer and yeah i know if i were to only set one property and didnt care about other data in a class i would just make the property static. Only for the example to show u can have total access between gta scripts.

Look at my tornado script... ill post here as example of only using static property instead of saving entire class to a static instance... beware it is messy but u can see the tornado consists of 2 parts (actual script has 4 classes, i removed a bunch for easier reading) and each part must know the main class's position. In this script the One class informs where the tornado's position is... via static properties and i believe encapsulating is better style wink.gif

and of course if ur running multiple threads u have to write logic for that type of environment using loops to wait for stuff to happen etc etc... of course u learn these things when debugging

CODE
//needs refactoring but writing the class once and then just copy pasting was easy enough (might or might not compile... i removed a bunch of stuff)

namespace Tornado
{
   using System;
   using System.Windows.Forms;
   using System.Media;
   using GTA;
   using GTA.Native;

   public class One : Script
   {
       const float FORCE = 1f;
       private static Vector3 _center, _destination;
       private static float _distance = 150f;
       internal static Vector3 Center
       {
           get { return _center; }
       }
       internal static Vector3 Destination
       {
           get { return _destination; }
       }
       internal static float Distance
       {
           get { return _distance; }
       }

       Blip[] blipArray;
       Random random;

       public One()
       {
           random = new Random();
           blipArray = new Blip[21];
       }

       float i; bool upper = true; float z, radius = 1f, h, k, moveCount; int count, offsetDirection;

       private void Main_Tick(object sender, EventArgs e)
       {
           if (upper)
           {
               upper = i < 21;
               i = upper ? i : 19;
           }
           else
           {
               upper = i < 0;
               z = upper ? _center.Z : z;
               radius = upper ? 1f : radius;
               i = upper ? 1f : i;
               count = upper ? 0 : count;
               if (upper)
               {
                   foreach (Blip blip in blipArray) { if (Game.Exists(blip)) blip.Delete(); }
               }
           }

           if (moveCount == 0) offsetDirection = random.Next(0, 4);

           _destination = moveCount == 0 ? World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(random.Next(0, 100))) : _destination;
           _center = moveCount == 0 ? PlayerOffset(offsetDirection == 0 ? Vector3.RelativeLeft * _distance : offsetDirection == 1 ? Vector3.RelativeRight * _distance : offsetDirection == 2 ? Vector3.RelativeFront * _distance : Vector3.RelativeBack * _distance) : _center + Vector3.Normalize(_destination - _center);
           moveCount++;
           moveCount = moveCount > _distance * 2 ? 0 : moveCount;
           h = _center.X; k = _center.Y;
           float x = OffsetXY(h, radius - i * radius / 10);
           Vector3 offset1 = new Vector3(x, GetY(h, k, x, upper), z);
           blipArray[count] = Blip.AddBlip(offset1); blipArray[count].Scale = 0.5f;
           blipArray[count].Color = BlipColor.Cyan;
           z += 0.5f; radius++;
           if (upper) i += 2f;
           else i -= 2f;
           count++;

           foreach (GTA.Object obj in World.GetAllObjects())
           {
               if (!Game.Exists(obj)) continue;
               if (obj.Position.DistanceTo(_center) > 15f) continue;

               obj.ApplyForce(Vector3.Normalize(offset1 - obj.Position) * FORCE);
           }
           foreach (Vehicle veh in World.GetVehicles(_center, 15f))
           {
               if (!Game.Exists(veh)) continue;

               veh.ApplyForce(Vector3.Normalize(offset1 - veh.Position) * FORCE);
           }
           foreach (Ped ped in World.GetPeds(_center, 15f))
           {
               if (!Game.Exists(ped)) continue;

               ped.ApplyForce(Vector3.Normalize(offset1 - ped.Position) * FORCE);
               ped.ForceRagdoll(5000, false);
           }
       }

       private Vector3 PlayerOffset(Vector3 offset)
       {
           return Game.LocalPlayer.Character.GetOffsetPosition(offset);
       }
       private float OffsetXY(float hk, float offset)
       {
           return hk + offset;
       }
       private float GetX(float h, float k, float y, bool upper)
       {
           float x = (upper ? 1 : -1) * (float)Math.Sqrt(Math.Pow((double)radius, 2) - Math.Pow((double)y - (double)k, 2)) + h;
           return x;
       }
       private float GetY(float h, float k, float x, bool upper)
       {
           float y = (upper ? 1 : -1) * (float)Math.Sqrt(Math.Pow((double)radius, 2) - Math.Pow((double)x - (double)h, 2)) + k;
           return y;
       }
   }
   public class Two : Script
   {
       const float FORCE = 1f;
       Vector3 center, destination;
       Blip[] blipArray;

       public Two()
       {
           blipArray = new Blip[21];
       }

       float i; bool upper; float z, radius = 1f, h, k, moveCount; int count;
       private void Main_Tick(object sender, EventArgs e)
       {
           if (upper)
           {
               upper = i > -1;
               z = !upper ? center.Z : z;
               radius = !upper ? 1f : radius;
               i = !upper ? 1f : i;
               count = !upper ? 0 : count;
               if (!upper)
               {
                   foreach (Blip blip in blipArray) { if (Game.Exists(blip)) blip.Delete(); }
               }
           }
           else
           {
               upper = i > 20;
               i = !upper ? i : 19;
           }

           destination = One.Destination;//passing static vector3 to class Two which is updated by class One
           center = One.Center;
           moveCount++;
           moveCount = moveCount > One.Distance * 2 ? 0 : moveCount;
           h = center.X; k = center.Y;
           float x = OffsetXY(h, -1 * radius + i * radius / 10);
           Vector3 offset1 = new Vector3(x, GetY(h, k, x, upper), z);
           blipArray[count] = Blip.AddBlip(offset1); blipArray[count].Scale = 0.5f;
           blipArray[count].Color = BlipColor.Cyan;
           z += 0.5f; radius++;
           if (!upper) i += 2f;
           else i -= 2f;
           count++;

           foreach (GTA.Object obj in World.GetAllObjects())
           {
               if (!Game.Exists(obj)) continue;
               if (obj.Position.DistanceTo(center) > 15f) continue;

               obj.ApplyForce(Vector3.Normalize(offset1 - obj.Position) * FORCE);
           }
           foreach (Vehicle veh in World.GetVehicles(center, 15f))
           {
               if (!Game.Exists(veh)) continue;

               veh.ApplyForce(Vector3.Normalize(offset1 - veh.Position) * FORCE);
           }
           foreach (Ped ped in World.GetPeds(center, 15f))
           {
               if (!Game.Exists(ped)) continue;

               ped.ApplyForce(Vector3.Normalize(offset1 - ped.Position) * FORCE);
               ped.ForceRagdoll(5000, false);
           }
       }
       private Vector3 PlayerOffset(Vector3 offset)
       {
           return Game.LocalPlayer.Character.GetOffsetPosition(offset);
       }
       private float OffsetXY(float hk, float offset)
       {
           return hk + offset;
       }
       private float GetX(float h, float k, float y, bool upper)
       {
           float x = (upper ? 1 : -1) * (float)Math.Sqrt(Math.Pow((double)radius, 2) - Math.Pow((double)y - (double)k, 2)) + h;
           return x;
       }
       private float GetY(float h, float k, float x, bool upper)
       {
           float y = (upper ? 1 : -1) * (float)Math.Sqrt(Math.Pow((double)radius, 2) - Math.Pow((double)x - (double)h, 2)) + k;
           return y;
       }
   }
}


but as far as gta is concerned i always do this to simulate multithreading and if u know the code in each class runs independent then u write your code to deal with that... simple smile.gif

pedro2555
  • pedro2555

    Open Sourcer

  • Members
  • Joined: 02 Sep 2012
  • Portugal

#15

Posted 22 May 2013 - 12:15 AM

QUOTE (jitsuin666 @ Tuesday, May 21 2013, 06:46)
QUOTE
But you can only read data that way.


no i dont think so...

You think wrong then.

Here is a little detail inside of the problem.

As I said before, the OS handles when and for how long a thread runs, it can stop it at any time of execution, for later resume it, that is how multi thread works (remember this is logical threads and not CPU threads). You, the programmer, must assure this OS behaviour does not affect the program's execution.

Consider the following code, found it online, but nevertheless very good example.
CODE

void Method(object obj)
{
   if (obj == null)
 return;
   string s = obj.ToString();
   obj = null;
}

This code runs in two threads simultaneously. Now if thread 1 does 'obj = null', right ater the other verified it isn't null, you can see we have a problem.
This is an obvious example, so very easy to catch.

To fix this problem we can do something like :
CODE

void Method(object obj)
{
lock (this)
{
 if (obj == null)
  return;
 string s = obj.ToString();
 obj = null;
}
}


Lock, ensures that this code always finishes execution before another one starts. Now you can see one of the tools to allow thread safe code. No loops or weird stuff.

But for your case this doesn't matter, right ? Both threads run entirely different code, how can that affect anything ? Lets see why your code is thread safe.

Here is your code:

CODE

destination = One.Destination;//passing static vector3 to class Two which is updated by class One


So you are creating an instance version of the shared static member. First I would like to know why you did that ?

I do believe you did that with thread safety in mind. But that only works because C# ensures data integrity for you, and will still work if you work with only one version of the members data.

But even if you go the sensible way, and don't duplicate data. You can still be working on outdated data.

Here is how C# assures data integrity.
Consider the code :
CODE

i++;

If we examine the CIL instructions behind the scenes, we will see :
CODE

ldloc.0  (Load local variable 0 onto stack.)
ldc.i4.1 (Push 1 onto the stack as int32.)
add   (Add two values, returning a new value.)
stloc.0  (Pop a value from stack into local variable 0.)

'local variable 0', is our 'i'. As you can see even this simple, single line operation takes 4 instructions. First is good to point out that the thread running this can be stopped at any instruction to give time to another one, so even this simple action can have problems in multi thread.

But you can already see how data integrity works here. i is only updated after all calculations, meaning that it will always have a valid value and not a weird half-calculated one. But it can still be outdated.

So in your case C# is smart enough to guarantee your script works.

And as I said "you can only read data", but I will add : in a thread safe way.

Lord_of_Light
  • Lord_of_Light

    Player Hater

  • BUSTED!
  • Joined: 21 May 2013

#16

Posted 22 May 2013 - 12:57 AM Edited by Lord_of_Light, 22 May 2013 - 01:08 AM.

i get what ur saying about race conditions as code in the thread has to be written for that situation, if u were only dealing with one thread then u can just write one line of code after another

more complex programs that have multiple threads working and each other is dependent on each other's data then you can use another approach

here are good practices for threading by msdn

http://msdn.microsof...y/1c9txz50.aspx

but this isnt even the situation here... here u can create multiple classes inheriting script and to the gamer they run side by side wink.gif

so i get what ur saying and it all makes sense but why cant i set a static property for both classes to read, if i added the set accessor i could have class Two set the Destination vector3 of class One... works whenever i do it

maybe one day im working in a different environment and i find this causes problems... then i would use another approach but for gta it works fine

pedro2555
  • pedro2555

    Open Sourcer

  • Members
  • Joined: 02 Sep 2012
  • Portugal

#17

Posted 02 June 2013 - 02:32 AM

QUOTE (Lord_of_Light @ Wednesday, May 22 2013, 00:57)
i get what ur saying about race conditions as code in the thread has to be written for that situation, if u were only dealing with one thread then u can just write one line of code after another

more complex programs that have multiple threads working and each other is dependent on each other's data then you can use another approach

here are good practices for threading by msdn

http://msdn.microsof...y/1c9txz50.aspx

but this isnt even the situation here... here u can create multiple classes inheriting script and to the gamer they run side by side wink.gif

so i get what ur saying and it all makes sense but why cant i set a static property for both classes to read, if i added the set accessor i could have class Two set the Destination vector3 of class One... works whenever i do it

maybe one day im working in a different environment and i find this causes problems... then i would use another approach but for gta it works fine

Thank you for your time here.

Actually, I have just done some quick tests. And still, your approach may not be that helpfull, but I missed a point before.

As far as I can tell (haven't looked the source code yet), scripthook runs each script on an exclusive thread. To avoid race conditions, it simply locks every execution from every script, meaning that they actually wait for each other to finish before starting again. This is not a big problem because they don't actually wait, they donate CPU time.

But this is a catch 22. At least it might be for my case. The problem I'm facing is simple, I have to continuously keep polling player actions, by that I mean every 100ms or less. And along with that I have to keep updating multiple data over time, the update method is going to be huge at some point. And reserving less than 100ms for it to run, if it starts to take more than 100ms to finish (which will be the case) I have a lag problem.

Using another script to avoid this looks, to me, as 'putting the dust under the carpet'. What happens is polling script wants to run, data scritpt says 'hey, I haven't finished', and polling script is forced to give his action time. Meaning they don't actually run parallel. And again if the data script says that for 100ms, I still have a lag problem (better, but still a problem).

Am I missing something ?

My conclusion for now stays the same, for small issues around script data sharing, the approach posted by sextitsboobsyeah seems ok (keep in mind you can declare a varible as internal and avoid properties or data duplication, to grant full access in between scripts in the assembly file) but it should be used with caution. For more complicated data sharing, using the thread class seems to be the best option.


Soyeldiablo
  • Soyeldiablo

    Player Hater

  • BUSTED!
  • Joined: 31 May 2013

#18

Posted 03 June 2013 - 02:35 AM

basically u cant run gta code on a new thread so u do sh*t like this, if u navigate your code correctly it will work, like adding data validation before u run sh*t

just write me something to illustrate your point but in gta if u want two scripts running or just create one script with multiple timers to monitor actions as perceived by the gamer as simutaneous then i usually have no problem having two scripts share a static variable




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users