Incoming User Function Changes

brolly

Administrator
Developer
If you are not using RL User Functions then you can ignore all this and go back to play some games, on the other hand if you are using User Functions make sure you read all of it to avoid starting to post that your User Functions are broken right after the next RL update is out.

Continuing with our update to make RL more class based and at the same time compatible with the upcoming AHKv2 there will be some important changes made to the User Functions feature that will require user manual intervention after the RL update that will be out shortly.
It's a very simple fix, so no one should have trouble with it as long as they read this info.

Make sure you only do this AFTER the next RL update, if you do this right now it will only break your User Functions.

Right now there are 2 User Function files:
Lib\User Functions.ahk
Lib\User Functions Init.ahk

You'll need to move and rename the User Functions.ahk file to "Lib\User Functions\Global.ahk".
If your User Functions Init.ahk file has no content you can delete it as it's no longer needed. If it has content you'll need to move it to the Global.ahk file which I will explain later.

This file is now class based so you will need to edit it in a text editor. Let's say your current User Functions.ahk file is currently:
Code:
; Use this function to define any code you want to run in every module on start
StartGlobalUserFeatures(){
	Log("StartGlobalUserFeatures - Starting")
	; INSERT CODE HERE
	Log("StartGlobalUserFeatures - Ending")
}

; Use this function to define any code you may need to stop or clean up in every module on exit
StopGlobalUserFeatures(){
	Log("StopGlobalUserFeatures - Starting")
	; INSERT CODE HERE
	Log("StopGlobalUserFeatures - Ending")
}

Edit the file so it will now look like:
Code:
class GlobalUserFunction extends UserFunction {

	; Use this function to define any code you want to run in every module on start
	StartUserFeatures() {
		Global RLLog,dbName,systemName
		RLLog.Info(A_ThisFunc . " - Starting")
		; INSERT CODE HERE
		RLLog.Info(A_ThisFunc . " - Ending")
	}
	
	; Use this function to define any code you may need to stop or clean up in every module on exit
	StopUserFeatures() {
		Global RLLog,dbName,systemName
		RLLog.Info(A_ThisFunc . " - Starting")
		; INSERT CODE HERE
		RLLog.Info(A_ThisFunc . " - Ending")
	}

}

As you can see it's pretty simple, you just need to add the class line at the top of the file, make sure you add the closing bracket at the end of the file and the function names are slightly different. If in doubt you can check the sample file that will be in the update to ensure you have the function names right.

If you also have content in your User Functions Init.ahk file you'll need to copy the content from that file and add it to a function called InitUserFeatures in Global.ahk, so it will look like:
Code:
; Use this function to define any code you want to run on initialization
	InitUserFeatures() {
		Global RLLog,dbName,systemName
		RLLog.Info(A_ThisFunc . " - Starting")
		; PASTE YOUR User Functions Init.ahk HERE
		RLLog.Info(A_ThisFunc . " - Ending")
	}

After that you can delete the old Init file. And that's it after following these steps you are done. If you have any issues getting it to work just post your old files here and we will help you out converting them.
This is all that's needed in order to update your files.

But, this isn't all of it! We also made User Functions a lot more flexible now and instead of using a single ahk file for every single system you can break it into separate files named after each system, emulator or even game. This will help you keep your code clean instead of having a bunch of if conditions inside your global script file. This is completely optional, if you want to keep everything stuffed in a single file you can still do it.

The new file structure will be:
Lib\User Functions\Global.ahk (This script is run for any game/system)
Lib\User Functions\%SystemName%.ahk (This script is run for this particular system only)
Lib\User Functions\Emulator\%EmulatorName%.ahk (This script is run for this particular emulator only, regardless of the system)
Lib\User Functions\%SystemName%\Emulators\%EmulatorName%.ahk (This script is run for this particular emulator and system only)
Lib\User Functions\%SystemName%\%RomName%.ahk (This script is run for this particular game and system only)

They will be run in the same order as listed above.

So you'll have a lot of flexibility on how you can organize your code. Every file should use the exact same function names, there will be a sample folder included in the update so it should be fairly simple to follow it.
There are some specific functions in the %RomName%.ahk these will be mostly useful with PCLauncher as it will allow you to run custom code if you want to emable/disable fullscreen for a game through RLUI settings or if the game needs some specific handling code in order for bezels to work properly. Those new functions are:
SetFullscreenPostLaunch
SetFullscreenPreLaunch
HaltGame
RestoreGame

They are also commented on the sample files so you can understand the purpose of each of them.

This should cover everything new regarding User Functions, if you have any doubts ask away.
And make sure you read all this BEFORE updating!
 

dustind900

Member
Supporter
RL Member
Wow! This is good stuff. Looks like I have a bunch of code that needs to be updated. Thanks for all the hard work guys...
 

neonrage

Member
RL Member
So my old User Functions file had the code to prevent cursors from leaving the screen area (when bezels were enabled)

Code:
; This function gets ran right after FadeInExit(), after the emulator is loaded
PostLoad(){
	Global RLLog,dbName,systemName,bezelScreenX,bezelScreenY,bezelScreenWidth,bezelScreenHeight,bezelEnabled
	RLLog.Info(A_ThisFunc . " - Starting")
	
	;Restrict Cursor Movement
	If (bezelEnabled = "true"){
		ClipCursor( True, (bezelScreenX + 4), (bezelScreenY + 4), (bezelScreenX + bezelScreenWidth - 4), (bezelScreenY + bezelScreenHeight - 4))
		RLLog.Info(A_ThisFunc . " - Restrict Cursor Movement from point " . bezelScreenX . "," . bezelScreenY . " in a " . bezelScreenWidth . "x"  . bezelScreenHeight . " area.")
	}
	
	RLLog.Info(A_ThisFunc . " - Ending")
}

; This function gets ran after the module thread ends and before RL exits
PostExit(){
	Global RLLog,dbName,systemName
	RLLog.Info(A_ThisFunc . " - Starting")
	; INSERT CODE HERE
	RLLog.Info(A_ThisFunc . " - Ending")
}


;Helper Functions
;Clip Cursor Function for bezel and lightgun games (not mame)
ClipCursor( Confine=True, x1=0 , y1=0, x2=1, y2=1 ) {
	VarSetCapacity(R,16,0),  NumPut(x1,&R+0),NumPut(y1,&R+4),NumPut(x2,&R+8),NumPut(y2,&R+12)
	Return Confine ? DllCall( "ClipCursor", UInt,&R ) : DllCall( "ClipCursor" )
}

I can't seem to find the right place to place the helper function ClipCursor. Everywhere I tried just results in an AHK error regarding a non-existent function. Where would this helper function go in the new format?
 

brolly

Administrator
Developer
This should work (put into Global.ahk):

Code:
class SystemUserFunction extends UserFunction {

	; Use this function to define any code you want to run on initialization
	InitUserFeatures() {
		Global initialLanguage
		; saving initial language input for future use 
		initialLanguage := this.GetKeyboardLanguage()
		; convert decimal initial language code to hex for future use
		SetFormat, IntegerFast, hex
		initialLanguage := initialLanguage
		initialLanguage .= ""
		SetFormat, IntegerFast, d
		RLLog.Info(A_ThisFunc . " - Initial language input: " . initialLanguage)
	}
	
	; This function gets ran right after FadeInExit(), after the emulator is loaded
	PostLoad(){
		; set language to English United States (US) (you can find other codes on: https://msdn.microsoft.com/en-us/library/dd318693(v=vs.85).aspx)
		this.SetDefaultKeyboard(0x0409)
		RLLog.Info(A_ThisFunc . " - Language input set to English: 0x0409")
	}
	
	; This function gets ran after the module thread ends and before RL exits
	PostExit(){
		Global initialLanguage
		; set language to initial language input
		this.SetDefaultKeyboard(initialLanguage)
		RLLog.Info(A_ThisFunc . " - Language input set to: " . initialLanguage)
	}

	GetKeyboardLanguage(){	
		if !KBLayout := DllCall("user32.dll\GetKeyboardLayout")
			return false
		return KBLayout & 0xFFFF
	}

	SetDefaultKeyboard(LocaleID){
		WinGet, List, List
			Loop % List
				SendMessage, 0x50,, LocaleID,, % "ahk_id" List%A_Index%
	}

}
 
Last edited by a moderator:

neonrage

Member
RL Member
Thank you brolly, so i had the function in the right place but it was the
Code:
this.ClipCursor(
that is the key. Works perfect now!
 

tonesmalone

Active member
RL Member
That wiki link + brollys post around the recent changes should pretty much cover it.


Sent from my iPhone using Tapatalk
 

bleasby

RocketLauncher Developer
Developer
I have a question about user function. What is it really for?
Anything that you want to implement that it is nor directly supported by the RL team.
Think of it as an end user plugin system for RL if you like.

Sent from my XT1068 using Tapatalk
 

Melu

Member
RL Member
Thanks for your answer, Bleasby. Could you tell me an example of this function please?
 

bleasby

RocketLauncher Developer
Developer
Just search the forums and you can find many "user functions" examples.

People used it to for example:
  • Setup customized UltraStik 360 Joysticks configs according games and systems;
  • Delete temporary files automatically (already supported natively by RL);
  • Backup save states;
  • Backup emulator configs;
  • Restrict cursor movements when bezel is enabled;
  • Run specific programs when running specific games;
  • and so on.

Just be aware that the majority of the functions posted on the forums used the old user function file structure.
You just need to follow this thread information to use it with the most up to date structure.
 

antroma

New member
Hi ;
Firstly i'm sorry for my English , i'm new as a rlauncher user and i'm french .
Since the last update i can't launch my nes emulator from hyperspin , it worked well before and now i have this text appearing when i'm launching any game

error nes.png

after the update i renamed userfunctions.ahk in lib/ global.ahk as it was said.
Please can you help me?
Thanks for your job .
 

bleasby

RocketLauncher Developer
Developer
antroma, your problem is unrelated with the user functions. Please, always create a new thread to report any issue instead of using existent ones to avoid a completely chaotic forum organization.
Also, try to read the troubleshooting guide on the wiki to know how to give enough information so we could try to help you.
Posts without a troubleshooting log are almost always useless to figure out what is going on, and do not provide enough info so we could try to help you.
 

retango

New member
Supporter
RL Member
Hi! Thanks to all for the update! I'm trying to put up to date the Ultrastick 360 programmer User Module, and am having a hard time.. some help would be welcome!.

When I use iniModule.Read for a section and key that does not exist, it used to return the string "ERROR". Now I see it returns a null string, but I can't find out how to check that.. I tried this below but does not work.. How do I ask if a variable is empty?
Code:
ugc := FunctionIni.Read("Roms", romName)
if ugc="" 
   logText := "Logging"


Also, I want to run UltraMap.exe , I created a Process object, but it's not working.. what am I doing wrong? Thanks a lot!

Code:
UltraMapExePath :=  "C:\Program Files (x86)\UltraMap\UltraMap.exe"
UltraMapExe := new Process(UltraMapExePath)
ugcPath:="C:\Program Files (x86)\UltraMap\"
UltraMapExe.Run(ugcPath .  ugc . ".ugc /logerrors" . ugcPath . "UltraMapLog.log")

Also, is there a function similar to "SplitPath"? I want to extract yhr string "C:\Program Files (x86)\UltraMap\" from "C:\Program Files (x86)\UltraMap\UltraMap.exe"

Thanks and happy new year to all!!
 

brolly

Administrator
Developer
Use:
If (!ugc)
logText := "Logging"

You can still use SplitPath if you want, but there's a SplitPath function in the StringUtils class that you should use instead.

Your Run call isn't working because you should only pass it the command line arguments not the whole command line, not even sure what you are passing it though because you are just appending a bunch of stuff without any spaces between it. Check your log to see what's being received by UltraMap exactly.

That being said, are you aware that Ultrastick integration is already built into RL since quite some time? You shouldn't need any custom code for this.

Happy new year!
 

retango

New member
Supporter
RL Member
Thanks a lot brolly! I think I was now able to do it, testing it now..
Yes, I saw the new implementarion of Ultrastick, it's great! However, as I understood it, I would need to manually enter the profile for each Mame game, which is what I'm trying to avoid by re-programming this User Function..
Thanks again!
 
Top