Steam

概要

このドキュメントでは、 Ebitengine ゲームを Steam に対応させるために必要な手順を説明します。 Steam でゲームをリリースするためには審査が必要です。審査を通すためには、単に Ebitengine ゲームを Go でビルドするだけではありません。様々な作業が必要になります。この記事では Ebitengine ゲームを審査に通すために必要な事項をまとめます。 Steamworks の一般的な解説は省きます。

以下の説明では、ゲーム名を yourgame、ユーザー名を Your Name などにしています。適宜読み替えてください。

Steamworks SDK

Steam の機能にはユーザーの言語取得や実績解除などがあります。それらは Steamworks SDK 経由で呼び出されます。 SDK のファイル形式は DLL や so ファイルなどの動的ライブラリなので、 Go から使うためには一工夫必要です。

そこで、 go-steamworks というバインディングを作りました。 import するだけで使えます。ただし、 Windows の場合は Steamworks の DLL を別途ダウンロードして同梱する必要があります。

例えば、「Steam クライアント経由で開いていなかったら開き直す」処理は次のように書けます。

package main

import (
	"os"

	"github.com/hajimehoshi/go-steamworks"
)

const appID = 480 // Use your application ID.

func init() {
	if steamworks.RestartAppIfNecessary(appID) {
		os.Exit(1)
	}
	if !steamworks.Init() {
		panic("steamworks.Init failed")
	}
}

なおこのバインディングはまだ API をほとんど実装していません。今後対応予定です。

Windows

Windows は最も簡単で、普通に Go でビルドするだけです。 Ebitengine は Windows においては Pure Go なので、 GOOSGOARCH を指定すればどこでもビルドできます。

Go アプリケーションを Windows Steam 環境で動かすときに、フリーズすることがある問題が報告されています (#3181golang/go#71242)。ワークアラウンドとして、 go build を実行する際に -ldflags="-X=runtime.godebugDefault=asyncpreemptoff=1" を指定してください。なお、下記の手順では更に -H=windowsgui を追加して、最初にコンソールが開かないようにしています。

PowerShell でビルドする場合は次のようになります。

$Env:GOARCH = 'amd64'
go build -o yourgame_windows_amd64.exe -ldflags="-X=runtime.godebugDefault=asyncpreemptoff=1 -H=windowsgui" .
Remove-Item Env:GOARCH

POSIX シェルでビルドする場合は次のようになります。

env GOOS=windows GOARCH=amd64 go build -o yourgame_windows_amd64.exe -ldflags="-X=runtime.godebugDefault=asyncpreemptoff=1 -H=windowsgui" .

アイコンは必須ではありません。気になる方はリソースを埋め込むツールを適宜使ってください。ゲーム実行中に ebiten.SetWindowIcon を呼ぶことで、タスクバーに表示されるアイコンを変更することはできます。

こうして出来上がった exe ファイルを zip で固めて、 Steamworks にビルドとしてアップロードしてください。

macOS

macOS の場合は .app 形式のアプリケーションを作る必要があります。また Apple から公証 (Notarization) を受ける必要があります。公証を受けるためには Apple Developer の登録が必須です。

本記事作成にあたって、ブログ記事「Releasing Steam Games on Mac Is a Monster Pain」を参考にしました。

まずアイコンファイルを icns 形式で用意します。この形式は PNG などを Preview.app で開いてエクスポートするだけで作れます。エクスポートのフォーマット一覧に出てこない場合は、 Option キーを押しながらフォーマット一覧を開くと出てきます。

次に必要最小限の .app を次のように作ります。アーキテクチャとして、 amd64 (Intel) と arm64 (ARM) のユニバーサルバイナリを lipo コマンドで作成します。

name=yourgame
app_name=YourGame.app
bundle_id=com.example.yourgame

rm -rf ${app_name}
mkdir -p ${app_name}/Contents/MacOS
mkdir -p ${app_name}/Contents/Resources
env CGO_ENABLED=1 CGO_CFLAGS=-mmacosx-version-min=10.12 CGO_LDFLAGS=-mmacosx-version-min=10.12 GOARCH=amd64 go build -o ${name}_amd64 .
env CGO_ENABLED=1 CGO_CFLAGS=-mmacosx-version-min=10.12 CGO_LDFLAGS=-mmacosx-version-min=10.12 GOARCH=arm64 go build -o ${name}_arm64 .
lipo ${name}_amd64 ${name}_arm64 -create -output ${app_name}/Contents/MacOS/${name}
rm ${name}_amd64
rm ${name}_arm64
cp icon.icns ${app_name}/Contents/Resources/icon.icns
echo '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleExecutable</key>
    <string>{{.Name}}</string>
    <key>CFBundleIdentifier</key>
    <string>{{.BundleID}}</string>
    <key>CFBundleIconFile</key>
    <string>icon.icns</string>
    <key>CFBundleVersion</key>
    <string>0.0.0</string>
    <key>CFBundleShortVersionString</key>
    <string>0.0.0</string>
    <key>NSHighResolutionCapable</key>
    <true />
    <key>LSMinimumSystemVersion</key>
    <string>10.12.0</string>
  </dict>
</plist>' |
    sed -e "s/{{.Name}}/${name}/g" |
    sed -e "s/{{.BundleID}}/${bundle_id}/g" > ${app_name}/Contents/Info.plist

次に App ID (Bundle ID) を、 Apple Developer の Account ページで、ない場合は作成します。

次に Developer ID (Developer ID Application) の Certificate を、 Apple Developer の Account ページで、ない場合は作成します。

次に App-Specific Password を作ります。 Apple ID のサイトから簡単に作れます。詳しくは Apple のヘルプページを参考にしてください。

次に公証を受けます。 stapler までの公証の手順を行うライブラリ notarize を作成いたしました。以下が使用例です。

package main

import (
	"fmt"
	"os"

	"github.com/hajimehoshi/notarize"
)

func main() {
	appPassword := os.Getenv("APP_PASSWORD")
	if appPassword == "" {
		fmt.Fprintln(os.Stderr, "an environment variable APP_PASSWORD must be set. see https://support.apple.com/en-us/HT204397")
		os.Exit(1)
	}

	op := ¬arize.NotarizeOptions{
		AppleID:         "[email protected]",
		SigningIdentity: "Developer ID Application: Your Name (YOURTEAMID)",
		TeamID:          "YOURTEAMID",
		AppPassword:     appPassword,
		ProgressOutput:  os.Stdout,
	}
	if err := notarize.Notarize("YourGame.app", op); err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
}

こうして出来上がった .app を Steamworks にビルドとしてアップロードする際、 zip コマンドや Finder のメニューで zip ファイルを作ってはいけません。公証を受けた .app には特殊なファイルをが含まれていて、普通に zip を作ろうとするとそのファイルが欠落してしまいます。代わりに ditto コマンドを使って zip ファイルを作ります。

ditto -c -k --keepParent YourGame.app yourgame_darwin_amd64.zip

Linux

Linux の場合は Steam Runtime が Dockerfile として用意されています。その環境下でビルドするのが最も簡単です。 2025 年 1 月現在の Docker イメージは Sniper をご参照ください。 32bit Linux には対応していないようです。

name=yourgame
STEAM_RUNTIME_VERSION=3.0.20250108.112707
GO_VERSION=$(go env GOVERSION)

mkdir -p .cache/${STEAM_RUNTIME_VERSION}

# Download binaries for amd64.
if [[ ! -f .cache/${STEAM_RUNTIME_VERSION}/com.valvesoftware.SteamRuntime.Sdk-amd64,i386-sniper-sysroot.Dockerfile ]]; then
    (cd .cache/${STEAM_RUNTIME_VERSION}; curl --location --remote-name https://repo.steampowered.com/steamrt-images-sniper/snapshots/${STEAM_RUNTIME_VERSION}/com.valvesoftware.SteamRuntime.Sdk-amd64,i386-sniper-sysroot.Dockerfile)
fi
if [[ ! -f .cache/${STEAM_RUNTIME_VERSION}/com.valvesoftware.SteamRuntime.Sdk-amd64,i386-sniper-sysroot.tar.gz ]]; then
    (cd .cache/${STEAM_RUNTIME_VERSION}; curl --location --remote-name https://repo.steampowered.com/steamrt-images-sniper/snapshots/${STEAM_RUNTIME_VERSION}/com.valvesoftware.SteamRuntime.Sdk-amd64,i386-sniper-sysroot.tar.gz)
fi
if [[ ! -f .cache/${GO_VERSION}.linux-amd64.tar.gz ]]; then
    (cd .cache; curl --location --remote-name https://golang.org/dl/${GO_VERSION}.linux-amd64.tar.gz)
fi

# Build for amd64
(cd .cache/${STEAM_RUNTIME_VERSION}; docker build -f com.valvesoftware.SteamRuntime.Sdk-amd64,i386-sniper-sysroot.Dockerfile -t steamrt_sniper_amd64:latest .)
docker run --rm --workdir=/work --volume $(pwd):/work steamrt_sniper_amd64:latest /bin/sh -c "
export PATH=\$PATH:/usr/local/go/bin
export CGO_CFLAGS=-std=gnu99

rm -rf /usr/local/go && tar -C /usr/local -xzf .cache/${GO_VERSION}.linux-amd64.tar.gz

go build -o ${name}_linux_amd64 .
"

出来上がった yourgame_linux_amd64 をそれぞれ zip に固めて、 Steamworks にビルドとしてアップロードしてください。

その他