Introduction
To begin with, in Steam terminology, an uploaded client is a "Workshop item", and on a technical level, a Workshop item is basically just a directory of files along with some metadata (like name, description and such). It works such that items are uploaded to the Workshop along with their metadata, where users can "subscribe" to them, at which point the Steam client will download the associated directory and whatever files it contains and make them available to the game.
Haven implements this by having the default client check for subscribed items at launch, before the primary main function is invoked, and if there are subscribed items, it will present the user with a dialog to choose which one to launch (this is the haven.WorkshopLauncher class). In order to know how to launch said client, the uploader needs to provide a file named workshop-client.properties in the item's root directory. This file is in Java's standard property format, and currently there are two separate ways to launch a client, each with its own pros and cons. More details further down.
Uploading
For uploading clients, Steam has no tools on its own, and instead relies on individual games to implement their own upload interfaces. Since Haven's custom client uploading is primarily aimed at programmers, Haven implements this with a command-line tool, in the haven.SteamWorkshop class. The idea is that you prepare your item directory, which needs to contain at least the workshop-client.properties file, and invoke the main function on this class to upload it. Said main function is prepared to support different subcommands, but right now, there is only one, "upload". You would call it as such:
- Code: Select all
java -cp build/hafen.jar haven.SteamWorkshop upload /path/to/my/item
The command supports standard POSIX option syntax, and the -h option (ie. haven.SteamWorkshop upload -h) will provide the ordinary usage message as a shorthand. It also supports a -q option to silence the progress messages it will otherwise output by default (which might be useful for automated build-and-upload scripts or such). Additionally, Steam allows for something similar to a "commit message" to be specified with an upload, which can be given as an optional extra argument after the path to the item directory, should you find this feature useful.
As for metadata, the upload tool reuses the workshop-client.properties file to specify it. There are four properties that are required for a steam upload:
- name: The name of the client as presented to the user.
- description: The description of the client as presented to the user.
- preview-image: An image presented as the icon to the user. Must be the name of an image file relative to the item's root directory. I realize a "preview image" might not make a whole lot of sense for a custom client, but Steam requires it, so it is what it is.
- visibility: A Workshop item can be set to different levels of visibility, as per so:
- private: The client can only be seen by its creator.
- friends: The client can only be seen by its creator and those on his/her friend list.
- public: The client can be seen by anyone.
Launching
As mentioned, when a user chooses a custom client to launch, the workshop-client.properties file is consulted on how to actually launch said client. Currently, the WorkshopLauncher supports two methods of launching, as follows.
"Direct" launching
By specifying the main-class and class-path properties, the Workshop launcher will load the specified classpath into the currently running JVM and run the public static void main(String[]) function of that class (with an empty argument array). As such, it starts rather quickly, but it otherwise inherits the JVM settings (classpath, system properties, heap size, &c) of the main client. Besides inheriting sysprops, sysprops can also be added and/or modified by setting sysprop.NAME=VALUE properties; for example sysprop.haven.errorurl=http://your-error-reporter.com/. Multiple Jar files can be specified in the class-path by specifying a colon-separated list, where each filename is relative to the item's root directory. Taken together a workshop-client.properties file for direct launching might look as such:
- Code: Select all
name=My Client
description=Some of features, I guess?
preview-image=icon.png
visibility=public
workshop-id=XXX
main-class=haven.MainFrame
class-path=my-hafen.jar:my-resources.jar
sysprop.haven.errorurl=stderr
Chained launching
By specifying the launcher property, the normal Haven launcher will be started with that launcher file, which will load your client however you see fit. The advantages are mainly that it allows you to specify JVM parameters freely and that it allows you to reuse your existing setup in case you're already using the Haven launcher, but given that it launches a new JVM, it increases startup time. It can be used as such:
- Code: Select all
name=My Client
description=Some of features, I guess?
preview-image=icon.png
visibility=public
workshop-id=XXX
launcher=launcher.hl
Where in turn, the launcher.hl file (also resolved relative to the item's root directory) might contain the follow to find your main launcher file if you host it on your own site:
- Code: Select all
validate always
title "Haven & Hearth"
icon "https://game.havenandhearth.com/java/icon.gif"
splash-image "https://game.havenandhearth.com/java/splash.gif"
validate tls-cert:key:rsa:99118FA7C13D9686021FFDB74F8FFE77CA5EB7D356A8D31BB907A31AFAE04D46
include https://game.havenandhearth.com/java/hafen.hl
Or something like that.
Miscellaneous
In order to make uploaded items visible in the workshop, Steam requires you to agree to the Workshop Legal Agreement, which is available to agree to in the Workshop tab in your Steam client. The upload tool will warn you if you haven't agreed.
If you don't like the above launching schemes, I'm open to adding others, as long as it's reasonable. Tell me what you'd like, and I'll consider adding it.
The JVM is the same one that ships with the default client, which is currently Java 21. Might be upgraded in the future if there's something interesting about newer Java releases.
As for malware in clients, obviously, just like any other custom client, a client can do anything any other program can do. My understanding is that it is common for other games as well to work like this in the Steam Workshop, so while it feels a bit weird, if it works for other games, I guess it works for Haven. I guess Steam simply relies on the Workshop Legal Agreement to police abuse?