Jump to content
    1. Welcome to GTAForums!

    1. GTANet.com

    1. GTA Online

      1. Los Santos Drug Wars
      2. Updates
      3. Find Lobbies & Players
      4. Guides & Strategies
      5. Vehicles
      6. Content Creator
      7. Help & Support
    2. Red Dead Online

      1. Blood Money
      2. Frontier Pursuits
      3. Find Lobbies & Outlaws
      4. Help & Support
    3. Crews

    1. Grand Theft Auto Series

      1. Bugs*
      2. St. Andrews Cathedral
    2. GTA VI

    3. GTA V

      1. Guides & Strategies
      2. Help & Support
    4. GTA IV

      1. The Lost and Damned
      2. The Ballad of Gay Tony
      3. Guides & Strategies
      4. Help & Support
    5. GTA San Andreas

      1. Classic GTA SA
      2. Guides & Strategies
      3. Help & Support
    6. GTA Vice City

      1. Classic GTA VC
      2. Guides & Strategies
      3. Help & Support
    7. GTA III

      1. Classic GTA III
      2. Guides & Strategies
      3. Help & Support
    8. Portable Games

      1. GTA Chinatown Wars
      2. GTA Vice City Stories
      3. GTA Liberty City Stories
    9. Top-Down Games

      1. GTA Advance
      2. GTA 2
      3. GTA
    1. Red Dead Redemption 2

      1. PC
      2. Help & Support
    2. Red Dead Redemption

    1. GTA Mods

      1. GTA V
      2. GTA IV
      3. GTA III, VC & SA
      4. Tutorials
    2. Red Dead Mods

      1. Documentation
    3. Mod Showroom

      1. Scripts & Plugins
      2. Maps
      3. Total Conversions
      4. Vehicles
      5. Textures
      6. Characters
      7. Tools
      8. Other
      9. Workshop
    4. Featured Mods

      1. Design Your Own Mission
      2. OpenIV
      3. GTA: Underground
      4. GTA: Liberty City
      5. GTA: State of Liberty
    1. Rockstar Games

    2. Rockstar Collectors

    1. Off-Topic

      1. General Chat
      2. Gaming
      3. Technology
      4. Movies & TV
      5. Music
      6. Sports
      7. Vehicles
    2. Expression

      1. Graphics / Visual Arts
      2. GFX Requests & Tutorials
      3. Writers' Discussion
      4. Debates & Discussion
    1. Announcements

    2. Support

    3. Suggestions

Scripthook multiple timers


pedro2555
 Share

Recommended Posts

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 :

 

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

 

 

new_timer_Tick :

 

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

 

 

But it doesn't work, the line

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.

Link to comment
Share on other sites

lindsayslorach

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).

Link to comment
Share on other sites

NomeSkavinski

Try reordering the start up procedure to:

 

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.

Link to comment
Share on other sites

lindsayslorach

@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);

Link to comment
Share on other sites

Try reordering the start up procedure to:

 

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

Prof_Farnsworth

 

~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:

 

 

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.

Link to comment
Share on other sites

pedro2555
~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:

 

 

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.

Link to comment
Share on other sites

sextitsboobsyeah

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

 

 

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");       }   }}

 

Edited by sextitsboobsyeah
Link to comment
Share on other sites

pedro2555

 

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.

Link to comment
Share on other sites

sextitsboobsyeah

 

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)

Edited by sextitsboobsyeah
Link to comment
Share on other sites

sextitsboobsyeah

 

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

 

 

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

Edited by sextitsboobsyeah
Link to comment
Share on other sites

  • 3 weeks later...

 

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

 

 

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:

 

internal static string _scriptName;

 

instead of :

 

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).

 

Link to comment
Share on other sites

jitsuin666

 

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

 

 

//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

Edited by jitsuin666
Link to comment
Share on other sites

 

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.

 

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 :

 

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:

 

 

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 :

 

i++;

 

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

 

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.

Link to comment
Share on other sites

Lord_of_Light

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.microsoft.com/en-us/library/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

Edited by Lord_of_Light
Link to comment
Share on other sites

  • 2 weeks later...
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.microsoft.com/en-us/library/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.

 

 

Link to comment
Share on other sites

Soyeldiablo

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

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • 1 User Currently Viewing
    0 members, 0 Anonymous, 1 Guest

×
×
  • Create New...

Important Information

By using GTAForums.com, you agree to our Terms of Use and Privacy Policy.