Sharing variables between plugin sections

JohnW63

I know this must be a beginners question, with C++, but my web surfing hasn't answered it, because most examples talk about programs with a main() not working with DLLs.. Here goes.

I want to use some conditional statements that use both tire temps and if the car is in the pits. The temps are referred to in the ExampleInternalsPlugin::UpdateTelemetry section of the Examples.cpp file and the check for being in the pits is referred to in the ExampleInternalsPlugin::UpdateScoring section of the same file. If these are separate Object instances, is there a way to pull data from each one to be able to use, or do I have to stay within the Object to use that data ?

Obviously, both of these point to structs in the header file, InternalsPlugin.hpp

Ideally, I would like to have my own Object that has pointers pointing at both scoring data and telemetry data.

My C++ instruction was almost 20 years ago, so please excuse my ignorance.
 
Global variables?

Scoring is updated twice a second... so create your own struct to store the info you want (or all the scoring info if you want.. doesn't take long) and refer to that when using the 'live' data in UpdateTelemetry's info var.

One more global var to set to true when the scoring has been updated, false when a new session starts (so you don't try to use the pit status before it's been updated), and you're good to go.

This is pretty basic stuff I have to say - has to be for me to be answering - you might be well served to go find some C/C++ tutorials/examples to refresh your memory.

Edit: Don't be put off by the fact it's a DLL... you can define global variables just like a normal program, and even functions outside of the whole ExampleInternalsPlugin:: framework.
 
I have been looking up tutorials. I just read through another one, which brings up this question.

Since many of the classes are defined as structs, which grant public access to the data within them, wouldn't they be accessible at any time ? I guess I need to read up on the difference between a global variable and a public one.
 
do not use global variables ! :)
It is worse practice ever.

Use class attributes instead, static ones repsectively. Static attributes/methods are accessible without creating an object from any point of code.
 
Last edited:
By using Static calls, I avoid changing the data I am pointing to, right ?
 
Yeah, global vars are an ugly solution... but they do work, and for someone more used to C than C++ (like me) they were the 'easier' option lol
 
Since many of the classes are defined as structs, which grant public access to the data within them, wouldn't they be accessible at any time ? I guess I need to read up on the difference between a global variable and a public one.

The functions you've referred to above (UpdateTelemetry, UpdateScoring, etc) are called by the main rFactor code, which passes in a pointer to the current data. So in each one you only have access to the data pointed to - not what is available in the other(s) when they are called. If you want to keep some of the data from one for use in the other, you need to store it yourself.

Since scoring is updated only twice a second, instead of the telemetry's 90 times a second, it makes sense to store the scoring info and use it as needed in UpdateTelemetry.

Using static attributes (as part of the existing class, rather than my ugly global variables solution) means there isn't any worry about instance variables - you'll have one class-wide value, so nothing gets created and it's all simpler. It's kind of like global variables in a class sense.
 
I have been continuing my crash course on C++ and I think I am getting closer to keeping the syntax straight. Maybe.

Can I use pointers to each class member function, such as a pointer to the UpdateScoring and another to the Update Telemetry and use them in calculations ? Something like this :


While ( ScoringPtr.mInPits == true ) && ( TelemPtr.velocity == 0 ) {

fprintf ( " Tire temp is :%.2f " , TelemPtr.mTireTemperature ) ;

}

The goal is to determine if they changed tires during a pit stop, and what that temperature is.


Or, would it simply be better to store the data in the update scoring section, and use that in the update telemetry section so I can do this ? I'm not sure how to make these "seen" by the other function. That's why I thought pointers to the data would work.
 
Last edited:
Hmm... I think I saw your previous post before your edit, and I figured MaXyM would be able to answer it because I'm not sure what you can or cannot rely on when it comes to function/data pointers... but looking at your 'example code' personally I think you're going about it the wrong way.

The ExampleInternalsPlugin class, which has the functions that are called by rFactor at specific points, also has at least one attribute/property (mET I think... don't have the code here) which is used to track the elapsed time. This is stored as a part of the class, so that no matter which function is currently executing that data is available.

What you want to do is the same thing for the data you need; if you want to step through all the vehicles in UpdateScoring, find the player's car, and check whether it's in the pits, you can create a class variable called CarInPits (or something), set it to true when you find the player's car is in the pits, or false otherwise. Since that var is then accessible from anywhere in the class you can read the value in UpdateTelemetry and use that in your test.

Looking at your code though, you probably need to think about how you're going to sequence it. That While loop will crash the game with an infinite loop because mInPits won't change and neither will the vehicle speed (remember you need to calculate the current speed, as they do in the example plugin; also, testing for 0 could potentially cause an issue if for whatever reason the car isn't sitting absolutely still - might be better to test for <0.x m/s instead), so as soon as that loop condition tests true it'll keep on looping and printing.

I'm not really sure what you're aiming for with the tyre temps, so I won't make any suggestions on how to deal with it.

In summary: I wouldn't try to link to functions or addresses where the incoming data was when each function was called. Create your own class-level variables (attributes, properties... I don't remember the right name) to store the data you need and use those as a link between your functions.

You would have come across a lot of examples of class properties, and the fact it's a DLL doesn't change how you do that.
 
The problem I have had with storing things is the way drivers get slots as they join the server. For example I store the pit status in an array that is indexed to mach the way the drivers are indexed in info.mVehicle. The problem is this index gets messed up in practice sessions as drivers come and go from the server. In race sessions this index is reliable because the session is closed. C++ is far from my native language so coding around this problem has been a pain.

JohnW63, here is one way to store the pit status:
Code:
int inPits[200];

void ExampleInternalsPlugin::UpdateScoring( const ScoringInfoV2 &info )
{
    for( long i = 0; i < info.mNumVehicles; ++i )
    {
        VehicleScoringInfoV2 &vinfo = info.mVehicle[ i ];
        inPits[i] = vinfo.mInPits;
    }
}

inPits can then be accessed from UpdateTelemetry. But as I stated above, sometimes the index between info.mVehicle and inPits gets out of sync. MaXyM probably has some hints on how to work around this.
 
Oh nooo! You used a global variable! ;)

The OP did talk about the player's car though... so can test for it being the player's car and just store that pit status.
 
I simplified things a bit presuming that driver name is unique.
It is 99,99% true while using in leagues (LVplugin or rF Admin Automation)
So, I decided to use hash_map to store drivers data, with driver name used as hashmap key. As value you can use any object you want.

And yes. C++ comparing to languages like C# is very uncomfortable. But after managing some basics (like how to make hash_map working) and wraping those into class, it will make work "possible" to do ;)
 
Lazza,

You're right. The while loop locked the game up. I had better luck with an if loop instead. You're also correct on the velocity thing. I found that I had to make "stopped" be equal to 0.0002 metersPerSecond. That was a surprise.

The reason I need the Tire Temps is because we have setup each tire compound to start with a different temp. We are requiring the drivers to use BOTH compounds during the race. By determining what tires they select at each pit stop, I can tell if they are following the rules.
 
Ok, yep, I know the feeling :)

I took a different approach to the problem, but there's no reason this way won't work. Testing for .mInPits is fair enough, but rather than testing for the car being 'stopped' I would store and check the condition of the tyres; as soon as the 'wear' (which actually starts at 1.0 and reduces with wear) increases from the previous update, you know that tyre has been changed. Then you can check the temperature, and determine the compound.

This way your test will only be triggered once (when the tyre is changed) rather than at 90Hz for the entire time the car is stationary. Also if a car comes into the pits with hot tyres it could trick your code as soon as it comes to a stop.

As with the speed you can't rely on exact figures, but if one compound starts at 85 and the other starts at 90, just test for >89 to detect the second one. If your pitmenu rules are set so that all 4 tyres are changed you need only check 1 tyre.

Might be an idea to limit it to race sessions when you've tested that it works too... and as for how you're reporting / recording it.. hmm... I'll leave that up to you lol
 
Lazza,

That's true. If I could use all telemetry data, I would be done by now. What I was thinking was wear goes to 1.0 on all four tires and the tire temp is the same on all 4 tires might be enough. I was going to check the tire load variable to see if the load goes to 0, when you're on the jacks too.
 
MaXyM, I just checked out your "rF Admin Automation" tool and thought it was really cool. It inspired me to write my own tool with functionality customized to our league. This tool is going to be a life saver for us. I plan to have commands like /!!warmup 5 which will send a chat message saying "Server will advance in 5mins" and then after 5 mins it will advance and then automatically set the grid.

We also send a series of messages before the race going over any directions that non forum readers probably missed. I can now send a command to trigger these messages. It is working when minimized too. I haven't tested it yet on a disconnected terminal server session. This has caused problems for me in the past for automation.
 
I just moved the app over to my server and tested it in a disconnected TS session and it is still working perfect. Wahoo. :)
 
My rFAA also works when minimized. But doesn't work when running with oneClick option. It's just because I'm sending commands/chat messages to dedicated server's window which is not initialized when running with oneClick option.

Have you developed some better way to send commands/chat?
 
The GUI is still visible when using +oneclick. It just advances through all the setup screens and then minimizes. My automation is working just fine even when using +oneclick.
 
68225_110033189068169_100001844651544_84539_7974336_n.jpg
 
I have found that checking for all temps to be equal, the tire load checking for 0 ( up on jacks ), and wear equal to 1.0 is enough to determine a pit stop and tire change. I'm now wrestling with getting only ONE instance to print out, rather than 90hz of them. Even on the jacks, the telemetry still goes on, doesn't it. The other issue is getting it to ignore the time the car is sitting in the garage. It seems to be on the jacks in there too.
 
You need to add code to slow it down for sure. If you run 90hz you will probably cause performance problems.

This is how I did it in my plugin:
Code:
float telemUpdateInterval = 0.5f;

void ExampleInternalsPlugin::UpdateTelemetry( const TelemInfoV2 &info )
{
	if(timeSinceLastUpdate < telemUpdateInterval){
		timeSinceLastUpdate += info.mDeltaTime ;
		return;
	}
	// Do some stuff
	
	timeSinceLastUpdate = 0.0f;
}

So you have a global for your update interval. In the example it is set to 0.5f (500ms or a half a second). Then when UpdateTelemetry is called it first checks to see if it has been 500ms since the last update, if it hasn't, it increments the timer and the returns without doing anything else. If it has been a 500ms+ then it does your work and then at the end of your work it resets your timer to 0.
 
I have found that checking for all temps to be equal, the tire load checking for 0 ( up on jacks ), and wear equal to 1.0 is enough to determine a pit stop and tire change. I'm now wrestling with getting only ONE instance to print out, rather than 90hz of them. Even on the jacks, the telemetry still goes on, doesn't it. The other issue is getting it to ignore the time the car is sitting in the garage. It seems to be on the jacks in there too.

Noel's given a good example to help, but I think you're still making this more complicated than it needs to be.

Those figures you're checking are probably fairly foolproof (wear should be 1.0 on at least the update after the tyre is changed, load will probably be ~0.0 during a pitstop - equal temps sounds slightly dodgy, may well work all the time but since they start cooling the moment they're fitted you do run the risk of a 0.000001 difference for whatever reason) but as you're finding you'll have entire seconds where testing those conditions will return TRUE. You're better off using something that will only trigger once per tyre change.

Assuming you'll always have tyre wear on when using this plugin, the simplest method I can see is to store your previous wear. Using something like this: (note: I don't have the source here... some function names are bound to be wrong)

Code:
float prevRLWear;

(rear-left; a driven tyre will absolutely ensure some wear, you could also get the _min() of front and rear on one side, or even all 4 tyres, but every tyre's wear will drop to 0.99999 pretty much as soon as you move)

In RealtimeEntered(), set prevRLWear to the starting value (1.0), and then in UpdateTelemetry() you need only check if the current RL wear is greater than prevRLWear. If it is, the tyre has been changed, so you can do your output or whatever. Finally, you set prevRLWear to the current RL wear (regardless of whether it was changed) for checking next time around.

This will run your output code once per tyrechange, and never in-between.

Also, if you want to avoid output or testing while in the garage/monitor, use a variable:

Code:
bool inCar = false;

In RealtimeEntered(), set it to true. In RealtimeExited() (or whatever it's called ;)) set it to false. Then the first thing you do in UpdateTelemetry is

Code:
if (inCar) {
   // test for tyrechange
   ...
}

and no testing will happen at the monitor.

If your series/mod allows tyres to be changed independently (left side, right side, front or rear, vs all 4 all the time) you might need 2 or 4 vars to check for tyre changes, but it'll still work the same way for each.
 
Lazza,

But isn't the inCar variable part of the UpdateScoring section, rather than the Update Telemetry section ? My C++ knowledge is too old to get the darn syntax correct to access one part from another.

I think I understand what you are suggesting. I need to write it down and check it out. My current thought is to check for the tire change and store the temp. If there car hasn't moved, I don't override the stored temp. If I could store it and not allow an over write until the inPits variable changed to false, that would work, I think .
 
But isn't the inCar variable part of the UpdateScoring section, rather than the Update Telemetry section ? My C++ knowledge is too old to get the darn syntax correct to access one part from another.

Nope, inCar is a variable I'm suggesting to declare as a global within the .cpp file.

Just throw
bool inCar = false;
near the top of the file somewhere, and you can access it from any function.

This is the simple, ugly, C way of doing it. MaXyM obviously isn't a fan, but it works...

You set it to false by default because you don't start in the car. When you enter the car, EnterRealtime() is called, so put inCar = true; in there. Whenever you exit the car (back to the monitor), ExitRealtime() is called, so put inCar = false; into that.

Now you can check the value of inCar (true or false) from any function (UpdateTelemetry(), UpdateScoring(), ...).

You can do exactly the same thing (declaring a global variable and accessing it from anywhere) with tyre temps, wear, current laps completed, ... anything you want. You just need to work out what you need to know at a given time (be it some telemetry data when you're processing a scoring update, or vice versa, or comparing current data to some previous ones) and store it as necessary.

I'm not giving you 'complete' example code because it won't help you understand what you're doing. You're just a bit stuck on this being a DLL at the moment, once you get past that you can concentrate on having your code actually do what you want.
 
Another simple question:

While the example files use different files for output for each section, can I have both the scoring and telemetry classes open and output to the same text file ? That way, all the data I am looking for would be in one file.
 
Yes, the UpdateTelemetry and UpdateScoring functions (not 'classes') contain code to append to an existing file ("a" in the fopen() call). So if you give the same filename in both functions they will append to the same file.
 
That's how I assumed it would work, but I didn't know if the file was "locked" while one was writing to it.
 
rFactor calls UpdateTelemetry 90 times a second. It calls UpdateScoring twice a second. When it's calling one, it's not calling the other... because it's one rFactor, not two (or more).
 

Back
Top