"Lobby" menus discussion / Pause menu research

I uploaded the resource to GitHub - CritteRo/fivem-lobby-pausemenu: Lobby style pause menu research resource if you want to check for yourself.

Found a couple of nice things:

  • TriggerScreenblurFadeIn(1) and TriggerScreenblurFadeOut(1000) are used to create that “menu screen blur” effect, but I’m sure you need to pair to other screen effects in order to get the original MP_PAUSE background feel.
  • Managed to get R* and mission type icons on the left-side buttons.
  • Made a “proof of concept” command for button press. (/pressfr command. “Enter” by default)

The menu is pretty much done:

  • Found 2 new string params in SET_COLUMN_TITLE scaleform. Turns, those are textureDirectory and textureName!!
    It’s suited for 256x128 images, otherwise it gets stretched. Works with existing texture files, streamed .YTD, and even with some CreateRuntimeTextureFromImage() action.

This will be my final update here. Expect a framework release soon.

Github, btw: GitHub - CritteRo/fivem-lobby-pausemenu: Lobby style pause menu research resource

1 Like

Excellent work! I tested some things with a menu like this month ago but never got the Image to work!
Thanks for all the work for the community!

1 Like

Glad you like it! It was a very fun resource to work on.

I found even more settings for the frontend menu, that could not get into this specific menu, like locking tabs, or the big textbox that you can see on the “Info” tab on the pause menu.

Also, I have reason to believe that all frontend menus are split into 3 columns. Don’t know what to do with this information yet, but it’s definitely good to know.

Next mission:
research_char_creation.lua (3.0 KB)

1 Like

Sorry, I only just got the @ now due to being overwhelmed with other forum notifications. I haven’t really seen any detailed explanations of the frontend menu system so I guess I’ll start from the beginning.

Within decompiled scaleform files, the term “pausemenu” is what natives / game script call “frontend” menu as essentially, they are the same UI but the actual in game pause menu is handled by the engine whereas “frontend” is manipulated by game scripts. This can cause much confusion as scaleform makes no mention of “frontend” except for some numerical control constants whereas natives have distinct pause menu functions as well as frontend ones.

When a frontend menu is being loaded (usually when ACTIVATE_FRONTEND_MENU is called) the game uses data loaded from the data/ui/pausemenu.xml file to figure out what functions it will call the pausemenu/frontend scaleforms with. The pause menu version being loaded determines what “screen” will be loaded initially, what “header” to use, the flags which the scaleform and UI backend within the game engine will use, the “pause menu contexts” to initially activate and the hud colour to be used on all menu items by default.

For the example frontend shown here, everything below the SETTINGS - PLAYERS - DETAILS bar is considered the frontend itself whereas that bar and all above is considered the “frontend header” which you control with the BEGIN_SCALEFORM_MOVIE_METHOD_ON_FRONTEND_HEADER native. Internally, the header is just another part of the root frontend menu object and some methods you call within the frontend will call methods on the header.

A menu “screen” will specify a GFx file to load, said GFx file will contain a class that extends com.rockstargames.gtav.pauseMenuPages.PauseMenuBasePage called the menu “page”. Like all other scaleforms, when this file is loaded, it will set the TIMELINE variable to an instance of said class however it also calls the method LOADED_PAGE on its 3rd level parent which will be a class that extends com.rockstargames.gtav.pauseMenu.pauseMenuItems.PauseMenuContentBase. Only one such class exists, that being com.rockstargames.gtav.pauseMenu.PAUSE_MENU_SP_CONTENT.

A menu “page” defines what menu “columns” to load, each column being a class that extends com.rockstargames.gtav.pauseMenu.pauseComponents.PauseMenuComponentBase. If a column wants to take advantage of the data slot interface, it needs to contain a “model” that extends com.rockstargames.ui.components.GUIModel which contains a “view” that extends com.rockstargames.gtav.pauseMenu.pauseMenuItems.PauseMenuViewBase. The view is then responsible for creating instances of and arranging the "item"s contained within it. The class for said item extends com.rockstargames.gtav.pauseMenu.pauseMenuItems.PauseMenuBaseItem.

Succinctly, a screen loads a page that contains 1 or more components that in this context are known as columns. Components that implement data slots have a model that contains an instance of a view that contains 1 or more items.

The class inheritance is very important here to understand where the data goes when you call, for example, the SET_DATA_SLOT method as the parameters get passed down through many layers of abstraction.

The parameters to SET_DATA_SLOT and UPDATE_SLOT are as follows however, some item implementations also use some of these “standard” parameters.

Argument Index Name Type Description
0 Column Index INT The column containing the slot to set the data of.
1 slotIndex INT The index of slot to set the data of.
2 menuID INT Used to keep track of what item of the pause menu this slot belongs to. You should use IDs from com.rockstargames.gtav.constants.PauseMenuLUT where it makes sense to do so.
3 uniqueID INT Used to uniquely identify items within a view. You can really use anything you want however, integers greater than 1000 can have special functions and execute actions when clicked that you’d otherwise not want to happen so, to be safe, don’t use anything greater than 1000.
4 type INT The subtype of this item.
5 initialIndex INT The initial index to show in a list item.
6 isSelectable BOOL A boolean controlling if the item can be selected. Unselectable items are greyed out when displayed and skipped over in navigation.
7 onwards Any Additional data used by the implementation of the item.

The process of figuring out what item implementations are used by what page gets annoying if you don’t have all the decompiled scaleform script easily searchable so I recommend you extract all actionscript from all of the scaleforms and put it into one directory you can search through. You also need to reference pausemenu.xml but since we’ll be doing this in FiveM, you want to use the file located in your FiveM Application Data at citizen\common\data\ui\pausemenu.xml.

I haven’t exactly got it down to a science yet but when a screen loads, it loads every <Item> with it. If those items have their <MenuAction> set as MENU_OPTION_ACTION_LINK then, the screen corresponding to the <MenuUniqueId> will also be loaded. The menu screen <depth> tag helps in this as <depth> = 0 screens are almost always headers, <depth> = 1 screens are columns and <depth> = 2 screens are context menus, the ones that contain options to kick players and such.

As an example, we’ll take a look at FE_MENU_VERSION_CORONA. So, we go from <InitialScreen>MENU_UNIQUE_ID_HEADER_CORONA</InitialScreen> to a <Item> <cTextId>PM_SCR_SET</cTextId> <MenuAction>MENU_OPTION_ACTION_LINK</MenuAction> <MenuUniqueId>MENU_UNIQUE_ID_CORONA_SETTINGS</MenuUniqueId> </Item> to a <cGfxFilename>PAUSE_MENU_PAGES_CORONA</cGfxFilename> meaning the file containing the page for the initial state of the menu version FE_MENU_VERSION_CORONA is pause_menu_pages_corona.swf.

The first frame of that GFx file puts an instance of com.rockstargames.gtav.pauseMenuPages.PAUSE_MENU_PAGES_CORONA onto the TIMELINE so that’s our page!

The setupPage function of this page looks like this:

function setupPage()
{
	super.setupPage();
	this.column1 = this.addColumn("freemodeList",1,0);
	this.column2 = this.addColumn("freemodeDetails",2,580);
	this.column3 = this.addColumn("freemodeMap",3,580);
	this.column4 = this.addColumn("mpFriendsList",4,290);
	this.setupColumns(this.column1,this.column2,this.column3,this.column4);
	this.setupColScroll([this.column1,this.column2,this.column3,this.column4],[true,false,false,false]);
}

The column at index 0 is an instance of the class registered as "freemodeList" and is offset by 0 on the x-axis. Column index 1 is a "freemodeDetails" instance offset by 580, appearing in the 3rd column visually. Column index 2 is a "freemodeMap" instance also appearing in the 3rd column, underneath the first component. Column index 3 is a "mpFriendsList" instance appearing in the 2nd column visually.

These strings, internally called linkageIDs, point to whichever class is registered with Object.registerClass(linkageID, class). For this example, here are all referenced classes and their actual names:
"freemodeList"com.rockstargames.gtav.pauseMenu.pauseComponents.PAUSE_MENU_FREE_MODE
"freemodeDetails"com.rockstargames.gtav.pauseMenu.pauseComponents.PAUSE_MENU_FREEMODE_DETAILS
"freemodeMap"com.rockstargames.gtav.pauseMenu.pauseComponents.PAUSE_MENU_FREEMODE_MAP
"mpFriendsList"com.rockstargames.gtav.pauseMenu.pauseComponents.PAUSE_MP_MENU_FRIENDS_LIST
"freemodeListItem"com.rockstargames.gtav.pauseMenu.pauseMenuItems.singleplayer.PauseMenuFreemodeItem

This is the INITIALISE function of component for column 0:

function INITIALISE(mc)
{
    // [snip]
	this.model = new com.rockstargames.gtav.pauseMenu.pauseMenuItems.singleplayer.PauseMenuFreemodeModel();
	this.model.createView(0,{id:0,x:0,y:0,rowSpacing:2,columnSpacing:0,container:this.CONTENT,linkage:["freemodeListItem"],visibleItems:16,selectstyle:com.rockstargames.ui.components.GUIView.SCROLL_SELECTSTYLE});
    // [snip]
}

For the 0th column, that has a this.model of com.rockstargames.gtav.pauseMenu.pauseMenuItems.singleplayer.PauseMenuFreemodeModel with a view that uses the class registered as "freemodeListItem" as it’s item class.

Looking into com.rockstargames.gtav.pauseMenu.pauseMenuItems.singleplayer.PauseMenuFreemodeItem, we can finally see how the parameters we pass to SET_DATA_SLOT actually get rendered and used!

Within said class, the function this.__get__data() gets an array of the parameters after argument 6 and other functions refer to previously documented names of the class. The interesting stuff is within the __set__data(_d) override. We can see that argument 7 is the itemLeftText, argument 8 controls the verifiedMC (verified [badge] Movie Clip) and so on.

Now that we know how to get our items to display however we want, how do we tell when the player has selected an item and most importantly which item they’ve selected? I discovered some natives through decompiled scripts to help with such which were already shared however, I’ve now also discovered where that data comes from.

When scaleform calls the game interface method LAYOUT_CHANGED or LAYOUT_CHANGED_FOR_SCRIPT_ONLY, the native 0x2E22FEFA0100275E is set to true for one frame and the native 0x7E17BE53E1AAABAF will return the 1st, 3rd and 2nd parameters to the game interface call in specifically that order. Similarly, when TRIGGER_PAUSE_MENU_EVENT is called, the native 0xF284AC67940C6812 will return true for one frame and according to decompiled scripts, the native 0x36C1451A88A09630 should be used to get the 1st and 3rd parameters to the game interface call however, 0x7E17BE53E1AAABAF works all the same.

0x7E17BE53E1AAABAF's parameters are effectively:

void 0x7E17BE53E1AAABAF(int* previousMenuState, int* selectedItemMenuId, int* selectedItemUniqueId);

// previousMenuState: The previous menuState of the pause menu - 1000. The previous menuState is usually set to the menuID of the last selected item.
// selectedItemMenuId: The menuID - 1000 of the currently selected item. Is -1 if the menu state wasn't changed since the last trigger.
// selectedItemUniqueId: The uniqueID of the currently selected item.

This is of great use to us as TRIGGER_PAUSE_MENU_EVENT will be called any time the user selects a frontend option and perhaps also when the user attempts to scroll a list item, I haven’t been able to verify such yet. Additionally, LAYOUT_CHANGE is called any time that the selected item changes allowing us to know what the user has selected and when they’ve actually selected it.

I’ll get these properly documented when I’ve got time :slight_smile:

That’s all I’ve got for frontend research, let me know if something doesn’t make sense as I basically wrote all of this down over the course of a day. Perhaps an effort could be made to document all of the different components and item implementations for future use…

8 Likes

That menuID-1000 reference is really helpful, as I never quite managed to get why it showed such a wild number on my frontend menu, but on the normal pause menu it had 0, 5, etc IDs. This can be used in a “submenu” type of functionality without having to reload everything.

Now it makes more sense why columnID for setting data is different from the columnID when changing header stats, or adding anything else. Makes me wonder if the players column was switched from the right column to center during development, or maybe some R* dev just didn’t care enough.

This is what I’m currently looking into. When building the menu, navigation through the buttons works, but I didn’t manage to get any “select” event, or input working, so I made my own.
I made a command attached to RegisterKeyMapping(), that uses 0x7E17BE53E1AAABAF to get the uniqueID, and then use that to trigger a specific event.
Works for normal items, but it gets tricky when you add list items in the mix because the uniqueID doesn’t change when the list item changes.

mc… movie clip… ohhh… OOOHHHHH so that’s what it means!

Overall this is a great post and insightful on the ‘behind-the-scenes’. Thank you!

2 Likes

Oh right! I entirely forgot to mention that TRIGGER_PAUSE_MENU_EVENT is invoked when a menu item is selected (what you’re looking for) and LAYOUT_CHANGED corresponds to when the selected item is changed and some other edge cases which are dependant on the loaded page.

Original post is now updated! (Also got the data getter natives the wrong way around so that’s also fixed but as I already pointed out, either data getter works :stuck_out_tongue:)

3 Likes

Thank you very much @Jaymo thats wonderful research!

etc., also explains the map column

4 Likes

Amazing job!! Just one question, is it a pause menu and then opens with esc? because I can’t open it on my server

Actually no, it’s a frontend menu, different than the regular pause menu. I built a small framework around it here if you want to create your own menus.

Menus would have to be opened using a clientside event trigger. You can see everything on the github wiki page.

1 Like

ah ok, thank you!!

1 Like

Mission Completed

Just stumbled on this in January 2022! This is amazing! Did you finish any of this? I’d love to chat through some ideas for my server. Really basic compared to what you’ve done tbh. XP Level, Tips to increase it, Job Level, tips to increase it, latest ‘city’ news, gang news, latest tweets, cars for sale, adverts. Police and EMS adverts for hiring.

Heyo, the lobby framework is out there already, if you want to use it!

If you have any questions on this, you can ask it in that thread.

1 Like

how to add such a my character in menu

how do I set this up? this a ACTIVATEFRONTENDMENU “FE_MENU_VERSION_MP_CHARACTER_CREATION”

I’m trying to find some info about the pause menu, I was trying to change something in the settings menu under keybinds with the GetHashKey(“ADD_TEXT_ENTRY”)… and I am looking for the cTextId for this option “FiveM” under Settings > Keybinds

I was looking through the pausemenu.xml and I see a couple options from this menu like

<PM_PANE_GENERAL>
<PM_PANE_FOOT>

etc, but I can’t find the one that is that FiveM menu name, I am trying to change the FiveM to something else, any help?

Place in any client script:

-- Change server name in Keybinds settings
Citizen.CreateThread(function()
    AddTextEntry('PM_PANE_CFX', 'My Cool Server')
end)

I cannot take credit for this nugget. In fact, I have no idea with whom to credit on this. I found it one day deep scrolling the forums. Wasn’t even looking for it, either.

1 Like