Skip to content

Commit 60fe979

Browse files
utkarshdalalUtkarsh Dalal
andauthored
Added experimental steam bionic client, enables online play (utkarshdalal#1430)
* Added connection to real steam android client, games booting * Small updates to bootstrap to stop crashes * added debug channel * updated bootstrap to load steamservice * updated bootstrap steamservice launching * Updated bootstrap - online games now work? * bootstrap? * Extract real steam on real steam mode * Fixed DLC in vampire survivors for online play * Added missing function * Create toggle for bionic steam client, untested * removed setting of steamcloud disabled, correctly fetch and extract lsteamclient * Updates to make vampire survivors work * undid change * preload libjpeg to fix xiaomi issues * final touches for bionic steam client * Updated strings for bionic steam client * Removed steam.exe from assets, added some more env vars * add experimental drm to downloads needed for bionic steam --------- Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
1 parent 75326dc commit 60fe979

37 files changed

Lines changed: 1065 additions & 26 deletions

File tree

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,8 @@ app/keystores/*
6262
*.pyc
6363
__pycache__/
6464
resign.keystore
65+
66+
# Steam bootstrap shim source — withheld out of respect for Valve, since it
67+
# encodes internal details of Steam's proprietary client that Valve does not
68+
# publish. See THIRD_PARTY_NOTICES ("Steam Client Bootstrap Shim").
69+
app/src/main/cpp/steambootstrap/steam_bootstrap.c

THIRD_PARTY_NOTICES

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,94 @@ in the sense of GPL-3.0 §0 ("based on").
102102

103103
---
104104

105+
## Steam Client Bootstrap Shim (Source Withheld)
106+
107+
**Component:** `libsteambootstrap.so`
108+
**Binary location:** `app/src/main/jniLibs/<abi>/libsteambootstrap.so`
109+
**Source location in repo:** intentionally absent
110+
(`app/src/main/cpp/steambootstrap/steam_bootstrap.c` is listed in
111+
`.gitignore` and is **not** committed to this repository)
112+
**Authorship:** Written from scratch by the GameNative maintainers. Not derived
113+
from Valve's Steam client, the Steamworks SDK, or any third-party project.
114+
**License of the binary as distributed in this APK:** Proprietary —
115+
all rights reserved by the GameNative maintainers.
116+
117+
`libsteambootstrap.so` is a small JNI shim loaded by the GameNative Android
118+
application when launching a title in "real Steam" mode. It performs only
119+
two jobs:
120+
121+
1. `dlopen()` the Android build of `libsteamclient.so` that ships inside the
122+
user's Steam container, and
123+
2. resolve a handful of exported entry points from that library and invoke
124+
them, in order, to bring the in-process Steam client to a logged-in state
125+
using the refresh token the GameNative application already obtained
126+
through the standard Steam authentication flow.
127+
128+
The shim does **not** wrap, embed, link against, or redistribute any code from
129+
Valve's Steam client, the Steamworks SDK, or any other Valve-owned component.
130+
It interacts with `libsteamclient.so` exclusively at runtime, in the user's
131+
own Steam installation, through the same public dynamic-loader interface that
132+
Valve's own `steam.exe` uses on every other platform.
133+
134+
### Why the source is not in this repository
135+
136+
The shim's implementation encodes specific knowledge about which exported
137+
symbols and proxy-vtable slots inside `libsteamclient.so` correspond to which
138+
Steam client operations (pipe creation, global-user attachment, login
139+
information / token registration, logon kickoff, logoff, pipe and user
140+
release, and connection-state polling). That information was derived
141+
exclusively from local static analysis of a `libsteamclient.so` binary
142+
already present on the maintainer's own device as part of an installed Steam
143+
container; it is not reproduced from, copied from, or based on any leaked,
144+
proprietary, or non-public Valve source material.
145+
146+
Those symbols and slot layouts are nevertheless internal details of Valve's
147+
proprietary Steam client. Valve does not document them, does not publish
148+
them, and does not treat them as a public interface. The GameNative project
149+
has decided to respect that: we use the information to make our own
150+
application work against a Steam client the user has already installed, but
151+
we do not publish a redistributable map of those internals as part of this
152+
repository.
153+
154+
Practically, this means the shim's source lives only on maintainer
155+
workstations and the binary built from it (`libsteambootstrap.so`) ships in
156+
the APK. The source file itself is listed in `.gitignore` so it cannot be
157+
committed by accident.
158+
159+
All of GameNative's own application logic — UI, store integration, container
160+
management, device configuration, the token-based login flow that produces
161+
the refresh token in the first place, and the JNI declaration that calls
162+
into this shim — remains fully open under GPL-3.0.
163+
164+
### Relationship to the GPL-3.0 application
165+
166+
GameNative's Kotlin/Java application is distributed under GPL-3.0 and remains
167+
so. `libsteambootstrap.so` is a separate native module. It exposes a small,
168+
well-defined JNI surface (currently `nativeInit` and `nativeShutdown`,
169+
declared in `app/src/main/java/app/gamenative/SteamBootstrap.kt`) and
170+
contains no code copied from the GPL-3.0 application. The application can
171+
be built and run without it; in that configuration "real Steam" launch mode
172+
is unavailable, but every other GameNative feature continues to work. Under
173+
the FSF's and SFLC's longstanding interpretation of the GPL's "mere
174+
aggregation" language (GPL-3.0 §5, GPL-2.0 §2), this makes
175+
`libsteambootstrap.so` a separate program aggregated with GameNative on the
176+
same distribution medium rather than a derivative work of the application.
177+
178+
### Replacing the shim
179+
180+
A fork or downstream packager who wishes to provide an alternative
181+
implementation may:
182+
183+
1. Implement the JNI methods declared on `app.gamenative.SteamBootstrap`
184+
(`nativeInit(...)`, `nativeShutdown()`) in their own native library, and
185+
2. Place the resulting `.so` at
186+
`app/src/main/jniLibs/<abi>/libsteambootstrap.so` in place of the binary
187+
shipped here.
188+
189+
No part of the GPL-3.0 application needs to be modified to do this.
190+
191+
---
192+
105193
## Kenney Input Prompts
106194

107195
**Asset Pack:** Input Prompts
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
["acledit","aclui","actctx","activeds","actxprxy","adpcm","adsldp","advapi","advpack","alsa","amsi","animate","appbar","apphelp","appwizcpl","appx","asmshader","aspi","atl","atlthunk","atmlib","atom","authz","avicap","avifile","avrt","bcrypt","bidi","bitblt","bitmap","bluetooth","bluetoothapis","browseui","button","bytecodewriter","cabinet","capi","capture","cards","cdosys","class","clipboard","clipping","clusapi","combase","combo","comboex","comm","commctrl","commdlg","compstui","comsvcs","concrt","connect","console","context","coreaudio","cred","credentials","credui","crypt","cryptasn","cryptdlg","cryptext","cryptnet","crypto","cryptui","ctapi32","cursor","d2d","d3d","d3d10","d3d10core","d3d11","d3d12","d3d8","d3d9","d3d_decl","d3d_shader","d3dcompiler","d3drm","d3dx","d3dxof","d3dxof_parsing","data","datetime","davclnt","dbgeng","dbghelp","dbghelp_coff","dbghelp_dwarf","dbghelp_macho","dbghelp_msc","dbghelp_stabs","dc","dciman","dcomp","ddeml","ddraw","ddrawex","debug_buffer","debugstr","devenum","dhcpcsvc","dhtmled","dialog","diasymreader","difxapi","dinput","display","dll","dmband","dmcompos","dmfile","dmime","dmloader","dmobj","dmscript","dmstyle","dmsynth","dmusic","dmusic32","dnsapi","dosmem","dpa","dplay","dpnet","dpnhpast","dpnhupnp","dpvoice","dragdrop","driver","dsa","dsdmo","dsound","dsound3d","dsquery","dssenh","dsuiext","dswave","dwmapi","dwrite","dx8vb","dxcore","dxdiag","dxgi","dxtrans","dxva2","edit","enhmetafile","enumeration","environ","err","event","eventlog","evr","exception","exec","explorerframe","faultrep","file","fixme","fixup","fltlib","fltmgr","font","fontcache","fontsub","fusion","fwpuclnt","g711","gamebar","gameux","gamingtcui","gdi","gdiplus","geolocator","gl_compat","global","globalmem","glu","graphics","gsm","handle","header","heap","hid","hlink","hnetcfg","hook","hostname","hotkey","htmlhelp","http","hvsi","iccvid","icm","icon","ieframe","image","imagehlp","imagelist","imm","inetcomm","inetcpl","inetmib1","infosoft","inkobj","input","inseng","int","int21","int31","ipaddress","iphlpapi","ir50_32","itss","joycpl","jscript","jsproxy","kerberos","kernelbase","keyboard","ksecdd","lanman","listbox","listview","loaddll","loader","loadperf","local","locale","localspl","localui","macdrv","manipulation","mapi","mci","mciavi","mcicda","mcimidi","mciqtz","mciwave","mdi","media","mediacontrol","menu","message","metafile","mfplat","mgmtapi","midi","mlang","mmaux","mmc","mmdevapi","mmio","mmsys","mmtime","model","module","monthcal","mountmgr","mp3dmod","mpeg3","mpr","mprapi","msacm","msado15","msasn1","msauddecmft","mscms","msctf","msctfmonitor","msdasql","msdmo","msdrm","msftedit","msg","mshtml","msi","msidb","msident","msimg32","msimtf","msisip","msisys","msmpeg2vdec","msnet","msopc","mspatcha","msrle32","msscript","mssign","mstask","msttsengine","msvcirt","msvcm","msvcp","msvcrt","msvidc32","msvideo","mswsock","msxml","nativefont","ncrypt","nddeapi","ndis","netapi32","netbios","netcfgx","netio","netprofm","ninput","nls","nonclient","nsi","nstc","ntdll","ntdsapi","ntlm","ntoskrnl","ntprint","objsel","odbc","ole","oleacc","oledb","oledlg","olemalloc","olepicture","opencl","opengl","oss","packager","pager","palette","path","pdh","perception","pidgen","pidl","pid","plugplay","powermgnt","powrprof","print","printui","prntvpt","process","profile","progress","propsheet","propsys","psdrv","pstores","pulse","qmgr","quartz","query","qwave","ras","rasdlg","rawinput","rebar","recyclebin","reg","region","relay","resource","richedit","richedit_lists","rpc","rstrtmgr","rtutils","sapi","schannel","schedsvc","scrobj","scroll","scrrun","scsiport","secur32","security","seh","selector","sensapi","service","setupapi","sfc","shcore","shdocvw","shell","shlctrl","slc","smbios","snmpapi","snoop","sound","speech","spoolss","sspicli","static","statusbar","sti","storage","stress","string","sxs","sync","syslevel","syslink","system","systray","t2embed","tab","tape","tapi","task","taskdialog","taskschd","tbs","tdh","tdi","text","theme_scroll","thread","threadpool","thunk","timestamp","toolbar","toolhelp","tooltips","trackbar","traffic","treeview","twain","twinapi","ui","uianimation","uiautomation","uiribbon","unloaddll","unwind","updown","updspapi","url","urlmon","usb","usbd","user","uxtheme","variant","vbscript","vcomp","vcruntime","vdmdbg","ver","virtdisk","vkd3d","volume","vulkan","vxd","warn","wavemap","waylanddrv","wbemdisp","wbemprox","webservices","wer","wevtapi","wgl","wia","wimgapi","win","wincodecs","winemapi","wineusb","wing","winhttp","wininet","winmm","winprint","winscard","winsock","winspool","winsta","winstation","winstring","wintab","wintab32","wintrust","wintypes","winusb","wlanapi","wldap32","wldp","wmadec","wmi","wmiutils","wmp","wmvcore","wnet","wofutil","wow","wpc","wpcap","wsdapi","wshom","wsnmp32","wtsapi","wuapi","x11drv","xaudio2","xdnd","xim","xinput","xmllite","xolehlp","xrandr","xrender","xvidmode"]
1+
["acledit","aclui","actctx","activeds","actxprxy","adpcm","adsldp","advapi","advpack","alsa","amsi","animate","appbar","apphelp","appwizcpl","appx","asmshader","aspi","atl","atlthunk","atmlib","atom","authz","avicap","avifile","avrt","bcrypt","bidi","bitblt","bitmap","bluetooth","bluetoothapis","browseui","button","bytecodewriter","cabinet","capi","capture","cards","cdosys","class","clipboard","clipping","clusapi","combase","combo","comboex","comm","commctrl","commdlg","compstui","comsvcs","concrt","connect","console","context","coreaudio","cred","credentials","credui","crypt","cryptasn","cryptdlg","cryptext","cryptnet","crypto","cryptui","ctapi32","cursor","d2d","d3d","d3d10","d3d10core","d3d11","d3d12","d3d8","d3d9","d3d_decl","d3d_shader","d3dcompiler","d3drm","d3dx","d3dxof","d3dxof_parsing","data","datetime","davclnt","dbgeng","dbghelp","dbghelp_coff","dbghelp_dwarf","dbghelp_macho","dbghelp_msc","dbghelp_stabs","dc","dciman","dcomp","ddeml","ddraw","ddrawex","debug_buffer","debugstr","devenum","dhcpcsvc","dhtmled","dialog","diasymreader","difxapi","dinput","display","dll","dmband","dmcompos","dmfile","dmime","dmloader","dmobj","dmscript","dmstyle","dmsynth","dmusic","dmusic32","dnsapi","dosmem","dpa","dplay","dpnet","dpnhpast","dpnhupnp","dpvoice","dragdrop","driver","dsa","dsdmo","dsound","dsound3d","dsquery","dssenh","dsuiext","dswave","dwmapi","dwrite","dx8vb","dxcore","dxdiag","dxgi","dxtrans","dxva2","edit","enhmetafile","enumeration","environ","err","event","eventlog","evr","exception","exec","explorerframe","faultrep","file","fixme","fixup","fltlib","fltmgr","font","fontcache","fontsub","fusion","fwpuclnt","g711","gamebar","gameux","gamingtcui","gdi","gdiplus","geolocator","gl_compat","global","globalmem","glu","graphics","gsm","handle","header","heap","hid","hlink","hnetcfg","hook","hostname","hotkey","htmlhelp","http","hvsi","iccvid","icm","icon","ieframe","image","imagehlp","imagelist","imm","inetcomm","inetcpl","inetmib1","infosoft","inkobj","input","inseng","int","int21","int31","ipaddress","iphlpapi","ir50_32","itss","joycpl","jscript","jsproxy","kerberos","kernelbase","keyboard","ksecdd","lanman","listbox","listview","loaddll","loader","loadperf","local","locale","localspl","localui","macdrv","manipulation","mapi","mci","mciavi","mcicda","mcimidi","mciqtz","mciwave","mdi","media","mediacontrol","menu","message","metafile","mfplat","mgmtapi","midi","mlang","mmaux","mmc","mmdevapi","mmio","mmsys","mmtime","model","module","monthcal","mountmgr","mp3dmod","mpeg3","mpr","mprapi","msacm","msado15","msasn1","msauddecmft","mscms","msctf","msctfmonitor","msdasql","msdmo","msdrm","msftedit","msg","mshtml","msi","msidb","msident","msimg32","msimtf","msisip","msisys","msmpeg2vdec","msnet","msopc","mspatcha","msrle32","msscript","mssign","mstask","msttsengine","msvcirt","msvcm","msvcp","msvcrt","msvidc32","msvideo","mswsock","msxml","nativefont","ncrypt","nddeapi","ndis","netapi32","netbios","netcfgx","netio","netprofm","ninput","nls","nonclient","nsi","nstc","ntdll","ntdsapi","ntlm","ntoskrnl","ntprint","objsel","odbc","ole","oleacc","oledb","oledlg","olemalloc","olepicture","opencl","opengl","oss","packager","pager","palette","path","pdh","perception","pidgen","pidl","pid","plugplay","powermgnt","powrprof","print","printui","prntvpt","process","profile","progress","propsheet","propsys","psdrv","pstores","pulse","qmgr","quartz","query","qwave","ras","rasdlg","rawinput","rebar","recyclebin","reg","region","relay","resource","richedit","richedit_lists","rpc","rstrtmgr","rtutils","sapi","schannel","schedsvc","scrobj","scroll","scrrun","scsiport","secur32","security","seh","selector","sensapi","service","setupapi","sfc","shcore","shdocvw","shell","shlctrl","slc","smbios","snmpapi","snoop","sound","speech","spoolss","sspicli","static","statusbar","steam","steamclient","sti","storage","stress","string","sxs","sync","syslevel","syslink","system","systray","t2embed","tab","tape","tapi","task","taskdialog","taskschd","tbs","tdh","tdi","text","theme_scroll","thread","threadpool","thunk","tid","timestamp","toolbar","toolhelp","tooltips","trackbar","traffic","treeview","twain","twinapi","ui","uianimation","uiautomation","uiribbon","unloaddll","unwind","updown","updspapi","url","urlmon","usb","usbd","user","uxtheme","variant","vbscript","vcomp","vcruntime","vdmdbg","ver","virtdisk","virtual","vkd3d","volume","vulkan","vxd","warn","wavemap","waylanddrv","wbemdisp","wbemprox","webservices","wer","wevtapi","wgl","wia","wimgapi","win","wincodecs","winemapi","wineusb","wing","winhttp","wininet","winmm","winprint","winscard","winsock","winspool","winsta","winstation","winstring","wintab","wintab32","wintrust","wintypes","winusb","wlanapi","wldap32","wldp","wmadec","wmi","wmiutils","wmp","wmvcore","wnet","wofutil","wow","wpc","wpcap","wsdapi","wshom","wsnmp32","wtsapi","wuapi","x11drv","xaudio2","xdnd","xim","xinput","xmllite","xolehlp","xrandr","xrender","xvidmode"]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
cmake_minimum_required(VERSION 3.22.1)
2+
3+
project(SteamBootstrap C)
4+
5+
add_library(steambootstrap SHARED steam_bootstrap.c)
6+
7+
target_compile_options(steambootstrap PRIVATE
8+
-fvisibility=hidden
9+
-fno-asynchronous-unwind-tables
10+
-fno-unwind-tables
11+
-ffunction-sections
12+
-fdata-sections)
13+
14+
target_link_options(steambootstrap PRIVATE
15+
-Wl,--gc-sections
16+
-Wl,--exclude-libs,ALL
17+
"$<$<NOT:$<CONFIG:Debug>>:-Wl,--strip-all>")
18+
19+
target_link_libraries(steambootstrap log dl)

app/src/main/java/app/gamenative/PluviaApp.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class PluviaApp : SplitCompatApplication() {
5050
override fun onCreate() {
5151
super.onCreate()
5252

53+
preloadSystemLibraries()
54+
5355
// Allows to find resource streams not closed within GameNative and JavaSteam
5456
if (BuildConfig.DEBUG) {
5557
StrictMode.setVmPolicy(
@@ -251,4 +253,34 @@ class PluviaApp : SplitCompatApplication() {
251253
fun isManualSuspendMode(): Boolean = activeSuspendPolicy.equals(Container.SUSPEND_POLICY_MANUAL, ignoreCase = true)
252254

253255
}
256+
257+
/**
258+
* Some native libraries we dlopen at runtime (libsteamclient.so via SteamBootstrap,
259+
* the lsfg-vk layer, etc.) depend on `libjpeg.so`, which isn't on every device's
260+
* dynamic linker search path. Pre-load the system copy here with RTLD_GLOBAL
261+
* semantics (System.load is global) so all subsequent dlopens find its symbols.
262+
*
263+
* Single place for all: runs once in Application.onCreate before any other
264+
* native lib is loaded by this process. Failures are non-fatal — devices that
265+
* don't have the file (or have it elsewhere) just fall through.
266+
*/
267+
private fun preloadSystemLibraries() {
268+
val is64 = android.os.Build.SUPPORTED_64_BIT_ABIS.isNotEmpty()
269+
val candidates = if (is64) {
270+
listOf("/system/lib64/libjpeg.so", "/system/lib/libjpeg.so")
271+
} else {
272+
listOf("/system/lib/libjpeg.so", "/system/lib64/libjpeg.so")
273+
}
274+
for (path in candidates) {
275+
if (!File(path).exists()) continue
276+
try {
277+
System.load(path)
278+
Timber.i("[PluviaApp]: Preloaded $path")
279+
return
280+
} catch (e: Throwable) {
281+
Timber.w(e, "[PluviaApp]: System.load($path) failed")
282+
}
283+
}
284+
Timber.w("[PluviaApp]: Could not preload system libjpeg.so (none of the candidate paths worked)")
285+
}
254286
}

app/src/main/java/app/gamenative/PrefManager.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,13 @@ object PrefManager {
468468
setPref(LAUNCH_REAL_STEAM, value)
469469
}
470470

471+
private val LAUNCH_BIONIC_STEAM = booleanPreferencesKey("launch_bionic_steam")
472+
var launchBionicSteam: Boolean
473+
get() = getPref(LAUNCH_BIONIC_STEAM, false)
474+
set(value) {
475+
setPref(LAUNCH_BIONIC_STEAM, value)
476+
}
477+
471478
private val FORCE_DLC = booleanPreferencesKey("force_dlc")
472479
var forceDlc: Boolean
473480
get() = getPref(FORCE_DLC, false)

0 commit comments

Comments
 (0)