Game Launcher

I’ve made a little launcher program. It is meant to help with writing config files, updating a game and shipping a game (if you don’t mind distributing py or pyc files). You don’t need to look into the innards of the program, you just need to edit a simple json file to change the look of the buttons, labels add or remove your own options.

There still might be some bugs, I didn’t yet test how(if) it works after packp3d/pdeploy.

If you download the files from the link here on the forum, you can test the update option. It will download the “CarouselDemo” changed to work with the launcher. If you get the files from github you may need to delete “update_0.zip” to download the update.

Download link:
p3d_launcher.zip (122 KB)
Github page:
github.com/wezu/p3d_launcher

If the explanations below are not clear do not be afraid to ask. I’m bad at explaining things :mrgreen:
…or you can look into main.py, and source\game.py I left some comments there. setup.json should also be simple to understand.

It currently looks like this:

It’s a bit on the ugly side, but that’s just to encourage people to make their own skins.

Features
-Start game
It will show a loading screen and then start your game

-Update
It can download a json file from the net, get url of some files in that json file, download them and unzip them.

-Config
It will read prc files, put the values found there in a easy to use dict like class. On the user side of things it will allow users to select options from simple lists, use sliders and bind keys.

Usage
To use it as a luncher for you own game you will need to put the python source files (or .pyc files) in the source directory. The launcher will try to import a “Game” class from “game”, look at the sample provided to see how you need to change your game to fit (basically, don’t inint ShowBase, call base.openMainWindow(), not base.run())

To configure the look and options of the launcher you need to edit the “setup.json” file. If you don’t like json, use some other file format or just make a python dictionary - because that’s all there is in that file, a dict.
The basic structure of the file looks like this:

{
    "basic": {},
    "style": {},
    "buttons": [],
    "slide": {},
    "key": {},
    "select": {}
}

“basic” hold some basic informations about the launcher:
“window_size”: [512,256]
The size of the launcher window in pixels.

“config_file”: “autoconfig.prc”
Name of the config file that the launcher reads/writes

“update_url”: “https://raw.githubusercontent.com/wezu/p3d_launcher/master/update.json
The url, where updates are downloaded from

“background”: “gui/luncher.png”,
Background image.

“title”:“Game Launcher”,
Title displayed in the window bar.

“undecorated”:0
Just like the undecorated config variable.

“msg”:{“new_key”:"Press a new key for:\n{0}\nThe current key is: ",“ok”:“OK”}
Text of messages used by the launcher.

The “style” dict has all the info needed to control the look of the buttons, labels and sliders. All options here should be self explanatory. The styles “default”, “label”, “wait”, “loading”, “key_input”, “select” and “slider” are used internally by the launcher, all other styles are referenced in the buttons defined later.

“buttons” contain buttons definitions in groups. For example:

"buttons": [
{"Start Game": {"style": "big_button","pos": [224,16],"func": "startGame()"},
"Exit": {"style": "big_button","pos": [224,208],"func": "exitGame()"},
"Update": {"style": "big_button","pos": [224,80],"func": "updateGame('Please wait...')"},
"Configure": {"style": "big_button","pos": [224,144],"func": "toggleButtonGroup(1)"}
},
{"Graphics": {"style": "tab_selected","pos": [0,0]},
"Sound": {"style": "tab_button","pos": [128,0],"func": "toggleButtonGroup(2)"},
"Keys": {"style": "tab_button","pos": [256,0],"func": "toggleButtonGroup(3)"},
"Gameplay": {"style": "tab_button","pos": [384,0],"func": "toggleButtonGroup(4)"},            
"Cancel": {"style": "small_button","pos": [224,208],"func": "toggleButtonGroup(0)"},
"Save": {"style": "small_button","pos": [368,208],"func": "saveConfig()"},
"Resolution:": {"style": "big_button","pos": [128,40],"select": "win-size"},
"Fullscreen:": {"style": "big_button","pos": [128,72],"select": "fullscreen"},
"Anti-aliasing:": {"style": "big_button","pos": [128,104],"select": "aa"}, 
"Shadows:": {"style": "big_button","pos": [128,136],"select": "shadow"},
"Post-process:": {"style": "big_button","pos": [128,168],"select": "filter"}
}
] 

The groups are made for showing and hiding many buttons at once, like tabs in a web browser. Each group contains a dictionary of buttons. Each button has a name and some properties eg.

"Start Game": {"style": "big_button","pos": [224,16],"func": "startGame()"}

Will create a button with the text “Start Game” it will use the style defined as “big_button” and it will be placed at x=224, y =16 (where x=0, y=0 is the top left corner of the window). When the button is pressed the function “startGame()” will be called.
The function gets executed by simple_eval, look in the main.py file to see how you can add your own functions, but the functions provided by default should be all you need for customizing the launcher. These functions are:
‘startGame’ - starts the game
‘exitGame’ -exit the launcher
‘updateGame’ -run the update
‘toggleButtonGroup’ - hide all button groups except the one mentioned (toggleButtonGroup(7) hides all groups that are not 7)
‘saveConfig’ - saves the config to a file and shows the 0 button group
There are some other that could be called, but should not be ( ‘updateDone’, ‘select’, ‘slide’, ‘key’, ‘keyInputDone’, ‘sliderDone’, ‘selectItem’, ‘showButton’)

The pos and style properties are mandatory, but the func is not. If the func key is not there, you can use 3 other options:
“select”: “something”
This will allow the user to select a option from a closed list. The name of the select is used as the config variable name, so if you want for example to let players select the window size you would add “select”: “win-size” to a button. The available options must be set in a “select” dictionary later in the file (explained lower)
“slide”: “something-else”
This alows a user to set a config variable using a slider.
“key”: “something-completely-different”
This allows the user to set a config variable by pressing a key or mouse button.

“slide” controls options set by sliders. For example:

"slide": { "master-volume":{"min":0, "max":100, "default":100, "round":0}

If a button has “slide”:“master-volume” then after clicking that button a slider will be shown, where the minimal value of the slider is 0, the maximum is 100, the default value is 100 (if none is set in a config file). When writing a config file, there will be a variable named “master-volume” with the value set by the slider. If “round” is set to 0, the value will be an int, if it’s a higher number the value will be a float rounded to the number of digits (eg. “round”:4, would write 100.0000)

“key” only holds default values for keybinding (if they have not yet been set by a config file)

“select” holds lists of options available for each config variable and labels for them. For example if you want to set the “win-size” variable:

 "win-size":{"800x600":"800 600", "640x480": "640 480"} 

This will display 2 options for the user to select 800x600 and 640x480. If the user clicks 640x480, then the launcher will write “win-size 640 480” in the config file.

This is cool! I guess it is platform independent and will run under Linux just as well as with windows?

What options does it have for the updating function? Can it maybe download the latest version from github directly, or do you have to set up your own webhost to serve the files?

It should work on Linux, but I didn’t test it personally. rdb did mention on IRC he tried to use it and it worked on linux (the only problem he mentioned was that the included demo got lunched full-screen and I didn’t put any way to close it :blush: ).

The update function is very simplistic. This it how it works:

  1. Download a file from a url written in the setup.json file (eg. basic{“update_url”:“some://url.dot.com/file.json”})

  2. The downloaded file is expected to be a json file with urls of files to download, this is what I used for the demo:

[
{"url":"https://rawgit.com/wezu/p3d_launcher/master/update_0.zip", "target":"update_1.zip"}
]

It’s a list containing a dict, so you could add more files like this

[
{"url":"https://rawgit.com/wezu/p3d_launcher/master/update_0.zip", "target":"update_1.zip"},
{"url":"https://some.place.on/the/internet/update_1.zip", "target":"update_2.zip"},
{"url":"http://i.am.making/this/url/up", "target":"some_other_update.zip"}
]

The “target” in this dict is the name of the file that’s going to be written on the clients disc, so the url may point to “update_0.zip” but it can just as well be written as “update_123.zip” on the client.

For each file mentioned there, the launcher will check if a file with that name exist on the client machine, if it’s not there it will download the file (or at least try to).

  1. Each downloaded zip file is extracted overriding any files with the same name (I’ve included the zipfile implementation from 2.7.10, because in versions lower then 2.7.4 had a bug that allowed to extract files outside the current directory, and that could be a bad thing).

  2. Finally the launcher walks its own directory deleting all files with 0 size (this is for the occasion where an update should delete files).

So to get the updates from the net, you just need a hosting service that gives direct links to files. I think it might work directly with github if you point it to the “Download zip” url, but I haven’t tested that. For the demo I used rawgit.com that gives a permanent(-ish?) link to github files and placed the update.zip directly inside the repo. I imagine this should work with google drive and/or dropbox as well.

As a side note - if anyone knows a pure python library that could pull from a git repo, let me know :wink: