Plugin - Sending Commands or Changing Values

Joel.Brown

I'm developing a plugin for a stock car racing mod that will allow me to clear and change penalties automatically based on certain rules that I define.

For example, under yellow flag a driver will get a Drive Thru penalty for speeding when it should be an End Of Longest Line. I would like the plugin to detect this and clear the DT penalty and add an EOLL penalty.

I have the ExamplePlugIn working fine and can get the data I need.
My question is, how do I tell rFactor to clear or change a penalty ?

I don't see a way to send commands or change the value of the variables.
 
I would like to have my .DLL tell rFactor to clear or add a penalty to a driver. Just need to know how to tell rFactor to do this.
 
I am not sure what data you are getting from the plugin but if your data is coming from UpdateScoring, you could run the plugin on the server and automate the server commands being typed into the chat box on the dedicated server. If your data is coming from UpdateTelemetry you are out of luck because it isn't called on the server.

I have automated the dedicated server using these User32.dll functions:
SendMessage
FindWindowEx
GetWindowText
GetWindowTextLength

I would first use FindWindowEx to find all the windows with the class "#32770" and title "ISI Dedicated Server". Then I would use FindWindowEx on each server (I run more than one dedicated server on my server) to find the handle to the Static text that has the server name and then use GetWindowText to get the server name to make sure it is the window I want to automate. Then I would use FindWindowEx to find the handle of the Edit box that I want to send the commands to and also find the handle to the send button. Then I use SendMessage to type the server command and then SendMessage to click the send button.

The server name is Static13, the send button is Button9 and the chat box is Edit1.
 
Last edited:
Thank you for the information. This will help a lot. I was hoping for a way to send commands back through an interface in the plugin itself but this may do what I need. I think everything I need is in UpdateScoring().
 
Guess I'm a little rusty. I tried to get this stub of code working just to test it out. I can't get a handle to the Edit1 control. I'm sure I'm doing something dumb, it's been a few years since I've done C++ and Windows API calls.

HWND ahwnd;
HWND edithwnd;
char text[100] = "Testing 1 2 3";

ahwnd = FindWindowEx(NULL, NULL, NULL, TEXT("ISI Dedicated Server"));

edithwnd = FindWindowEx(ahwnd, NULL, (LPCWSTR) "Edit1", NULL);
// I never get a handle to the control. Always returns 0x0000
// I also tried this
// edithwnd = FindWindowEx(ahwnd, NULL, TEXT("Edit1"), NULL);

SendMessage(edithwnd, WM_SETTEXT, 0, (LPARAM) text);
 
It took me several hours to figure out how to use these calls. Lots of googleing and lots of trial and error. I didn't actually write my automation app in C++ though. I wrote it in VB.Net using pinvokes to the windows apis. Here is the function I wrote to find the correct server. It should give you some clues.

Code:
	Function findServer(srvName As String) As IntPtr
		Dim rfdHand As New IntPtr(0)
		Dim srvNameHand As New IntPtr(0)
		For i = 1 To 50 ' Loop through all the dedicated servers. Surely I will never have more than 50 running.
			rfdHand = FindWindowEx(IntPtr.Zero, rfdHand, "#32770", "ISI Dedicated Server")
			If rfdHand = 0 Then
				Return 0
			Else
				If GetText(FindWindowEx(rfdHand, 0, "Static", vbNullString)) = "1.255" Then
					For c = 1 To 13 ' Call FindWindowEx 13 times to locate Static13
						srvNameHand = FindWindowEx(rfdHand, srvNameHand, "Static", vbNullString)
					Next
					If GetText(srvNameHand) = srvName Then
						Return rfdHand
					Else
						srvNameHand = 0 ' If this isn't the server I am looking for, reset the handle to 0 and then continue to the next server
						Continue For
					End If
				Else
					Continue For
				End If
			End If
		Next
		Return 0
	End Function

As you can see you can't just supply Static13 as an argument. You have to call it with "Static" as an argument. The first time you call FindWindowEx it will return the handle to the first Static field. Then you call FindWindowEx again, this time supplying the handle to the first Static as an argument as the starting point. Keep doing this 13 times. In your case you just want Edit1 so you only have to call FindWindowEx once with "Edit" and a 0 for the handle.

Here is the function I wrote for actually sending a chat message. The chat box is limited to 67 characters so I have logic in here to split long messages up into several messages.
Code:
	Function SendChat(msg As String, srvHwnd As IntPtr) As Integer
		Dim chatHwnd As New IntPtr(0)
		Dim sendButtonHwnd As New IntPtr(0)
		Dim msgSB As New StringBuilder
		chatHwnd = FindWindowEx(srvHwnd, chatHwnd, "Edit", vbNullString)
		For i = 1 To 9
			sendButtonHwnd = FindWindowEx(srvHwnd, sendButtonHwnd, "Button", vbNullString)
		Next
		For Each word As String In msg.Split(" ")
			If msgSB.Length = 0 Then
				msgSB.Append(word)
			ElseIf msgSB.Length + word.Length + 1 <= 67 Then
				msgSB.Append(" " & word)
			Else
				SendMessage(chatHwnd, WM_SETTEXT, msgSB.Length, msgSB)
				SendMessage(sendButtonHwnd, BM_CLICK, 0, 0)
				msgSB.Clear()
				msgSB.Append(word)
			End If
		Next
		SendMessage(chatHwnd, WM_SETTEXT, msgSB.Length, msgSB)
		SendMessage(sendButtonHwnd, BM_CLICK, 0, 0)
		Return 0
	End Function

Sorry the samples aren't in C++ but it should still give you some direction.

Note: You may want to copy this code into another editor because the forum is line wrapping making a mess of things.
 
Last edited:
Word of warning if you're going to be giving multiple commands directly to a server window... you need to give a bit of delay between each one, because if you just dump in one line after another ('clicking' the chat button in between) the server won't process all of them.
 
Another thing to remember I wrote some time agone. This method will not work if DS is run in oneclick mode (without GUI)
 
Another thing to remember I wrote some time agone. This method will not work if DS is run in oneclick mode (without GUI)

The code I posted above works fine with +oneclick +autohide. But there is still a GUI with +oneclick. I know there is an option to totally hide the GUI but I can't recall what it was. If there isn't a GUI, my code probably wouldn't work.

Does anyone remember the option to totally hide the GUI?

Ah.. I found it finally. The option that totally hides the GUI is +nowindow. My code works fine with +oneclick though. Even when the window is minimized.
 
Last edited:
Here's my example C++ code that communcates with the ISI Dedicated Server. I'm posting it just in case some other person needs it. When compiling the code make sure the character set is set to NOT SET. The default in VC++ is Unicode which won't work.

Thanks for all the help.

==================
#include "stdafx.h"
#include "windows.h"
#include "winuser.h"
#include "stdlib.h"


int _tmain(int argc, _TCHAR* argv[])
{
HWND ahwnd;
HWND edithwnd;
HWND sendhwnd;

char *message = "SCE plugin coming soon...";


ahwnd = FindWindowEx(NULL, NULL, NULL, "ISI Dedicated Server");

edithwnd = FindWindowEx(ahwnd, NULL, "Edit", NULL);

sendhwnd = FindWindowEx(ahwnd, NULL, "Button", NULL); // must call first time with 2nd parm NULL
for (int i=0; i<8; i++) // then iterate though 8 more times to find the Send button
sendhwnd = FindWindowEx(ahwnd, sendhwnd, "Button", NULL);


SendMessage(edithwnd, WM_SETTEXT, 0, (LPARAM) message);
SendMessage(sendhwnd, BM_CLICK, 0, 0);

return 0;
}
 
Last edited:
Very nice. That tool could come in handy. The only thing I would suggest is adding code to ensure that the server isn't on one of the config pages. I did this in my sample code above but testing to see if Static1 was "1.255". It would also be handy to have a second argument for the server name for those that run more than one instance of rF Dedicated.exe.
 
I'm going to do all that. This is just a stub of code that I wanted to test with. I wanted to post some C++ code that was actually working so others in the same boat could get started.

Thanks for all your help.

Still would like for ISI to have a plugin interface that allowed communication back to rFactor.
 
Well good news. It looks like ISI added the ability to send chat messages via the plugin API in the latest rF2 update a few days ago.
 
Wow, I just checked the HPP file and there it is.

virtual bool WantsToDisplayMessage( MessageInfoV01 &msgInfo ) { return( false ); } // set message and return true

It doesn't say it's specifically for rf2. I have to atleast try and see if it'll work in rf1. Not holding my breath but what the heck.
 
Finally have my plugin working so we can have NASCAR rules in rFactor1. We're down to final testing and tweaking but it looks like it's going to work out. Thanks again Noel for all the help. Couldn't have done it without you.

btw - it works with up to 100 differant servers on the same machine.

If you ever want to run some laps in an oval mod come check us out at www.StockCarEvolution.com. We'll eventually have an rFactor2 version but that's a slow process at this point.

Now my brain hurts from having to remember how to write C++ code. C# spoiled me.
 
Very cool. Glad I could help. I am in the same boat you are. C# has spoiled me too. Hahaha.

I find it interesting that rFactor is so old and people are just now starting to get creative with plugins. rFactor 2 has a bunch of new plugins already. There are so many cool things you could do even with rFactor's very limited plugin API. rFactor2 is much better but could still improve some more. But I suspect the plugin API improvements will come later in the dev process.
 
I'm having an interesting issue with the plugin I wrote. It works fine on XP and Windows7 desktop versions, doing either single or multiple dedicated servers, but when it's installed on the NRT servers (this is a server rental place) using Windows Server 2003 the plugin never gets called. I set it up to write a log file before it tries to do anything else and for some reason it never creates or writes the log file. They tried to start the rFactor Dedicated.exe normally without using +oneclick and still the same result. No log file, nothing.

Has anyone seen a situation where the rFactor Dedicated.exe doesn't startup any plugins ?
 
I've never seen that. My guess is there is a pathing problem of some sort. I take it you don't have direct access to the server to see exactly how it is setup?
 
No I don't have direct access yet. They're a little nervous about that because they don't really know me very well, other than I work on the StockCarEvolution mod.

btw - my plugin worked fine on a differant server rental place using Win2003 Server. Actually it works fine on every server except this particular one. Just thought I'd ask in case anyone had seen anything like this before.
 
Found the solution. Turned out that even though I compiled my .DLL plugin in C++ it still relied on .NET being up to date. Once the server rental place installed .NET 3.5.1 it worked fine.
 
Yes...I can confirm, Joel's plugin works as advertized.

It did require Net 3.5.1 and it's service pack.

Dave

NRT
 

Back
Top