Skip to content
View in the app

A better way to browse. Learn more.

LCPDFR.com

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Tips for Developers

Featured Replies

  • Popular Post

So being that I am a software developer for a living, I thought I would create this thread to share some tips with fellow developers...especially those who are new.

Anyone can contribute to this topic...I myself am a newbie when it comes to RAGE Plugin Hook and LSPDFR. So anything useful that you come across, feel free to post it!

Some of what I have written below may not make sense to a new programmer, so if you have questions, or it seems like I am speaking Martian, feel free to ask questions!!

I'll start off by saying...

  • ORGANIZE YOUR CODE!!
    • This means folders and namespaces for just about everything. Don't keep all your classes at the root level, and do not, for god's sake, do every part of your code in the Main class. Feel free to ask questions about this if you are confused, but LukeD's example API is a great way to start with namespaces.
       
    • Take advantage of inheritance and polymorphism! Use base classes and interfaces for everything! (Callouts, pedestrians, vehicles, etc)
      • For example, I have started on the framework for my callout plugin. I have a CalloutBase class, which inherits the LSPDFR Callout class, and it has all of my method overrides. The overridden methods in CalloutBase perform any actions which are common to all of my callouts. Each callout type will have its own class, which would, you guessed it: inherit the CalloutBase class.
      • My CalloutBase class implements my "ICalloutBase" interface. The interface is for declaring methods or properties that CalloutBase may not be able to do anything with, but if you use the abstract keyword in C# (or MustOverride in VB.NET), then each callout class/type you create would have it's own set of actions for that function.
        • For example, my ICalloutBase interface declares a method called "OnArrivalAtScene()". The CalloutBase class implements this as an abstract method, meaning, any class that inherits CalloutBase (say, ShotsFiredCall, for example) has to have a method called "OnArrivalAtScene", which would be a set of actions for the program to take when the officer arrives on scene OF THE SHOTS FIRED CALL ONLY.
        • The abstract method I just created would be called in the Process() method of the CalloutBase class. Remember, Process() is an LSPDFR method that is called continuously. CalloutBase performs actions which are common to ALL callouts, including calling the OnArrivalAtScene() method. In CalloutBase.Process(), I check to see if the player's location is getting close to my callout location. If so, and if the callout state is still "Unit Responding", then OnArrivalAtScene() is called, and the state is changed to "At Scene".
        • Now, when you create a ShotsFiredCall class, there is no need to call OnArrivalAtScene() from ShotsFiredCall.Process(), because CalloutBase.Process() has already done it for you!
        • The example I stated above requires you to track the state of the callout (i.e. Dispatched, Unit Responding, At Scene, Call Completed, Call Cancelled, etc), but it just gives you an idea on how efficient you can make your code. Its a bit of legwork in the beginning, but it pays off BIG TIME as your plugin grows and matures.
      • Using base classes and interfaces is not only useful for Callout classes. You can use them to track which Peds are suspects, and which are civilians, or what role a certain vehicle plays in your callout. You can add properties to Peds that don't exist in the Rage Plugin, like "MyPlugin.MySuspect.IsBankRobber", for a simple example.
         
  • ERROR HANDLING!!
    • Always, always, ALWAYS use proper error handling. One bad block of code can crash a user's entire game.
    • Use Try...Catch blocks, and use the Catch block to log exceptions, so that when a user says "Your plugin is shit!!", then their log file will tell you what is wrong.
    • Always use Ped.Exists or Vehicle.Exists (I think its called Exists for cars) to make sure that GTA V has not disposed of your object. GTA V does not mess around in terms of garbage collection and memory management. Always check if an object is null, and then if it has an Exists property, check that as well.
       
  • ANYONE CAN CODE!!
    • VB.NET and C# are both very easy to learn. It does take years to master programming, and a university education in Computer Science or Software Engineering definitely helps. But if you're a newbie, and want to learn, jump in with both feet! Search Google for tutorials, or buy a book at your local book store. I guarantee that you can write a Hello World! program in less than 5 minutes!
       
  • VISUAL STUDIO IS FREE!!
    • That's right! Visual Studio Community Edition 2013 is completely free. It doesn't have all the features that the pros use, but who cares? You aren't a software company!
       
  • USE A CODE REPO AND SOURCE CONTROL!!
    • When you download Visual Studio Community, you get the option of creating a Visual Studio Online account. DO IT!
    • Visual Studio Online gives you free and unlimited access to code repositories where you can store and back up your code. If any programmers are reading this, they provide both TFS and Git based repos.
    • Source Control allows you to keep track of each change you make to your code. Last update crashed everyone's GTA? No problem, just roll back your changes! Computer hard drive crash? Laptop stolen? No problem! Once you're up and running, install Visual Studio again, connect to your code repo, and all your code is back!
    • For those that use TFS, you can create work items for yourself to track all changes that you need to make to your plugin. Each time you check in code, you can tie a set of changes (called a changeset) to a specific work item.
       
  • BRANCH YOUR CODE!!
    • This, at an elementary level, means to maintain two copies of your code. Your main branch (also referred to as your 'master' or 'trunk' branch) contains the most stable version of your code. Your second branch is called your 'dev', or development branch. All of your code changes and testing should be made on the dev branch.
    • When you're ready to release, you merge all of the relevant changes from your dev branch to the trunk. This allows you to make different code changes to your project, all in parallel.
    • This allowed me to recently release a LSPDFR 0.3 compatibility patch for Code 3 Callouts (I made those code changes in my trunk branch), while still working on the next update in my dev branch.
    • See this MSDN link for more details: https://msdn.microsoft.com/en-us/library/ee782536.aspx 
       
  • CHECK ALL DEPENDENCIES ON STARTUP!!
    • Check file versions on EVERY DLL or assembly referenced by your project. You don't need to check the Microsoft .NET ones, but you should be checking file versions on RagePluginHook.exe, the LSPDFR DLL, and any other DLL (including RageNative UI, a Common DLL you've written yourself, etc) that is used by your project and needs to be installed in the GTA V folder.
    • And NO, do NOT, I repeat, NOT use the Assembly class to load the DLL's into memory. That is just a waste of resources. Use the GetVersionInfo function of the System.Diagnostics.FileVersionInfo class instead.
       
  • More to come...

Edited by Stealth22

Stealth22
LSPDFR Tester | Plugin Developer
My Plugins: Code 3 Callouts | Traffic Control | Keep Calm | ALPR+

Please do not PM me for any kind of technical support.
I unfortunately do not have enough free time to answer every PM that I get. For issues with my plugins, please post in the comments section of the file, or it's forum thread. You'll get a much quicker response from me there than if you send me a PM; I do my best to respond to every question in the comments sections. For API/programming questions, please post them in the API Development forum, so all developers can benefit from the answer as well. Thanks!

  • Replies 20
  • Views 6.5k
  • Created
  • Last Reply

Top Posters In This Topic

Most Popular Posts

  • Thanks for that, I'm sure it will help people. A few things I'd like to add...   Don't sleep in your main thread!If you do, I will pay you a visit and it won't be nice! No, but seriously, the Process 

  • It's totally fine to do it like this. I was more talking about sleeping (also longer than 0 ms which is what Yield does) in the callout's Process function. What you do in a separate game fiber is comp

  • Stealth22
    Stealth22

    This step is done after compiling your DLL. Whichever tool you use, it will want to know where to find the Rage SDK and LSPDFR DLL's. Just keep copies of those two in the same folder as your callout D

  • Management Team
  • Popular Post

Thanks for that, I'm sure it will help people.

A few things I'd like to add...

 

  • Don't sleep in your main thread!
    • If you do, I will pay you a visit and it won't be nice! No, but seriously, the Process function is called every tick from LSPDFR's CalloutManager and if you decide to sleep there, you will block other callouts from working correctly. If you need to await something, do it in a separate GameFiber (if it requires game logic) or System.Threading.Thread if not.
  • Use logging!
    • Not only as a beginner, you should use logging extensively. You can create overloads of your logging function so that it only happens in debug build (like most LSPDFR logging) or under certain circumstances (such as logging levels), but use it. It will help you to better understand what went wrong on a user's end. Especially since as a beginner you probably have no real experience with debugging or checking crashes via crash offsets. Note that debugging V is unfortunately not really possible right now, as the DRM doesn't really like it. I didn't test on 393 yet, but earlier versions didn't work well.
  • Keep an eye on performance
    • Every now and then it might be a good idea to measure the time the main loop (your Process function) takes to run. 1-2 ms? Good! 20ms? Not so good... Don't be afraid to add a lot of general logic, your CPU can handle it. Game calls and poor code (such as unnecessary loops) will most likely be the cause of performance issues.
  • Use comments
    • I'm not an idiot, I know what I'm doing here. Sure, now you do. But will you remember why you passed that 0x2 value to the native call in 2 weeks? Chances are you will not. And why did you apply the Wait task to the passenger again? Use comments to describe why you are doing such actions. It will help you later on, trust me.

Please do not PM me unless really necessary (knowing you helps). If you think you need my attention in a topic, tag me.

What program will you recommend for programming C#? I want something clean and easy to use. Any tutorial worth mentioning? Thanks for your help!

:-D

Edited by yaucpthomas

What program will you recommend for programming C#? I want something clean and easy to use. Thanks :D

  • VISUAL STUDIO IS FREE!!
    • That's right! Visual Studio Community Edition 2013 is completely free. It doesn't have all the features that the pros use, but who cares? You aren't a software company!

logov1_zpsd8d8fbe3.jpeg

Help my channel grow and I can explain to my wife why this addiction to coding is a good thing!

 

  • VISUAL STUDIO IS FREE!!
    • That's right! Visual Studio Community Edition 2013 is completely free. It doesn't have all the features that the pros use, but who cares? You aren't a software company!

Oops didn't see that! XD Thanks!!!

  • 2 weeks later...
  • Management Team

It's totally fine to do it like this. I was more talking about sleeping (also longer than 0 ms which is what Yield does) in the callout's Process function. What you do in a separate game fiber is completely okay and even necessary here. Keep it up!

Please do not PM me unless really necessary (knowing you helps). If you think you need my attention in a topic, tag me.

  • Author

I wonder if anyone has any tips for protecting your plugin from being re-uploaded and protecting your code from being decompiled and accessed? I had a nasty experience with an unauthorised re-upload of my mod.

Yup, its called obfuscating your code. There are .NET tools that will do this. I prefer not to say which one I am using, but there are several available, including free ones.

Stealth22
LSPDFR Tester | Plugin Developer
My Plugins: Code 3 Callouts | Traffic Control | Keep Calm | ALPR+

Please do not PM me for any kind of technical support.
I unfortunately do not have enough free time to answer every PM that I get. For issues with my plugins, please post in the comments section of the file, or it's forum thread. You'll get a much quicker response from me there than if you send me a PM; I do my best to respond to every question in the comments sections. For API/programming questions, please post them in the API Development forum, so all developers can benefit from the answer as well. Thanks!

  • Author

Will do it immediately, thank you.

This step is done after compiling your DLL. Whichever tool you use, it will want to know where to find the Rage SDK and LSPDFR DLL's. Just keep copies of those two in the same folder as your callout DLL when you go to obfuscate it. This goes for any DLL's that you reference in your project, by the way.

I have a build folder on my hard drive that I copy all of my compiled DLL's into. (i.e. C:\Builds\Code 3 Callouts\0.1.0)

I compile my DLL with Visual Studio, then copy that DLL to my build folder, along with all my referenced DLL's, and obfuscate it. Then, I test the obfuscated DLL in GTA V, and upload it.

Also, when obfuscating, if you get any errors that it can't find an assembly called "RagePluginHook", and your RagePluginHookSDK.dll is in the same folder as your DLL, rename it to RagePluginHook.dll. I really pulled my hair out until I figured out that one.

Stealth22
LSPDFR Tester | Plugin Developer
My Plugins: Code 3 Callouts | Traffic Control | Keep Calm | ALPR+

Please do not PM me for any kind of technical support.
I unfortunately do not have enough free time to answer every PM that I get. For issues with my plugins, please post in the comments section of the file, or it's forum thread. You'll get a much quicker response from me there than if you send me a PM; I do my best to respond to every question in the comments sections. For API/programming questions, please post them in the API Development forum, so all developers can benefit from the answer as well. Thanks!

  • 5 months later...
On 7/17/2015 at 11:37 PM, LMS said:

Note that debugging V is unfortunately not really possible right now, as the DRM doesn't really like it. I didn't test on 393 yet, but earlier versions didn't work well.

Has there been any progress with this? It would be really nice to be able to interactively debug an LSPDFR plugin. 

[REL] Coastal Callouts: An action-packed mod with new vehicles, maps, capabilities, and callouts in and around the waters of Los Santos

[REL] Police Tape: Make your scenes more realistic while stopping peds and traffic

[REL] Better EMS: Realistic and dynamic EMS response

Join the Parks Benefactor Program to support my work and get early beta access!

  • Management Team
On ‎1‎/‎25‎/‎2016 at 4:53 AM, PNWParksFan said:

Has there been any progress with this? It would be really nice to be able to interactively debug an LSPDFR plugin. 

When nopping/hooking the anti debug checks, at least unmanaged debugging works fine for me. Managed only works through WinDbg for me though, VS fails to get notified. Not sure, but maybe they mess with the remote debugging thread or something.

Please do not PM me unless really necessary (knowing you helps). If you think you need my attention in a topic, tag me.

  • Author

Added a tip to the original post re: branching your source code.

Stealth22
LSPDFR Tester | Plugin Developer
My Plugins: Code 3 Callouts | Traffic Control | Keep Calm | ALPR+

Please do not PM me for any kind of technical support.
I unfortunately do not have enough free time to answer every PM that I get. For issues with my plugins, please post in the comments section of the file, or it's forum thread. You'll get a much quicker response from me there than if you send me a PM; I do my best to respond to every question in the comments sections. For API/programming questions, please post them in the API Development forum, so all developers can benefit from the answer as well. Thanks!

  • 2 weeks later...
  • Author

Here's another one. Check your DLL references on startup!! It's good practice, it reduces crashes, and it makes your users smarter!! Not to mention it reduces the number of support requests that make you pull your hair out. :woot:

And PLEASE, for the love of God, do NOT use the Assembly class to load a DLL into memory JUST to check it's version number. You're loading a DLL twice by doing that, and wasting valuable memory. Use the GetVersionInfo function of the FileVersionInfo class instead.

1 minute ago, Stealth22 said:

For any devs using RageNative UI, are you guys checking the DLL version on startup? I know that Alex changed some of the class names recently, so it would break a few plugins if they aren't updated.

As a rule, you should be checking file versions on EVERY dependency of your plugin. This includes RPH, LSPDFR, RageNative UI, and any other DLL referenced by your project that needs to be present in the GTA V directory.

Now that being said, some of them will still crash on startup if .NET goes to use/reference them when your plugin is loaded. But in most cases, it should at least let you exit the plugin gracefully without crashing.

 

Stealth22
LSPDFR Tester | Plugin Developer
My Plugins: Code 3 Callouts | Traffic Control | Keep Calm | ALPR+

Please do not PM me for any kind of technical support.
I unfortunately do not have enough free time to answer every PM that I get. For issues with my plugins, please post in the comments section of the file, or it's forum thread. You'll get a much quicker response from me there than if you send me a PM; I do my best to respond to every question in the comments sections. For API/programming questions, please post them in the API Development forum, so all developers can benefit from the answer as well. Thanks!

2 hours ago, Stealth22 said:

Here's another one. Check your DLL references on startup!! It's good practice, it reduces crashes, and it makes your users smarter!! Not to mention it reduces the number of support requests that make you pull your hair out. :woot:

And PLEASE, for the love of God, do NOT use the Assembly class to load a DLL into memory JUST to check it's version number. You're loading a DLL twice by doing that, and wasting valuable memory. Use the GetVersionInfo function of the FileVersionInfo class instead.

 

I completly agree, but what if you're checking version using Assembly.getName().Version, on assembly from inside its code, I mean on-self checking version of assembly, It should not load twice, as its already loaded ?

Edited by w35

  • Author
34 minutes ago, w35 said:

I completly agree, but what if you're checking version using Assembly.getName().Version, on assembly from inside its code, I mean on-self checking version of assembly, It should not load twice, as its already loaded ?

Sorry, I should have clarified. Assembly.GetName() is okay, as long as its done properly.

For example:

Assembly assembly = Assembly.LoadFrom("MyAssembly.dll");
Version ver = assembly.GetName().Version;

is WRONG. The reason is, the LoadFrom function loads MyAssembly.dll into the AppDomain, which is a bad idea. It could cause an issue, and wastes memory.

What you want to do is this:

// Get the file version for the notepad.
FileVersionInfo myFileVersionInfo = FileVersionInfo.GetVersionInfo("RagePluginHook.exe");
// Or.....  FileVersionInfo myFileVersionInfo = FileVersionInfo.GetVersionInfo("Plugins\LSPD First Response.dll");

// Print the file name and version number.
Console.WriteLine("File: " + myFileVersionInfo.FileDescription + '\n' +
                  "Version number: " + myFileVersionInfo.FileVersion);


And if you want to get your OWN assembly's version...

System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
string version = fvi.FileVersion;

// You can also do assembly.GetName().Version -- This gets you the assembly version, whereas the above code gets you the file version
// For our purposes, they are going to be the same, as long as you make them the same in your Application Properties.

I personally code in VB.NET for my plugins, so I just use My.Application.Info.Version. There is no such property in C#, but the research I have done says that GetExecutingAssembly is the correct way of doing it. You can use either Assembly.GetName().Version (as long as you don't use Assembly.LoadFrom!) or FileVersionInfo for your own DLL version, as they will be the same value 99% of the time.

But if you're checking a different DLL/EXE (i.e. anything but the executing plugin), its better to use FileVersionInfo.GetVersionInfo(), and then use the FileVersion property. Its way less overhead than System.Reflection.

See the following Stack Overflow posts for more details (Disclaimer: The code snippets above are from the following two links)

http://stackoverflow.com/questions/1755504/programatically-get-the-version-number-of-a-dll 

http://stackoverflow.com/questions/909555/how-can-i-get-the-assembly-file-version 

EDIT: Make sure you call File.Exists(pathToAssembly), which returns true/false, first. If you try to check the version of a file that does not exist, you're going to get an exception, lol. And the File class is under the System.IO namespace, FYI, so you'll need to add "using System.IO;" at the top of your code file.

Edited by Stealth22

Stealth22
LSPDFR Tester | Plugin Developer
My Plugins: Code 3 Callouts | Traffic Control | Keep Calm | ALPR+

Please do not PM me for any kind of technical support.
I unfortunately do not have enough free time to answer every PM that I get. For issues with my plugins, please post in the comments section of the file, or it's forum thread. You'll get a much quicker response from me there than if you send me a PM; I do my best to respond to every question in the comments sections. For API/programming questions, please post them in the API Development forum, so all developers can benefit from the answer as well. Thanks!

I thought so. I use it this way

Version ver = typeof(MY_ALREADY_LOADED_SELF_CHECKING_DLL_CLASS).Assembly.GetName().Version

To get self-reference to my dll, and then get its assembly properies (version).

Its more C/C++ way a bit.

  • Author

Yeah, that's alright too, as long as its a class in your project, not just a referenced DLL. I have more experience with VB.NET, but I think that should be fine. If there is any doubt, you can just fall back to GetExecutingAssembly().

It might work for referenced DLL's too, but if a user has a weird version installed, or that dependency is just plain missing, then it might throw an exception. That's why I prefer to check the file version directly. It allows you to display a message like, "RageNative UI not found!" if the File.Exists check fails, and then you can check the version number and display a warning if its too old.

Also, just to clarify, use the CompareTo function of the Version class; that way, you can just check for a minimum version, and not have the version check fail if the user has a newer version installed than what you're looking for. (i.e.  requiredVersion.CompareTo(installedVersion) returns 1 if the installed version is too old...I believe it returns 0 if they are the same, and -1 if the installed version is newer)

Edited by Stealth22

Stealth22
LSPDFR Tester | Plugin Developer
My Plugins: Code 3 Callouts | Traffic Control | Keep Calm | ALPR+

Please do not PM me for any kind of technical support.
I unfortunately do not have enough free time to answer every PM that I get. For issues with my plugins, please post in the comments section of the file, or it's forum thread. You'll get a much quicker response from me there than if you send me a PM; I do my best to respond to every question in the comments sections. For API/programming questions, please post them in the API Development forum, so all developers can benefit from the answer as well. Thanks!

5 hours ago, Stealth22 said:

Yeah, that's alright too, as long as its a class in your project, not just a referenced DLL. I have more experience with VB.NET, but I think that should be fine. If there is any doubt, you can just fall back to GetExecutingAssembly().

It might work for referenced DLL's too, but if a user has a weird version installed, or that dependency is just plain missing, then it might throw an exception.

Frankly speaking I would not use this for referenced external dll without checking its existence - if its missing in assembly path, I would get NullReferenceException or the like. Sell referencing with use of typeof, on class from the same assembly itself, that's the only use of this in this case I might think of. Refrection is quite new, but very important part of .NET langauages, it gives .NET lanuages more flexibility and simplicity in syntax known from C/C++, and more abilities. In c/c++ getting self reference in similar way, or type casting was available for many years ago.

I have experience in .net coding at least a few years back, also native languages, in short - everything what was needed to get that basic level degree in Computer Science in my country. Looking at your code you have posted, you're also person with formal IT education I guess.

5 hours ago, Stealth22 said:

Also, just to clarify, use the CompareTo function of the Version class; that way, you can just check for a minimum version, and not have the version check fail if the user has a newer version installed than what you're looking for. (i.e.  requiredVersion.CompareTo(installedVersion) returns 1 if the installed version is too old...I believe it returns 0 if they are the same, and -1 if the installed version is newer)

For example this code :

 

might be handy

Edited by w35

  • Author
8 minutes ago, w35 said:

Frankly speaking I would not use this for referenced external dll without checking its existence - if its missing in assembly path, I would get NullReferenceException or the like. Sell referencing with use of typeof, on class from the same assembly itself, that's the only use of this in this case I might think of. Refrection is quite new, but very important part of .NET langauages, it gives .NET lanuages more flexibility and simplicity in syntax known from C/C++, and more abilities. In c/c++ getting self reference in similar way, or type casting was available for many years ago.

I have experience in .net counting at least a few years back, also native languages, in short - everything what was needed to get that basic level degree in Computer Science in my country. Looking at your code you have posted, you're also person with formal IT education I guess.

Yeah, that's why I said I prefer to use FileVersionInfo for referenced DLL's instead. Way less chance for error, lol.

And yeah, I studied Computer Science, and I'm a .NET software developer for a living. :smile:

Stealth22
LSPDFR Tester | Plugin Developer
My Plugins: Code 3 Callouts | Traffic Control | Keep Calm | ALPR+

Please do not PM me for any kind of technical support.
I unfortunately do not have enough free time to answer every PM that I get. For issues with my plugins, please post in the comments section of the file, or it's forum thread. You'll get a much quicker response from me there than if you send me a PM; I do my best to respond to every question in the comments sections. For API/programming questions, please post them in the API Development forum, so all developers can benefit from the answer as well. Thanks!

23 minutes ago, Stealth22 said:

Yeah, that's why I said I prefer to use FileVersionInfo for referenced DLL's instead. Way less chance for error, lol.

And yeah, I studied Computer Science, and I'm a .NET software developer for a living. :smile:

Greetings form Poland then :) I'm web developer (that's my specialisation, also in back-end - PHP, MySql etc. ) but I like more Windows development (as of my preference), also I'm doing some coding for Windows, for living. In recent years I was more involved in more coding for Web, though.

As of referenced external dll's I must completly agree. Just wanted to add some information of special case - getting version of self referenced assembly, so people don't get it as general rule, in topic of getting assembly version. However topic is about getting external assemblies versions, so that was my 2 cents, and thats it.

Edited by w35

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

Recently Browsing 0

  • No registered users viewing this page.

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.