Initial commit.

This commit is contained in:
Mr_Goldberg 2019-04-13 12:21:56 -04:00
commit d968c3e1b5
No known key found for this signature in database
GPG Key ID: 8597D87419DEF278
238 changed files with 86384 additions and 0 deletions

47
Makefile Normal file
View File

@ -0,0 +1,47 @@
.DEFAULT_GOAL := all
CXX=clang++
CXX_FLAGS += -fPIC -std=c++11
LD_FLAGS += -shared -lprotobuf-lite -Wl,--no-undefined
LIBRARY_NAME=libsteam_api.so
RM = rm -f
PROTOS := $(wildcard dll/*.proto)
PROTO_CC := $(PROTOS:.proto=.pb.cc)
PROTO_OBJ := $(PROTOS:.proto=.pb.o)
PROTO_HEADERS := $(PROTOS:.proto=.pb.h)
HEADERS := $(wildcard dll/*.h) $(PROTO_HEADERS)
SRC_NOPROTO := $(wildcard dll/*.cpp)
SRC := $(SRC_NOPROTO) $(PROTO_CC)
OBJ_NOPROTO := $(SRC_NOPROTO:.cpp=.o)
OBJ := $(OBJ_NOPROTO) $(PROTO_OBJ)
$(PROTO_CC) : $(PROTOS)
$(PROTO_HEADERS) : $(PROTO_CC)
$(OBJ_NOPROTO) : $(PROTO_CC) $(PROTO_HEADERS)
$(OBJ) : $(HEADERS)
release: CXX_FLAGS += -DNDEBUG -DEMU_RELEASE_BUILD -Ofast
release: LD_FLAGS += -lpthread
debug: CXX_FLAGS += -g3 -fsanitize=address
debug: LD_FLAGS += -lasan
release: library
debug: library
all: release
library: $(LIBRARY_NAME)
$(PROTO_CC) :
protoc -I./dll/ --cpp_out=./dll/ ./dll/*.proto
$(PROTO_OBJ) : %.o : %.cc
$(CXX) $(CXX_FLAGS) -c -o $@ $<
$(OBJ_NOPROTO) : %.o : %.cpp
$(CXX) $(CXX_FLAGS) -c -o $@ $<
$(LIBRARY_NAME): $(OBJ)
$(CXX) $(CXX_FLAGS) -o $@ $^ $(LD_FLAGS)
clean:
$(RM) $(OBJ) $(LIBRARY_NAME) $(PROTO_CC) $(PROTOS:.proto=.pb.h)

106
README.md Normal file
View File

@ -0,0 +1,106 @@
# Goldberg Steam Emulator
This is a steam emulator that emulates steam online features on a LAN. It works on both Linux and Windows. For a readme on how to use it see: [The Release Readme](Readme_release.txt)
You replace the steam api .dll or .so with mine (for complete steps see the [Release Readme](Readme_release.txt)) and then you can put steam in the trash and play your games either in single player on on LAN without steam (Assuming the games have no DRM and use Steam for online).
If you are a game developper and made the mistake of depending too much on the steam api and want to release of version of your game without it and don't want to rewrite your game, this is for you. It is licenced LGPLv3+ so the only source code you need to publish is the source code of this emulator (and only if you make modification to it).
## Contributions
One of the reasons I made this code open source is because I want contributions. Unless your code is related to the experimental stuff it needs to work on both Linux and Windows. Having accurate behavior is more important than making games work. Having inaccurate behavior might fix one game but it will break others.
## Building
Dependencies: protobuf-lite
#### Linux
Install protobuf-lite (the dev package) and protoc (or protobuf-compiler or whatever it's called in your distro) using your package manager.
Then do: `make`
And it will build the release build (Don't forget to to add something like `-j8` if your computer isn't a piece of shit and you want it to build at a decent speed).
To build the debug build: `make debug`
My makefile sucks so you might need to do: `make clean` if you want to build the debug build after building the release build or the opposite.
For my release build I build it on steamOS using the `build_steamos.sh` script. For it to work you need a x86 version of protobuf installed to: `../protobuf/prefix_x86/` and a x64 version installed to: `../protobuf/prefix/`
#### Windows
The first thing you should do is install git for windows. [Git for Windows](https://git-scm.com/download/win)
Then install visual studio build tools: [Microsoft Build tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16) (Make sure you install the C++ build tools. Just select `C++ build tools` in the installer and press install.)
Create a new folder somewhere on your computer.
Go in that folder then right-click open the git command prompt. (Right click in folder->Git Bash Here)
Run the commands:
```
git clone https://github.com/Microsoft/vcpkg
cd vcpkg
./bootstrap-vcpkg.bat
./vcpkg install protobuf --triplet x86-windows-static
./vcpkg install protobuf --triplet x64-windows-static
cd ..
git clone https://gitlab.com/Mr_Goldberg/goldberg_emulator.git
cd goldberg_emulator
```
This should build and install all the dependencies and clone the repo. Some commands like the bootstrap-vcpkg.bat and vcpkg install might take a while.
Then to build the debug experimental version run: `build_win_debug_experimental.bat`
To build the release version run: `build_win_release.bat`
If for some reason you want to set the protobuf directories to something different you can edit: `build_set_protobuf_directories.bat`
##### Pulling the latest code
Go in the goldberg_emulator folder then right-click open the git command prompt. (Right click in folder->Git Bash Here)
Run the command:
```
git pull
```
## Design Choices / FAQ
##### Why are there no ini files like all the other Steam Emulators?
I think that the way other steam emulators have an ini when you set everything on a per game setting is dumb. The only things that should be set on a per game setting is the things that are specific to that game like the appid, DLC, mods, interface versions, etc...
The rest like your name should be set in a global place because I don't like having to set every fucking name of everyone in an ini for every game I copy to people when I want to copy them games to play on my LAN.
My emu is made in a way that you can just install it on a game and then copy the game to people and they don't have to change anything.
I agree that the fact that I have multiple files might be dumb but it's actually not. Your filesystem is a database so why would you have to replicate that by making one config file when you can just have many. It's a lot easier to manage coding wise.
##### Is there any difference between the Windows and Linux versions?
There is no difference in functionality between the normal windows version and the linux version. Windows has a experimental build which has features that only makes sense on windows.
##### What is the experimental version?
Read this if you want to know what it is: [The Experimental Readme](Readme_experimental.txt)
##### Is this illegal?
It's as illegal as Wine or any HLE console emulator. All this does is remove the steam dependency from your steam games.
##### But it breaks Steam DRM ?
It doesn't break any DRM. If the game has a protection that doesn't let you use a custom steam api dll it needs to be cracked before you use my emulator. Steam is a DRM as much as any API is a DRM. Steam has actual DRM called steamstub which can easily be cracked but this won't crack it for you.
##### Will you support CS:GO or Dota 2?
No, I don't care about making these games work because they use apis like the game coordinator that no other game uses. Also valve keeps changing them.
However if someone else wastes their time to get them working and I will happily merge their work.

32
Readme_experimental.txt Normal file
View File

@ -0,0 +1,32 @@
This is a build of my emulator that blocks all outgoing connections from the game to non LAN ips and lets you use CPY cracks that use the
steam_api dll to patch the exe in memory when the SteamAPI_Init() method is called.
To use a CPY style crack just rename the steam_api(64).dll crack to cracksteam_api.dll or cracksteam_api64.dll depending on the original name and replace the steamclient(64) dll with the one from this folder.
Then use my emu like you normally would. (Don't forget to put a steam_appid.txt) (If the original steam_api dll is old you must also put a steam_interfaces.txt)
For the LAN only connections feature:
If for some reason you want to disable this feature create a file named: disable_lan_only.txt beside the steam_api dll.
I noticed a lot of games seem to connect to analytics services and other crap that I hate.
Blocking the game from communicating with online ips without affecting the LAN functionality of my emu is a pain if you try to use a firewall so I made this special build.
In this folder is an experimental build of my emulator with code that hooks a few windows socket functions. It should block all connections from the game to non LAN ips. This means the game should work without any problems for LAN play (even with VPN LAN as long as you use standard LAN ips (10.x.x.x, 192.168.x.x, etc...)
It likely doesn't work for some games but it should work for most of them.
Since this blocks all non LAN connections doing things like hosting a cracked server for people on the internet will not work or connecting to a cracked server that's hosted on an internet ip will not work.
I will likely add this as a config option to the normal build in the future but I want to know if there are any problems first.
Let me know if there are any issues.
All ips except these ranges are blocked:
10.0.0.0 - 10.255.255.255
127.0.0.0 - 127.255.255.255
169.254.0.0 - 169.254.255.255
172.16.0.0 - 172.31.255.255
192.168.0.0 - 192.168.255.255
224.0.0.0 - 255.255.255.255

10
Readme_lobby_connect.txt Normal file
View File

@ -0,0 +1,10 @@
This is a small tool that discovers people playing on the network using my emu and lets you launch your game with parameters that will connect you to their games.
This is necessary for some games (like stonehearth). It will also let you join games with lobbies that are not public.
Steam has something called rich presence (the connect key) that lets games tell your friends what command line parameters to run your game with to join their game.
Most steam games also let you join lobbies in game without having started the game by starting the game with +connect_lobby <lobby id>.
Just run this tool and follow the instructions then pick the exe of the game. Make sure that you have installed my emu on the game first and that it works or it won't work.
If you want to help me improve it, the source can be found in the source folder.

89
Readme_release.txt Normal file
View File

@ -0,0 +1,89 @@
====Goldberg Steam Emulator====
An emulator that supports LAN multiplayer without steam.
How to use: Replace the steam_api(64).dll (libsteam_api.so on linux) from the game with mine. (For linux make sure to use the right build).
Put a steam_appid.txt file that contains the appid of the game right beside it if there isn't one already.
If your game has an original steam_api(64).dll older than may 2016 (Properties->Digital Signatures->Timestamp) you might have to add a steam_interfaces.txt beside my emulator library if the game isn't working.
This file contains interface versions, an example (for resident evil 5) is provided. To get those you can just get a hex editor and search for them (Search for the string: SteamUser0) in the original steam_api dll (or libsteam_api.so on linux) or look in the ini of a crack that works on that game.
If the game has DRM (other than steamworks) you need to remove/crack it first.
Default save location: C:\Users\<Your windows user name>\AppData\Roaming\Goldberg SteamEmu Saves\
For linux: $HOME/Goldberg SteamEmu Saves/
In the settings folder in that save location you will find 3 files (if you have used the emulator at least once):
account_name.txt (Edit this file to change your name)
listen_port.txt (Edit this file if you want to change the UDP/TCP port the emulator listens on (You should probably not change this because everyone needs to use the same port or you won't find yourselves on the network))
user_steam_id.txt (this is where your steam id is saved, you can change it (if your saves for a game are locked to a specific steam id see below for a way to change it on a per game basis) but it has to be valid)
language.txt (Edit this to change the language the emulator will report to the game, default is english, it must be a valid steam language name or the game might have weird behaviour (list provided at the end of this readme))
Note that these are global so you won't have to change them for each game. For game unique stuff (stats and remote storage) a folder is created with the appid of the game.
If you want to change your steam_id on a per game basis, simply create a settings folder in the game unique directory (Full path: C:\Users\<Your windows user name>\AppData\Roaming\Goldberg SteamEmu Saves\<appid>\settings)
In that settings folder create a user_steam_id.txt file that contains the valid steam id that you want to use for that game only.
If for some reason you want it to save in the game directory you can create a file named local_save.txt right beside steam_api(64).dll (libsteam_api.so on linux)
The only thing that file should contain is the name of the save directory. This can be useful if you want to use different global settings like a different account name or steam id for a particular game.
Note that this save directory will be beside where the emu dll (or .so) is which may not be the same as the game path.
DLC:
By default the emulator will try to unlock all DLCs (by returning true when the game calls the BIsDlcInstalled function). If the game uses the other function you will need to
provide a list of DLCs to my emulator. To do this first create a steam_settings folder right beside where you put my emulator.
In this folder, put a DLC.txt file. (path will be <path where my emu is>\steam_settings\DLC.txt)
If the DLC file is present, the emulator will only unlock the DLCs in that file. If the file is empty all DLCs will be locked.
The contents of this file are: appid=DLC name
See the steam_settings.EXAMPLE folder for an example.
Mods:
Put your mods in the steam_settings\mods\ folder. The steam_settings folder must be placed right beside my emulator dll.
Mod folders must be a number corresponding to the file id of the mod.
See the steam_settings.EXAMPLE folder for an example.
Steam appid:
The steam_appid.txt can be put in the steam_settings folder if for some reason you can't put it beside the steam api dll.
The steam appid can also be set using the SteamAppId or SteamGameId env variables (this is how real steam tells games what their appid is).
Support for CPY steam_api(64).dll cracks: See the build in the experimental folder.
Notes:
You must all be on the same LAN for it to work. This is an early work so a lot of games will likely not work.
IMPORTANT:
Do not run more than one steam game with the same appid at the same time on the same computer with my emu or there might be network issues (dedicated servers should be fine though).
List of valid steam languages:
arabic
bulgarian
schinese
tchinese
czech
danish
dutch
english
finnish
french
german
greek
hungarian
italian
japanese
koreana
norwegian
polish
portuguese
brazilian
romanian
russian
spanish
swedish
thai
turkish
ukrainian

16
build_env_x64.bat Normal file
View File

@ -0,0 +1,16 @@
if exist "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat" goto vs14
if exist "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat" goto vs2017
if exist "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat" goto vs2019
:vs14
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat"
goto batend
:vs2017
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
goto batend
:vs2019
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
goto batend
:batend

16
build_env_x86.bat Normal file
View File

@ -0,0 +1,16 @@
if exist "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat" goto vs14
if exist "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars32.bat" goto vs2017
if exist "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars32.bat" goto vs2019
:vs14
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat"
goto batend
:vs2017
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars32.bat"
goto batend
:vs2019
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars32.bat"
goto batend
:batend

2
build_linux.sh Normal file
View File

@ -0,0 +1,2 @@
protoc -I./dll/ --cpp_out=./dll/ ./dll/*.proto
clang++ -shared -fPIC -o libsteam_api.so dll/*.cpp dll/*.cc -g3 -Wno-return-type -fsanitize=address -lasan -lprotobuf-lite -std=c++11 && echo built

View File

@ -0,0 +1,4 @@
SET PROTOBUF_X86_DIRECTORY=..\vcpkg\packages\protobuf_x86-windows-static
SET PROTOBUF_X64_DIRECTORY=..\vcpkg\packages\protobuf_x64-windows-static
rem location of protoc in protobuf directories:
SET PROTOC_DIRECTORY=\tools\protobuf\protoc.exe

7
build_steamos.sh Normal file
View File

@ -0,0 +1,7 @@
rm -rf linux
mkdir -p linux/x86
mkdir -p linux/x86_64
../protobuf/prefix_x86/bin/protoc -I./dll/ --cpp_out=./dll/ ./dll/*.proto
g++ -m32 -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -s -o linux/x86/libsteam_api.so dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -std=c++11 && echo built32
../protobuf/prefix/bin/protoc -I./dll/ --cpp_out=./dll/ ./dll/*.proto
g++ -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -s -o linux/x86_64/libsteam_api.so dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -std=c++11 && echo built64

View File

@ -0,0 +1,12 @@
call build_set_protobuf_directories.bat
%PROTOBUF_X86_DIRECTORY%%PROTOC_DIRECTORY% -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /I%PROTOBUF_X86_DIRECTORY%\include\ /DEMU_EXPERIMENTAL_BUILD dll/*.cpp dll/*.cc detours/*.cpp %PROTOBUF_X86_DIRECTORY%\lib\libprotobuf-lite.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /link /OUT:steam_api.dll
cl /LD steamclient.cpp /EHsc /MP12 /link /OUT:steamclient.dll
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets.dll
call build_env_x64.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /I%PROTOBUF_X64_DIRECTORY%\include\ /DEMU_EXPERIMENTAL_BUILD dll/*.cpp dll/*.cc detours/*.cpp %PROTOBUF_X64_DIRECTORY%\lib\libprotobuf-lite.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /link /OUT:steam_api64.dll
cl /LD steamclient.cpp /EHsc /MP12 /link /OUT:steamclient64.dll
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets64.dll

View File

@ -0,0 +1,12 @@
mkdir release\lobby_connect
del /Q release\lobby_connect\*
call build_set_protobuf_directories.bat
%PROTOBUF_X86_DIRECTORY%%PROTOC_DIRECTORY% -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /DNO_DISK_WRITES /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ lobby_connect.cpp dll/*.cpp dll/*.cc %PROTOBUF_X86_DIRECTORY%\lib\libprotobuf-lite.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Comdlg32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\lobby_connect\lobby_connect.exe
del /Q /S release\lobby_connect\*.lib
del /Q /S release\lobby_connect\*.exp
copy Readme_lobby_connect.txt release\lobby_connect\Readme.txt
mkdir release\lobby_connect\source\
copy lobby_connect.cpp release\lobby_connect\source\

18
build_win_release.bat Normal file
View File

@ -0,0 +1,18 @@
del /Q /S release\*
rmdir /S /Q release\experimental
rmdir /S /Q release\lobby_connect
rmdir /S /Q release
mkdir release
call build_set_protobuf_directories.bat
%PROTOBUF_X86_DIRECTORY%%PROTOC_DIRECTORY% -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc %PROTOBUF_X86_DIRECTORY%\lib\libprotobuf-lite.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\steam_api.dll
%PROTOBUF_X64_DIRECTORY%%PROTOC_DIRECTORY% -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x64.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc %PROTOBUF_X64_DIRECTORY%\lib\libprotobuf-lite.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\steam_api64.dll
copy Readme_release.txt release\Readme.txt
xcopy /s files_example\* release\
call build_win_release_experimental.bat
call build_win_lobby_connect.bat

View File

@ -0,0 +1,14 @@
mkdir release\experimental
del /Q release\experimental\*
call build_set_protobuf_directories.bat
%PROTOBUF_X86_DIRECTORY%%PROTOC_DIRECTORY% -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp %PROTOBUF_X86_DIRECTORY%\lib\libprotobuf-lite.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api.dll
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DNDEBUG steamclient.cpp /EHsc /MP12 /Ox /link /OUT:release\experimental\steamclient.dll
%PROTOBUF_X64_DIRECTORY%%PROTOC_DIRECTORY% -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x64.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp %PROTOBUF_X64_DIRECTORY%\lib\libprotobuf-lite.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api64.dll
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DNDEBUG steamclient.cpp /EHsc /MP12 /Ox /link /OUT:release\experimental\steamclient64.dll
copy Readme_experimental.txt release\experimental\Readme.txt

1586
detours/creatwth.cpp Normal file

File diff suppressed because it is too large Load Diff

2461
detours/detours.cpp Normal file

File diff suppressed because it is too large Load Diff

1075
detours/detours.h Normal file

File diff suppressed because it is too large Load Diff

27
detours/detver.h Normal file
View File

@ -0,0 +1,27 @@
//////////////////////////////////////////////////////////////////////////////
//
// Common version parameters.
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#define _USING_V110_SDK71_ 1
#include "winver.h"
#if 0
#include <windows.h>
#include <detours.h>
#else
#ifndef DETOURS_STRINGIFY
#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x)
#define DETOURS_STRINGIFY_(x) #x
#endif
#define VER_FILEFLAGSMASK 0x3fL
#define VER_FILEFLAGS 0x0L
#define VER_FILEOS 0x00040004L
#define VER_FILETYPE 0x00000002L
#define VER_FILESUBTYPE 0x00000000L
#endif
#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS)

4250
detours/disasm.cpp Normal file

File diff suppressed because it is too large Load Diff

2
detours/disolarm.cpp Normal file
View File

@ -0,0 +1,2 @@
#define DETOURS_ARM_OFFLINE_LIBRARY
#include "disasm.cpp"

2
detours/disolarm64.cpp Normal file
View File

@ -0,0 +1,2 @@
#define DETOURS_ARM64_OFFLINE_LIBRARY
#include "disasm.cpp"

2
detours/disolia64.cpp Normal file
View File

@ -0,0 +1,2 @@
#define DETOURS_IA64_OFFLINE_LIBRARY
#include "disasm.cpp"

2
detours/disolx64.cpp Normal file
View File

@ -0,0 +1,2 @@
#define DETOURS_X64_OFFLINE_LIBRARY
#include "disasm.cpp"

2
detours/disolx86.cpp Normal file
View File

@ -0,0 +1,2 @@
#define DETOURS_X86_OFFLINE_LIBRARY
#include "disasm.cpp"

2251
detours/image.cpp Normal file

File diff suppressed because it is too large Load Diff

929
detours/modules.cpp Normal file
View File

@ -0,0 +1,929 @@
//////////////////////////////////////////////////////////////////////////////
//
// Module Enumeration Functions (modules.cpp of detours.lib)
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Module enumeration functions.
//
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
#pragma warning(disable:4068) // unknown pragma (suppress)
#if _MSC_VER >= 1900
#pragma warning(push)
#pragma warning(disable:4091) // empty typedef
#endif
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
#include <windows.h>
#if (_MSC_VER < 1310)
#else
#pragma warning(push)
#if _MSC_VER > 1400
#pragma warning(disable:6102 6103) // /analyze warnings
#endif
#include <strsafe.h>
#pragma warning(pop)
#endif
// #define DETOUR_DEBUG 1
#define DETOURS_INTERNAL
#include "detours.h"
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
#error detours.h version mismatch
#endif
#if _MSC_VER >= 1900
#pragma warning(pop)
#endif
#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]
#define IAT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT]
//////////////////////////////////////////////////////////////////////////////
//
const GUID DETOUR_EXE_RESTORE_GUID = {
0x2ed7a3ff, 0x3339, 0x4a8d,
{ 0x80, 0x5c, 0xd4, 0x98, 0x15, 0x3f, 0xc2, 0x8f }};
//////////////////////////////////////////////////////////////////////////////
//
PDETOUR_SYM_INFO DetourLoadImageHlp(VOID)
{
static DETOUR_SYM_INFO symInfo;
static PDETOUR_SYM_INFO pSymInfo = NULL;
static BOOL failed = false;
if (failed) {
return NULL;
}
if (pSymInfo != NULL) {
return pSymInfo;
}
ZeroMemory(&symInfo, sizeof(symInfo));
// Create a real handle to the process.
#if 0
DuplicateHandle(GetCurrentProcess(),
GetCurrentProcess(),
GetCurrentProcess(),
&symInfo.hProcess,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
#else
symInfo.hProcess = GetCurrentProcess();
#endif
symInfo.hDbgHelp = LoadLibraryExW(L"dbghelp.dll", NULL, 0);
if (symInfo.hDbgHelp == NULL) {
abort:
failed = true;
if (symInfo.hDbgHelp != NULL) {
FreeLibrary(symInfo.hDbgHelp);
}
symInfo.pfImagehlpApiVersionEx = NULL;
symInfo.pfSymInitialize = NULL;
symInfo.pfSymSetOptions = NULL;
symInfo.pfSymGetOptions = NULL;
symInfo.pfSymLoadModule64 = NULL;
symInfo.pfSymGetModuleInfo64 = NULL;
symInfo.pfSymFromName = NULL;
return NULL;
}
symInfo.pfImagehlpApiVersionEx
= (PF_ImagehlpApiVersionEx)GetProcAddress(symInfo.hDbgHelp,
"ImagehlpApiVersionEx");
symInfo.pfSymInitialize
= (PF_SymInitialize)GetProcAddress(symInfo.hDbgHelp, "SymInitialize");
symInfo.pfSymSetOptions
= (PF_SymSetOptions)GetProcAddress(symInfo.hDbgHelp, "SymSetOptions");
symInfo.pfSymGetOptions
= (PF_SymGetOptions)GetProcAddress(symInfo.hDbgHelp, "SymGetOptions");
symInfo.pfSymLoadModule64
= (PF_SymLoadModule64)GetProcAddress(symInfo.hDbgHelp, "SymLoadModule64");
symInfo.pfSymGetModuleInfo64
= (PF_SymGetModuleInfo64)GetProcAddress(symInfo.hDbgHelp, "SymGetModuleInfo64");
symInfo.pfSymFromName
= (PF_SymFromName)GetProcAddress(symInfo.hDbgHelp, "SymFromName");
API_VERSION av;
ZeroMemory(&av, sizeof(av));
av.MajorVersion = API_VERSION_NUMBER;
if (symInfo.pfImagehlpApiVersionEx == NULL ||
symInfo.pfSymInitialize == NULL ||
symInfo.pfSymLoadModule64 == NULL ||
symInfo.pfSymGetModuleInfo64 == NULL ||
symInfo.pfSymFromName == NULL) {
goto abort;
}
symInfo.pfImagehlpApiVersionEx(&av);
if (av.MajorVersion < API_VERSION_NUMBER) {
goto abort;
}
if (!symInfo.pfSymInitialize(symInfo.hProcess, NULL, FALSE)) {
// We won't retry the initialize if it fails.
goto abort;
}
if (symInfo.pfSymGetOptions != NULL && symInfo.pfSymSetOptions != NULL) {
DWORD dw = symInfo.pfSymGetOptions();
dw &= ~(SYMOPT_CASE_INSENSITIVE |
SYMOPT_UNDNAME |
SYMOPT_DEFERRED_LOADS |
0);
dw |= (
#if defined(SYMOPT_EXACT_SYMBOLS)
SYMOPT_EXACT_SYMBOLS |
#endif
#if defined(SYMOPT_NO_UNQUALIFIED_LOADS)
SYMOPT_NO_UNQUALIFIED_LOADS |
#endif
SYMOPT_DEFERRED_LOADS |
#if defined(SYMOPT_FAIL_CRITICAL_ERRORS)
SYMOPT_FAIL_CRITICAL_ERRORS |
#endif
#if defined(SYMOPT_INCLUDE_32BIT_MODULES)
SYMOPT_INCLUDE_32BIT_MODULES |
#endif
0);
symInfo.pfSymSetOptions(dw);
}
pSymInfo = &symInfo;
return pSymInfo;
}
PVOID WINAPI DetourFindFunction(_In_ PCSTR pszModule,
_In_ PCSTR pszFunction)
{
/////////////////////////////////////////////// First, try GetProcAddress.
//
#pragma prefast(suppress:28752, "We don't do the unicode conversion for LoadLibraryExA.")
HMODULE hModule = LoadLibraryExA(pszModule, NULL, 0);
if (hModule == NULL) {
return NULL;
}
PBYTE pbCode = (PBYTE)GetProcAddress(hModule, pszFunction);
if (pbCode) {
return pbCode;
}
////////////////////////////////////////////////////// Then try ImageHelp.
//
DETOUR_TRACE(("DetourFindFunction(%hs, %hs)\n", pszModule, pszFunction));
PDETOUR_SYM_INFO pSymInfo = DetourLoadImageHlp();
if (pSymInfo == NULL) {
DETOUR_TRACE(("DetourLoadImageHlp failed: %d\n",
GetLastError()));
return NULL;
}
if (pSymInfo->pfSymLoadModule64(pSymInfo->hProcess, NULL,
(PCHAR)pszModule, NULL,
(DWORD64)hModule, 0) == 0) {
if (ERROR_SUCCESS != GetLastError()) {
DETOUR_TRACE(("SymLoadModule64(%p) failed: %d\n",
pSymInfo->hProcess, GetLastError()));
return NULL;
}
}
HRESULT hrRet;
CHAR szFullName[512];
IMAGEHLP_MODULE64 modinfo;
ZeroMemory(&modinfo, sizeof(modinfo));
modinfo.SizeOfStruct = sizeof(modinfo);
if (!pSymInfo->pfSymGetModuleInfo64(pSymInfo->hProcess, (DWORD64)hModule, &modinfo)) {
DETOUR_TRACE(("SymGetModuleInfo64(%p, %p) failed: %d\n",
pSymInfo->hProcess, hModule, GetLastError()));
return NULL;
}
hrRet = StringCchCopyA(szFullName, sizeof(szFullName)/sizeof(CHAR), modinfo.ModuleName);
if (FAILED(hrRet)) {
DETOUR_TRACE(("StringCchCopyA failed: %08x\n", hrRet));
return NULL;
}
hrRet = StringCchCatA(szFullName, sizeof(szFullName)/sizeof(CHAR), "!");
if (FAILED(hrRet)) {
DETOUR_TRACE(("StringCchCatA failed: %08x\n", hrRet));
return NULL;
}
hrRet = StringCchCatA(szFullName, sizeof(szFullName)/sizeof(CHAR), pszFunction);
if (FAILED(hrRet)) {
DETOUR_TRACE(("StringCchCatA failed: %08x\n", hrRet));
return NULL;
}
struct CFullSymbol : SYMBOL_INFO {
CHAR szRestOfName[512];
} symbol;
ZeroMemory(&symbol, sizeof(symbol));
//symbol.ModBase = (ULONG64)hModule;
symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
#ifdef DBHLPAPI
symbol.MaxNameLen = sizeof(symbol.szRestOfName)/sizeof(symbol.szRestOfName[0]);
#else
symbol.MaxNameLength = sizeof(symbol.szRestOfName)/sizeof(symbol.szRestOfName[0]);
#endif
if (!pSymInfo->pfSymFromName(pSymInfo->hProcess, szFullName, &symbol)) {
DETOUR_TRACE(("SymFromName(%hs) failed: %d\n", szFullName, GetLastError()));
return NULL;
}
#if defined(DETOURS_IA64)
// On the IA64, we get a raw code pointer from the symbol engine
// and have to convert it to a wrapped [code pointer, global pointer].
//
PPLABEL_DESCRIPTOR pldEntry = (PPLABEL_DESCRIPTOR)DetourGetEntryPoint(hModule);
PPLABEL_DESCRIPTOR pldSymbol = new PLABEL_DESCRIPTOR;
pldSymbol->EntryPoint = symbol.Address;
pldSymbol->GlobalPointer = pldEntry->GlobalPointer;
return (PBYTE)pldSymbol;
#elif defined(DETOURS_ARM)
// On the ARM, we get a raw code pointer, which we must convert into a
// valied Thumb2 function pointer.
return DETOURS_PBYTE_TO_PFUNC(symbol.Address);
#else
return (PBYTE)symbol.Address;
#endif
}
//////////////////////////////////////////////////// Module Image Functions.
//
HMODULE WINAPI DetourEnumerateModules(_In_opt_ HMODULE hModuleLast)
{
PBYTE pbLast = (PBYTE)hModuleLast + MM_ALLOCATION_GRANULARITY;
MEMORY_BASIC_INFORMATION mbi;
ZeroMemory(&mbi, sizeof(mbi));
// Find the next memory region that contains a mapped PE image.
//
for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) {
if (VirtualQuery(pbLast, &mbi, sizeof(mbi)) <= 0) {
break;
}
// Skip uncommitted regions and guard pages.
//
if ((mbi.State != MEM_COMMIT) ||
((mbi.Protect & 0xff) == PAGE_NOACCESS) ||
(mbi.Protect & PAGE_GUARD)) {
continue;
}
__try {
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pbLast;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE ||
(DWORD)pDosHeader->e_lfanew > mbi.RegionSize ||
(DWORD)pDosHeader->e_lfanew < sizeof(*pDosHeader)) {
continue;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
continue;
}
return (HMODULE)pDosHeader;
}
#pragma prefast(suppress:28940, "A bad pointer means this probably isn't a PE header.")
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
continue;
}
}
return NULL;
}
PVOID WINAPI DetourGetEntryPoint(_In_opt_ HMODULE hModule)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
if (hModule == NULL) {
pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL);
}
__try {
#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL.
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return NULL;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
SetLastError(ERROR_INVALID_EXE_SIGNATURE);
return NULL;
}
if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return NULL;
}
PDETOUR_CLR_HEADER pClrHeader = NULL;
if (pNtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
if (((PIMAGE_NT_HEADERS32)pNtHeader)->CLR_DIRECTORY.VirtualAddress != 0 &&
((PIMAGE_NT_HEADERS32)pNtHeader)->CLR_DIRECTORY.Size != 0) {
pClrHeader = (PDETOUR_CLR_HEADER)
(((PBYTE)pDosHeader)
+ ((PIMAGE_NT_HEADERS32)pNtHeader)->CLR_DIRECTORY.VirtualAddress);
}
}
else if (pNtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
if (((PIMAGE_NT_HEADERS64)pNtHeader)->CLR_DIRECTORY.VirtualAddress != 0 &&
((PIMAGE_NT_HEADERS64)pNtHeader)->CLR_DIRECTORY.Size != 0) {
pClrHeader = (PDETOUR_CLR_HEADER)
(((PBYTE)pDosHeader)
+ ((PIMAGE_NT_HEADERS64)pNtHeader)->CLR_DIRECTORY.VirtualAddress);
}
}
if (pClrHeader != NULL) {
// For MSIL assemblies, we want to use the _Cor entry points.
HMODULE hClr = GetModuleHandleW(L"MSCOREE.DLL");
if (hClr == NULL) {
return NULL;
}
SetLastError(NO_ERROR);
return GetProcAddress(hClr, "_CorExeMain");
}
SetLastError(NO_ERROR);
// Pure resource DLLs have neither an entry point nor CLR information
// so handle them by returning NULL (LastError is NO_ERROR)
if (pNtHeader->OptionalHeader.AddressOfEntryPoint == 0) {
return NULL;
}
return ((PBYTE)pDosHeader) +
pNtHeader->OptionalHeader.AddressOfEntryPoint;
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return NULL;
}
}
ULONG WINAPI DetourGetModuleSize(_In_opt_ HMODULE hModule)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
if (hModule == NULL) {
pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL);
}
__try {
#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL.
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return NULL;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
SetLastError(ERROR_INVALID_EXE_SIGNATURE);
return NULL;
}
if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return NULL;
}
SetLastError(NO_ERROR);
return (pNtHeader->OptionalHeader.SizeOfImage);
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return NULL;
}
}
HMODULE WINAPI DetourGetContainingModule(_In_ PVOID pvAddr)
{
MEMORY_BASIC_INFORMATION mbi;
ZeroMemory(&mbi, sizeof(mbi));
__try {
if (VirtualQuery(pvAddr, &mbi, sizeof(mbi)) <= 0) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return NULL;
}
// Skip uncommitted regions and guard pages.
//
if ((mbi.State != MEM_COMMIT) ||
((mbi.Protect & 0xff) == PAGE_NOACCESS) ||
(mbi.Protect & PAGE_GUARD)) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return NULL;
}
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return NULL;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
SetLastError(ERROR_INVALID_EXE_SIGNATURE);
return NULL;
}
if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return NULL;
}
SetLastError(NO_ERROR);
return (HMODULE)pDosHeader;
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
SetLastError(ERROR_INVALID_EXE_SIGNATURE);
return NULL;
}
}
static inline PBYTE RvaAdjust(_Pre_notnull_ PIMAGE_DOS_HEADER pDosHeader, _In_ DWORD raddr)
{
if (raddr != NULL) {
return ((PBYTE)pDosHeader) + raddr;
}
return NULL;
}
BOOL WINAPI DetourEnumerateExports(_In_ HMODULE hModule,
_In_opt_ PVOID pContext,
_In_ PF_DETOUR_ENUMERATE_EXPORT_CALLBACK pfExport)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
if (hModule == NULL) {
pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL);
}
__try {
#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL.
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return NULL;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
SetLastError(ERROR_INVALID_EXE_SIGNATURE);
return FALSE;
}
if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return FALSE;
}
PIMAGE_EXPORT_DIRECTORY pExportDir
= (PIMAGE_EXPORT_DIRECTORY)
RvaAdjust(pDosHeader,
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
if (pExportDir == NULL) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return FALSE;
}
PBYTE pExportDirEnd = (PBYTE)pExportDir + pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
PDWORD pdwFunctions = (PDWORD)RvaAdjust(pDosHeader, pExportDir->AddressOfFunctions);
PDWORD pdwNames = (PDWORD)RvaAdjust(pDosHeader, pExportDir->AddressOfNames);
PWORD pwOrdinals = (PWORD)RvaAdjust(pDosHeader, pExportDir->AddressOfNameOrdinals);
for (DWORD nFunc = 0; nFunc < pExportDir->NumberOfFunctions; nFunc++) {
PBYTE pbCode = (pdwFunctions != NULL)
? (PBYTE)RvaAdjust(pDosHeader, pdwFunctions[nFunc]) : NULL;
PCHAR pszName = NULL;
// if the pointer is in the export region, then it is a forwarder.
if (pbCode > (PBYTE)pExportDir && pbCode < pExportDirEnd) {
pbCode = NULL;
}
for (DWORD n = 0; n < pExportDir->NumberOfNames; n++) {
if (pwOrdinals[n] == nFunc) {
pszName = (pdwNames != NULL)
? (PCHAR)RvaAdjust(pDosHeader, pdwNames[n]) : NULL;
break;
}
}
ULONG nOrdinal = pExportDir->Base + nFunc;
if (!pfExport(pContext, nOrdinal, pszName, pbCode)) {
break;
}
}
SetLastError(NO_ERROR);
return TRUE;
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return NULL;
}
}
BOOL WINAPI DetourEnumerateImportsEx(_In_opt_ HMODULE hModule,
_In_opt_ PVOID pContext,
_In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile,
_In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK_EX pfImportFunc)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
if (hModule == NULL) {
pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL);
}
__try {
#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL.
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return FALSE;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
SetLastError(ERROR_INVALID_EXE_SIGNATURE);
return FALSE;
}
if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return FALSE;
}
PIMAGE_IMPORT_DESCRIPTOR iidp
= (PIMAGE_IMPORT_DESCRIPTOR)
RvaAdjust(pDosHeader,
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
if (iidp == NULL) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return FALSE;
}
for (; iidp->OriginalFirstThunk != 0; iidp++) {
PCSTR pszName = (PCHAR)RvaAdjust(pDosHeader, iidp->Name);
if (pszName == NULL) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return FALSE;
}
PIMAGE_THUNK_DATA pThunks = (PIMAGE_THUNK_DATA)
RvaAdjust(pDosHeader, iidp->OriginalFirstThunk);
PVOID * pAddrs = (PVOID *)
RvaAdjust(pDosHeader, iidp->FirstThunk);
HMODULE hFile = DetourGetContainingModule(pAddrs[0]);
if (pfImportFile != NULL) {
if (!pfImportFile(pContext, hFile, pszName)) {
break;
}
}
DWORD nNames = 0;
if (pThunks) {
for (; pThunks[nNames].u1.Ordinal; nNames++) {
DWORD nOrdinal = 0;
PCSTR pszFunc = NULL;
if (IMAGE_SNAP_BY_ORDINAL(pThunks[nNames].u1.Ordinal)) {
nOrdinal = (DWORD)IMAGE_ORDINAL(pThunks[nNames].u1.Ordinal);
}
else {
pszFunc = (PCSTR)RvaAdjust(pDosHeader,
(DWORD)pThunks[nNames].u1.AddressOfData + 2);
}
if (pfImportFunc != NULL) {
if (!pfImportFunc(pContext,
nOrdinal,
pszFunc,
&pAddrs[nNames])) {
break;
}
}
}
if (pfImportFunc != NULL) {
pfImportFunc(pContext, 0, NULL, NULL);
}
}
}
if (pfImportFile != NULL) {
pfImportFile(pContext, NULL, NULL);
}
SetLastError(NO_ERROR);
return TRUE;
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return FALSE;
}
}
// Context for DetourEnumerateImportsThunk, which adapts "regular" callbacks for use with "Ex".
struct _DETOUR_ENUMERATE_IMPORTS_THUNK_CONTEXT
{
PVOID pContext;
PF_DETOUR_IMPORT_FUNC_CALLBACK pfImportFunc;
};
// Callback for DetourEnumerateImportsEx that adapts DetourEnumerateImportsEx
// for use with a DetourEnumerateImports callback -- derefence the IAT and pass the value on.
static
BOOL
CALLBACK
DetourEnumerateImportsThunk(_In_ PVOID VoidContext,
_In_ DWORD nOrdinal,
_In_opt_ PCSTR pszFunc,
_In_opt_ PVOID* ppvFunc)
{
_DETOUR_ENUMERATE_IMPORTS_THUNK_CONTEXT const * const
pContext = (_DETOUR_ENUMERATE_IMPORTS_THUNK_CONTEXT*)VoidContext;
return pContext->pfImportFunc(pContext->pContext, nOrdinal, pszFunc, ppvFunc ? *ppvFunc : NULL);
}
BOOL WINAPI DetourEnumerateImports(_In_opt_ HMODULE hModule,
_In_opt_ PVOID pContext,
_In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile,
_In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK pfImportFunc)
{
_DETOUR_ENUMERATE_IMPORTS_THUNK_CONTEXT const context = { pContext, pfImportFunc };
return DetourEnumerateImportsEx(hModule,
(PVOID)&context,
pfImportFile,
&DetourEnumerateImportsThunk);
}
static PDETOUR_LOADED_BINARY WINAPI GetPayloadSectionFromModule(HMODULE hModule)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
if (hModule == NULL) {
pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL);
}
__try {
#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL.
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return NULL;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
SetLastError(ERROR_INVALID_EXE_SIGNATURE);
return NULL;
}
if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return NULL;
}
PIMAGE_SECTION_HEADER pSectionHeaders
= (PIMAGE_SECTION_HEADER)((PBYTE)pNtHeader
+ sizeof(pNtHeader->Signature)
+ sizeof(pNtHeader->FileHeader)
+ pNtHeader->FileHeader.SizeOfOptionalHeader);
for (DWORD n = 0; n < pNtHeader->FileHeader.NumberOfSections; n++) {
if (strcmp((PCHAR)pSectionHeaders[n].Name, ".detour") == 0) {
if (pSectionHeaders[n].VirtualAddress == 0 ||
pSectionHeaders[n].SizeOfRawData == 0) {
break;
}
PBYTE pbData = (PBYTE)pDosHeader + pSectionHeaders[n].VirtualAddress;
DETOUR_SECTION_HEADER *pHeader = (DETOUR_SECTION_HEADER *)pbData;
if (pHeader->cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) ||
pHeader->nSignature != DETOUR_SECTION_HEADER_SIGNATURE) {
break;
}
if (pHeader->nDataOffset == 0) {
pHeader->nDataOffset = pHeader->cbHeaderSize;
}
SetLastError(NO_ERROR);
return (PBYTE)pHeader;
}
}
SetLastError(ERROR_EXE_MARKED_INVALID);
return NULL;
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return NULL;
}
}
DWORD WINAPI DetourGetSizeOfPayloads(_In_opt_ HMODULE hModule)
{
PDETOUR_LOADED_BINARY pBinary = GetPayloadSectionFromModule(hModule);
if (pBinary == NULL) {
// Error set by GetPayloadSectionFromModule.
return 0;
}
__try {
DETOUR_SECTION_HEADER *pHeader = (DETOUR_SECTION_HEADER *)pBinary;
if (pHeader->cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) ||
pHeader->nSignature != DETOUR_SECTION_HEADER_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return 0;
}
SetLastError(NO_ERROR);
return pHeader->cbDataSize;
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
SetLastError(ERROR_INVALID_HANDLE);
return 0;
}
}
_Writable_bytes_(*pcbData)
_Readable_bytes_(*pcbData)
_Success_(return != NULL)
PVOID WINAPI DetourFindPayload(_In_opt_ HMODULE hModule,
_In_ REFGUID rguid,
_Out_ DWORD *pcbData)
{
PBYTE pbData = NULL;
if (pcbData) {
*pcbData = 0;
}
PDETOUR_LOADED_BINARY pBinary = GetPayloadSectionFromModule(hModule);
if (pBinary == NULL) {
// Error set by GetPayloadSectionFromModule.
return NULL;
}
__try {
DETOUR_SECTION_HEADER *pHeader = (DETOUR_SECTION_HEADER *)pBinary;
if (pHeader->cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) ||
pHeader->nSignature != DETOUR_SECTION_HEADER_SIGNATURE) {
SetLastError(ERROR_INVALID_EXE_SIGNATURE);
return NULL;
}
PBYTE pbBeg = ((PBYTE)pHeader) + pHeader->nDataOffset;
PBYTE pbEnd = ((PBYTE)pHeader) + pHeader->cbDataSize;
for (pbData = pbBeg; pbData < pbEnd;) {
DETOUR_SECTION_RECORD *pSection = (DETOUR_SECTION_RECORD *)pbData;
if (pSection->guid.Data1 == rguid.Data1 &&
pSection->guid.Data2 == rguid.Data2 &&
pSection->guid.Data3 == rguid.Data3 &&
pSection->guid.Data4[0] == rguid.Data4[0] &&
pSection->guid.Data4[1] == rguid.Data4[1] &&
pSection->guid.Data4[2] == rguid.Data4[2] &&
pSection->guid.Data4[3] == rguid.Data4[3] &&
pSection->guid.Data4[4] == rguid.Data4[4] &&
pSection->guid.Data4[5] == rguid.Data4[5] &&
pSection->guid.Data4[6] == rguid.Data4[6] &&
pSection->guid.Data4[7] == rguid.Data4[7]) {
if (pcbData) {
*pcbData = pSection->cbBytes - sizeof(*pSection);
SetLastError(NO_ERROR);
return (PBYTE)(pSection + 1);
}
}
pbData = (PBYTE)pSection + pSection->cbBytes;
}
SetLastError(ERROR_INVALID_HANDLE);
return NULL;
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
SetLastError(ERROR_INVALID_HANDLE);
return NULL;
}
}
_Writable_bytes_(*pcbData)
_Readable_bytes_(*pcbData)
_Success_(return != NULL)
PVOID WINAPI DetourFindPayloadEx(_In_ REFGUID rguid,
_Out_ DWORD * pcbData)
{
for (HMODULE hMod = NULL; (hMod = DetourEnumerateModules(hMod)) != NULL;) {
PVOID pvData;
pvData = DetourFindPayload(hMod, rguid, pcbData);
if (pvData != NULL) {
return pvData;
}
}
SetLastError(ERROR_MOD_NOT_FOUND);
return NULL;
}
BOOL WINAPI DetourRestoreAfterWithEx(_In_reads_bytes_(cbData) PVOID pvData,
_In_ DWORD cbData)
{
PDETOUR_EXE_RESTORE pder = (PDETOUR_EXE_RESTORE)pvData;
if (pder->cb != sizeof(*pder) || pder->cb > cbData) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return FALSE;
}
DWORD dwPermIdh = ~0u;
DWORD dwPermInh = ~0u;
DWORD dwPermClr = ~0u;
DWORD dwIgnore;
BOOL fSucceeded = FALSE;
BOOL fUpdated32To64 = FALSE;
if (pder->pclr != NULL && pder->clr.Flags != ((PDETOUR_CLR_HEADER)pder->pclr)->Flags) {
// If we had to promote the 32/64-bit agnostic IL to 64-bit, we can't restore
// that.
fUpdated32To64 = TRUE;
}
if (DetourVirtualProtectSameExecute(pder->pidh, pder->cbidh,
PAGE_EXECUTE_READWRITE, &dwPermIdh)) {
if (DetourVirtualProtectSameExecute(pder->pinh, pder->cbinh,
PAGE_EXECUTE_READWRITE, &dwPermInh)) {
CopyMemory(pder->pidh, &pder->idh, pder->cbidh);
CopyMemory(pder->pinh, &pder->inh, pder->cbinh);
if (pder->pclr != NULL && !fUpdated32To64) {
if (DetourVirtualProtectSameExecute(pder->pclr, pder->cbclr,
PAGE_EXECUTE_READWRITE, &dwPermClr)) {
CopyMemory(pder->pclr, &pder->clr, pder->cbclr);
VirtualProtect(pder->pclr, pder->cbclr, dwPermClr, &dwIgnore);
fSucceeded = TRUE;
}
}
else {
fSucceeded = TRUE;
}
VirtualProtect(pder->pinh, pder->cbinh, dwPermInh, &dwIgnore);
}
VirtualProtect(pder->pidh, pder->cbidh, dwPermIdh, &dwIgnore);
}
return fSucceeded;
}
BOOL WINAPI DetourRestoreAfterWith()
{
PVOID pvData;
DWORD cbData;
pvData = DetourFindPayloadEx(DETOUR_EXE_RESTORE_GUID, &cbData);
if (pvData != NULL && cbData != 0) {
return DetourRestoreAfterWithEx(pvData, cbData);
}
SetLastError(ERROR_MOD_NOT_FOUND);
return FALSE;
}
// End of File

269
detours/uimports.cc Normal file
View File

@ -0,0 +1,269 @@
//////////////////////////////////////////////////////////////////////////////
//
// Add DLLs to a module import table (uimports.cpp of detours.lib)
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Note that this file is included into creatwth.cpp one or more times
// (once for each supported module format).
//
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
#error detours.h version mismatch
#endif
// UpdateImports32 aka UpdateImports64
static BOOL UPDATE_IMPORTS_XX(HANDLE hProcess,
HMODULE hModule,
__in_ecount(nDlls) LPCSTR *plpDlls,
DWORD nDlls)
{
BOOL fSucceeded = FALSE;
DWORD cbNew = 0;
BYTE * pbNew = NULL;
DWORD i;
SIZE_T cbRead;
DWORD n;
PBYTE pbModule = (PBYTE)hModule;
IMAGE_DOS_HEADER idh;
ZeroMemory(&idh, sizeof(idh));
if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), &cbRead)
|| cbRead < sizeof(idh)) {
DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %d\n",
pbModule, pbModule + sizeof(idh), GetLastError()));
finish:
if (pbNew != NULL) {
delete[] pbNew;
pbNew = NULL;
}
return fSucceeded;
}
IMAGE_NT_HEADERS_XX inh;
ZeroMemory(&inh, sizeof(inh));
if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), &cbRead)
|| cbRead < sizeof(inh)) {
DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %d\n",
pbModule + idh.e_lfanew,
pbModule + idh.e_lfanew + sizeof(inh),
GetLastError()));
goto finish;
}
if (inh.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC_XX) {
DETOUR_TRACE(("Wrong size image (%04x != %04x).\n",
inh.OptionalHeader.Magic, IMAGE_NT_OPTIONAL_HDR_MAGIC_XX));
SetLastError(ERROR_INVALID_BLOCK);
goto finish;
}
// Zero out the bound table so loader doesn't use it instead of our new table.
inh.BOUND_DIRECTORY.VirtualAddress = 0;
inh.BOUND_DIRECTORY.Size = 0;
// Find the size of the mapped file.
DWORD dwSec = idh.e_lfanew +
FIELD_OFFSET(IMAGE_NT_HEADERS_XX, OptionalHeader) +
inh.FileHeader.SizeOfOptionalHeader;
for (i = 0; i < inh.FileHeader.NumberOfSections; i++) {
IMAGE_SECTION_HEADER ish;
ZeroMemory(&ish, sizeof(ish));
if (!ReadProcessMemory(hProcess, pbModule + dwSec + sizeof(ish) * i, &ish,
sizeof(ish), &cbRead)
|| cbRead < sizeof(ish)) {
DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %d\n",
pbModule + dwSec + sizeof(ish) * i,
pbModule + dwSec + sizeof(ish) * (i + 1),
GetLastError()));
goto finish;
}
DETOUR_TRACE(("ish[%d] : va=%08x sr=%d\n", i, ish.VirtualAddress, ish.SizeOfRawData));
// If the file didn't have an IAT_DIRECTORY, we assign it...
if (inh.IAT_DIRECTORY.VirtualAddress == 0 &&
inh.IMPORT_DIRECTORY.VirtualAddress >= ish.VirtualAddress &&
inh.IMPORT_DIRECTORY.VirtualAddress < ish.VirtualAddress + ish.SizeOfRawData) {
inh.IAT_DIRECTORY.VirtualAddress = ish.VirtualAddress;
inh.IAT_DIRECTORY.Size = ish.SizeOfRawData;
}
}
DETOUR_TRACE((" Imports: %p..%p\n",
(DWORD_PTR)pbModule + inh.IMPORT_DIRECTORY.VirtualAddress,
(DWORD_PTR)pbModule + inh.IMPORT_DIRECTORY.VirtualAddress +
inh.IMPORT_DIRECTORY.Size));
DWORD nOldDlls = inh.IMPORT_DIRECTORY.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
DWORD obRem = sizeof(IMAGE_IMPORT_DESCRIPTOR) * nDlls;
DWORD obOld = obRem + sizeof(IMAGE_IMPORT_DESCRIPTOR) * nOldDlls;
DWORD obTab = PadToDwordPtr(obOld);
DWORD obDll = obTab + sizeof(DWORD_XX) * 4 * nDlls;
DWORD obStr = obDll;
cbNew = obStr;
for (n = 0; n < nDlls; n++) {
cbNew += PadToDword((DWORD)strlen(plpDlls[n]) + 1);
}
_Analysis_assume_(cbNew >
sizeof(IMAGE_IMPORT_DESCRIPTOR) * (nDlls + nOldDlls)
+ sizeof(DWORD_XX) * 4 * nDlls);
pbNew = new BYTE [cbNew];
if (pbNew == NULL) {
DETOUR_TRACE(("new BYTE [cbNew] failed.\n"));
goto finish;
}
ZeroMemory(pbNew, cbNew);
PBYTE pbBase = pbModule;
PBYTE pbNext = pbBase
+ inh.OptionalHeader.BaseOfCode
+ inh.OptionalHeader.SizeOfCode
+ inh.OptionalHeader.SizeOfInitializedData
+ inh.OptionalHeader.SizeOfUninitializedData;
if (pbBase < pbNext) {
pbBase = pbNext;
}
DETOUR_TRACE(("pbBase = %p\n", pbBase));
PBYTE pbNewIid = FindAndAllocateNearBase(hProcess, pbModule, pbBase, cbNew);
if (pbNewIid == NULL) {
DETOUR_TRACE(("FindAndAllocateNearBase failed.\n"));
goto finish;
}
PIMAGE_IMPORT_DESCRIPTOR piid = (PIMAGE_IMPORT_DESCRIPTOR)pbNew;
DWORD_XX *pt;
DWORD obBase = (DWORD)(pbNewIid - pbModule);
DWORD dwProtect = 0;
if (inh.IMPORT_DIRECTORY.VirtualAddress != 0) {
// Read the old import directory if it exists.
DETOUR_TRACE(("IMPORT_DIRECTORY perms=%x\n", dwProtect));
if (!ReadProcessMemory(hProcess,
pbModule + inh.IMPORT_DIRECTORY.VirtualAddress,
&piid[nDlls],
nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR), &cbRead)
|| cbRead < nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR)) {
DETOUR_TRACE(("ReadProcessMemory(imports) failed: %d\n", GetLastError()));
goto finish;
}
}
for (n = 0; n < nDlls; n++) {
HRESULT hrRet = StringCchCopyA((char*)pbNew + obStr, cbNew - obStr, plpDlls[n]);
if (FAILED(hrRet)) {
DETOUR_TRACE(("StringCchCopyA failed: %d\n", GetLastError()));
goto finish;
}
// After copying the string, we patch up the size "??" bits if any.
hrRet = ReplaceOptionalSizeA((char*)pbNew + obStr,
cbNew - obStr,
DETOURS_STRINGIFY(DETOURS_BITS_XX));
if (FAILED(hrRet)) {
DETOUR_TRACE(("ReplaceOptionalSizeA failed: %d\n", GetLastError()));
goto finish;
}
DWORD nOffset = obTab + (sizeof(DWORD_XX) * (4 * n));
piid[n].OriginalFirstThunk = obBase + nOffset;
pt = ((DWORD_XX*)(pbNew + nOffset));
pt[0] = IMAGE_ORDINAL_FLAG_XX + 1;
pt[1] = 0;
nOffset = obTab + (sizeof(DWORD_XX) * ((4 * n) + 2));
piid[n].FirstThunk = obBase + nOffset;
pt = ((DWORD_XX*)(pbNew + nOffset));
pt[0] = IMAGE_ORDINAL_FLAG_XX + 1;
pt[1] = 0;
piid[n].TimeDateStamp = 0;
piid[n].ForwarderChain = 0;
piid[n].Name = obBase + obStr;
obStr += PadToDword((DWORD)strlen(plpDlls[n]) + 1);
}
_Analysis_assume_(obStr <= cbNew);
#if 0
for (i = 0; i < nDlls + nOldDlls; i++) {
DETOUR_TRACE(("%8d. Look=%08x Time=%08x Fore=%08x Name=%08x Addr=%08x\n",
i,
piid[i].OriginalFirstThunk,
piid[i].TimeDateStamp,
piid[i].ForwarderChain,
piid[i].Name,
piid[i].FirstThunk));
if (piid[i].OriginalFirstThunk == 0 && piid[i].FirstThunk == 0) {
break;
}
}
#endif
if (!WriteProcessMemory(hProcess, pbNewIid, pbNew, obStr, NULL)) {
DETOUR_TRACE(("WriteProcessMemory(iid) failed: %d\n", GetLastError()));
goto finish;
}
DETOUR_TRACE(("obBaseBef = %08x..%08x\n",
inh.IMPORT_DIRECTORY.VirtualAddress,
inh.IMPORT_DIRECTORY.VirtualAddress + inh.IMPORT_DIRECTORY.Size));
DETOUR_TRACE(("obBaseAft = %08x..%08x\n", obBase, obBase + obStr));
// If the file doesn't have an IAT_DIRECTORY, we create it...
if (inh.IAT_DIRECTORY.VirtualAddress == 0) {
inh.IAT_DIRECTORY.VirtualAddress = obBase;
inh.IAT_DIRECTORY.Size = cbNew;
}
inh.IMPORT_DIRECTORY.VirtualAddress = obBase;
inh.IMPORT_DIRECTORY.Size = cbNew;
/////////////////////// Update the NT header for the new import directory.
//
if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders,
PAGE_EXECUTE_READWRITE, &dwProtect)) {
DETOUR_TRACE(("VirtualProtectEx(inh) write failed: %d\n", GetLastError()));
goto finish;
}
inh.OptionalHeader.CheckSum = 0;
if (!WriteProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {
DETOUR_TRACE(("WriteProcessMemory(idh) failed: %d\n", GetLastError()));
goto finish;
}
DETOUR_TRACE(("WriteProcessMemory(idh:%p..%p)\n", pbModule, pbModule + sizeof(idh)));
if (!WriteProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), NULL)) {
DETOUR_TRACE(("WriteProcessMemory(inh) failed: %d\n", GetLastError()));
goto finish;
}
DETOUR_TRACE(("WriteProcessMemory(inh:%p..%p)\n",
pbModule + idh.e_lfanew,
pbModule + idh.e_lfanew + sizeof(inh)));
if (!VirtualProtectEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders,
dwProtect, &dwProtect)) {
DETOUR_TRACE(("VirtualProtectEx(idh) restore failed: %d\n", GetLastError()));
goto finish;
}
fSucceeded = TRUE;
goto finish;
}

654
dll/base.cpp Normal file
View File

@ -0,0 +1,654 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
#ifdef STEAM_WIN32
#include <windows.h>
#define SystemFunction036 NTAPI SystemFunction036
#include <ntsecapi.h>
#undef SystemFunction036
static void
randombytes(char * const buf, const size_t size)
{
RtlGenRandom((PVOID) buf, (ULONG) size);
}
std::string get_env_variable(std::string name)
{
char env_variable[1024];
DWORD ret = GetEnvironmentVariableA(name.c_str(), env_variable, sizeof(env_variable));
if (ret <= 0) {
return std::string();
}
env_variable[ret - 1] = 0;
return std::string(env_variable);
}
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
static int fd = -1;
static void randombytes(char *buf, size_t size)
{
int i;
if (fd == -1) {
for (;;) {
fd = open("/dev/urandom",O_RDONLY);
if (fd != -1) break;
sleep(1);
}
}
while (size > 0) {
if (size < 1048576) i = size; else i = 1048576;
i = read(fd,buf,i);
if (i < 1) {
sleep(1);
continue;
}
buf += i;
size -= i;
}
}
std::string get_env_variable(std::string name)
{
char *env = getenv(name.c_str());
if (!env) {
return std::string();
}
return std::string(env);
}
#endif
std::recursive_mutex global_mutex;
SteamAPICall_t generate_steam_api_call_id() {
static SteamAPICall_t a;
randombytes((char *)&a, sizeof(a));
++a;
if (a == 0) ++a;
return a;
}
int generate_random_int() {
int a;
randombytes((char *)&a, sizeof(a));
return a;
}
static uint32 generate_steam_ticket_id() {
/* not random starts with 2? */
static uint32 a = 1;
++a;
if (a == 0) ++a;
return a;
}
static unsigned generate_account_id()
{
int a;
randombytes((char *)&a, sizeof(a));
a = abs(a);
if (!a) ++a;
return a;
}
CSteamID generate_steam_id_user()
{
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance, k_EUniversePublic, k_EAccountTypeIndividual);
}
static CSteamID generate_steam_anon_user()
{
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance, k_EUniversePublic, k_EAccountTypeAnonUser);
}
CSteamID generate_steam_id_server()
{
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance, k_EUniversePublic, k_EAccountTypeGameServer);
}
CSteamID generate_steam_id_anonserver()
{
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance, k_EUniversePublic, k_EAccountTypeAnonGameServer);
}
CSteamID generate_steam_id_lobby()
{
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance | k_EChatInstanceFlagLobby, k_EUniversePublic, k_EAccountTypeChat);
}
#ifndef STEAM_WIN32
#include <sys/types.h>
#include <dirent.h>
std::string get_lib_path() {
std::string dir = "/proc/self/map_files";
DIR *dp;
int i = 0;
struct dirent *ep;
dp = opendir (dir.c_str());
unsigned long long int p = (unsigned long long int)&get_lib_path;
if (dp != NULL)
{
while ((ep = readdir (dp))) {
if (memcmp(ep->d_name, ".", 2) != 0 && memcmp(ep->d_name, "..", 3) != 0) {
char *upper = NULL;
unsigned long long int lower_bound = strtoull(ep->d_name, &upper, 16);
if (lower_bound) {
++upper;
unsigned long long int upper_bound = strtoull(upper, &upper, 16);
if (upper_bound && (lower_bound < p && p < upper_bound)) {
std::string path = dir + PATH_SEPARATOR + ep->d_name;
char link[PATH_MAX] = {};
if (readlink(path.c_str(), link, sizeof(link)) > 0) {
std::string lib_path = link;
(void) closedir (dp);
return link;
}
}
}
i++;
}
}
(void) closedir (dp);
}
return ".";
}
#endif
std::string get_full_program_path()
{
std::string program_path;
#if defined(STEAM_WIN32)
char DllPath[MAX_PATH] = {0};
GetModuleFileName((HINSTANCE)&__ImageBase, DllPath, _countof(DllPath));
program_path = DllPath;
#else
program_path = get_lib_path();
#endif
program_path = program_path.substr(0, program_path.rfind(PATH_SEPARATOR)).append(PATH_SEPARATOR);
return program_path;
}
static void steam_auth_ticket_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_auth_ticket_callback\n");
Auth_Ticket_Manager *auth_ticket_manager = (Auth_Ticket_Manager *)object;
auth_ticket_manager->Callback(msg);
}
Auth_Ticket_Manager::Auth_Ticket_Manager(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks) {
this->network = network;
this->settings = settings;
this->callbacks = callbacks;
this->network->setCallback(CALLBACK_ID_AUTH_TICKET, settings->get_local_steam_id(), &steam_auth_ticket_callback, this);
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &steam_auth_ticket_callback, this);
}
void Auth_Ticket_Manager::launch_callback(CSteamID id, EAuthSessionResponse resp)
{
ValidateAuthTicketResponse_t data;
data.m_SteamID = id;
data.m_eAuthSessionResponse = resp;
data.m_OwnerSteamID = id;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
void Auth_Ticket_Manager::launch_callback_gs(CSteamID id, bool approved)
{
if (approved) {
GSClientApprove_t data;
data.m_SteamID = data.m_OwnerSteamID = id;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
} else {
GSClientDeny_t data;
data.m_SteamID = id;
data.m_eDenyReason = k_EDenyNotLoggedOn; //TODO: other reasons?
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
#define STEAM_ID_OFFSET_TICKET (4 + 8)
#define STEAM_TICKET_MIN_SIZE (4 + 8 + 8)
Auth_Ticket_Data Auth_Ticket_Manager::getTicketData( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
{
uint64 steam_id = settings->get_local_steam_id().ConvertToUint64();
memset(pTicket, 123, cbMaxTicket);
((char *)pTicket)[0] = 0x14;
((char *)pTicket)[1] = 0;
((char *)pTicket)[2] = 0;
((char *)pTicket)[3] = 0;
memcpy((char *)pTicket + STEAM_ID_OFFSET_TICKET, &steam_id, sizeof(steam_id));
*pcbTicket = cbMaxTicket;
Auth_Ticket_Data ticket_data;
ticket_data.id = settings->get_local_steam_id();
ticket_data.number = generate_steam_ticket_id();
uint32 ttt = ticket_data.number;
memcpy(((char *)pTicket) + sizeof(uint64), &ttt, sizeof(ttt));
return ticket_data;
}
//Conan Exiles doesn't work with 512 or 128, 256 seems to be the good size
//Steam returns 234
#define STEAM_AUTH_TICKET_SIZE 234
uint32 Auth_Ticket_Manager::getTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
{
if (cbMaxTicket < STEAM_TICKET_MIN_SIZE) return 0;
if (cbMaxTicket > STEAM_AUTH_TICKET_SIZE) cbMaxTicket = STEAM_AUTH_TICKET_SIZE;
Auth_Ticket_Data ticket_data = getTicketData(pTicket, cbMaxTicket, pcbTicket );
uint32 ttt = ticket_data.number;
GetAuthSessionTicketResponse_t data;
data.m_hAuthTicket = ttt;
data.m_eResult = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
outbound.push_back(ticket_data);
return ttt;
}
CSteamID Auth_Ticket_Manager::fakeUser()
{
Auth_Ticket_Data data = {};
data.id = generate_steam_anon_user();
inbound.push_back(data);
return data.id;
}
void Auth_Ticket_Manager::cancelTicket(uint32 number)
{
auto ticket = std::find_if(outbound.begin(), outbound.end(), [&number](Auth_Ticket_Data const& item) { return item.number == number; });
if (outbound.end() == ticket)
return;
Auth_Ticket *auth_ticket = new Auth_Ticket();
auth_ticket->set_number(number);
auth_ticket->set_type(Auth_Ticket::CANCEL);
Common_Message msg;
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
msg.set_allocated_auth_ticket(auth_ticket);
network->sendToAll(&msg, true);
outbound.erase(ticket);
}
bool Auth_Ticket_Manager::SendUserConnectAndAuthenticate( uint32 unIPClient, const void *pvAuthBlob, uint32 cubAuthBlobSize, CSteamID *pSteamIDUser )
{
if (cubAuthBlobSize < STEAM_TICKET_MIN_SIZE) return false;
Auth_Ticket_Data data;
uint64 id;
memcpy(&id, (char *)pvAuthBlob + STEAM_ID_OFFSET_TICKET, sizeof(id));
uint32 number;
memcpy(&number, ((char *)pvAuthBlob) + sizeof(uint64), sizeof(number));
data.id = CSteamID(id);
data.number = number;
if (pSteamIDUser) *pSteamIDUser = data.id;
for (auto & t : inbound) {
if (t.id == data.id) {
//Should this return false?
launch_callback_gs(id, true);
return true;
}
}
inbound.push_back(data);
launch_callback_gs(id, true);
return true;
}
EBeginAuthSessionResult Auth_Ticket_Manager::beginAuth(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID )
{
if (cbAuthTicket < STEAM_TICKET_MIN_SIZE) return k_EBeginAuthSessionResultInvalidTicket;
Auth_Ticket_Data data;
uint64 id;
memcpy(&id, (char *)pAuthTicket + STEAM_ID_OFFSET_TICKET, sizeof(id));
uint32 number;
memcpy(&number, ((char *)pAuthTicket) + sizeof(uint64), sizeof(number));
data.id = CSteamID(id);
data.number = number;
for (auto & t : inbound) {
if (t.id == data.id) {
return k_EBeginAuthSessionResultDuplicateRequest;
}
}
inbound.push_back(data);
launch_callback(steamID, k_EAuthSessionResponseOK);
return k_EBeginAuthSessionResultOK;
}
uint32 Auth_Ticket_Manager::countInboundAuth()
{
return inbound.size();
}
bool Auth_Ticket_Manager::endAuth(CSteamID id)
{
auto ticket = std::find_if(inbound.begin(), inbound.end(), [&id](Auth_Ticket_Data const& item) { return item.id == id; });
if (inbound.end() == ticket)
return false;
inbound.erase(ticket);
return true;
}
void Auth_Ticket_Manager::Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::CONNECT) {
}
if (msg->low_level().type() == Low_Level::DISCONNECT) {
PRINT_DEBUG("TICKET DISCONNECT\n");
auto t = std::begin(inbound);
while (t != std::end(inbound)) {
if (t->id.ConvertToUint64() == msg->source_id()) {
launch_callback(t->id, k_EAuthSessionResponseUserNotConnectedToSteam);
t = inbound.erase(t);
} else {
++t;
}
}
}
}
if (msg->has_auth_ticket()) {
if (msg->auth_ticket().type() == Auth_Ticket::CANCEL) {
PRINT_DEBUG("TICKET CANCEL %llu\n", msg->source_id());
uint32 number = msg->auth_ticket().number();
auto t = std::begin(inbound);
while (t != std::end(inbound)) {
if (t->id.ConvertToUint64() == msg->source_id() && t->number == number) {
launch_callback(t->id, k_EAuthSessionResponseAuthTicketCanceled);
t = inbound.erase(t);
} else {
++t;
}
}
}
}
}
#ifdef EMU_EXPERIMENTAL_BUILD
#ifdef STEAM_WIN32
#include "../detours/detours.h"
static bool is_lan_ip(const sockaddr *addr, int namelen)
{
if (!namelen) return false;
if (addr->sa_family == AF_INET) {
struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
unsigned char ip[4];
memcpy(ip, &addr_in->sin_addr, sizeof(ip));
PRINT_DEBUG("CHECK LAN IP %hhu.%hhu.%hhu.%hhu:%u\n", ip[0], ip[1], ip[2], ip[3], ntohs(addr_in->sin_port));
if (ip[0] == 127) return true;
if (ip[0] == 10) return true;
if (ip[0] == 192 && ip[1] == 168) return true;
if (ip[0] == 169 && ip[1] == 254 && ip[2] != 0) return true;
if (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) return true;
if ((ip[0] == 100) && ((ip[1] & 0xC0) == 0x40)) return true;
if (ip[0] == 239) return true; //multicast
if (ip[0] == 0) return true; //Current network
if (ip[0] == 192 && (ip[1] == 18 || ip[1] == 19)) return true; //Used for benchmark testing of inter-network communications between two separate subnets.
if (ip[0] >= 224) return true; //ip multicast (224 - 239) future use (240.0.0.0255.255.255.254) broadcast (255.255.255.255)
} else if (addr->sa_family == AF_INET6) {
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr;
unsigned char ip[16];
unsigned char zeroes[16] = {};
memcpy(ip, &addr_in6->sin6_addr, sizeof(ip));
PRINT_DEBUG("CHECK LAN IP6 %hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu...%hhu\n", ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[15]);
if (((ip[0] == 0xFF) && (ip[1] < 3) && (ip[15] == 1)) ||
((ip[0] == 0xFE) && ((ip[1] & 0xC0) == 0x80))) return true;
if (memcmp(zeroes, ip, sizeof(ip)) == 0) return true;
if (memcmp(zeroes, ip, sizeof(ip) - 1) == 0 && ip[15] == 1) return true;
if (ip[0] == 0xff) return true; //multicast
if (ip[0] == 0xfc) return true; //unique local
if (ip[0] == 0xfd) return true; //unique local
//TODO: ipv4 mapped?
}
PRINT_DEBUG("NOT LAN IP\n");
return false;
}
int ( WINAPI *Real_SendTo )( SOCKET s, const char *buf, int len, int flags, const sockaddr *to, int tolen) = sendto;
int ( WINAPI *Real_Connect )( SOCKET s, const sockaddr *addr, int namelen ) = connect;
int ( WINAPI *Real_WSAConnect )( SOCKET s, const sockaddr *addr, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS) = WSAConnect;
static int WINAPI Mine_SendTo( SOCKET s, const char *buf, int len, int flags, const sockaddr *to, int tolen) {
PRINT_DEBUG("Mine_SendTo\n");
if (is_lan_ip(to, tolen)) {
return Real_SendTo( s, buf, len, flags, to, tolen );
} else {
return len;
}
}
static int WINAPI Mine_Connect( SOCKET s, const sockaddr *addr, int namelen )
{
PRINT_DEBUG("Mine_Connect\n");
if (is_lan_ip(addr, namelen)) {
return Real_Connect(s, addr, namelen);
} else {
WSASetLastError(WSAECONNREFUSED);
return SOCKET_ERROR;
}
}
static int WINAPI Mine_WSAConnect( SOCKET s, const sockaddr *addr, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS)
{
PRINT_DEBUG("Mine_WSAConnect\n");
if (is_lan_ip(addr, namelen)) {
return Real_WSAConnect(s, addr, namelen, lpCallerData, lpCalleeData, lpSQOS, lpGQOS);
} else {
WSASetLastError(WSAECONNREFUSED);
return SOCKET_ERROR;
}
}
inline bool file_exists (const std::string& name) {
struct stat buffer;
return (stat (name.c_str(), &buffer) == 0);
}
#ifdef DETOURS_64BIT
#define DLL_NAME "steam_api64.dll"
#else
#define DLL_NAME "steam_api.dll"
#endif
HMODULE (WINAPI *Real_GetModuleHandleA)(LPCSTR lpModuleName) = GetModuleHandleA;
HMODULE WINAPI Mine_GetModuleHandleA(LPCSTR lpModuleName)
{
PRINT_DEBUG("Mine_GetModuleHandleA %s\n", lpModuleName);
if (!lpModuleName) return Real_GetModuleHandleA(lpModuleName);
std::string in(lpModuleName);
if (in == std::string(DLL_NAME)) {
in = std::string("crack") + in;
}
return Real_GetModuleHandleA(in.c_str());
}
static void redirect_crackdll()
{
DetourTransactionBegin();
DetourUpdateThread( GetCurrentThread() );
DetourAttach( &(PVOID &)Real_GetModuleHandleA, Mine_GetModuleHandleA );
DetourTransactionCommit();
}
static void unredirect_crackdll()
{
DetourTransactionBegin();
DetourUpdateThread( GetCurrentThread() );
DetourDetach( &(PVOID &)Real_GetModuleHandleA, Mine_GetModuleHandleA );
DetourTransactionCommit();
}
HMODULE crack_dll_handle;
static void load_dll()
{
std::string path = get_full_program_path();
path += "crack";
//path += PATH_SEPARATOR;
path += DLL_NAME;
PRINT_DEBUG("Crack file %s\n", path.c_str());
if (file_exists(path)) {
redirect_crackdll();
crack_dll_handle = LoadLibraryA(path.c_str());
unredirect_crackdll();
PRINT_DEBUG("Loaded crack file\n");
}
}
//For some reason when this function is optimized it breaks the shogun 2 prophet (reloaded) crack.
#pragma optimize( "", off )
bool crack_SteamAPI_RestartAppIfNecessary(uint32 unOwnAppID)
{
if (crack_dll_handle) {
bool (__stdcall* restart_app)(uint32) = (bool (__stdcall *)(uint32))GetProcAddress(crack_dll_handle, "SteamAPI_RestartAppIfNecessary");
if (restart_app) {
PRINT_DEBUG("Call crack SteamAPI_RestartAppIfNecessary\n");
redirect_crackdll();
bool ret = restart_app(unOwnAppID);
unredirect_crackdll();
return ret;
}
}
return false;
}
#pragma optimize( "", on )
bool crack_SteamAPI_Init()
{
if (crack_dll_handle) {
bool (__stdcall* init_app)() = (bool (__stdcall *)())GetProcAddress(crack_dll_handle, "SteamAPI_Init");
if (init_app) {
PRINT_DEBUG("Call crack SteamAPI_Init\n");
redirect_crackdll();
bool ret = init_app();
unredirect_crackdll();
return ret;
}
}
return false;
}
#include <winhttp.h>
HINTERNET (WINAPI *Real_WinHttpConnect)(
IN HINTERNET hSession,
IN LPCWSTR pswzServerName,
IN INTERNET_PORT nServerPort,
IN DWORD dwReserved
);
HINTERNET WINAPI Mine_WinHttpConnect(
IN HINTERNET hSession,
IN LPCWSTR pswzServerName,
IN INTERNET_PORT nServerPort,
IN DWORD dwReserved
) {
PRINT_DEBUG("Mine_WinHttpConnect %ls %u\n", pswzServerName, nServerPort);
struct sockaddr_in ip4;
struct sockaddr_in6 ip6;
ip4.sin_family = AF_INET;
ip6.sin6_family = AF_INET6;
if ((InetPtonW(AF_INET, pswzServerName, &(ip4.sin_addr)) && is_lan_ip((sockaddr *)&ip4, sizeof(ip4))) || (InetPtonW(AF_INET6, pswzServerName, &(ip6.sin6_addr)) && is_lan_ip((sockaddr *)&ip6, sizeof(ip6)))) {
return Real_WinHttpConnect(hSession, pswzServerName, nServerPort, dwReserved);
} else {
return Real_WinHttpConnect(hSession, L"127.1.33.7", nServerPort, dwReserved);
}
}
static bool network_functions_attached = false;
BOOL WINAPI DllMain( HINSTANCE, DWORD dwReason, LPVOID ) {
switch ( dwReason ) {
case DLL_PROCESS_ATTACH:
if (!file_exists(get_full_program_path() + "disable_lan_only.txt")) {
PRINT_DEBUG("Hooking lan only functions\n");
DetourTransactionBegin();
DetourUpdateThread( GetCurrentThread() );
DetourAttach( &(PVOID &)Real_SendTo, Mine_SendTo );
DetourAttach( &(PVOID &)Real_Connect, Mine_Connect );
DetourAttach( &(PVOID &)Real_WSAConnect, Mine_WSAConnect );
HMODULE winhttp = GetModuleHandle("winhttp.dll");
if (winhttp) {
Real_WinHttpConnect = (decltype(Real_WinHttpConnect))GetProcAddress(winhttp, "WinHttpConnect");
DetourAttach( &(PVOID &)Real_WinHttpConnect, Mine_WinHttpConnect );
}
DetourTransactionCommit();
network_functions_attached = true;
}
load_dll();
break;
case DLL_PROCESS_DETACH:
if (network_functions_attached) {
DetourTransactionBegin();
DetourUpdateThread( GetCurrentThread() );
DetourDetach( &(PVOID &)Real_SendTo, Mine_SendTo );
DetourDetach( &(PVOID &)Real_Connect, Mine_Connect );
DetourDetach( &(PVOID &)Real_WSAConnect, Mine_WSAConnect );
if (Real_WinHttpConnect) {
DetourDetach( &(PVOID &)Real_WinHttpConnect, Mine_WinHttpConnect );
}
DetourTransactionCommit();
}
break;
}
return TRUE;
}
#endif
#endif

473
dll/base.h Normal file
View File

@ -0,0 +1,473 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef BASE_INCLUDE
#define BASE_INCLUDE
#if defined(WIN32) || defined(_WIN32)
#define STEAM_WIN32
#pragma warning( disable : 4716)
#endif
#define STEAM_API_EXPORTS
#include "../sdk_includes/steam_gameserver.h"
#include "../sdk_includes/steamdatagram_tickets.h"
#include <algorithm>
#include <vector>
#include <map>
#include <mutex>
//#define PRINT_DEBUG(...) {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, __VA_ARGS__); fclose(t);}
#ifdef STEAM_WIN32
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <processthreadsapi.h>
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define PATH_SEPARATOR "\\"
#ifndef EMU_RELEASE_BUILD
#define PRINT_DEBUG(a, ...) do {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, "%u " a, GetCurrentThreadId(), __VA_ARGS__); fclose(t); WSASetLastError(0);} while (0)
#endif
#else
#define PATH_SEPARATOR "/"
#ifndef EMU_RELEASE_BUILD
#define PRINT_DEBUG(...) {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, __VA_ARGS__); fclose(t);}
#endif
#endif
//#define PRINT_DEBUG(...) fprintf(stdout, __VA_ARGS__)
#ifdef EMU_RELEASE_BUILD
#define PRINT_DEBUG(...)
#endif
#include "settings.h"
#include "local_storage.h"
#include "network.h"
#include "defines.h"
#define PUSH_BACK_IF_NOT_IN(vector, element) { if(std::find(vector.begin(), vector.end(), element) == vector.end()) vector.push_back(element); }
extern std::recursive_mutex global_mutex;
std::string get_env_variable(std::string name);
class CCallbackMgr
{
public:
static void SetRegister(class CCallbackBase *pCallback, int iCallback) {
pCallback->m_nCallbackFlags |= CCallbackBase::k_ECallbackFlagsRegistered;
pCallback->m_iCallback = iCallback;
};
static void SetUnregister(class CCallbackBase *pCallback) {
if (pCallback)
pCallback->m_nCallbackFlags &= !CCallbackBase::k_ECallbackFlagsRegistered;
};
static bool isServer(class CCallbackBase *pCallback) {
return (pCallback->m_nCallbackFlags & CCallbackBase::k_ECallbackFlagsGameServer) != 0;
};
};
#define STEAM_CALLRESULT_TIMEOUT 120.0
struct Steam_Call_Result {
Steam_Call_Result(SteamAPICall_t a, int icb, void *r, unsigned int s, double r_in, bool run_cc_cb) {
api_call = a;
result.resize(s);
memcpy(&(result[0]), r, s);
created = std::chrono::high_resolution_clock::now();
run_in = r_in;
run_call_completed_cb = run_cc_cb;
iCallback = icb;
}
bool operator==(const struct Steam_Call_Result& a)
{
return a.api_call == api_call && a.callbacks == callbacks;
}
bool timed_out() {
return check_timedout(created, STEAM_CALLRESULT_TIMEOUT);
}
bool can_execute() {
return (!reserved) && (!to_delete) && check_timedout(created, run_in);
}
bool has_cb() {
return callbacks.size() > 0;
}
SteamAPICall_t api_call;
std::vector<class CCallbackBase *> callbacks;
std::vector<char> result;
bool to_delete = false;
bool reserved = false;
std::chrono::high_resolution_clock::time_point created;
double run_in;
bool run_call_completed_cb;
int iCallback;
};
int generate_random_int();
SteamAPICall_t generate_steam_api_call_id();
CSteamID generate_steam_id_user();
CSteamID generate_steam_id_server();
CSteamID generate_steam_id_anonserver();
CSteamID generate_steam_id_lobby();
std::string get_full_program_path();
class SteamCallResults {
std::vector<struct Steam_Call_Result> callresults;
std::vector<class CCallbackBase *> completed_callbacks;
public:
void addCallCompleted(class CCallbackBase *cb) {
if (std::find(completed_callbacks.begin(), completed_callbacks.end(), cb) == completed_callbacks.end()) {
completed_callbacks.push_back(cb);
}
}
void rmCallCompleted(class CCallbackBase *cb) {
auto c = std::find(completed_callbacks.begin(), completed_callbacks.end(), cb);
if (c != completed_callbacks.end()) {
completed_callbacks.erase(c);
}
}
void addCallBack(SteamAPICall_t api_call, class CCallbackBase *cb) {
auto cb_result = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
if (cb_result != callresults.end()) {
cb_result->callbacks.push_back(cb);
CCallbackMgr::SetRegister(cb, cb->GetICallback());
}
}
bool exists(SteamAPICall_t api_call) {
auto cr = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
if (cr == callresults.end()) return false;
if (cr->reserved) return false;
return true;
}
bool callback_result(SteamAPICall_t api_call, void *copy_to, unsigned int size) {
auto cb_result = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
if (cb_result != callresults.end()) {
if (cb_result->reserved) return false;
if (cb_result->result.size() > size) return false;
memcpy(copy_to, &(cb_result->result[0]), cb_result->result.size());
cb_result->to_delete = true;
return true;
} else {
return false;
}
}
void rmCallBack(SteamAPICall_t api_call, class CCallbackBase *cb) {
auto cb_result = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
if (cb_result != callresults.end()) {
auto it = std::find(cb_result->callbacks.begin(), cb_result->callbacks.end(), cb);
if (it != cb_result->callbacks.end()) {
cb_result->callbacks.erase(it);
CCallbackMgr::SetUnregister(cb);
}
}
}
void rmCallBack(class CCallbackBase *cb) {
//TODO: check if callback is callback or call result?
for (auto & cr: callresults) {
auto it = std::find(cr.callbacks.begin(), cr.callbacks.end(), cb);
if (it != cr.callbacks.end()) {
cr.callbacks.erase(it);
}
if (cr.callbacks.size() == 0) {
cr.to_delete = true;
}
}
}
SteamAPICall_t addCallResult(SteamAPICall_t api_call, int iCallback, void *result, unsigned int size, double timeout=0.0, bool run_call_completed_cb=true) {
auto cb_result = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
if (cb_result != callresults.end()) {
if (cb_result->reserved) {
std::vector<class CCallbackBase *> temp_cbs = cb_result->callbacks;
*cb_result = Steam_Call_Result(api_call, iCallback, result, size, timeout, run_call_completed_cb);
cb_result->callbacks = temp_cbs;
return cb_result->api_call;
}
} else {
struct Steam_Call_Result res = Steam_Call_Result(api_call, iCallback, result, size, timeout, run_call_completed_cb);
callresults.push_back(res);
return callresults.back().api_call;
}
PRINT_DEBUG("addCallResult ERROR\n");
return 0;
}
SteamAPICall_t reserveCallResult() {
struct Steam_Call_Result res = Steam_Call_Result(generate_steam_api_call_id(), 0, NULL, 0, 0.0, true);
res.reserved = true;
callresults.push_back(res);
return callresults.back().api_call;
}
SteamAPICall_t addCallResult(int iCallback, void *result, unsigned int size, double timeout=0.0, bool run_call_completed_cb=true) {
return addCallResult(generate_steam_api_call_id(), iCallback, result, size, timeout, run_call_completed_cb);
}
void runCallResults() {
unsigned long current_size = callresults.size();
for (unsigned i = 0; i < current_size; ++i) {
unsigned index = i;
if (!callresults[index].to_delete) {
if (callresults[index].can_execute()) {
std::vector<char> result = callresults[index].result;
SteamAPICall_t api_call = callresults[index].api_call;
bool run_call_completed_cb = callresults[index].run_call_completed_cb;
int iCallback = callresults[index].iCallback;
if (run_call_completed_cb) {
callresults[index].run_call_completed_cb = false;
}
if (callresults[index].has_cb()) {
std::vector<class CCallbackBase *> temp_cbs = callresults[index].callbacks;
callresults[index].to_delete = true;
for (auto & cb : temp_cbs) {
PRINT_DEBUG("Calling callresult %p %i\n", cb, cb->GetICallback());
global_mutex.unlock();
//TODO: unlock relock doesn't work if mutex was locked more than once.
if (run_call_completed_cb) { //run the right function depending on if it's a callback or a call result.
cb->Run(&(result[0]), false, api_call);
} else {
cb->Run(&(result[0]));
}
//COULD BE DELETED SO DON'T TOUCH CB
global_mutex.lock();
PRINT_DEBUG("callresult done\n");
}
}
if (run_call_completed_cb) {
//can it happen that one is removed during the callback?
std::vector<class CCallbackBase *> callbacks = completed_callbacks;
for (auto & cb: callbacks) {
SteamAPICallCompleted_t data;
data.m_hAsyncCall = api_call;
data.m_iCallback = iCallback;
data.m_cubParam = result.size();
PRINT_DEBUG("Call complete cb %i %p %llu\n", iCallback, cb, api_call);
//TODO: check if this is a problem or not.
global_mutex.unlock();
cb->Run(&data);
global_mutex.lock();
}
}
} else {
if (callresults[index].timed_out()) {
callresults[index].to_delete = true;
}
}
}
}
PRINT_DEBUG("runCallResults erase to_delete\n");
auto c = std::begin(callresults);
while (c != std::end(callresults)) {
if (c->to_delete) {
if (c->timed_out()) {
c = callresults.erase(c);
} else {
++c;
}
} else {
++c;
}
}
}
};
struct Steam_Call_Back {
std::vector<class CCallbackBase *> callbacks;
std::vector<std::vector<char>> results;
};
class SteamCallBacks {
std::map<int, struct Steam_Call_Back> callbacks;
SteamCallResults *results;
public:
SteamCallBacks(SteamCallResults *results) {
this->results = results;
}
void addCallBack(int iCallback, class CCallbackBase *cb) {
PRINT_DEBUG("addCallBack %i\n", iCallback);
if (iCallback == SteamAPICallCompleted_t::k_iCallback) {
results->addCallCompleted(cb);
CCallbackMgr::SetRegister(cb, iCallback);
return;
}
if (std::find(callbacks[iCallback].callbacks.begin(), callbacks[iCallback].callbacks.end(), cb) == callbacks[iCallback].callbacks.end()) {
callbacks[iCallback].callbacks.push_back(cb);
CCallbackMgr::SetRegister(cb, iCallback);
for (auto & res: callbacks[iCallback].results) {
//TODO: timeout?
SteamAPICall_t api_id = results->addCallResult(iCallback, &(res[0]), res.size(), 0.0, false);
results->addCallBack(api_id, cb);
}
}
}
void addCBResult(int iCallback, void *result, unsigned int size, double timeout, bool dont_post_if_already) {
if (dont_post_if_already) {
for (auto & r : callbacks[iCallback].results) {
if (r.size() == size) {
if (memcmp(&(r[0]), result, size) == 0) {
//cb already posted
return;
}
}
}
}
std::vector<char> temp;
temp.resize(size);
memcpy(&(temp[0]), result, size);
callbacks[iCallback].results.push_back(temp);
for (auto cb: callbacks[iCallback].callbacks) {
SteamAPICall_t api_id = results->addCallResult(iCallback, result, size, timeout, false);
results->addCallBack(api_id, cb);
}
}
void addCBResult(int iCallback, void *result, unsigned int size) {
addCBResult(iCallback, result, size, 0.0, false);
}
void addCBResult(int iCallback, void *result, unsigned int size, bool dont_post_if_already) {
addCBResult(iCallback, result, size, 0.0, dont_post_if_already);
}
void addCBResult(int iCallback, void *result, unsigned int size, double timeout) {
addCBResult(iCallback, result, size, timeout, false);
}
void rmCallBack(int iCallback, class CCallbackBase *cb) {
if (iCallback == SteamAPICallCompleted_t::k_iCallback) {
results->rmCallCompleted(cb);
CCallbackMgr::SetUnregister(cb);
return;
}
auto c = std::find(callbacks[iCallback].callbacks.begin(), callbacks[iCallback].callbacks.end(), cb);
if (c != callbacks[iCallback].callbacks.end()) {
callbacks[iCallback].callbacks.erase(c);
CCallbackMgr::SetUnregister(cb);
results->rmCallBack(cb);
}
}
void runCallBacks() {
for (auto & c : callbacks) {
std::vector<std::vector<char>> res_back = c.second.results;
c.second.results.clear();
for (auto r : res_back) {
for (auto cb: c.second.callbacks) {
//PRINT_DEBUG("Calling callback %i\n", cb->GetICallback());
//cb->Run(&(r[0]));
}
}
}
}
};
struct Auth_Ticket_Data {
CSteamID id;
uint64 number;
};
class Auth_Ticket_Manager {
class Settings *settings;
class Networking *network;
class SteamCallBacks *callbacks;
void launch_callback(CSteamID id, EAuthSessionResponse resp);
void launch_callback_gs(CSteamID id, bool approved);
std::vector<struct Auth_Ticket_Data> inbound, outbound;
public:
Auth_Ticket_Manager(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks);
void Callback(Common_Message *msg);
uint32 getTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket );
void cancelTicket(uint32 number);
EBeginAuthSessionResult beginAuth(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID);
bool endAuth(CSteamID id);
uint32 countInboundAuth();
bool SendUserConnectAndAuthenticate( uint32 unIPClient, const void *pvAuthBlob, uint32 cubAuthBlobSize, CSteamID *pSteamIDUser );
CSteamID fakeUser();
Auth_Ticket_Data getTicketData( void *pTicket, int cbMaxTicket, uint32 *pcbTicket );
};
struct RunCBs {
void (*function)(void *object);
void *object;
};
class RunEveryRunCB {
std::vector<struct RunCBs> cbs;
public:
void add(void (*cb)(void *object), void *object) {
remove(cb, object);
RunCBs rcb;
rcb.function = cb;
rcb.object = object;
cbs.push_back(rcb);
}
void remove(void (*cb)(void *object), void *object) {
auto c = std::begin(cbs);
while (c != std::end(cbs)) {
if (c->function == cb && c->object == object) {
c = cbs.erase(c);
} else {
++c;
}
}
}
void run() {
std::vector<struct RunCBs> temp_cbs = cbs;
for (auto c : temp_cbs) {
c.function(c.object);
}
}
};
#ifdef EMU_EXPERIMENTAL_BUILD
bool crack_SteamAPI_RestartAppIfNecessary(uint32 unOwnAppID);
bool crack_SteamAPI_Init();
#endif
#endif

12
dll/defines.h Normal file
View File

@ -0,0 +1,12 @@
//TODO: put these in a common .h
#define CLIENT_HSTEAMUSER 12
#define SERVER_HSTEAMUSER 13
#define CLIENT_STEAM_PIPE 5
#define SERVER_STEAM_PIPE 6
#define DEFAULT_NAME "Goldberg"
#define PROGRAM_NAME "Goldberg SteamEmu"
#define DEFAULT_LANGUAGE "english"
#define LOBBY_CONNECT_APPID ((uint32)-2)

653
dll/dll.cpp Normal file
View File

@ -0,0 +1,653 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "dll.h"
static char old_client[128] = "SteamClient017";
static char old_gameserver[128] = "SteamGameServer012";
static char old_gameserver_stats[128] = "SteamGameServerStats001";
static char old_user[128] = "SteamUser018";
static char old_friends[128] = "SteamFriends015";
static char old_utils[128] = "SteamUtils007";
static char old_matchmaking[128] = "SteamMatchMaking009";
static char old_matchmaking_servers[128] = "SteamMatchMakingServers002";
static char old_userstats[128] = "STEAMUSERSTATS_INTERFACE_VERSION011";
static char old_apps[128] = "STEAMAPPS_INTERFACE_VERSION007";
static char old_networking[128] = "SteamNetworking005";
static char old_remote_storage_interface[128] = "STEAMREMOTESTORAGE_INTERFACE_VERSION013";
static char old_screenshots[128] = "STEAMSCREENSHOTS_INTERFACE_VERSION002";
static char old_http[128] = "STEAMHTTP_INTERFACE_VERSION002";
static char old_unified_messages[128] = "STEAMUNIFIEDMESSAGES_INTERFACE_VERSION001";
static char old_controller[128] = "SteamController003";
static char old_ugc_interface[128] = "STEAMUGC_INTERFACE_VERSION007";
static char old_applist[128] = "STEAMAPPLIST_INTERFACE_VERSION001";
static char old_music[128] = "STEAMMUSIC_INTERFACE_VERSION001";
static char old_music_remote[128] = "STEAMMUSICREMOTE_INTERFACE_VERSION001";
static char old_html_surface[128] = "STEAMHTMLSURFACE_INTERFACE_VERSION_003";
static char old_inventory[128] = "STEAMINVENTORY_INTERFACE_V001";
static char old_video[128] = "STEAMVIDEO_INTERFACE_V001";
static char old_masterserver_updater[128] = "SteamMasterServerUpdater001";
#include <fstream>
static void load_old_interface_versions()
{
static bool loaded = false;
if (loaded) return;
std::string interfaces_path = Local_Storage::get_program_path() + "steam_interfaces.txt";
std::ifstream input( interfaces_path );
PRINT_DEBUG("load from: %s\n", interfaces_path.c_str());
for( std::string line; getline( input, line ); )
{
PRINT_DEBUG("line: %s\n", line.c_str());
line.erase(std::remove(line.begin(), line.end(), ' '), line.end());
line.erase(std::remove(line.begin(), line.end(), '\n'), line.end());
line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
line.erase(std::remove(line.begin(), line.end(), '\t'), line.end());
#define REPLACE_WITH_FILE(s, f) {if (line.find(s) != std::string::npos) {strncpy(f, line.c_str(), sizeof(f) - 1); continue;}}
REPLACE_WITH_FILE("SteamClient", old_client);
REPLACE_WITH_FILE("SteamFriends", old_friends);
REPLACE_WITH_FILE("SteamMatchMakingServers", old_matchmaking_servers);
REPLACE_WITH_FILE("SteamMatchMaking", old_matchmaking);
REPLACE_WITH_FILE("STEAMREMOTESTORAGE_INTERFACE_VERSION", old_remote_storage_interface);
REPLACE_WITH_FILE("STEAMCONTROLLER_INTERFACE_VERSION", old_controller);
REPLACE_WITH_FILE("SteamController", old_controller);
REPLACE_WITH_FILE("STEAMUGC_INTERFACE_VERSION", old_ugc_interface);
REPLACE_WITH_FILE("STEAMINVENTORY_INTERFACE", old_inventory);
REPLACE_WITH_FILE("STEAMUSERSTATS_INTERFACE_VERSION", old_userstats);
REPLACE_WITH_FILE("SteamNetworking", old_networking);
REPLACE_WITH_FILE("SteamUser", old_user);
PRINT_DEBUG("NOT REPLACED %s\n", line.c_str());
#undef REPLACE_WITH_FILE
}
PRINT_DEBUG("client: %s\n", old_client);
PRINT_DEBUG("user: %s\n", old_user);
PRINT_DEBUG("friends: %s\n", old_friends);
PRINT_DEBUG("matchmaking: %s\n", old_matchmaking);
PRINT_DEBUG("remote: %s\n", old_remote_storage_interface);
PRINT_DEBUG("controller %s\n", old_controller);
PRINT_DEBUG("ugc: %s\n", old_ugc_interface);
PRINT_DEBUG("inventory: %s\n", old_inventory);
PRINT_DEBUG("userstats: %s\n", old_userstats);
PRINT_DEBUG("networking: %s\n", old_networking);
loaded = true;
}
//steam_api_internal.h
S_API HSteamUser SteamAPI_GetHSteamUser()
{
PRINT_DEBUG("SteamAPI_GetHSteamUser\n");
return CLIENT_HSTEAMUSER;
}
S_API ISteamClient *g_pSteamClientGameServer;
ISteamClient *g_pSteamClientGameServer;
Steam_Client *get_steam_client()
{
std::lock_guard<std::recursive_mutex> lock(global_mutex);
static Steam_Client *client = new Steam_Client();
if (!g_pSteamClientGameServer) g_pSteamClientGameServer = client;
return client;
}
Steam_Client *get_steam_client_old()
{
return get_steam_client();
}
Steam_Client *get_steam_clientserver_old()
{
return get_steam_client();
}
S_API void * S_CALLTYPE SteamInternal_CreateInterface( const char *ver )
{
PRINT_DEBUG("SteamInternal_CreateInterface %s\n", ver);
void *steam_client;
if (strcmp(ver, "SteamClient007") == 0) {
steam_client = (ISteamClient007 *)get_steam_client();
} else if (strcmp(ver, "SteamClient008") == 0) {
steam_client = (ISteamClient008 *)get_steam_client();
} else if (strcmp(ver, "SteamClient009") == 0) {
steam_client = (ISteamClient009 *)get_steam_client();
} else if (strcmp(ver, "SteamClient010") == 0) {
steam_client = (ISteamClient010 *)get_steam_client();
} else if (strcmp(ver, "SteamClient011") == 0) {
steam_client = (ISteamClient011 *)get_steam_client();
} else if (strcmp(ver, "SteamClient012") == 0) {
steam_client = (ISteamClient012 *)get_steam_client();
} else if (strcmp(ver, "SteamClient013") == 0) {
steam_client = (ISteamClient013 *)get_steam_client();
} else if (strcmp(ver, "SteamClient014") == 0) {
steam_client = (ISteamClient014 *)get_steam_client();
} else if (strcmp(ver, "SteamClient015") == 0) {
steam_client = (ISteamClient015 *)get_steam_client();
} else if (strcmp(ver, "SteamClient016") == 0) {
steam_client = (ISteamClient016 *)get_steam_client();
} else if (strcmp(ver, "SteamClient017") == 0) {
steam_client = (ISteamClient017 *)get_steam_client();
} else if (strcmp(ver, STEAMCLIENT_INTERFACE_VERSION) == 0) {
steam_client = (ISteamClient *)get_steam_client();
} else {
steam_client = (ISteamClient *)get_steam_client();
}
if (steam_client) g_pSteamClientGameServer = (ISteamClient *)steam_client;
return steam_client;
}
struct ContextInitData { void (*pFn)(void* pCtx); uintp counter; CSteamAPIContext ctx; };
S_API void * S_CALLTYPE SteamInternal_ContextInit( void *pContextInitData )
{
//PRINT_DEBUG("SteamInternal_ContextInit\n");
struct ContextInitData *contextInitData = (struct ContextInitData *)pContextInitData;
if (!contextInitData->counter) {
PRINT_DEBUG("SteamInternal_ContextInit initializing\n");
contextInitData->pFn(&contextInitData->ctx);
//this is hackish but whatever.
if (contextInitData->ctx.SteamUser()) contextInitData->counter = 1;
}
return &contextInitData->ctx;
}
//steam_api.h
// SteamAPI_Init must be called before using any other API functions. If it fails, an
// error message will be output to the debugger (or stderr) with further information.
S_API bool S_CALLTYPE SteamAPI_Init()
{
PRINT_DEBUG("SteamAPI_Init called\n");
#ifdef EMU_EXPERIMENTAL_BUILD
crack_SteamAPI_Init();
#endif
load_old_interface_versions();
get_steam_client()->userLogIn();
return true;
}
//TODO: not sure if this is the right signature for this function.
S_API bool S_CALLTYPE SteamAPI_InitAnonymousUser()
{
PRINT_DEBUG("SteamAPI_InitAnonymousUser called\n");
return SteamAPI_Init();
}
// SteamAPI_Shutdown should be called during process shutdown if possible.
S_API void S_CALLTYPE SteamAPI_Shutdown()
{
PRINT_DEBUG("SteamAPI_Shutdown\n");
}
// SteamAPI_RestartAppIfNecessary ensures that your executable was launched through Steam.
//
// Returns true if the current process should terminate. Steam is now re-launching your application.
//
// Returns false if no action needs to be taken. This means that your executable was started through
// the Steam client, or a steam_appid.txt file is present in your game's directory (for development).
// Your current process should continue if false is returned.
//
// NOTE: If you use the Steam DRM wrapper on your primary executable file, this check is unnecessary
// since the DRM wrapper will ensure that your application was launched properly through Steam.
S_API bool S_CALLTYPE SteamAPI_RestartAppIfNecessary( uint32 unOwnAppID )
{
PRINT_DEBUG("SteamAPI_RestartAppIfNecessary %u\n", unOwnAppID);
#ifdef EMU_EXPERIMENTAL_BUILD
crack_SteamAPI_RestartAppIfNecessary(unOwnAppID);
#endif
get_steam_client()->setAppID(unOwnAppID);
return false;
}
// Many Steam API functions allocate a small amount of thread-local memory for parameter storage.
// SteamAPI_ReleaseCurrentThreadMemory() will free API memory associated with the calling thread.
// This function is also called automatically by SteamAPI_RunCallbacks(), so a single-threaded
// program never needs to explicitly call this function.
S_API void S_CALLTYPE SteamAPI_ReleaseCurrentThreadMemory()
{
PRINT_DEBUG("SteamAPI_ReleaseCurrentThreadMemory\n");
}
// crash dump recording functions
S_API void S_CALLTYPE SteamAPI_WriteMiniDump( uint32 uStructuredExceptionCode, void* pvExceptionInfo, uint32 uBuildID )
{
PRINT_DEBUG("SteamAPI_WriteMiniDump\n");
}
S_API void S_CALLTYPE SteamAPI_SetMiniDumpComment( const char *pchMsg )
{
PRINT_DEBUG("SteamAPI_SetMiniDumpComment: %s\n", pchMsg);
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------//
// steam callback and call-result helpers
//
// The following macros and classes are used to register your application for
// callbacks and call-results, which are delivered in a predictable manner.
//
// STEAM_CALLBACK macros are meant for use inside of a C++ class definition.
// They map a Steam notification callback directly to a class member function
// which is automatically prototyped as "void func( callback_type *pParam )".
//
// CCallResult is used with specific Steam APIs that return "result handles".
// The handle can be passed to a CCallResult object's Set function, along with
// an object pointer and member-function pointer. The member function will
// be executed once the results of the Steam API call are available.
//
// CCallback and CCallbackManual classes can be used instead of STEAM_CALLBACK
// macros if you require finer control over registration and unregistration.
//
// Callbacks and call-results are queued automatically and are only
// delivered/executed when your application calls SteamAPI_RunCallbacks().
//----------------------------------------------------------------------------------------------------------------------------------------------------------//
// SteamAPI_RunCallbacks is safe to call from multiple threads simultaneously,
// but if you choose to do this, callback code could be executed on any thread.
// One alternative is to call SteamAPI_RunCallbacks from the main thread only,
// and call SteamAPI_ReleaseCurrentThreadMemory regularly on other threads.
S_API void S_CALLTYPE SteamAPI_RunCallbacks()
{
PRINT_DEBUG("SteamAPI_RunCallbacks\n");
get_steam_client()->RunCallbacks(true, false);
std::this_thread::sleep_for(std::chrono::microseconds(1)); //fixes resident evil revelations lagging.
}
// Declares a callback member function plus a helper member variable which
// registers the callback on object creation and unregisters on destruction.
// The optional fourth 'var' param exists only for backwards-compatibility
// and can be ignored.
//#define STEAM_CALLBACK( thisclass, func, .../*callback_type, [deprecated] var*/ ) \
// _STEAM_CALLBACK_SELECT( ( __VA_ARGS__, 4, 3 ), ( /**/, thisclass, func, __VA_ARGS__ ) )
// Declares a callback function and a named CCallbackManual variable which
// has Register and Unregister functions instead of automatic registration.
//#define STEAM_CALLBACK_MANUAL( thisclass, func, callback_type, var ) \
// CCallbackManual< thisclass, callback_type > var; void func( callback_type *pParam )
// Internal functions used by the utility CCallback objects to receive callbacks
S_API void S_CALLTYPE SteamAPI_RegisterCallback( class CCallbackBase *pCallback, int iCallback )
{
PRINT_DEBUG("SteamAPI_RegisterCallback %p %u funct:%u\n", pCallback, iCallback, pCallback->GetICallback());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
get_steam_client()->RegisterCallback(pCallback, iCallback);
}
S_API void S_CALLTYPE SteamAPI_UnregisterCallback( class CCallbackBase *pCallback )
{
PRINT_DEBUG("SteamAPI_UnregisterCallback %p\n", pCallback);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
get_steam_client()->UnregisterCallback(pCallback);
}
// Internal functions used by the utility CCallResult objects to receive async call results
S_API void S_CALLTYPE SteamAPI_RegisterCallResult( class CCallbackBase *pCallback, SteamAPICall_t hAPICall )
{
PRINT_DEBUG("SteamAPI_RegisterCallResult\n");
if (!hAPICall)
return;
get_steam_client()->RegisterCallResult(pCallback, hAPICall);
}
S_API void S_CALLTYPE SteamAPI_UnregisterCallResult( class CCallbackBase *pCallback, SteamAPICall_t hAPICall )
{
PRINT_DEBUG("SteamAPI_UnregisterCallResult\n");
if (!hAPICall)
return;
get_steam_client()->UnregisterCallResult(pCallback, hAPICall);
}
S_API void *S_CALLTYPE SteamInternal_FindOrCreateUserInterface( HSteamUser hSteamUser, const char *pszVersion )
{
PRINT_DEBUG("SteamInternal_FindOrCreateUserInterface %i %s\n", hSteamUser, pszVersion);
return get_steam_client()->GetISteamGenericInterface(hSteamUser, SteamAPI_GetHSteamPipe(), pszVersion);
}
S_API void *S_CALLTYPE SteamInternal_FindOrCreateGameServerInterface( HSteamUser hSteamUser, const char *pszVersion )
{
PRINT_DEBUG("SteamInternal_FindOrCreateGameServerInterface %i %s\n", hSteamUser, pszVersion);
return get_steam_client()->GetISteamGenericInterface(hSteamUser, SteamGameServer_GetHSteamPipe(), pszVersion);
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------//
// steamclient.dll private wrapper functions
//
// The following functions are part of abstracting API access to the steamclient.dll, but should only be used in very specific cases
//----------------------------------------------------------------------------------------------------------------------------------------------------------//
// SteamAPI_IsSteamRunning() returns true if Steam is currently running
S_API bool S_CALLTYPE SteamAPI_IsSteamRunning()
{
PRINT_DEBUG("SteamAPI_IsSteamRunning\n");
return true;
}
// Pumps out all the steam messages, calling registered callbacks.
// NOT THREADSAFE - do not call from multiple threads simultaneously.
S_API void Steam_RunCallbacks( HSteamPipe hSteamPipe, bool bGameServerCallbacks )
{
PRINT_DEBUG("Steam_RunCallbacks\n");
SteamAPI_RunCallbacks();
if (bGameServerCallbacks)
SteamGameServer_RunCallbacks();
}
// register the callback funcs to use to interact with the steam dll
S_API void Steam_RegisterInterfaceFuncs( void *hModule )
{
PRINT_DEBUG("Steam_RegisterInterfaceFuncs\n");
}
// returns the HSteamUser of the last user to dispatch a callback
S_API HSteamUser Steam_GetHSteamUserCurrent()
{
PRINT_DEBUG("Steam_GetHSteamUserCurrent\n");
//TODO?
return CLIENT_HSTEAMUSER;
}
// returns the filename path of the current running Steam process, used if you need to load an explicit steam dll by name.
// DEPRECATED - implementation is Windows only, and the path returned is a UTF-8 string which must be converted to UTF-16 for use with Win32 APIs
S_API const char *SteamAPI_GetSteamInstallPath()
{
PRINT_DEBUG("SteamAPI_GetSteamInstallPath\n");
static char steam_folder[1024];
std::string path = Local_Storage::get_program_path();
strcpy(steam_folder, path.c_str());
steam_folder[path.length() - 1] = 0;
return steam_folder;
}
// returns the pipe we are communicating to Steam with
S_API HSteamPipe SteamAPI_GetHSteamPipe()
{
PRINT_DEBUG("SteamAPI_GetHSteamPipe\n");
return CLIENT_STEAM_PIPE;
}
// sets whether or not Steam_RunCallbacks() should do a try {} catch (...) {} around calls to issuing callbacks
S_API void SteamAPI_SetTryCatchCallbacks( bool bTryCatchCallbacks )
{
PRINT_DEBUG("SteamAPI_SetTryCatchCallbacks\n");
}
// backwards compat export, passes through to SteamAPI_ variants
S_API HSteamPipe GetHSteamPipe()
{
PRINT_DEBUG("GetHSteamPipe\n");
return CLIENT_STEAM_PIPE;
}
S_API HSteamUser GetHSteamUser()
{
PRINT_DEBUG("GetHSteamUser\n");
return CLIENT_HSTEAMUSER;
}
// exists only for backwards compat with code written against older SDKs
S_API bool S_CALLTYPE SteamAPI_InitSafe()
{
PRINT_DEBUG("SteamAPI_InitSafe\n");
SteamAPI_Init();
return true;
}
S_API ISteamClient *SteamClient() {
PRINT_DEBUG("SteamClient()\n");
load_old_interface_versions();
return (ISteamClient *)SteamInternal_CreateInterface(old_client);
}
S_API ISteamUser *SteamUser() { PRINT_DEBUG("SteamUser()\n");return get_steam_client_old()->GetISteamUser(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_user); }
S_API ISteamFriends *SteamFriends() { PRINT_DEBUG("SteamFriends()\n");return get_steam_client_old()->GetISteamFriends(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_friends ); }
S_API ISteamUtils *SteamUtils() { PRINT_DEBUG("SteamUtils()\n");return get_steam_client_old()->GetISteamUtils(SteamAPI_GetHSteamPipe(), old_utils); }
S_API ISteamMatchmaking *SteamMatchmaking() { PRINT_DEBUG("SteamMatchmaking()\n");return get_steam_client_old()->GetISteamMatchmaking(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_matchmaking); }
S_API ISteamUserStats *SteamUserStats() { PRINT_DEBUG("SteamUserStats()\n");return get_steam_client_old()->GetISteamUserStats(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_userstats); }
S_API ISteamApps *SteamApps() { PRINT_DEBUG("SteamApps()\n");return get_steam_client_old()->GetISteamApps(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_apps); }
S_API ISteamMatchmakingServers *SteamMatchmakingServers() { PRINT_DEBUG("SteamMatchmakingServers()\n");return get_steam_client_old()->GetISteamMatchmakingServers(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_matchmaking_servers); }
S_API ISteamNetworking *SteamNetworking() { PRINT_DEBUG("SteamNetworking()\n");return get_steam_client_old()->GetISteamNetworking(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_networking); }
S_API ISteamRemoteStorage *SteamRemoteStorage() { PRINT_DEBUG("SteamRemoteStorage()\n");return get_steam_client_old()->GetISteamRemoteStorage(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_remote_storage_interface); }
S_API ISteamScreenshots *SteamScreenshots() { PRINT_DEBUG("SteamScreenshots()\n");return get_steam_client_old()->GetISteamScreenshots(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_screenshots); }
S_API ISteamHTTP *SteamHTTP() { PRINT_DEBUG("SteamHTTP()\n");return get_steam_client_old()->GetISteamHTTP(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_http); }
S_API ISteamController *SteamController() { PRINT_DEBUG("SteamController()\n");return get_steam_client_old()->GetISteamController(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_controller); }
S_API ISteamUGC *SteamUGC() { PRINT_DEBUG("SteamUGC()\n");return get_steam_client_old()->GetISteamUGC(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_ugc_interface ); }
S_API ISteamAppList *SteamAppList() { PRINT_DEBUG("SteamAppList()\n");return get_steam_client_old()->GetISteamAppList(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_applist); }
S_API ISteamMusic *SteamMusic() { PRINT_DEBUG("SteamMusic()\n");return get_steam_client_old()->GetISteamMusic(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_music); }
S_API ISteamMusicRemote *SteamMusicRemote() { PRINT_DEBUG("SteamMusicRemote()\n");return get_steam_client_old()->GetISteamMusicRemote(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_music_remote); }
S_API ISteamHTMLSurface *SteamHTMLSurface() { PRINT_DEBUG("SteamHTMLSurface()\n");return get_steam_client_old()->GetISteamHTMLSurface(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_html_surface); }
S_API ISteamInventory *SteamInventory() { PRINT_DEBUG("SteamInventory()\n");return get_steam_client_old()->GetISteamInventory(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_inventory); }
S_API ISteamVideo *SteamVideo() { PRINT_DEBUG("SteamVideo()\n");return get_steam_client_old()->GetISteamVideo(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_video); }
S_API ISteamParentalSettings *SteamParentalSettings() { PRINT_DEBUG("SteamParentalSettings()\n");return get_steam_client_old()->GetISteamParentalSettings(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), ""); }
//Gameserver stuff
S_API void * S_CALLTYPE SteamGameServerInternal_CreateInterface( const char *ver )
{
PRINT_DEBUG("SteamGameServerInternal_CreateInterface %s\n", ver);
return SteamInternal_CreateInterface(ver);
}
S_API HSteamPipe S_CALLTYPE SteamGameServer_GetHSteamPipe()
{
PRINT_DEBUG("SteamGameServer_GetHSteamPipe\n");
return SERVER_STEAM_PIPE;
}
S_API HSteamUser S_CALLTYPE SteamGameServer_GetHSteamUser()
{
PRINT_DEBUG("SteamGameServer_GetHSteamUser\n");
return SERVER_HSTEAMUSER;
}
S_API bool S_CALLTYPE SteamGameServer_InitSafe(uint32 unIP, uint16 usSteamPort, uint16 usGamePort, uint16 usQueryPort, EServerMode eServerMode, const char *pchVersionString )
{
return SteamInternal_GameServer_Init( unIP, usSteamPort, usGamePort, usQueryPort, eServerMode, pchVersionString );
}
S_API bool S_CALLTYPE SteamInternal_GameServer_Init( uint32 unIP, uint16 usPort, uint16 usGamePort, uint16 usQueryPort, EServerMode eServerMode, const char *pchVersionString )
{
PRINT_DEBUG("SteamInternal_GameServer_Init %u %hu %hu %hu %u %s\n", unIP, usPort, usGamePort, usQueryPort, eServerMode, pchVersionString);
SteamGameServerClient();
get_steam_client()->serverInit();
return get_steam_client()->GetISteamGameServer(SERVER_HSTEAMUSER, SERVER_STEAM_PIPE, /* TODO: figure out right interface version */old_gameserver)->InitGameServer(unIP, usGamePort, usQueryPort, eServerMode, 0, pchVersionString);
}
S_API bool SteamGameServer_Init( uint32 unIP, uint16 usSteamPort, uint16 usGamePort, uint16 usQueryPort, EServerMode eServerMode, const char *pchVersionString )
{
return SteamInternal_GameServer_Init( unIP, usSteamPort, usGamePort, usQueryPort, eServerMode, pchVersionString );
}
S_API void SteamGameServer_Shutdown()
{
PRINT_DEBUG("SteamGameServer_Shutdown\n");
get_steam_clientserver_old()->serverShutdown();
}
S_API void SteamGameServer_RunCallbacks()
{
PRINT_DEBUG("SteamGameServer_RunCallbacks\n");
get_steam_client()->RunCallbacks(false, true);
}
S_API bool SteamGameServer_BSecure()
{
PRINT_DEBUG("SteamGameServer_BSecure\n");
return true;
}
S_API uint64 SteamGameServer_GetSteamID()
{
PRINT_DEBUG("SteamGameServer_GetSteamID\n");
ISteamGameServer *gs = get_steam_client()->GetISteamGameServer(SERVER_HSTEAMUSER, SERVER_STEAM_PIPE, /* TODO: figure out right interface version */old_gameserver);
if (!gs) return 0;
return gs->GetSteamID().ConvertToUint64();
}
S_API ISteamClient *SteamGameServerClient() {
PRINT_DEBUG("SteamGameServerClient()\n");
load_old_interface_versions();
get_steam_clientserver_old();
return (ISteamClient *)SteamInternal_CreateInterface(old_client);
}
S_API ISteamGameServer *SteamGameServer() { PRINT_DEBUG("SteamGameServer()\n"); return get_steam_clientserver_old()->GetISteamGameServer(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_gameserver ); }
S_API ISteamUtils *SteamGameServerUtils() { PRINT_DEBUG("SteamGameServerUtils()\n"); return get_steam_clientserver_old()->GetISteamUtils(SteamGameServer_GetHSteamPipe(), old_utils ); }
S_API ISteamNetworking *SteamGameServerNetworking() { PRINT_DEBUG("SteamGameServerNetworking()\n"); return get_steam_clientserver_old()->GetISteamNetworking(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_networking ); }
S_API ISteamGameServerStats *SteamGameServerStats() { PRINT_DEBUG("SteamGameServerStats()\n"); return get_steam_clientserver_old()->GetISteamGameServerStats(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_gameserver_stats ); }
S_API ISteamHTTP *SteamGameServerHTTP() { PRINT_DEBUG("SteamGameServerHTTP()\n"); return get_steam_clientserver_old()->GetISteamHTTP(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_http ); }
S_API ISteamInventory *SteamGameServerInventory() { PRINT_DEBUG("SteamGameServerInventory()\n"); return get_steam_clientserver_old()->GetISteamInventory(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_inventory ); }
S_API ISteamUGC *SteamGameServerUGC() { PRINT_DEBUG("SteamGameServerUGC()\n"); return get_steam_clientserver_old()->GetISteamUGC(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_ugc_interface ); }
S_API ISteamApps *SteamGameServerApps() { PRINT_DEBUG("SteamGameServerApps()\n"); return get_steam_clientserver_old()->GetISteamApps(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_apps ); }
S_API ISteamMasterServerUpdater *SteamMasterServerUpdater() {PRINT_DEBUG("SteamMasterServerUpdater()\n"); return get_steam_clientserver_old()->GetISteamMasterServerUpdater(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_masterserver_updater); }
S_API uint32 SteamGameServer_GetIPCCallCount()
{
return get_steam_client()->GetIPCCallCount();
}
S_API void S_CALLTYPE SteamAPI_UseBreakpadCrashHandler( char const *pchVersion, char const *pchDate, char const *pchTime, bool bFullMemoryDumps, void *pvContext, PFNPreMinidumpCallback m_pfnPreMinidumpCallback )
{
}
S_API void S_CALLTYPE SteamAPI_SetBreakpadAppID( uint32 unAppID )
{
}
//VR stuff
S_API void *VR_Init(int *error, int type)
{
if (error) *error = 108; //HmdError_Init_HmdNotFound
return NULL;
}
S_API void *VR_GetGenericInterface( const char *pchInterfaceVersion, int *peError )
{
return NULL;
}
S_API const char *VR_GetStringForHmdError( int error )
{
return "";
}
S_API bool VR_IsHmdPresent()
{
return false;
}
S_API void VR_Shutdown()
{
}
S_API bool SteamAPI_RestartApp( uint32 appid )
{
PRINT_DEBUG("SteamAPI_RestartApp %u\n", appid);
return SteamAPI_RestartAppIfNecessary(appid);
}
//OLD steam_c stuff
/*
ISteamApps_BIsCybercafe
ISteamApps_BIsLowViolence
ISteamApps_BIsSubscribed
ISteamApps_BIsSubscribedApp
ISteamApps_GetAvailableGameLanguages
ISteamApps_GetCurrentGameLanguage
ISteamClient_SetLocalIPBinding
ISteamGameServer_BLoggedOn
ISteamGameServer_BSecure
ISteamGameServer_BUpdateUserData
ISteamGameServer_CreateUnauthenticatedUserConnection
ISteamGameServer_GetSteamID
ISteamGameServer_SendUserConnectAndAuthenticate
ISteamGameServer_SendUserDisconnect
ISteamGameServer_SetGameType
ISteamGameServer_SetServerType
ISteamGameServer_UpdateSpectatorPort
ISteamGameServer_UpdateStatus
S_API bool ISteamMasterServerUpdater_AddMasterServer
ISteamMasterServerUpdater_ClearAllKeyValues
ISteamMasterServerUpdater_ForceHeartbeat
ISteamMasterServerUpdater_GetMasterServerAddress
ISteamMasterServerUpdater_GetNextOutgoingPacket
ISteamMasterServerUpdater_GetNumMasterServers
ISteamMasterServerUpdater_HandleIncomingPacket
ISteamMasterServerUpdater_NotifyShutdown
ISteamMasterServerUpdater_RemoveMasterServer
ISteamMasterServerUpdater_SetActive
ISteamMasterServerUpdater_SetBasicServerData
ISteamMasterServerUpdater_SetHeartbeatInterval
ISteamMasterServerUpdater_SetKeyValue
ISteamMasterServerUpdater_WasRestartRequested
ISteamUser_BLoggedOn
ISteamUser_InitiateGameConnection
ISteamUser_TerminateGameConnection
ISteamUtils_GetAppID
SteamContentServer
SteamContentServerUtils
SteamContentServer_Init
SteamContentServer_RunCallbacks
SteamContentServer_Shutdown
SteamGameServer_BSecure
SteamGameServer_GetHSteamPipe
SteamGameServer_GetHSteamUser
SteamGameServer_GetIPCCallCount
SteamGameServer_GetSteamID
SteamGameServer_Init
SteamGameServer_InitSafe
SteamGameServer_RunCallbacks
SteamGameServer_Shutdown
SteamMasterServerUpdater
S_API bool Steam_BGetCallback( HSteamPipe hSteamPipe, CallbackMsg_t *pCallbackMsg )
{
return false;
}
S_API void Steam_FreeLastCallback( HSteamPipe hSteamPipe )
{
}
S_API bool Steam_GetAPICallResult( HSteamPipe hSteamPipe, SteamAPICall_t hSteamAPICall, void* pCallback, int cubCallback, int iCallbackExpected, bool* pbFailed )
{
return false;
}
*/

52
dll/dll.h Normal file
View File

@ -0,0 +1,52 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "steam_client.h"
Steam_Client *get_steam_client();
S_API ISteamClient *SteamClient();
S_API ISteamUser *SteamUser();
S_API ISteamFriends *SteamFriends();
S_API ISteamUtils *SteamUtils();
S_API ISteamMatchmaking *SteamMatchmaking();
S_API ISteamUserStats *SteamUserStats();
S_API ISteamApps *SteamApps();
S_API ISteamNetworking *SteamNetworking();
S_API ISteamMatchmakingServers *SteamMatchmakingServers();
S_API ISteamRemoteStorage *SteamRemoteStorage();
S_API ISteamScreenshots *SteamScreenshots();
S_API ISteamHTTP *SteamHTTP();
S_API ISteamController *SteamController();
S_API ISteamUGC *SteamUGC();
S_API ISteamAppList *SteamAppList();
S_API ISteamMusic *SteamMusic();
S_API ISteamMusicRemote *SteamMusicRemote();
S_API ISteamHTMLSurface *SteamHTMLSurface();
S_API ISteamInventory *SteamInventory();
S_API ISteamVideo *SteamVideo();
S_API ISteamParentalSettings *SteamParentalSettings();
S_API ISteamClient *SteamGameServerClient();
S_API ISteamGameServer *SteamGameServer();
S_API ISteamUtils *SteamGameServerUtils();
S_API ISteamNetworking *SteamGameServerNetworking();
S_API ISteamGameServerStats *SteamGameServerStats();
S_API ISteamHTTP *SteamGameServerHTTP();
S_API ISteamInventory *SteamGameServerInventory();
S_API ISteamUGC *SteamGameServerUGC();
S_API ISteamApps *SteamGameServerApps();

4649
dll/flat.cpp Normal file

File diff suppressed because it is too large Load Diff

656
dll/local_storage.cpp Normal file
View File

@ -0,0 +1,656 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "local_storage.h"
#include <fstream>
#ifdef NO_DISK_WRITES
std::string Local_Storage::get_program_path()
{
return " ";
}
std::string Local_Storage::get_user_appdata_path()
{
return " ";
}
std::string Local_Storage::get_game_settings_path()
{
return " ";
}
std::string Local_Storage::get_path(std::string folder)
{
return "";
}
Local_Storage::Local_Storage(std::string save_directory)
{
}
void Local_Storage::setAppId(uint32 appid)
{
}
int Local_Storage::store_file_data(std::string folder, std::string file, char *data, unsigned int length)
{
return -1;
}
int Local_Storage::store_data(std::string folder, std::string file, char *data, unsigned int length)
{
return -1;
}
int Local_Storage::store_data_settings(std::string file, char *data, unsigned int length)
{
return -1;
}
int Local_Storage::get_file_data(std::string full_path, char *data, unsigned int max_length)
{
return -1;
}
int Local_Storage::get_data(std::string folder, std::string file, char *data, unsigned int max_length)
{
return -1;
}
int Local_Storage::get_data_settings(std::string file, char *data, unsigned int max_length)
{
return 0;
}
int Local_Storage::count_files(std::string folder)
{
return 0;
}
bool Local_Storage::file_exists(std::string folder, std::string file)
{
return false;
}
unsigned int Local_Storage::file_size(std::string folder, std::string file)
{
return 0;
}
bool Local_Storage::file_delete(std::string folder, std::string file)
{
return false;
}
uint64_t Local_Storage::file_timestamp(std::string folder, std::string file)
{
return 0;
}
bool Local_Storage::iterate_file(std::string folder, int index, char *output_filename, int32 *output_size)
{
return false;
}
bool Local_Storage::update_save_filenames(std::string folder)
{
return true;
}
std::vector<std::string> Local_Storage::get_filenames_path(std::string path)
{
return std::vector<std::string>();
}
#else
#if defined(WIN32) || defined(_WIN32)
#include <windows.h>
static BOOL DirectoryExists(LPCSTR szPath)
{
DWORD dwAttrib = GetFileAttributesA(szPath);
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
static void createDirectoryRecursively(std::string path)
{
unsigned long long pos = 0;
do
{
pos = path.find_first_of("\\/", pos + 1);
CreateDirectoryA(path.substr(0, pos).c_str(), NULL);
} while (pos != std::string::npos);
}
static void create_directory(std::string strPath)
{
if (DirectoryExists(strPath.c_str()) == FALSE)
createDirectoryRecursively(strPath);
}
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
static int count_files_directory(std::string path)
{
path = path.append("*");
int counter = 0;
WIN32_FIND_DATAA ffd;
HANDLE hFind = INVALID_HANDLE_VALUE;
// Start iterating over the files in the path directory.
hFind = ::FindFirstFileA (path.c_str(), &ffd);
if (hFind != INVALID_HANDLE_VALUE)
{
do // Managed to locate and create an handle to that folder.
{
if (strcmp(".", ffd.cFileName) == 0) continue;
if (strcmp("..", ffd.cFileName) == 0) continue;
counter++;
} while (::FindNextFileA(hFind, &ffd) == TRUE);
::FindClose(hFind);
} else {
//printf("Failed to find path: %s", path.c_str());
}
return counter;
}
static bool get_filename_at_index(std::string strPath, char *filename, int index)
{
strPath = strPath.append("\\*");
int counter = 0;
WIN32_FIND_DATAA ffd;
HANDLE hFind = INVALID_HANDLE_VALUE;
// Start iterating over the files in the path directory.
hFind = ::FindFirstFileA (strPath.c_str(), &ffd);
if (hFind != INVALID_HANDLE_VALUE)
{
do // Managed to locate and create an handle to that folder.
{
if (strcmp(".", ffd.cFileName) == 0) continue;
if (strcmp("..", ffd.cFileName) == 0) continue;
if (counter == index) {
memcpy(filename, ffd.cFileName, MAX_PATH);
::FindClose(hFind);
return true;
}
counter++;
} while (::FindNextFileA(hFind, &ffd) == TRUE);
::FindClose(hFind);
} else {
printf("Failed to find path: %s", strPath.c_str());
}
return false;
}
static std::vector<std::string> get_filenames(std::string strPath)
{
std::vector<std::string> output;
strPath = strPath.append("\\*");
WIN32_FIND_DATAA ffd;
HANDLE hFind = INVALID_HANDLE_VALUE;
// Start iterating over the files in the path directory.
hFind = ::FindFirstFileA (strPath.c_str(), &ffd);
if (hFind != INVALID_HANDLE_VALUE)
{
do // Managed to locate and create an handle to that folder.
{
if (strcmp(".", ffd.cFileName) == 0) continue;
if (strcmp("..", ffd.cFileName) == 0) continue;
output.push_back(ffd.cFileName);
} while (::FindNextFileA(hFind, &ffd) == TRUE);
::FindClose(hFind);
} else {
printf("Failed to find path: %s", strPath.c_str());
}
return output;
}
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <dirent.h>
#define PATH_MAX_STRING_SIZE 512
/* recursive mkdir */
static int mkdir_p(const char *dir, const mode_t mode) {
char tmp[PATH_MAX_STRING_SIZE];
char *p = NULL;
struct stat sb;
size_t len;
/* copy path */
len = strnlen (dir, PATH_MAX_STRING_SIZE);
if (len == 0 || len == PATH_MAX_STRING_SIZE) {
return -1;
}
memcpy (tmp, dir, len);
tmp[len] = '\0';
/* remove trailing slash */
if(tmp[len - 1] == '/') {
tmp[len - 1] = '\0';
}
/* check if path exists and is a directory */
if (stat (tmp, &sb) == 0) {
if (S_ISDIR (sb.st_mode)) {
return 0;
}
}
/* recursive mkdir */
for(p = tmp + 1; *p; p++) {
if(*p == '/') {
*p = 0;
/* test path */
if (stat(tmp, &sb) != 0) {
/* path does not exist - create directory */
if (mkdir(tmp, mode) < 0) {
return -1;
}
} else if (!S_ISDIR(sb.st_mode)) {
/* not a directory */
return -1;
}
*p = '/';
}
}
/* test path */
if (stat(tmp, &sb) != 0) {
/* path does not exist - create directory */
if (mkdir(tmp, mode) < 0) {
return -1;
}
} else if (!S_ISDIR(sb.st_mode)) {
/* not a directory */
return -1;
}
return 0;
}
static void create_directory(std::string strPath)
{
mkdir_p(strPath.c_str(), 0777);
}
static int count_files_directory(std::string strPath)
{
DIR *dp;
int i = 0;
struct dirent *ep;
dp = opendir (strPath.c_str());
if (dp != NULL)
{
while ((ep = readdir (dp)))
i++;
(void) closedir (dp);
}
if (i < 2) i = 2;
return i - 2;
}
//filename should be 256 big
static bool get_filename_at_index(std::string strPath, char *filename, int index)
{
DIR *dp;
int i = 0;
struct dirent *ep;
dp = opendir (strPath.c_str());
if (dp != NULL)
{
while ((ep = readdir (dp))) {
if (memcmp(ep->d_name, ".", 2) != 0 && memcmp(ep->d_name, "..", 3) != 0) {
if (i == (index)) {
memcpy(filename, ep->d_name, 256);
(void) closedir (dp);
return true;
}
i++;
}
}
(void) closedir (dp);
}
return false;
}
static std::vector<std::string> get_filenames(std::string strPath)
{
DIR *dp;
int i = 0;
struct dirent *ep;
std::vector<std::string> output;
dp = opendir (strPath.c_str());
if (dp != NULL)
{
while ((ep = readdir (dp))) {
if (memcmp(ep->d_name, ".", 2) != 0 && memcmp(ep->d_name, "..", 3) != 0) {
output.push_back(ep->d_name);
i++;
}
}
(void) closedir (dp);
}
return output;
}
#endif
std::string Local_Storage::get_program_path()
{
return get_full_program_path();
}
std::string Local_Storage::get_game_settings_path()
{
return get_program_path().append(GAME_SETTINGS_FOLDER).append(PATH_SEPARATOR);
}
#if defined(STEAM_WIN32)
#include <shlobj.h>
#include <sstream>
#endif
std::string Local_Storage::get_user_appdata_path()
{
std::string user_appdata_path = "SAVE";
#if defined(STEAM_WIN32)
CHAR szPath[MAX_PATH] = {};
HRESULT hr = SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, 0, szPath);
if (SUCCEEDED(hr)) {
user_appdata_path = szPath;
}
#else
char *homedir = getenv("HOME");
if (homedir) {
user_appdata_path = homedir;
}
#endif
return user_appdata_path.append(PATH_SEPARATOR).append(PROGRAM_NAME).append(" Saves");
}
static std::string replace_with(std::string s, std::string old, char *new_str)
{
int pos;
while ((pos = s.find(old)) != std::string::npos)
s.replace(pos, old.length(), new_str);
return s;
}
static std::string sanitize_file_name(std::string name)
{
//I'm not sure all of these are necessary but just to be sure
if (name[0] == '.' && name.size() > 2 && (name[1] == '\\' || name[1] == '/')) name.erase(0, 2);
name = replace_with(name, PATH_SEPARATOR, ".SLASH.");
name = replace_with(name, "\\", ".SLASH.");
name = replace_with(name, "/", ".SLASH.");
name = replace_with(name, "|", ".V_SLASH.");
name = replace_with(name, ":", ".COLON.");
name = replace_with(name, "*", ".ASTERISK.");
name = replace_with(name, "\"", ".QUOTE.");
name = replace_with(name, "?", ".Q_MARK.");
return name;
}
static std::string desanitize_file_name(std::string name)
{
//I'm not sure all of these are necessary but just to be sure
name = replace_with(name, ".SLASH.", "/");
name = replace_with(name, ".B_SLASH.", "\\");
name = replace_with(name, ".F_SLASH.", "/");
name = replace_with(name, ".V_SLASH.", "|");
name = replace_with(name, ".COLON.", ":");
name = replace_with(name, ".ASTERISK.", "*");
name = replace_with(name, ".QUOTE.", "\"");
name = replace_with(name, ".Q_MARK.", "?");
return name;
}
Local_Storage::Local_Storage(std::string save_directory)
{
this->save_directory = save_directory;
if (this->save_directory.back() != *PATH_SEPARATOR) {
this->save_directory.append(PATH_SEPARATOR);
}
}
void Local_Storage::setAppId(uint32 appid)
{
this->appid = std::to_string(appid) + PATH_SEPARATOR;
}
int Local_Storage::store_file_data(std::string folder, std::string file, char *data, unsigned int length)
{
if (folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
file = sanitize_file_name(file);
create_directory(folder);
std::ofstream myfile;
myfile.open(folder + file, std::ios::binary | std::ios::out);
if (!myfile.is_open()) return -1;
myfile.write(data, length);
int pos = myfile.tellp();
myfile.close();
return pos;
}
std::string Local_Storage::get_path(std::string folder)
{
std::string path = save_directory + appid + folder;
create_directory(path);
return path;
}
std::vector<std::string> Local_Storage::get_filenames_path(std::string path)
{
if (path.back() != *PATH_SEPARATOR) {
path.append(PATH_SEPARATOR);
}
return get_filenames(path);
}
int Local_Storage::store_data(std::string folder, std::string file, char *data, unsigned int length)
{
if (folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
return store_file_data(save_directory + appid + folder, file, data, length);
}
int Local_Storage::store_data_settings(std::string file, char *data, unsigned int length)
{
return store_file_data(save_directory + SETTINGS_STORAGE_FOLDER, file, data, length);
}
int Local_Storage::get_file_data(std::string full_path, char *data, unsigned int max_length)
{
std::ifstream myfile;
myfile.open(full_path, std::ios::binary | std::ios::in);
if (!myfile.is_open()) return -1;
std::streampos size = myfile.tellg();
myfile.seekg (0, std::ios::beg);
if (size > max_length) max_length = size;
myfile.read (data, max_length);
myfile.close();
return myfile.gcount();
}
int Local_Storage::get_data(std::string folder, std::string file, char *data, unsigned int max_length)
{
file = sanitize_file_name(file);
if (folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
std::string full_path = save_directory + appid + folder + file;
return get_file_data(full_path, data, max_length);
}
int Local_Storage::get_data_settings(std::string file, char *data, unsigned int max_length)
{
file = sanitize_file_name(file);
std::string full_path = save_directory + SETTINGS_STORAGE_FOLDER + PATH_SEPARATOR + file;
return get_file_data(full_path, data, max_length);
}
int Local_Storage::count_files(std::string folder)
{
if (folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
return count_files_directory(save_directory + appid + folder);
}
bool Local_Storage::file_exists(std::string folder, std::string file)
{
file = sanitize_file_name(file);
if (folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
std::string full_path = save_directory + appid + folder + file;
struct stat buffer;
return (stat (full_path.c_str(), &buffer) == 0);
}
unsigned int Local_Storage::file_size(std::string folder, std::string file)
{
file = sanitize_file_name(file);
if (folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
std::string full_path = save_directory + appid + folder + file;
struct stat buffer = {};
if (stat (full_path.c_str(), &buffer) != 0) return 0;
return buffer.st_size;
}
bool Local_Storage::file_delete(std::string folder, std::string file)
{
file = sanitize_file_name(file);
if (folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
std::string full_path = save_directory + appid + folder + file;
return remove(full_path.c_str()) == 0;
}
uint64_t Local_Storage::file_timestamp(std::string folder, std::string file)
{
file = sanitize_file_name(file);
if (folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
std::string full_path = save_directory + appid + folder + file;
struct stat buffer = {};
if (stat (full_path.c_str(), &buffer) != 0) return 0;
return buffer.st_mtime;
}
bool Local_Storage::iterate_file(std::string folder, int index, char *output_filename, int32 *output_size)
{
if (folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
char temp[1024] = {};
if (get_filename_at_index(save_directory + appid + folder, temp, index)) {
std::string name = desanitize_file_name(temp);
if (output_size) *output_size = file_size(folder, name);
strcpy(output_filename, name.c_str());
return true;
}
return false;
}
bool Local_Storage::update_save_filenames(std::string folder)
{
int num_files = count_files(folder);
std::vector<std::string> files;
for (int i = 0; i < num_files; ++i) {
char out_folder[1024];
if (get_filename_at_index(save_directory + appid + folder, out_folder, i)) {
files.push_back(out_folder);
}
}
for (auto &path : files) {
PRINT_DEBUG("Local_Storage:: remote file %s\n", path.c_str());
std::string to = sanitize_file_name(desanitize_file_name(path));
if (path != to) {
std::string from = (save_directory + appid + folder + PATH_SEPARATOR + path);
to = (save_directory + appid + folder + PATH_SEPARATOR + to);
PRINT_DEBUG("Local_Storage::update_save_filenames renaming %s to %s\n", from.c_str(), to.c_str());
if (std::rename(from.c_str(), to.c_str()) < 0) {
PRINT_DEBUG("ERROR RENAMING\n");
}
}
}
return true;
}
#endif

62
dll/local_storage.h Normal file
View File

@ -0,0 +1,62 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
#ifndef LOCAL_STORAGE_INCLUDE
#define LOCAL_STORAGE_INCLUDE
#define SETTINGS_STORAGE_FOLDER "settings"
#define REMOTE_STORAGE_FOLDER "remote"
#define STATS_STORAGE_FOLDER "stats"
#define USER_DATA_FOLDER "userdata"
#define GAME_SETTINGS_FOLDER "steam_settings"
#include <string>
#define MAX_FILENAME_LENGTH 300
class Local_Storage {
std::string save_directory;
std::string appid;
public:
static std::string get_program_path();
static std::string get_game_settings_path();
static std::string get_user_appdata_path();
Local_Storage(std::string save_directory);
static int get_file_data(std::string full_path, char *data, unsigned int max_length);
void setAppId(uint32 appid);
static int store_file_data(std::string folder, std::string file, char *data, unsigned int length);
static std::vector<std::string> get_filenames_path(std::string path);
int store_data(std::string folder, std::string file, char *data, unsigned int length);
int store_data_settings(std::string file, char *data, unsigned int length);
int get_data(std::string folder, std::string file, char *data, unsigned int max_length);
int get_data_settings(std::string file, char *data, unsigned int max_length);
int count_files(std::string folder);
bool iterate_file(std::string folder, int index, char *output_filename, int32 *output_size);
bool file_exists(std::string folder, std::string file);
unsigned int file_size(std::string folder, std::string file);
bool file_delete(std::string folder, std::string file);
uint64_t file_timestamp(std::string folder, std::string file);
std::string get_path(std::string folder);
bool update_save_filenames(std::string folder);
};
#endif

204
dll/net.proto Normal file
View File

@ -0,0 +1,204 @@
syntax = "proto3";
option optimize_for = LITE_RUNTIME;
message Announce {
enum Types {
PING = 0;
PONG = 1;
}
Types type = 1;
repeated uint64 ids = 2;
message Other_Peers {
uint64 id = 1;
uint32 ip = 2;
uint32 udp_port = 3;
uint32 appid = 4;
}
uint32 tcp_port = 3;
repeated Other_Peers peers = 4;
uint32 appid = 5;
}
message Lobby {
uint64 room_id = 1;
uint64 owner = 2;
map<string, bytes> values = 3;
message Member {
uint64 id = 1;
map<string, bytes> values = 2;
}
repeated Member members = 4;
message Gameserver {
uint64 id = 1;
uint32 ip = 2;
uint32 port = 3;
}
Gameserver gameserver = 5;
uint32 member_limit = 6;
uint32 type = 7; //ELobbyType
bool joinable = 8;
uint32 appid = 9;
bool deleted = 32;
uint64 time_deleted = 33;
}
message Lobby_Messages {
uint64 id = 1;
enum Types {
JOIN = 0;
LEAVE = 1;
CHANGE_OWNER = 2;
MEMBER_DATA = 3;
CHAT_MESSAGE = 4;
}
Types type = 2;
uint64 idata = 3;
bytes bdata = 4;
map<string, bytes> map = 5;
}
message Low_Level {
enum Types {
HEARTBEAT = 0;
CONNECT = 1;
DISCONNECT = 2;
}
Types type = 1;
}
message Network {
uint32 channel = 1;
bytes data = 2;
enum Types {
DATA = 0;
CLEAR_BEFORE = 1;
}
Types type = 3;
bool processed = 128;
uint64 time_processed = 129;
}
message Network_Old {
enum Types {
CONNECTION_REQUEST_IP = 0;
CONNECTION_REQUEST_STEAMID = 1;
CONNECTION_ACCEPTED = 2;
CONNECTION_END = 3;
DATA = 4;
}
Types type = 1;
uint64 connection_id = 2;
uint64 connection_id_from = 3;
uint32 port = 4;
bytes data = 5;
}
message Networking_Sockets {
enum Types {
CONNECTION_REQUEST_IP = 0;
CONNECTION_REQUEST_STEAMID = 1;
CONNECTION_ACCEPTED = 2;
CONNECTION_END = 3;
DATA = 4;
}
Types type = 1;
uint32 port = 2;
uint64 connection_id = 3;
uint64 connection_id_from = 4;
bytes data = 5;
}
message Gameserver {
uint64 id = 1;
bytes game_description = 2;
bytes mod_dir = 3;
bool dedicated_server = 4;
uint32 max_player_count = 5;
uint32 bot_player_count = 6;
bytes server_name = 7;
bytes map_name = 8;
bool password_protected = 9;
uint32 spectator_port = 10;
bytes spectator_server_name = 11;
map<string, bytes> values = 12;
bytes tags = 13;
bytes gamedata = 14;
bytes region = 15;
bytes product = 16;
bool secure = 17;
uint32 num_players = 18;
uint32 version = 19;
uint32 ip = 32;
uint32 port = 33;
uint32 query_port = 34;
uint32 appid = 35;
bool offline = 48;
}
message Friend {
uint64 id = 1;
bytes name = 2;
map<string, bytes> rich_presence = 3;
uint32 appid = 4;
uint64 lobby_id = 5;
}
message Auth_Ticket {
uint32 number = 1;
enum Types {
CANCEL = 0;
}
Types type = 2;
}
message Friend_Messages {
enum Types {
LOBBY_INVITE = 0;
}
Types type = 1;
uint64 lobby_id = 2;
}
message Common_Message {
uint64 source_id = 1;
uint64 dest_id = 2;
oneof messages {
Announce announce = 3;
Low_Level low_level = 4;
Lobby lobby = 5;
Lobby_Messages lobby_messages = 6;
Network network = 7;
Gameserver gameserver = 8;
Friend friend = 9;
Auth_Ticket auth_ticket = 10;
Friend_Messages friend_messages = 11;
Network_Old network_old = 12;
Networking_Sockets networking_sockets = 13;
}
uint32 source_ip = 128;
uint32 source_port = 129;
}

1163
dll/network.cpp Normal file

File diff suppressed because it is too large Load Diff

139
dll/network.h Normal file
View File

@ -0,0 +1,139 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
#ifndef NETWORK_INCLUDE
#define NETWORK_INCLUDE
#include "net.pb.h"
#include <chrono>
inline bool protobuf_message_equal(const google::protobuf::MessageLite& msg_a,
const google::protobuf::MessageLite& msg_b) {
return (msg_a.GetTypeName() == msg_b.GetTypeName()) &&
(msg_a.SerializeAsString() == msg_b.SerializeAsString());
}
#define DEFAULT_PORT 47584
#if defined(STEAM_WIN32)
typedef unsigned int sock_t;
#else
typedef int sock_t;
#endif
bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout);
struct IP_PORT {
uint32 ip;
uint16 port;
};
struct Network_Callback {
void (*message_callback)(void *object, Common_Message *msg);
void *object;
CSteamID steam_id;
};
enum Callback_Ids {
CALLBACK_ID_USER_STATUS,
CALLBACK_ID_LOBBY,
CALLBACK_ID_NETWORKING,
CALLBACK_ID_GAMESERVER,
CALLBACK_ID_FRIEND,
CALLBACK_ID_AUTH_TICKET,
CALLBACK_ID_FRIEND_MESSAGES,
CALLBACK_ID_NETWORKING_SOCKETS,
CALLBACK_IDS_MAX
};
struct Network_Callback_Container {
std::vector<struct Network_Callback> callbacks;
};
struct TCP_Socket {
sock_t sock = ~0;
bool received_data = false;
std::vector<char> recv_buffer;
std::vector<char> send_buffer;
std::chrono::high_resolution_clock::time_point last_heartbeat_sent, last_heartbeat_received;
};
struct Connection {
struct TCP_Socket tcp_socket_outgoing, tcp_socket_incoming;
bool connected = false;
IP_PORT udp_ip_port;
bool udp_pinged = false;
IP_PORT tcp_ip_port;
std::vector<CSteamID> ids;
uint32 appid;
std::chrono::high_resolution_clock::time_point last_received;
};
class Networking {
bool enabled = false;
bool alive;
std::chrono::high_resolution_clock::time_point last_run;
sock_t udp_socket, tcp_socket;
uint16 udp_port, tcp_port;
uint32 own_ip;
std::vector<struct Connection> connections;
struct Connection *find_connection(CSteamID id, uint32 appid = 0);
struct Connection *new_connection(CSteamID id, uint32 appid);
bool handle_announce(Common_Message *msg, IP_PORT ip_port);
bool handle_low_level_udp(Common_Message *msg, IP_PORT ip_port);
bool handle_tcp(Common_Message *msg, struct TCP_Socket &socket);
void send_announce_broadcasts();
std::vector<CSteamID> ids;
uint32 appid;
std::chrono::high_resolution_clock::time_point last_broadcast;
std::vector<struct TCP_Socket> accepted;
std::recursive_mutex mutex;
struct Network_Callback_Container callbacks[CALLBACK_IDS_MAX];
std::vector<Common_Message> local_send;
bool add_id_connection(struct Connection *connection, CSteamID steam_id);
void run_callbacks(Callback_Ids id, Common_Message *msg);
void run_callback_user(CSteamID steam_id, bool online, uint32 appid);
void do_callbacks_message(Common_Message *msg);
Common_Message create_announce(bool request);
public:
Networking(CSteamID id, uint32 appid, uint16 port);
void addListenId(CSteamID id);
void setAppID(uint32 appid);
void Run();
bool sendTo(Common_Message *msg, bool reliable, Connection *conn = NULL);
bool sendToAllIndividuals(Common_Message *msg, bool reliable);
bool sendToAll(Common_Message *msg, bool reliable);
bool sendToIPPort(Common_Message *msg, uint32 ip, uint16 port, bool reliable);
bool setCallback(Callback_Ids id, CSteamID steam_id, void (*message_callback)(void *object, Common_Message *msg), void *object);
uint32 getOwnIP();
void shutDown();
bool isAlive();
};
#endif

4
dll/rtlgenrandom.c Normal file
View File

@ -0,0 +1,4 @@
#include <windows.h>
#define RtlGenRandom SystemFunction036
#define DLLEXPORT __declspec(dllexport)
DLLEXPORT BOOLEAN WINAPI RtlGenRandom(PVOID in, ULONG len) {}

3
dll/rtlgenrandom.def Normal file
View File

@ -0,0 +1,3 @@
LIBRARY advapi32.dll
EXPORTS
SystemFunction036

184
dll/settings.cpp Normal file
View File

@ -0,0 +1,184 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "settings.h"
std::string Settings::sanitize(std::string name)
{
name.erase(std::remove(name.begin(), name.end(), '\n'), name.end());
name.erase(std::remove(name.begin(), name.end(), '\r'), name.end());
for (int i = 0; i < name.size(); ++i) {
if (name[i] >= 'a' && name[i] <= 'z') continue;
if (name[i] >= 'A' && name[i] <= 'Z') continue;
if (name[i] >= '0' && name[i] <= '9') continue;
name[i] = ' ';
}
return name;
}
Settings::Settings(CSteamID steam_id, CGameID game_id, std::string name, std::string language)
{
this->steam_id = steam_id;
this->game_id = game_id;
this->name = sanitize(name);
if (this->name.size() == 0) {
this->name = " ";
}
if (this->name.size() == 1) {
this->name = this->name + " ";
}
auto lang = sanitize(language);
std::transform(lang.begin(), lang.end(), lang.begin(), ::tolower);
lang.erase(std::remove(lang.begin(), lang.end(), ' '), lang.end());
this->language = lang;
this->lobby_id = k_steamIDNil;
this->unlockAllDLCs = true;
}
CSteamID Settings::get_local_steam_id()
{
return steam_id;
}
CGameID Settings::get_local_game_id()
{
return game_id;
}
const char *Settings::get_local_name()
{
return name.c_str();
}
const char *Settings::get_language()
{
return language.c_str();
}
void Settings::set_game_id(CGameID game_id)
{
this->game_id = game_id;
}
void Settings::set_lobby(CSteamID lobby_id)
{
this->lobby_id = lobby_id;
}
CSteamID Settings::get_lobby()
{
return this->lobby_id;
}
void Settings::unlockAllDLC(bool value)
{
this->unlockAllDLCs = value;
}
void Settings::addDLC(AppId_t appID, std::string name, bool available)
{
auto f = std::find_if(DLCs.begin(), DLCs.end(), [&appID](DLC_entry const& item) { return item.appID == appID; });
if (DLCs.end() != f) {
f->name = name;
f->available = available;
return;
}
DLC_entry new_entry;
new_entry.appID = appID;
new_entry.name = name;
new_entry.available = available;
DLCs.push_back(new_entry);
}
void Settings::addMod(PublishedFileId_t id, std::string title, std::string path)
{
auto f = std::find_if(mods.begin(), mods.end(), [&id](Mod_entry const& item) { return item.id == id; });
if (mods.end() != f) {
f->title = title;
f->path = path;
return;
}
Mod_entry new_entry;
new_entry.id = id;
new_entry.title = title;
new_entry.path = path;
mods.push_back(new_entry);
}
Mod_entry Settings::getMod(PublishedFileId_t id)
{
auto f = std::find_if(mods.begin(), mods.end(), [&id](Mod_entry const& item) { return item.id == id; });
if (mods.end() != f) {
return *f;
}
return Mod_entry();
}
bool Settings::isModInstalled(PublishedFileId_t id)
{
auto f = std::find_if(mods.begin(), mods.end(), [&id](Mod_entry const& item) { return item.id == id; });
if (mods.end() != f) {
return true;
}
return false;
}
std::set<PublishedFileId_t> Settings::modSet()
{
std::set<PublishedFileId_t> ret_set;
for (auto & m: mods) {
ret_set.insert(m.id);
}
return ret_set;
}
unsigned int Settings::DLCCount()
{
return this->DLCs.size();
}
bool Settings::hasDLC(AppId_t appID)
{
if (this->unlockAllDLCs) return true;
auto f = std::find_if(DLCs.begin(), DLCs.end(), [&appID](DLC_entry const& item) { return item.appID == appID; });
if (DLCs.end() == f)
return false;
return f->available;
}
bool Settings::getDLC(unsigned int index, AppId_t &appID, bool &available, std::string &name)
{
if (index >= DLCs.size()) return false;
appID = DLCs[index].appID;
available = DLCs[index].available;
name = DLCs[index].name;
return true;
}

70
dll/settings.h Normal file
View File

@ -0,0 +1,70 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
#include <set>
#ifndef SETTINGS_INCLUDE
#define SETTINGS_INCLUDE
struct DLC_entry {
AppId_t appID;
std::string name;
bool available;
};
struct Mod_entry {
PublishedFileId_t id;
std::string title;
std::string path;
};
class Settings {
CSteamID steam_id;
CGameID game_id;
std::string name, language;
CSteamID lobby_id;
bool unlockAllDLCs;
std::vector<struct DLC_entry> DLCs;
std::vector<struct Mod_entry> mods;
public:
static std::string sanitize(std::string name);
Settings(CSteamID steam_id, CGameID game_id, std::string name, std::string language);
CSteamID get_local_steam_id();
CGameID get_local_game_id();
const char *get_local_name();
const char *get_language();
void set_game_id(CGameID game_id);
void set_lobby(CSteamID lobby_id);
CSteamID get_lobby();
//DLC stuff
void unlockAllDLC(bool value);
void addDLC(AppId_t appID, std::string name, bool available);
unsigned int DLCCount();
bool hasDLC(AppId_t appID);
bool getDLC(unsigned int index, AppId_t &appID, bool &available, std::string &name);
//mod stuff
void addMod(PublishedFileId_t id, std::string title, std::string path);
Mod_entry getMod(PublishedFileId_t id);
bool isModInstalled(PublishedFileId_t id);
std::set<PublishedFileId_t> modSet();
};
#endif

339
dll/steam_HTMLsurface.h Normal file
View File

@ -0,0 +1,339 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_HTMLsurface :
public ISteamHTMLSurface001,
public ISteamHTMLSurface002,
public ISteamHTMLSurface003,
public ISteamHTMLSurface004,
public ISteamHTMLSurface
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
public:
Steam_HTMLsurface(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
{
this->settings = settings;
this->network = network;
this->callback_results = callback_results;
this->callbacks = callbacks;
}
// Must call init and shutdown when starting/ending use of the interface
bool Init()
{
PRINT_DEBUG("Steam_HTMLsurface::Init\n");
return true;
}
bool Shutdown()
{
PRINT_DEBUG("Steam_HTMLsurface::Shutdown\n");
return true;
}
// Create a browser object for display of a html page, when creation is complete the call handle
// will return a HTML_BrowserReady_t callback for the HHTMLBrowser of your new browser.
// The user agent string is a substring to be added to the general user agent string so you can
// identify your client on web servers.
// The userCSS string lets you apply a CSS style sheet to every displayed page, leave null if
// you do not require this functionality.
//
// YOU MUST HAVE IMPLEMENTED HANDLERS FOR HTML_BrowserReady_t, HTML_StartRequest_t,
// HTML_JSAlert_t, HTML_JSConfirm_t, and HTML_FileOpenDialog_t! See the CALLBACKS
// section of this interface (AllowStartRequest, etc) for more details. If you do
// not implement these callback handlers, the browser may appear to hang instead of
// navigating to new pages or triggering javascript popups.
//
STEAM_CALL_RESULT( HTML_BrowserReady_t )
SteamAPICall_t CreateBrowser( const char *pchUserAgent, const char *pchUserCSS )
{
PRINT_DEBUG("Steam_HTMLsurface::CreateBrowser\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
HTML_BrowserReady_t data;
data.unBrowserHandle = 1234869;
//callback too?
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));;
}
// Call this when you are done with a html surface, this lets us free the resources being used by it
void RemoveBrowser( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG("Steam_HTMLsurface::RemoveBrowser\n");
}
// Navigate to this URL, results in a HTML_StartRequest_t as the request commences
void LoadURL( HHTMLBrowser unBrowserHandle, const char *pchURL, const char *pchPostData )
{
PRINT_DEBUG("Steam_HTMLsurface::LoadURL %s %s\n", pchURL, pchPostData);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
static char url[256];
strncpy(url, pchURL, sizeof(url));
static char target[] = "_self";
static char title[] = "title";
{
HTML_StartRequest_t data;
data.unBrowserHandle = unBrowserHandle;
data.pchURL = url;
data.pchTarget = target;
data.pchPostData = "";
data.bIsRedirect = false;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1);
}
{
HTML_FinishedRequest_t data;
data.unBrowserHandle = unBrowserHandle;
data.pchURL = url;
data.pchPageTitle = title;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.8);
}
}
// Tells the surface the size in pixels to display the surface
void SetSize( HHTMLBrowser unBrowserHandle, uint32 unWidth, uint32 unHeight )
{
PRINT_DEBUG("Steam_HTMLsurface::SetSize\n");
}
// Stop the load of the current html page
void StopLoad( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG("Steam_HTMLsurface::StopLoad\n");
}
// Reload (most likely from local cache) the current page
void Reload( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG("Steam_HTMLsurface::Reload\n");
}
// navigate back in the page history
void GoBack( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG("Steam_HTMLsurface::GoBack\n");
}
// navigate forward in the page history
void GoForward( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG("Steam_HTMLsurface::GoForward\n");
}
// add this header to any url requests from this browser
void AddHeader( HHTMLBrowser unBrowserHandle, const char *pchKey, const char *pchValue )
{
PRINT_DEBUG("Steam_HTMLsurface::AddHeader\n");
}
// run this javascript script in the currently loaded page
void ExecuteJavascript( HHTMLBrowser unBrowserHandle, const char *pchScript )
{
PRINT_DEBUG("Steam_HTMLsurface::ExecuteJavascript\n");
}
// Mouse click and mouse movement commands
void MouseUp( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton )
{
PRINT_DEBUG("Steam_HTMLsurface::MouseUp\n");
}
void MouseDown( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton )
{
PRINT_DEBUG("Steam_HTMLsurface::MouseDown\n");
}
void MouseDoubleClick( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton )
{
PRINT_DEBUG("Steam_HTMLsurface::MouseDoubleClick\n");
}
// x and y are relative to the HTML bounds
void MouseMove( HHTMLBrowser unBrowserHandle, int x, int y )
{
PRINT_DEBUG("Steam_HTMLsurface::MouseMove\n");
}
// nDelta is pixels of scroll
void MouseWheel( HHTMLBrowser unBrowserHandle, int32 nDelta )
{
PRINT_DEBUG("Steam_HTMLsurface::MouseWheel\n");
}
// keyboard interactions, native keycode is the key code value from your OS
void KeyDown( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers, bool bIsSystemKey = false )
{
PRINT_DEBUG("Steam_HTMLsurface::KeyDown\n");
}
void KeyDown( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers)
{
PRINT_DEBUG("Steam_HTMLsurface::KeyDown old\n");
KeyDown(unBrowserHandle, nNativeKeyCode, eHTMLKeyModifiers, false);
}
void KeyUp( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers )
{
PRINT_DEBUG("Steam_HTMLsurface::KeyUp\n");
}
// cUnicodeChar is the unicode character point for this keypress (and potentially multiple chars per press)
void KeyChar( HHTMLBrowser unBrowserHandle, uint32 cUnicodeChar, EHTMLKeyModifiers eHTMLKeyModifiers )
{
PRINT_DEBUG("Steam_HTMLsurface::KeyChar\n");
}
// programmatically scroll this many pixels on the page
void SetHorizontalScroll( HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll )
{
PRINT_DEBUG("Steam_HTMLsurface::SetHorizontalScroll\n");
}
void SetVerticalScroll( HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll )
{
PRINT_DEBUG("Steam_HTMLsurface::SetVerticalScroll\n");
}
// tell the html control if it has key focus currently, controls showing the I-beam cursor in text controls amongst other things
void SetKeyFocus( HHTMLBrowser unBrowserHandle, bool bHasKeyFocus )
{
PRINT_DEBUG("Steam_HTMLsurface::SetKeyFocus\n");
}
// open the current pages html code in the local editor of choice, used for debugging
void ViewSource( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG("Steam_HTMLsurface::ViewSource\n");
}
// copy the currently selected text on the html page to the local clipboard
void CopyToClipboard( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG("Steam_HTMLsurface::CopyToClipboard\n");
}
// paste from the local clipboard to the current html page
void PasteFromClipboard( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG("Steam_HTMLsurface::PasteFromClipboard\n");
}
// find this string in the browser, if bCurrentlyInFind is true then instead cycle to the next matching element
void Find( HHTMLBrowser unBrowserHandle, const char *pchSearchStr, bool bCurrentlyInFind, bool bReverse )
{
PRINT_DEBUG("Steam_HTMLsurface::Find\n");
}
// cancel a currently running find
void StopFind( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG("Steam_HTMLsurface::StopFind\n");
}
// return details about the link at position x,y on the current page
void GetLinkAtPosition( HHTMLBrowser unBrowserHandle, int x, int y )
{
PRINT_DEBUG("Steam_HTMLsurface::GetLinkAtPosition\n");
}
// set a webcookie for the hostname in question
void SetCookie( const char *pchHostname, const char *pchKey, const char *pchValue, const char *pchPath, RTime32 nExpires, bool bSecure, bool bHTTPOnly )
{
PRINT_DEBUG("Steam_HTMLsurface::SetCookie\n");
}
// Zoom the current page by flZoom ( from 0.0 to 2.0, so to zoom to 120% use 1.2 ), zooming around point X,Y in the page (use 0,0 if you don't care)
void SetPageScaleFactor( HHTMLBrowser unBrowserHandle, float flZoom, int nPointX, int nPointY )
{
PRINT_DEBUG("Steam_HTMLsurface::SetPageScaleFactor\n");
}
// Enable/disable low-resource background mode, where javascript and repaint timers are throttled, resources are
// more aggressively purged from memory, and audio/video elements are paused. When background mode is enabled,
// all HTML5 video and audio objects will execute ".pause()" and gain the property "._steam_background_paused = 1".
// When background mode is disabled, any video or audio objects with that property will resume with ".play()".
void SetBackgroundMode( HHTMLBrowser unBrowserHandle, bool bBackgroundMode )
{
PRINT_DEBUG("Steam_HTMLsurface::SetBackgroundMode\n");
}
// Scale the output display space by this factor, this is useful when displaying content on high dpi devices.
// Specifies the ratio between physical and logical pixels.
void SetDPIScalingFactor( HHTMLBrowser unBrowserHandle, float flDPIScaling )
{
PRINT_DEBUG("Steam_HTMLsurface::SetDPIScalingFactor\n");
}
void OpenDeveloperTools( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG("Steam_HTMLsurface::OpenDeveloperTools\n");
}
// CALLBACKS
//
// These set of functions are used as responses to callback requests
//
// You MUST call this in response to a HTML_StartRequest_t callback
// Set bAllowed to true to allow this navigation, false to cancel it and stay
// on the current page. You can use this feature to limit the valid pages
// allowed in your HTML surface.
void AllowStartRequest( HHTMLBrowser unBrowserHandle, bool bAllowed )
{
PRINT_DEBUG("Steam_HTMLsurface::AllowStartRequest\n");
}
// You MUST call this in response to a HTML_JSAlert_t or HTML_JSConfirm_t callback
// Set bResult to true for the OK option of a confirm, use false otherwise
void JSDialogResponse( HHTMLBrowser unBrowserHandle, bool bResult )
{
PRINT_DEBUG("Steam_HTMLsurface::JSDialogResponse\n");
}
// You MUST call this in response to a HTML_FileOpenDialog_t callback
STEAM_IGNOREATTR()
void FileLoadDialogResponse( HHTMLBrowser unBrowserHandle, const char **pchSelectedFiles )
{
PRINT_DEBUG("Steam_HTMLsurface::FileLoadDialogResponse\n");
}
};

46
dll/steam_applist.cpp Normal file
View File

@ -0,0 +1,46 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "steam_applist.h"
uint32 Steam_Applist::GetNumInstalledApps()
{
PRINT_DEBUG("Steam_Applist::GetNumInstalledApps\n");
}
uint32 Steam_Applist::GetInstalledApps( AppId_t *pvecAppID, uint32 unMaxAppIDs )
{
PRINT_DEBUG("Steam_Applist::GetInstalledApps\n");
}
// returns -1 if no name was found
int Steam_Applist::GetAppName( AppId_t nAppID, STEAM_OUT_STRING() char *pchName, int cchNameMax )
{
PRINT_DEBUG("Steam_Applist::GetAppName\n");
}
// returns -1 if no dir was found
int Steam_Applist::GetAppInstallDir( AppId_t nAppID, char *pchDirectory, int cchNameMax )
{
PRINT_DEBUG("Steam_Applist::GetAppInstallDir\n");
}
// return the buildid of this app, may change at any time based on backend updates to the game
int Steam_Applist::GetAppBuildId( AppId_t nAppID )
{
PRINT_DEBUG("Steam_Applist::GetAppBuildId\n");
}

30
dll/steam_applist.h Normal file
View File

@ -0,0 +1,30 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Applist : public ISteamAppList
{
public:
uint32 GetNumInstalledApps();
uint32 GetInstalledApps( AppId_t *pvecAppID, uint32 unMaxAppIDs );
int GetAppName( AppId_t nAppID, STEAM_OUT_STRING() char *pchName, int cchNameMax ); // returns -1 if no name was found
int GetAppInstallDir( AppId_t nAppID, char *pchDirectory, int cchNameMax ); // returns -1 if no dir was found
int GetAppBuildId( AppId_t nAppID ); // return the buildid of this app, may change at any time based on backend updates to the game
};

276
dll/steam_apps.cpp Normal file
View File

@ -0,0 +1,276 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "steam_apps.h"
Steam_Apps::Steam_Apps(Settings *settings, class SteamCallResults *callback_results)
{
this->settings = settings;
this->callback_results = callback_results;
}
bool Steam_Apps::BIsSubscribed()
{
PRINT_DEBUG("BIsSubscribed\n");
return true;
}
bool Steam_Apps::BIsLowViolence()
{
PRINT_DEBUG("BIsLowViolence\n");
return false;
}
bool Steam_Apps::BIsCybercafe()
{
PRINT_DEBUG("BIsCybercafe\n");
return false;
}
bool Steam_Apps::BIsVACBanned()
{
PRINT_DEBUG("BIsVACBanned\n");
return false;
}
const char *Steam_Apps::GetCurrentGameLanguage()
{
PRINT_DEBUG("GetCurrentGameLanguage\n");
return settings->get_language();
}
const char *Steam_Apps::GetAvailableGameLanguages()
{
PRINT_DEBUG("GetAvailableGameLanguages\n");
//TODO?
return "";
}
// only use this member if you need to check ownership of another game related to yours, a demo for example
bool Steam_Apps::BIsSubscribedApp( AppId_t appID )
{
PRINT_DEBUG("BIsSubscribedApp %u\n", appID);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (appID == 0) return true; //I think appid 0 is always owned
if (appID == settings->get_local_game_id().AppID()) return true;
return settings->hasDLC(appID);
}
// Takes AppID of DLC and checks if the user owns the DLC & if the DLC is installed
bool Steam_Apps::BIsDlcInstalled( AppId_t appID )
{
PRINT_DEBUG("BIsDlcInstalled %u\n", appID);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (appID == 0) return true;
return settings->hasDLC(appID);
}
// returns the Unix time of the purchase of the app
uint32 Steam_Apps::GetEarliestPurchaseUnixTime( AppId_t nAppID )
{
PRINT_DEBUG("GetEarliestPurchaseUnixTime\n");
//TODO ?
return 1;
}
// Checks if the user is subscribed to the current app through a free weekend
// This function will return false for users who have a retail or other type of license
// Before using, please ask your Valve technical contact how to package and secure your free weekened
bool Steam_Apps::BIsSubscribedFromFreeWeekend()
{
PRINT_DEBUG("BIsSubscribedFromFreeWeekend\n");
return false;
}
// Returns the number of DLC pieces for the running app
int Steam_Apps::GetDLCCount()
{
PRINT_DEBUG("GetDLCCount\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return settings->DLCCount();
}
// Returns metadata for DLC by index, of range [0, GetDLCCount()]
bool Steam_Apps::BGetDLCDataByIndex( int iDLC, AppId_t *pAppID, bool *pbAvailable, char *pchName, int cchNameBufferSize )
{
PRINT_DEBUG("BGetDLCDataByIndex\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
AppId_t appid;
bool available;
std::string name;
if (!settings->getDLC(iDLC, appid, available, name)) return false;
if (pAppID) *pAppID = appid;
if (pbAvailable) *pbAvailable = available;
if (pchName && cchNameBufferSize > 0) {
if (cchNameBufferSize > name.size()) {
cchNameBufferSize = name.size();
pchName[cchNameBufferSize] = 0;
}
//TODO: should pchName be null terminated if the size is smaller or equal to the name size?
memcpy(pchName, name.data(), cchNameBufferSize);
}
return true;
}
// Install/Uninstall control for optional DLC
void Steam_Apps::InstallDLC( AppId_t nAppID )
{
PRINT_DEBUG("InstallDLC\n");
}
void Steam_Apps::UninstallDLC( AppId_t nAppID )
{
PRINT_DEBUG("UninstallDLC\n");
}
// Request legacy cd-key for yourself or owned DLC. If you are interested in this
// data then make sure you provide us with a list of valid keys to be distributed
// to users when they purchase the game, before the game ships.
// You'll receive an AppProofOfPurchaseKeyResponse_t callback when
// the key is available (which may be immediately).
void Steam_Apps::RequestAppProofOfPurchaseKey( AppId_t nAppID )
{
PRINT_DEBUG("RequestAppProofOfPurchaseKey\n");
}
// returns current beta branch name, 'public' is the default branch
bool Steam_Apps::GetCurrentBetaName( char *pchName, int cchNameBufferSize )
{
PRINT_DEBUG("GetCurrentBetaName %i\n", cchNameBufferSize);
if (sizeof("public") > cchNameBufferSize) {
return false;
}
memcpy(pchName, "public", sizeof("public"));
return true;
}
// signal Steam that game files seems corrupt or missing
bool Steam_Apps::MarkContentCorrupt( bool bMissingFilesOnly )
{
PRINT_DEBUG("MarkContentCorrupt\n");
//TODO: warn user
return true;
}
// return installed depots in mount order
uint32 Steam_Apps::GetInstalledDepots( AppId_t appID, DepotId_t *pvecDepots, uint32 cMaxDepots )
{
PRINT_DEBUG("GetInstalledDepots\n");
return 0;
}
// returns current app install folder for AppID, returns folder name length
uint32 Steam_Apps::GetAppInstallDir( AppId_t appID, char *pchFolder, uint32 cchFolderBufferSize )
{
PRINT_DEBUG("GetAppInstallDir %u\n", cchFolderBufferSize);
//TODO return real path instead of dll path
if (!pchFolder || !cchFolderBufferSize) return 0;
std::string path = get_full_program_path();
snprintf(pchFolder, cchFolderBufferSize, "%s", path.c_str());
return strlen(pchFolder);
}
// returns true if that app is installed (not necessarily owned)
bool Steam_Apps::BIsAppInstalled( AppId_t appID )
{
PRINT_DEBUG("BIsAppInstalled %u\n", appID);
return true;
}
// returns the SteamID of the original owner. If different from current user, it's borrowed
CSteamID Steam_Apps::GetAppOwner()
{
PRINT_DEBUG("GetAppOwner\n");
return settings->get_local_steam_id();
}
// Returns the associated launch param if the game is run via steam://run/<appid>//?param1=value1;param2=value2;param3=value3 etc.
// Parameter names starting with the character '@' are reserved for internal use and will always return and empty string.
// Parameter names starting with an underscore '_' are reserved for steam features -- they can be queried by the game,
// but it is advised that you not param names beginning with an underscore for your own features.
const char *Steam_Apps::GetLaunchQueryParam( const char *pchKey )
{
PRINT_DEBUG("GetLaunchQueryParam\n");
return "";
}
// get download progress for optional DLC
bool Steam_Apps::GetDlcDownloadProgress( AppId_t nAppID, uint64 *punBytesDownloaded, uint64 *punBytesTotal )
{
PRINT_DEBUG("GetDlcDownloadProgress\n");
return false;
}
// return the buildid of this app, may change at any time based on backend updates to the game
int Steam_Apps::GetAppBuildId()
{
PRINT_DEBUG("GetAppBuildId\n");
return 1;
}
// Request all proof of purchase keys for the calling appid and asociated DLC.
// A series of AppProofOfPurchaseKeyResponse_t callbacks will be sent with
// appropriate appid values, ending with a final callback where the m_nAppId
// member is k_uAppIdInvalid (zero).
void Steam_Apps::RequestAllProofOfPurchaseKeys()
{
PRINT_DEBUG("RequestAllProofOfPurchaseKeys\n");
}
STEAM_CALL_RESULT( FileDetailsResult_t )
SteamAPICall_t Steam_Apps::GetFileDetails( const char* pszFileName )
{
PRINT_DEBUG("GetFileDetails\n");
return 0;
}
// Get command line if game was launched via Steam URL, e.g. steam://run/<appid>//<command line>/.
// This method of passing a connect string (used when joining via rich presence, accepting an
// invite, etc) is preferable to passing the connect string on the operating system command
// line, which is a security risk. In order for rich presence joins to go through this
// path and not be placed on the OS command line, you must set a value in your app's
// configuration on Steam. Ask Valve for help with this.
//
// If game was already running and launched again, the NewUrlLaunchParameters_t will be fired.
int Steam_Apps::GetLaunchCommandLine( char *pszCommandLine, int cubCommandLine )
{
PRINT_DEBUG("GetLaunchCommandLine\n");
return 0;
}
// Check if user borrowed this game via Family Sharing, If true, call GetAppOwner() to get the lender SteamID
bool Steam_Apps::BIsSubscribedFromFamilySharing()
{
PRINT_DEBUG("BIsSubscribedFromFamilySharing\n");
return false;
}

92
dll/steam_apps.h Normal file
View File

@ -0,0 +1,92 @@
#include "base.h"
class Steam_Apps : public ISteamApps
{
Settings *settings;
class SteamCallResults *callback_results;
public:
Steam_Apps(Settings *settings, class SteamCallResults *callback_results);
bool BIsSubscribed();
bool BIsLowViolence();
bool BIsCybercafe();
bool BIsVACBanned();
const char *GetCurrentGameLanguage();
const char *GetAvailableGameLanguages();
// only use this member if you need to check ownership of another game related to yours, a demo for example
bool BIsSubscribedApp( AppId_t appID );
// Takes AppID of DLC and checks if the user owns the DLC & if the DLC is installed
bool BIsDlcInstalled( AppId_t appID );
// returns the Unix time of the purchase of the app
uint32 GetEarliestPurchaseUnixTime( AppId_t nAppID );
// Checks if the user is subscribed to the current app through a free weekend
// This function will return false for users who have a retail or other type of license
// Before using, please ask your Valve technical contact how to package and secure your free weekened
bool BIsSubscribedFromFreeWeekend();
// Returns the number of DLC pieces for the running app
int GetDLCCount();
// Returns metadata for DLC by index, of range [0, GetDLCCount()]
bool BGetDLCDataByIndex( int iDLC, AppId_t *pAppID, bool *pbAvailable, char *pchName, int cchNameBufferSize );
// Install/Uninstall control for optional DLC
void InstallDLC( AppId_t nAppID );
void UninstallDLC( AppId_t nAppID );
// Request legacy cd-key for yourself or owned DLC. If you are interested in this
// data then make sure you provide us with a list of valid keys to be distributed
// to users when they purchase the game, before the game ships.
// You'll receive an AppProofOfPurchaseKeyResponse_t callback when
// the key is available (which may be immediately).
void RequestAppProofOfPurchaseKey( AppId_t nAppID );
bool GetCurrentBetaName( char *pchName, int cchNameBufferSize ); // returns current beta branch name, 'public' is the default branch
bool MarkContentCorrupt( bool bMissingFilesOnly ); // signal Steam that game files seems corrupt or missing
uint32 GetInstalledDepots( AppId_t appID, DepotId_t *pvecDepots, uint32 cMaxDepots ); // return installed depots in mount order
// returns current app install folder for AppID, returns folder name length
uint32 GetAppInstallDir( AppId_t appID, char *pchFolder, uint32 cchFolderBufferSize );
bool BIsAppInstalled( AppId_t appID ); // returns true if that app is installed (not necessarily owned)
CSteamID GetAppOwner(); // returns the SteamID of the original owner. If different from current user, it's borrowed
// Returns the associated launch param if the game is run via steam://run/<appid>//?param1=value1;param2=value2;param3=value3 etc.
// Parameter names starting with the character '@' are reserved for internal use and will always return and empty string.
// Parameter names starting with an underscore '_' are reserved for steam features -- they can be queried by the game,
// but it is advised that you not param names beginning with an underscore for your own features.
const char *GetLaunchQueryParam( const char *pchKey );
// get download progress for optional DLC
bool GetDlcDownloadProgress( AppId_t nAppID, uint64 *punBytesDownloaded, uint64 *punBytesTotal );
// return the buildid of this app, may change at any time based on backend updates to the game
int GetAppBuildId();
// Request all proof of purchase keys for the calling appid and asociated DLC.
// A series of AppProofOfPurchaseKeyResponse_t callbacks will be sent with
// appropriate appid values, ending with a final callback where the m_nAppId
// member is k_uAppIdInvalid (zero).
void RequestAllProofOfPurchaseKeys();
STEAM_CALL_RESULT( FileDetailsResult_t )
SteamAPICall_t GetFileDetails( const char* pszFileName );
// Get command line if game was launched via Steam URL, e.g. steam://run/<appid>//<command line>/.
// This method of passing a connect string (used when joining via rich presence, accepting an
// invite, etc) is preferable to passing the connect string on the operating system command
// line, which is a security risk. In order for rich presence joins to go through this
// path and not be placed on the OS command line, you must set a value in your app's
// configuration on Steam. Ask Valve for help with this.
//
// If game was already running and launched again, the NewUrlLaunchParameters_t will be fired.
int GetLaunchCommandLine( char *pszCommandLine, int cubCommandLine );
// Check if user borrowed this game via Family Sharing, If true, call GetAppOwner() to get the lender SteamID
bool BIsSubscribedFromFamilySharing();
};

1582
dll/steam_client.cpp Normal file

File diff suppressed because it is too large Load Diff

273
dll/steam_client.h Normal file
View File

@ -0,0 +1,273 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
#include "steam_user.h"
#include "steam_friends.h"
#include "steam_utils.h"
#include "steam_matchmaking.h"
#include "steam_matchmaking_servers.h"
#include "steam_user_stats.h"
#include "steam_apps.h"
#include "steam_networking.h"
#include "steam_remote_storage.h"
#include "steam_screenshots.h"
#include "steam_http.h"
#include "steam_controller.h"
#include "steam_ugc.h"
#include "steam_applist.h"
#include "steam_music.h"
#include "steam_musicremote.h"
#include "steam_HTMLsurface.h"
#include "steam_inventory.h"
#include "steam_video.h"
#include "steam_parental.h"
#include "steam_game_coordinator.h"
#include "steam_networking_socketsserialized.h"
#include "steam_networking_sockets.h"
#include "steam_networking_utils.h"
#include "steam_unified_messages.h"
#include "steam_gamesearch.h"
#include "steam_parties.h"
#include "steam_gameserver.h"
#include "steam_gameserverstats.h"
#include "steam_masterserver_updater.h"
#include <thread>
class Steam_Client :
public ISteamClient007,
public ISteamClient008,
public ISteamClient009,
public ISteamClient010,
public ISteamClient011,
public ISteamClient012,
public ISteamClient013,
public ISteamClient014,
public ISteamClient015,
public ISteamClient016,
public ISteamClient017,
public ISteamClient
{
public:
Networking *network;
SteamCallResults *callback_results_server, *callback_results_client;
SteamCallBacks *callbacks_server, *callbacks_client;
Settings *settings_client, *settings_server;
Local_Storage *local_storage;
RunEveryRunCB *run_every_runcb;
Steam_User *steam_user;
Steam_Friends *steam_friends;
Steam_Utils *steam_utils;
Steam_Matchmaking *steam_matchmaking;
Steam_Matchmaking_Servers *steam_matchmaking_servers;
Steam_User_Stats *steam_user_stats;
Steam_Apps *steam_apps;
Steam_Networking *steam_networking;
Steam_Remote_Storage *steam_remote_storage;
Steam_Screenshots *steam_screenshots;
Steam_HTTP *steam_http;
Steam_Controller *steam_controller;
Steam_UGC *steam_ugc;
Steam_Applist *steam_applist;
Steam_Music *steam_music;
Steam_MusicRemote *steam_musicremote;
Steam_HTMLsurface *steam_HTMLsurface;
Steam_Inventory *steam_inventory;
Steam_Video *steam_video;
Steam_Parental *steam_parental;
Steam_Networking_Sockets *steam_networking_sockets;
Steam_Networking_Sockets_Serialized *steam_networking_sockets_serialized;
Steam_Game_Coordinator *steam_game_coordinator;
Steam_Networking_Utils *steam_networking_utils;
Steam_Unified_Messages *steam_unified_messages;
Steam_Game_Search *steam_game_search;
Steam_Parties *steam_parties;
Steam_GameServer *steam_gameserver;
Steam_Utils *steam_gameserver_utils;
Steam_GameServerStats *steam_gameserverstats;
Steam_Networking *steam_gameserver_networking;
Steam_HTTP *steam_gameserver_http;
Steam_Inventory *steam_gameserver_inventory;
Steam_UGC *steam_gameserver_ugc;
Steam_Apps *steam_gameserver_apps;
Steam_Networking_Sockets *steam_gameserver_networking_sockets;
Steam_Networking_Sockets_Serialized *steam_gameserver_networking_sockets_serialized;
Steam_Game_Coordinator *steam_gameserver_game_coordinator;
Steam_Masterserver_Updater *steam_masterserver_updater;
bool user_logged_in = false;
bool server_init = false;
std::thread network_keepalive;
Steam_Client();
~Steam_Client();
// Creates a communication pipe to the Steam client.
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
HSteamPipe CreateSteamPipe();
// Releases a previously created communications pipe
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
bool BReleaseSteamPipe( HSteamPipe hSteamPipe );
// connects to an existing global user, failing if none exists
// used by the game to coordinate with the steamUI
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
HSteamUser ConnectToGlobalUser( HSteamPipe hSteamPipe );
// used by game servers, create a steam user that won't be shared with anyone else
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
HSteamUser CreateLocalUser( HSteamPipe *phSteamPipe, EAccountType eAccountType );
HSteamUser CreateLocalUser( HSteamPipe *phSteamPipe );
// removes an allocated user
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
void ReleaseUser( HSteamPipe hSteamPipe, HSteamUser hUser );
// retrieves the ISteamUser interface associated with the handle
ISteamUser *GetISteamUser( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// retrieves the ISteamGameServer interface associated with the handle
ISteamGameServer *GetISteamGameServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// set the local IP and Port to bind to
// this must be set before CreateLocalUser()
void SetLocalIPBinding( uint32 unIP, uint16 usPort );
// returns the ISteamFriends interface
ISteamFriends *GetISteamFriends( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// returns the ISteamUtils interface
ISteamUtils *GetISteamUtils( HSteamPipe hSteamPipe, const char *pchVersion );
// returns the ISteamMatchmaking interface
ISteamMatchmaking *GetISteamMatchmaking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// returns the ISteamMatchmakingServers interface
ISteamMatchmakingServers *GetISteamMatchmakingServers( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// returns the a generic interface
void *GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// returns the ISteamUserStats interface
ISteamUserStats *GetISteamUserStats( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// returns the ISteamGameServerStats interface
ISteamGameServerStats *GetISteamGameServerStats( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
// returns apps interface
ISteamApps *GetISteamApps( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// networking
ISteamNetworking *GetISteamNetworking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// remote storage
ISteamRemoteStorage *GetISteamRemoteStorage( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
// user screenshots
ISteamScreenshots *GetISteamScreenshots( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
// Deprecated. Applications should use SteamAPI_RunCallbacks() or SteamGameServer_RunCallbacks() instead.
STEAM_PRIVATE_API( void RunFrame(); )
// returns the number of IPC calls made since the last time this function was called
// Used for perf debugging so you can understand how many IPC calls your game makes per frame
// Every IPC call is at minimum a thread context switch if not a process one so you want to rate
// control how often you do them.
uint32 GetIPCCallCount();
// API warning handling
// 'int' is the severity; 0 for msg, 1 for warning
// 'const char *' is the text of the message
// callbacks will occur directly after the API function is called that generated the warning or message.
void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction );
// Trigger global shutdown for the DLL
bool BShutdownIfAllPipesClosed();
// Expose HTTP interface
ISteamHTTP *GetISteamHTTP( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
// Deprecated - the ISteamUnifiedMessages interface is no longer intended for public consumption.
STEAM_PRIVATE_API( void *DEPRECATED_GetISteamUnifiedMessages( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) ; )
ISteamUnifiedMessages *GetISteamUnifiedMessages( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
// Exposes the ISteamController interface
ISteamController *GetISteamController( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// Exposes the ISteamUGC interface
ISteamUGC *GetISteamUGC( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// returns app list interface, only available on specially registered apps
ISteamAppList *GetISteamAppList( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// Music Player
ISteamMusic *GetISteamMusic( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
// Music Player Remote
ISteamMusicRemote *GetISteamMusicRemote(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion);
// html page display
ISteamHTMLSurface *GetISteamHTMLSurface(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion);
// Helper functions for internal Steam usage
STEAM_PRIVATE_API( void DEPRECATED_Set_SteamAPI_CPostAPIResultInProcess( void (*)() ); )
STEAM_PRIVATE_API( void DEPRECATED_Remove_SteamAPI_CPostAPIResultInProcess( void (*)() ); )
STEAM_PRIVATE_API( void Set_SteamAPI_CCheckCallbackRegisteredInProcess( SteamAPI_CheckCallbackRegistered_t func ); )
void Set_SteamAPI_CPostAPIResultInProcess( SteamAPI_PostAPIResultInProcess_t func );
void Remove_SteamAPI_CPostAPIResultInProcess( SteamAPI_PostAPIResultInProcess_t func );
// inventory
ISteamInventory *GetISteamInventory( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
// Video
ISteamVideo *GetISteamVideo( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
// Parental controls
ISteamParentalSettings *GetISteamParentalSettings( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
//
ISteamMasterServerUpdater *GetISteamMasterServerUpdater( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
ISteamContentServer *GetISteamContentServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// game search
ISteamGameSearch *GetISteamGameSearch( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
// Exposes the Steam Input interface for controller support
ISteamInput *GetISteamInput( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// Steam Parties interface
ISteamParties *GetISteamParties( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
void RegisterCallback( class CCallbackBase *pCallback, int iCallback);
void UnregisterCallback( class CCallbackBase *pCallback);
void RegisterCallResult( class CCallbackBase *pCallback, SteamAPICall_t hAPICall);
void UnregisterCallResult( class CCallbackBase *pCallback, SteamAPICall_t hAPICall);
void RunCallbacks(bool runClientCB, bool runGameserverCB);
void setAppID(uint32 appid);
void userLogIn();
void serverInit();
void serverShutdown();
bool IsServerInit();
bool IsUserLogIn();
};

354
dll/steam_controller.h Normal file
View File

@ -0,0 +1,354 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Controller :
public ISteamController001,
public ISteamController003,
public ISteamController004,
public ISteamController005,
public ISteamController006,
public ISteamController,
public ISteamInput
{
public:
// Init and Shutdown must be called when starting/ending use of this interface
bool Init()
{
PRINT_DEBUG("Steam_Controller::Init()\n");
return true;
}
bool Init( const char *pchAbsolutePathToControllerConfigVDF )
{
PRINT_DEBUG("Steam_Controller::Init() old\n");
return Init();
}
bool Shutdown()
{
PRINT_DEBUG("Steam_Controller::Shutdown()\n");
return true;
}
void SetOverrideMode( const char *pchMode )
{
PRINT_DEBUG("Steam_Controller::SetOverrideMode\n");
}
// Synchronize API state with the latest Steam Controller inputs available. This
// is performed automatically by SteamAPI_RunCallbacks, but for the absolute lowest
// possible latency, you call this directly before reading controller state.
void RunFrame()
{
PRINT_DEBUG("Steam_Controller::RunFrame()\n");
}
bool GetControllerState( uint32 unControllerIndex, SteamControllerState001_t *pState )
{
PRINT_DEBUG("Steam_Controller::GetControllerState()\n");
return false;
}
// Enumerate currently connected controllers
// handlesOut should point to a STEAM_CONTROLLER_MAX_COUNT sized array of ControllerHandle_t handles
// Returns the number of handles written to handlesOut
int GetConnectedControllers( ControllerHandle_t *handlesOut )
{
PRINT_DEBUG("GetConnectedControllers\n");
return 0;
}
// Invokes the Steam overlay and brings up the binding screen
// Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode
bool ShowBindingPanel( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("ShowBindingPanel\n");
return false;
}
// ACTION SETS
// Lookup the handle for an Action Set. Best to do this once on startup, and store the handles for all future API calls.
ControllerActionSetHandle_t GetActionSetHandle( const char *pszActionSetName )
{
PRINT_DEBUG("GetActionSetHandle %s\n", pszActionSetName);
return 124;
}
// Reconfigure the controller to use the specified action set (ie 'Menu', 'Walk' or 'Drive')
// This is cheap, and can be safely called repeatedly. It's often easier to repeatedly call it in
// your state loops, instead of trying to place it in all of your state transitions.
void ActivateActionSet( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle )
{
PRINT_DEBUG("ActivateActionSet\n");
}
ControllerActionSetHandle_t GetCurrentActionSet( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("GetCurrentActionSet\n");
return 124;
}
void ActivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle )
{
PRINT_DEBUG("ActivateActionSetLayer\n");
}
void DeactivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle )
{
PRINT_DEBUG("DeactivateActionSetLayer\n");
}
void DeactivateAllActionSetLayers( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("DeactivateAllActionSetLayers\n");
}
int GetActiveActionSetLayers( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t *handlesOut )
{
PRINT_DEBUG("GetActiveActionSetLayers\n");
return 0;
}
// ACTIONS
// Lookup the handle for a digital action. Best to do this once on startup, and store the handles for all future API calls.
ControllerDigitalActionHandle_t GetDigitalActionHandle( const char *pszActionName )
{
PRINT_DEBUG("GetDigitalActionHandle %s\n", pszActionName);
return 123;
}
// Returns the current state of the supplied digital game action
ControllerDigitalActionData_t GetDigitalActionData( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle )
{
PRINT_DEBUG("GetDigitalActionData\n");
ControllerDigitalActionData_t digitalData;
digitalData.bActive = false;
return digitalData;
}
// Get the origin(s) for a digital action within an action set. Returns the number of origins supplied in originsOut. Use this to display the appropriate on-screen prompt for the action.
// originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles
int GetDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerDigitalActionHandle_t digitalActionHandle, EControllerActionOrigin *originsOut )
{
PRINT_DEBUG("GetDigitalActionOrigins\n");
return 0;
}
int GetDigitalActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputDigitalActionHandle_t digitalActionHandle, EInputActionOrigin *originsOut )
{
PRINT_DEBUG("GetDigitalActionOrigins steaminput\n");
return 0;
}
// Lookup the handle for an analog action. Best to do this once on startup, and store the handles for all future API calls.
ControllerAnalogActionHandle_t GetAnalogActionHandle( const char *pszActionName )
{
PRINT_DEBUG("GetAnalogActionHandle %s\n", pszActionName);
return 125;
}
// Returns the current state of these supplied analog game action
ControllerAnalogActionData_t GetAnalogActionData( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle )
{
PRINT_DEBUG("GetAnalogActionData\n");
ControllerAnalogActionData_t data;
data.eMode = k_EInputSourceMode_None;
data.x = data.y = 0;
data.bActive = false;
return data;
}
// Get the origin(s) for an analog action within an action set. Returns the number of origins supplied in originsOut. Use this to display the appropriate on-screen prompt for the action.
// originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles
int GetAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerAnalogActionHandle_t analogActionHandle, EControllerActionOrigin *originsOut )
{
PRINT_DEBUG("GetAnalogActionOrigins\n");
return 0;
}
int GetAnalogActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputAnalogActionHandle_t analogActionHandle, EInputActionOrigin *originsOut )
{
PRINT_DEBUG("GetAnalogActionOrigins steaminput\n");
return 0;
}
void StopAnalogActionMomentum( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t eAction )
{
PRINT_DEBUG("StopAnalogActionMomentum\n");
}
// Trigger a haptic pulse on a controller
void TriggerHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec )
{
PRINT_DEBUG("TriggerHapticPulse\n");
}
void TriggerHapticPulse( uint32 unControllerIndex, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec )
{
PRINT_DEBUG("TriggerHapticPulse old\n");
TriggerHapticPulse(unControllerIndex, eTargetPad, usDurationMicroSec );
}
// Trigger a pulse with a duty cycle of usDurationMicroSec / usOffMicroSec, unRepeat times.
// nFlags is currently unused and reserved for future use.
void TriggerRepeatedHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec, unsigned short usOffMicroSec, unsigned short unRepeat, unsigned int nFlags )
{
PRINT_DEBUG("TriggerRepeatedHapticPulse\n");
}
// Tigger a vibration event on supported controllers.
void TriggerVibration( ControllerHandle_t controllerHandle, unsigned short usLeftSpeed, unsigned short usRightSpeed )
{
PRINT_DEBUG("TriggerVibration\n");
}
// Set the controller LED color on supported controllers.
void SetLEDColor( ControllerHandle_t controllerHandle, uint8 nColorR, uint8 nColorG, uint8 nColorB, unsigned int nFlags )
{
PRINT_DEBUG("SetLEDColor\n");
}
// Returns the associated gamepad index for the specified controller, if emulating a gamepad
int GetGamepadIndexForController( ControllerHandle_t ulControllerHandle )
{
PRINT_DEBUG("GetGamepadIndexForController\n");
return 0;
}
// Returns the associated controller handle for the specified emulated gamepad
ControllerHandle_t GetControllerForGamepadIndex( int nIndex )
{
PRINT_DEBUG("GetControllerForGamepadIndex\n");
return 0;
}
// Returns raw motion data from the specified controller
ControllerMotionData_t GetMotionData( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("GetMotionData\n");
ControllerMotionData_t data = {};
return data;
}
// Attempt to display origins of given action in the controller HUD, for the currently active action set
// Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode
bool ShowDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle, float flScale, float flXPosition, float flYPosition )
{
PRINT_DEBUG("ShowDigitalActionOrigins\n");
return true;
}
bool ShowAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle, float flScale, float flXPosition, float flYPosition )
{
PRINT_DEBUG("ShowAnalogActionOrigins\n");
return true;
}
// Returns a localized string (from Steam's language setting) for the specified origin
const char *GetStringForActionOrigin( EControllerActionOrigin eOrigin )
{
PRINT_DEBUG("GetStringForActionOrigin\n");
return "Button String";
}
const char *GetStringForActionOrigin( EInputActionOrigin eOrigin )
{
PRINT_DEBUG("GetStringForActionOrigin steaminput\n");
return "Button String";
}
// Get a local path to art for on-screen glyph for a particular origin
const char *GetGlyphForActionOrigin( EControllerActionOrigin eOrigin )
{
PRINT_DEBUG("GetGlyphForActionOrigin\n");
return "";
}
const char *GetGlyphForActionOrigin( EInputActionOrigin eOrigin )
{
PRINT_DEBUG("GetGlyphForActionOrigin steaminput\n");
return "";
}
// Returns the input type for a particular handle
ESteamInputType GetInputTypeForHandle( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("GetInputTypeForHandle\n");
return k_ESteamInputType_Unknown;
}
const char *GetStringForXboxOrigin( EXboxOrigin eOrigin )
{
PRINT_DEBUG("GetStringForXboxOrigin\n");
return "";
}
const char *GetGlyphForXboxOrigin( EXboxOrigin eOrigin )
{
PRINT_DEBUG("GetGlyphForXboxOrigin\n");
return "";
}
EControllerActionOrigin GetActionOriginFromXboxOrigin_( ControllerHandle_t controllerHandle, EXboxOrigin eOrigin )
{
PRINT_DEBUG("GetActionOriginFromXboxOrigin\n");
return k_EControllerActionOrigin_None;
}
EInputActionOrigin GetActionOriginFromXboxOrigin( InputHandle_t inputHandle, EXboxOrigin eOrigin )
{
PRINT_DEBUG("GetActionOriginFromXboxOrigin steaminput\n");
return k_EInputActionOrigin_None;
}
EControllerActionOrigin TranslateActionOrigin( ESteamInputType eDestinationInputType, EControllerActionOrigin eSourceOrigin )
{
PRINT_DEBUG("TranslateActionOrigin\n");
return k_EControllerActionOrigin_None;
}
EInputActionOrigin TranslateActionOrigin( ESteamInputType eDestinationInputType, EInputActionOrigin eSourceOrigin )
{
PRINT_DEBUG("TranslateActionOrigin steaminput\n");
return k_EInputActionOrigin_None;
}
};

986
dll/steam_friends.h Normal file
View File

@ -0,0 +1,986 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
#define SEND_FRIEND_RATE 4.0
class Steam_Friends :
public ISteamFriends004,
public ISteamFriends005,
public ISteamFriends006,
public ISteamFriends007,
public ISteamFriends008,
public ISteamFriends009,
public ISteamFriends010,
public ISteamFriends011,
public ISteamFriends012,
public ISteamFriends013,
public ISteamFriends014,
public ISteamFriends015,
public ISteamFriends016,
public ISteamFriends
{
class Settings *settings;
class Networking *network;
class SteamCallBacks *callbacks;
class SteamCallResults *callback_results;
class RunEveryRunCB *run_every_runcb;
Friend us;
bool modified;
std::vector<Friend> friends;
unsigned img_count;
CSteamID lobby_id;
std::chrono::high_resolution_clock::time_point last_sent_friends;
Friend *find_friend(CSteamID id)
{
auto f = std::find_if(friends.begin(), friends.end(), [&id](Friend const& item) { return item.id() == id.ConvertToUint64(); });
if (friends.end() == f)
return NULL;
return &(*f);
}
void persona_change(CSteamID id, EPersonaChange flags)
{
PersonaStateChange_t data;
data.m_ulSteamID = id.ConvertToUint64();
data.m_nChangeFlags = flags;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
void rich_presence_updated(CSteamID id, AppId_t appid)
{
FriendRichPresenceUpdate_t data;
data.m_steamIDFriend = id;
data.m_nAppID = appid;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
bool isSameAppId(Friend *f)
{
//in my emu 0 appid is the same as any appid, it's useful for things like my lobby connect program
if (!settings->get_local_game_id().AppID()) return true;
return settings->get_local_game_id().AppID() == f->appid();
}
public:
static void steam_friends_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("Steam_Friends::steam_friends_callback\n");
Steam_Friends *steam_friends = (Steam_Friends *)object;
steam_friends->Callback(msg);
}
static void steam_friends_run_every_runcb(void *object)
{
PRINT_DEBUG("Steam_Friends::steam_friends_run_every_runcb\n");
Steam_Friends *steam_friends = (Steam_Friends *)object;
steam_friends->RunCallbacks();
}
Steam_Friends(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->callbacks = callbacks;
this->callback_results = callback_results;
this->run_every_runcb = run_every_runcb;
this->network->setCallback(CALLBACK_ID_FRIEND, settings->get_local_steam_id(), &Steam_Friends::steam_friends_callback, this);
this->network->setCallback(CALLBACK_ID_FRIEND_MESSAGES, settings->get_local_steam_id(), &Steam_Friends::steam_friends_callback, this);
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Friends::steam_friends_callback, this);
this->run_every_runcb->add(&Steam_Friends::steam_friends_run_every_runcb, this);
modified = false;
}
~Steam_Friends()
{
//TODO rm network callbacks
this->run_every_runcb->remove(&Steam_Friends::steam_friends_run_every_runcb, this);
}
static bool ok_friend_flags(int iFriendFlags)
{
if (iFriendFlags & k_EFriendFlagBlocked) return false;
if (iFriendFlags & k_EFriendFlagIgnored) return false;
if (iFriendFlags & k_EFriendFlagIgnoredFriend) return false;
if (iFriendFlags & k_EFriendFlagFriendshipRequested) return false;
if (iFriendFlags & k_EFriendFlagRequestingFriendship) return false;
return true;
}
// returns the local players name - guaranteed to not be NULL.
// this is the same name as on the users community profile page
// this is stored in UTF-8 format
// like all the other interface functions that return a char *, it's important that this pointer is not saved
// off; it will eventually be free'd or re-allocated
const char *GetPersonaName()
{
PRINT_DEBUG("Steam_Friends::GetPersonaName\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
const char *local_name = settings->get_local_name();
return local_name;
}
// Sets the player name, stores it on the server and publishes the changes to all friends who are online.
// Changes take place locally immediately, and a PersonaStateChange_t is posted, presuming success.
//
// The final results are available through the return value SteamAPICall_t, using SetPersonaNameResponse_t.
//
// If the name change fails to happen on the server, then an additional global PersonaStateChange_t will be posted
// to change the name back, in addition to the SetPersonaNameResponse_t callback.
STEAM_CALL_RESULT( SetPersonaNameResponse_t )
SteamAPICall_t SetPersonaName( const char *pchPersonaName )
{
PRINT_DEBUG("Steam_Friends::SetPersonaName\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
SetPersonaNameResponse_t data;
data.m_bSuccess = true;
data.m_bLocalSuccess = false;
data.m_result = k_EResultOK;
persona_change(settings->get_local_steam_id(), k_EPersonaChangeName);
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
void SetPersonaName_old( const char *pchPersonaName )
{
PRINT_DEBUG("Steam_Friends::SetPersonaName old\n");
SetPersonaName(pchPersonaName);
}
// gets the status of the current user
EPersonaState GetPersonaState()
{
PRINT_DEBUG("Steam_Friends::GetPersonaState\n");
return k_EPersonaStateOnline;
}
// friend iteration
// takes a set of k_EFriendFlags, and returns the number of users the client knows about who meet that criteria
// then GetFriendByIndex() can then be used to return the id's of each of those users
int GetFriendCount( int iFriendFlags )
{
PRINT_DEBUG("Steam_Friends::GetFriendCount\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
int count = 0;
if (ok_friend_flags(iFriendFlags)) count = friends.size();
return count;
}
int GetFriendCount( EFriendFlags eFriendFlags )
{
PRINT_DEBUG("Steam_Friends::GetFriendCount old\n");
return GetFriendCount((int)eFriendFlags);
}
// returns the steamID of a user
// iFriend is a index of range [0, GetFriendCount())
// iFriendsFlags must be the same value as used in GetFriendCount()
// the returned CSteamID can then be used by all the functions below to access details about the user
CSteamID GetFriendByIndex( int iFriend, int iFriendFlags )
{
PRINT_DEBUG("Steam_Friends::GetFriendByIndex\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
CSteamID id = k_steamIDNil;
if (ok_friend_flags(iFriendFlags)) if (iFriend < friends.size()) id = CSteamID((uint64)friends[iFriend].id());
return id;
}
CSteamID GetFriendByIndex( int iFriend, EFriendFlags eFriendFlags )
{
PRINT_DEBUG("Steam_Friends::GetFriendByIndex old\n");
return GetFriendByIndex(iFriend, (int)eFriendFlags );
}
// returns a relationship to a user
EFriendRelationship GetFriendRelationship( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::GetFriendRelationship %llu\n", steamIDFriend.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
EFriendRelationship r = k_EFriendRelationshipNone;
if (find_friend(steamIDFriend)) r = k_EFriendRelationshipFriend;
return r;
}
// returns the current status of the specified user
// this will only be known by the local user if steamIDFriend is in their friends list; on the same game server; in a chat room or lobby; or in a small group with the local user
EPersonaState GetFriendPersonaState( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::GetFriendPersonaState %llu\n", steamIDFriend.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
EPersonaState state = k_EPersonaStateOffline;
if (steamIDFriend == settings->get_local_steam_id() || find_friend(steamIDFriend)) {
state = k_EPersonaStateOnline;
}
//works because all of those who could be in a lobby are our friends
return state;
}
// returns the name another user - guaranteed to not be NULL.
// same rules as GetFriendPersonaState() apply as to whether or not the user knowns the name of the other user
// note that on first joining a lobby, chat room or game server the local user will not known the name of the other users automatically; that information will arrive asyncronously
//
const char *GetFriendPersonaName( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::GetFriendPersonaName %llu\n", steamIDFriend.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
const char *name = "Unknown User";
if (steamIDFriend == settings->get_local_steam_id()) {
name = settings->get_local_name();
} else {
Friend *f = find_friend(steamIDFriend);
if (f) name = f->name().c_str();
}
return name;
}
// returns true if the friend is actually in a game, and fills in pFriendGameInfo with an extra details
bool GetFriendGamePlayed( CSteamID steamIDFriend, STEAM_OUT_STRUCT() FriendGameInfo_t *pFriendGameInfo )
{
PRINT_DEBUG("Steam_Friends::GetFriendGamePlayed\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
bool ret = false;
Friend *f = find_friend(steamIDFriend);
if (f) {
if (pFriendGameInfo) {
pFriendGameInfo->m_gameID = CGameID(f->appid());
pFriendGameInfo->m_unGameIP = 0;
pFriendGameInfo->m_usGamePort = 0;
pFriendGameInfo->m_usQueryPort = 0;
pFriendGameInfo->m_steamIDLobby = CSteamID((uint64)f->lobby_id());
PRINT_DEBUG("%u %llu\n", f->appid(), f->lobby_id());
}
ret = true;
}
return ret;
}
bool GetFriendGamePlayed( CSteamID steamIDFriend, uint64 *pulGameID, uint32 *punGameIP, uint16 *pusGamePort, uint16 *pusQueryPort )
{
PRINT_DEBUG("Steam_Friends::GetFriendGamePlayed old\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
FriendGameInfo_t info;
bool ret = GetFriendGamePlayed(steamIDFriend, &info);
if (ret) {
if (pulGameID) *pulGameID = info.m_gameID.ToUint64();
if (punGameIP) *punGameIP = info.m_unGameIP;
if (pusGamePort) *pusGamePort = info.m_usGamePort;
if (pusQueryPort) *pusQueryPort = info.m_usQueryPort;
}
return ret;
}
// accesses old friends names - returns an empty string when their are no more items in the history
const char *GetFriendPersonaNameHistory( CSteamID steamIDFriend, int iPersonaName )
{
PRINT_DEBUG("Steam_Friends::GetFriendPersonaNameHistory\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
const char *ret = "";
if (iPersonaName == 0) ret = GetFriendPersonaName(steamIDFriend);
else if (iPersonaName == 1) ret = "Some Old Name";
return ret;
}
// friends steam level
int GetFriendSteamLevel( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::GetFriendSteamLevel\n");
return 100;
}
// Returns nickname the current user has set for the specified player. Returns NULL if the no nickname has been set for that player.
const char *GetPlayerNickname( CSteamID steamIDPlayer )
{
PRINT_DEBUG("Steam_Friends::GetPlayerNickname\n");
return NULL;
}
// friend grouping (tag) apis
// returns the number of friends groups
int GetFriendsGroupCount()
{
PRINT_DEBUG("Steam_Friends::GetFriendsGroupCount\n");
return 0;
}
// returns the friends group ID for the given index (invalid indices return k_FriendsGroupID_Invalid)
FriendsGroupID_t GetFriendsGroupIDByIndex( int iFG )
{
PRINT_DEBUG("Steam_Friends::GetFriendsGroupIDByIndex\n");
return k_FriendsGroupID_Invalid;
}
// returns the name for the given friends group (NULL in the case of invalid friends group IDs)
const char *GetFriendsGroupName( FriendsGroupID_t friendsGroupID )
{
PRINT_DEBUG("Steam_Friends::GetFriendsGroupName\n");
return NULL;
}
// returns the number of members in a given friends group
int GetFriendsGroupMembersCount( FriendsGroupID_t friendsGroupID )
{
PRINT_DEBUG("Steam_Friends::GetFriendsGroupMembersCount\n");
return 0;
}
// gets up to nMembersCount members of the given friends group, if fewer exist than requested those positions' SteamIDs will be invalid
void GetFriendsGroupMembersList( FriendsGroupID_t friendsGroupID, STEAM_OUT_ARRAY_CALL(nMembersCount, GetFriendsGroupMembersCount, friendsGroupID ) CSteamID *pOutSteamIDMembers, int nMembersCount )
{
PRINT_DEBUG("Steam_Friends::GetFriendsGroupMembersList\n");
}
// returns true if the specified user meets any of the criteria specified in iFriendFlags
// iFriendFlags can be the union (binary or, |) of one or more k_EFriendFlags values
bool HasFriend( CSteamID steamIDFriend, int iFriendFlags )
{
PRINT_DEBUG("Steam_Friends::HasFriend\n");
bool ret = false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (ok_friend_flags(iFriendFlags)) if (find_friend(steamIDFriend)) ret = true;
return ret;
}
bool HasFriend( CSteamID steamIDFriend, EFriendFlags eFriendFlags )
{
PRINT_DEBUG("Steam_Friends::HasFriend old\n");
return HasFriend(steamIDFriend, (int)eFriendFlags );
}
// clan (group) iteration and access functions
int GetClanCount()
{
PRINT_DEBUG("Steam_Friends::GetClanCount\n");
return 0;
}
CSteamID GetClanByIndex( int iClan )
{
PRINT_DEBUG("Steam_Friends::GetClanByIndex\n");
return k_steamIDNil;
}
const char *GetClanName( CSteamID steamIDClan )
{
PRINT_DEBUG("Steam_Friends::GetClanName\n");
return "";
}
const char *GetClanTag( CSteamID steamIDClan )
{
PRINT_DEBUG("Steam_Friends::GetClanTag\n");
return "";
}
// returns the most recent information we have about what's happening in a clan
bool GetClanActivityCounts( CSteamID steamIDClan, int *pnOnline, int *pnInGame, int *pnChatting )
{
PRINT_DEBUG("Steam_Friends::GetClanActivityCounts %llu\n", steamIDClan.ConvertToUint64());
return false;
}
// for clans a user is a member of, they will have reasonably up-to-date information, but for others you'll have to download the info to have the latest
SteamAPICall_t DownloadClanActivityCounts( STEAM_ARRAY_COUNT(cClansToRequest) CSteamID *psteamIDClans, int cClansToRequest )
{
PRINT_DEBUG("Steam_Friends::DownloadClanActivityCounts\n");
return 0;
}
// iterators for getting users in a chat room, lobby, game server or clan
// note that large clans that cannot be iterated by the local user
// note that the current user must be in a lobby to retrieve CSteamIDs of other users in that lobby
// steamIDSource can be the steamID of a group, game server, lobby or chat room
int GetFriendCountFromSource( CSteamID steamIDSource )
{
PRINT_DEBUG("Steam_Friends::GetFriendCountFromSource\n");
//TODO
return 0;
}
CSteamID GetFriendFromSourceByIndex( CSteamID steamIDSource, int iFriend )
{
PRINT_DEBUG("Steam_Friends::GetFriendFromSourceByIndex\n");
return k_steamIDNil;
}
// returns true if the local user can see that steamIDUser is a member or in steamIDSource
bool IsUserInSource( CSteamID steamIDUser, CSteamID steamIDSource )
{
PRINT_DEBUG("Steam_Friends::IsUserInSource %llu %llu\n", steamIDUser.ConvertToUint64(), steamIDSource.ConvertToUint64());
//TODO
return false;
}
// User is in a game pressing the talk button (will suppress the microphone for all voice comms from the Steam friends UI)
void SetInGameVoiceSpeaking( CSteamID steamIDUser, bool bSpeaking )
{
PRINT_DEBUG("Steam_Friends::SetInGameVoiceSpeaking\n");
}
// activates the game overlay, with an optional dialog to open
// valid options are "Friends", "Community", "Players", "Settings", "OfficialGameGroup", "Stats", "Achievements"
void ActivateGameOverlay( const char *pchDialog )
{
PRINT_DEBUG("Steam_Friends::ActivateGameOverlay %s\n", pchDialog);
}
// activates game overlay to a specific place
// valid options are
// "steamid" - opens the overlay web browser to the specified user or groups profile
// "chat" - opens a chat window to the specified user, or joins the group chat
// "jointrade" - opens a window to a Steam Trading session that was started with the ISteamEconomy/StartTrade Web API
// "stats" - opens the overlay web browser to the specified user's stats
// "achievements" - opens the overlay web browser to the specified user's achievements
// "friendadd" - opens the overlay in minimal mode prompting the user to add the target user as a friend
// "friendremove" - opens the overlay in minimal mode prompting the user to remove the target friend
// "friendrequestaccept" - opens the overlay in minimal mode prompting the user to accept an incoming friend invite
// "friendrequestignore" - opens the overlay in minimal mode prompting the user to ignore an incoming friend invite
void ActivateGameOverlayToUser( const char *pchDialog, CSteamID steamID )
{
PRINT_DEBUG("Steam_Friends::ActivateGameOverlayToUser %s %llu\n", pchDialog, steamID.ConvertToUint64());
}
// activates game overlay web browser directly to the specified URL
// full address with protocol type is required, e.g. http://www.steamgames.com/
void ActivateGameOverlayToWebPage( const char *pchURL, EActivateGameOverlayToWebPageMode eMode = k_EActivateGameOverlayToWebPageMode_Default )
{
PRINT_DEBUG("Steam_Friends::ActivateGameOverlayToWebPage\n");
}
void ActivateGameOverlayToWebPage( const char *pchURL )
{
PRINT_DEBUG("Steam_Friends::ActivateGameOverlayToWebPage old\n");
ActivateGameOverlayToWebPage( pchURL, k_EActivateGameOverlayToWebPageMode_Default );
}
// activates game overlay to store page for app
void ActivateGameOverlayToStore( AppId_t nAppID, EOverlayToStoreFlag eFlag )
{
PRINT_DEBUG("Steam_Friends::ActivateGameOverlayToStore\n");
}
void ActivateGameOverlayToStore( AppId_t nAppID)
{
PRINT_DEBUG("Steam_Friends::ActivateGameOverlayToStore old\n");
}
// Mark a target user as 'played with'. This is a client-side only feature that requires that the calling user is
// in game
void SetPlayedWith( CSteamID steamIDUserPlayedWith )
{
PRINT_DEBUG("Steam_Friends::SetPlayedWith\n");
}
// activates game overlay to open the invite dialog. Invitations will be sent for the provided lobby.
void ActivateGameOverlayInviteDialog( CSteamID steamIDLobby )
{
PRINT_DEBUG("Steam_Friends::ActivateGameOverlayInviteDialog\n");
}
// gets the small (32x32) avatar of the current user, which is a handle to be used in IClientUtils::GetImageRGBA(), or 0 if none set
int GetSmallFriendAvatar( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::GetSmallFriendAvatar\n");
++img_count;
return (img_count * 3) + 1;
}
// gets the medium (64x64) avatar of the current user, which is a handle to be used in IClientUtils::GetImageRGBA(), or 0 if none set
int GetMediumFriendAvatar( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::GetMediumFriendAvatar\n");
++img_count;
return (img_count * 3) + 2;
}
// gets the large (184x184) avatar of the current user, which is a handle to be used in IClientUtils::GetImageRGBA(), or 0 if none set
// returns -1 if this image has yet to be loaded, in this case wait for a AvatarImageLoaded_t callback and then call this again
int GetLargeFriendAvatar( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::GetLargeFriendAvatar\n");
++img_count;
return (img_count * 3) + 0;
}
int GetFriendAvatar( CSteamID steamIDFriend, int eAvatarSize )
{
PRINT_DEBUG("Steam_Friends::GetFriendAvatar\n");
if (eAvatarSize == k_EAvatarSize32x32) {
return GetSmallFriendAvatar(steamIDFriend);
} else if (eAvatarSize == k_EAvatarSize64x64) {
return GetMediumFriendAvatar(steamIDFriend);
} else if (eAvatarSize == k_EAvatarSize184x184) {
return GetLargeFriendAvatar(steamIDFriend);
} else {
return 0;
}
}
// requests information about a user - persona name & avatar
// if bRequireNameOnly is set, then the avatar of a user isn't downloaded
// - it's a lot slower to download avatars and churns the local cache, so if you don't need avatars, don't request them
// if returns true, it means that data is being requested, and a PersonaStateChanged_t callback will be posted when it's retrieved
// if returns false, it means that we already have all the details about that user, and functions can be called immediately
bool RequestUserInformation( CSteamID steamIDUser, bool bRequireNameOnly )
{
PRINT_DEBUG("Steam_Friends::RequestUserInformation\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//persona_change(steamIDUser, k_EPersonaChangeName);
//We already know everything
return false;
}
// requests information about a clan officer list
// when complete, data is returned in ClanOfficerListResponse_t call result
// this makes available the calls below
// you can only ask about clans that a user is a member of
// note that this won't download avatars automatically; if you get an officer,
// and no avatar image is available, call RequestUserInformation( steamID, false ) to download the avatar
STEAM_CALL_RESULT( ClanOfficerListResponse_t )
SteamAPICall_t RequestClanOfficerList( CSteamID steamIDClan )
{
PRINT_DEBUG("Steam_Friends::RequestClanOfficerList\n");
return 0;
}
// iteration of clan officers - can only be done when a RequestClanOfficerList() call has completed
// returns the steamID of the clan owner
CSteamID GetClanOwner( CSteamID steamIDClan )
{
PRINT_DEBUG("Steam_Friends::GetClanOwner\n");
return k_steamIDNil;
}
// returns the number of officers in a clan (including the owner)
int GetClanOfficerCount( CSteamID steamIDClan )
{
PRINT_DEBUG("Steam_Friends::GetClanOfficerCount\n");
return 0;
}
// returns the steamID of a clan officer, by index, of range [0,GetClanOfficerCount)
CSteamID GetClanOfficerByIndex( CSteamID steamIDClan, int iOfficer )
{
PRINT_DEBUG("Steam_Friends::GetClanOfficerByIndex\n");
return k_steamIDNil;
}
// if current user is chat restricted, he can't send or receive any text/voice chat messages.
// the user can't see custom avatars. But the user can be online and send/recv game invites.
// a chat restricted user can't add friends or join any groups.
uint32 GetUserRestrictions()
{
PRINT_DEBUG("Steam_Friends::GetUserRestrictions\n");
return k_nUserRestrictionNone;
}
EUserRestriction GetUserRestrictions_old()
{
PRINT_DEBUG("Steam_Friends::GetUserRestrictions old\n");
return k_nUserRestrictionNone;
}
// Rich Presence data is automatically shared between friends who are in the same game
// Each user has a set of Key/Value pairs
// Note the following limits: k_cchMaxRichPresenceKeys, k_cchMaxRichPresenceKeyLength, k_cchMaxRichPresenceValueLength
// There are two magic keys:
// "status" - a UTF-8 string that will show up in the 'view game info' dialog in the Steam friends list
// "connect" - a UTF-8 string that contains the command-line for how a friend can connect to a game
// GetFriendRichPresence() returns an empty string "" if no value is set
// SetRichPresence() to a NULL or an empty string deletes the key
// You can iterate the current set of keys for a friend with GetFriendRichPresenceKeyCount()
// and GetFriendRichPresenceKeyByIndex() (typically only used for debugging)
bool SetRichPresence( const char *pchKey, const char *pchValue )
{
PRINT_DEBUG("Steam_Friends::SetRichPresence %s %s\n", pchKey, pchValue ? pchValue : "NULL");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pchValue) {
#ifdef SHOW_DIALOG_RICH_CONNECT
if (std::string(pchKey) == std::string("connect"))
MessageBox(0, pchValue, pchKey, MB_OK);
#endif
(*us.mutable_rich_presence())[pchKey] = pchValue;
} else {
auto to_remove = us.mutable_rich_presence()->find(pchKey);
if (to_remove != us.mutable_rich_presence()->end()) {
us.mutable_rich_presence()->erase(to_remove);
}
}
modified = true;
return true;
}
void ClearRichPresence()
{
PRINT_DEBUG("Steam_Friends::ClearRichPresence\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
us.mutable_rich_presence()->clear();
modified = true;
}
const char *GetFriendRichPresence( CSteamID steamIDFriend, const char *pchKey )
{
PRINT_DEBUG("Steam_Friends::GetFriendRichPresence %llu %s\n", steamIDFriend.ConvertToUint64(), pchKey);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
const char *value = "";
Friend *f = find_friend(steamIDFriend);
if (f && isSameAppId(f)) {
auto result = f->rich_presence().find(pchKey);
if (result != f->rich_presence().end()) value = result->second.c_str();
}
return value;
}
int GetFriendRichPresenceKeyCount( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::GetFriendRichPresenceKeyCount\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
int num = 0;
Friend *f = find_friend(steamIDFriend);
if (f && isSameAppId(f)) num = f->rich_presence().size();
return num;
}
const char *GetFriendRichPresenceKeyByIndex( CSteamID steamIDFriend, int iKey )
{
PRINT_DEBUG("Steam_Friends::GetFriendRichPresenceKeyByIndex\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
const char *key = "";
Friend *f = find_friend(steamIDFriend);
if (f && isSameAppId(f) && f->rich_presence().size() > iKey && iKey >= 0) {
auto friend_data = f->rich_presence().begin();
for (int i = 0; i < iKey; ++i) ++friend_data;
key = friend_data->first.c_str();
}
return key;
}
// Requests rich presence for a specific user.
void RequestFriendRichPresence( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::RequestFriendRichPresence\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
Friend *f = find_friend(steamIDFriend);
if (f) rich_presence_updated(steamIDFriend, settings->get_local_game_id().AppID());
}
// rich invite support
// if the target accepts the invite, the pchConnectString gets added to the command-line for launching the game
// if the game is already running, a GameRichPresenceJoinRequested_t callback is posted containing the connect string
// invites can only be sent to friends
bool InviteUserToGame( CSteamID steamIDFriend, const char *pchConnectString )
{
PRINT_DEBUG("Steam_Friends::InviteUserToGame\n");
//TODO
return true;
}
// recently-played-with friends iteration
// this iterates the entire list of users recently played with, across games
// GetFriendCoplayTime() returns as a unix time
int GetCoplayFriendCount()
{
PRINT_DEBUG("Steam_Friends::GetCoplayFriendCount\n");
return 0;
}
CSteamID GetCoplayFriend( int iCoplayFriend )
{
PRINT_DEBUG("Steam_Friends::GetCoplayFriend\n");
return k_steamIDNil;
}
int GetFriendCoplayTime( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::GetFriendCoplayTime\n");
return 0;
}
AppId_t GetFriendCoplayGame( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::GetFriendCoplayGame\n");
return 0;
}
// chat interface for games
// this allows in-game access to group (clan) chats from in the game
// the behavior is somewhat sophisticated, because the user may or may not be already in the group chat from outside the game or in the overlay
// use ActivateGameOverlayToUser( "chat", steamIDClan ) to open the in-game overlay version of the chat
STEAM_CALL_RESULT( JoinClanChatRoomCompletionResult_t )
SteamAPICall_t JoinClanChatRoom( CSteamID steamIDClan )
{
PRINT_DEBUG("Steam_Friends::JoinClanChatRoom\n");
return 0;
}
bool LeaveClanChatRoom( CSteamID steamIDClan )
{
PRINT_DEBUG("Steam_Friends::LeaveClanChatRoom\n");
return false;
}
int GetClanChatMemberCount( CSteamID steamIDClan )
{
PRINT_DEBUG("Steam_Friends::GetClanChatMemberCount\n");
return 0;
}
CSteamID GetChatMemberByIndex( CSteamID steamIDClan, int iUser )
{
PRINT_DEBUG("Steam_Friends::GetChatMemberByIndex\n");
return k_steamIDNil;
}
bool SendClanChatMessage( CSteamID steamIDClanChat, const char *pchText )
{
PRINT_DEBUG("Steam_Friends::SendClanChatMessage\n");
return false;
}
int GetClanChatMessage( CSteamID steamIDClanChat, int iMessage, void *prgchText, int cchTextMax, EChatEntryType *peChatEntryType, STEAM_OUT_STRUCT() CSteamID *psteamidChatter )
{
PRINT_DEBUG("Steam_Friends::GetClanChatMessage\n");
return 0;
}
bool IsClanChatAdmin( CSteamID steamIDClanChat, CSteamID steamIDUser )
{
PRINT_DEBUG("Steam_Friends::IsClanChatAdmin\n");
return false;
}
// interact with the Steam (game overlay / desktop)
bool IsClanChatWindowOpenInSteam( CSteamID steamIDClanChat )
{
PRINT_DEBUG("Steam_Friends::IsClanChatWindowOpenInSteam\n");
return false;
}
bool OpenClanChatWindowInSteam( CSteamID steamIDClanChat )
{
PRINT_DEBUG("Steam_Friends::OpenClanChatWindowInSteam\n");
return true;
}
bool CloseClanChatWindowInSteam( CSteamID steamIDClanChat )
{
PRINT_DEBUG("Steam_Friends::CloseClanChatWindowInSteam\n");
return true;
}
// peer-to-peer chat interception
// this is so you can show P2P chats inline in the game
bool SetListenForFriendsMessages( bool bInterceptEnabled )
{
PRINT_DEBUG("Steam_Friends::SetListenForFriendsMessages\n");
return true;
}
bool ReplyToFriendMessage( CSteamID steamIDFriend, const char *pchMsgToSend )
{
PRINT_DEBUG("Steam_Friends::ReplyToFriendMessage\n");
return false;
}
int GetFriendMessage( CSteamID steamIDFriend, int iMessageID, void *pvData, int cubData, EChatEntryType *peChatEntryType )
{
PRINT_DEBUG("Steam_Friends::GetFriendMessage\n");
return 0;
}
// following apis
STEAM_CALL_RESULT( FriendsGetFollowerCount_t )
SteamAPICall_t GetFollowerCount( CSteamID steamID )
{
PRINT_DEBUG("Steam_Friends::GetFollowerCount\n");
return 0;
}
STEAM_CALL_RESULT( FriendsIsFollowing_t )
SteamAPICall_t IsFollowing( CSteamID steamID )
{
PRINT_DEBUG("Steam_Friends::IsFollowing\n");
return 0;
}
STEAM_CALL_RESULT( FriendsEnumerateFollowingList_t )
SteamAPICall_t EnumerateFollowingList( uint32 unStartIndex )
{
PRINT_DEBUG("Steam_Friends::EnumerateFollowingList\n");
return 0;
}
bool IsClanPublic( CSteamID steamIDClan )
{
PRINT_DEBUG("Steam_Friends::IsClanPublic\n");
return false;
}
bool IsClanOfficialGameGroup( CSteamID steamIDClan )
{
PRINT_DEBUG("Steam_Friends::IsClanOfficialGameGroup\n");
return false;
}
int GetNumChatsWithUnreadPriorityMessages()
{
PRINT_DEBUG("Steam_Friends::GetNumChatsWithUnreadPriorityMessages\n");
return 0;
}
void RunCallbacks()
{
PRINT_DEBUG("Steam_Friends::RunCallbacks\n");
if (settings->get_lobby() != lobby_id) {
lobby_id = settings->get_lobby();
modified = true;
}
if (modified) {
Common_Message msg;
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
Friend *f = new Friend(us);
f->set_id(settings->get_local_steam_id().ConvertToUint64());
f->set_name(settings->get_local_name());
f->set_appid(settings->get_local_game_id().AppID());
f->set_lobby_id(settings->get_lobby().ConvertToUint64());
msg.set_allocated_friend_(f);
network->sendToAllIndividuals(&msg, true);
modified = false;
last_sent_friends = std::chrono::high_resolution_clock::now();
}
}
void Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::DISCONNECT) {
PRINT_DEBUG("Steam_Friends Disconnect\n");
uint64 id = msg->source_id();
auto f = std::find_if(friends.begin(), friends.end(), [&id](Friend const& item) { return item.id() == id; });
if (friends.end() != f) {
persona_change((uint64)f->id(), k_EPersonaChangeStatus);
friends.erase(f);
}
}
if (msg->low_level().type() == Low_Level::CONNECT) {
PRINT_DEBUG("Steam_Friends Connect\n");
Common_Message msg_;
msg_.set_source_id(settings->get_local_steam_id().ConvertToUint64());
msg_.set_dest_id(msg->source_id());
Friend *f = new Friend(us);
f->set_id(settings->get_local_steam_id().ConvertToUint64());
f->set_name(settings->get_local_name());
f->set_appid(settings->get_local_game_id().AppID());
f->set_lobby_id(settings->get_lobby().ConvertToUint64());
msg_.set_allocated_friend_(f);
network->sendTo(&msg_, true);
}
}
if (msg->has_friend_()) {
PRINT_DEBUG("Steam_Friends Friend %llu %llu\n", msg->friend_().id(), msg->friend_().lobby_id());
Friend *f = find_friend((uint64)msg->friend_().id());
if (!f) {
if (msg->friend_().id() != settings->get_local_steam_id().ConvertToUint64()) {
friends.push_back(msg->friend_());
persona_change((uint64)msg->friend_().id(), k_EPersonaChangeName);
}
} else {
std::map<std::string, std::string> map1(f->rich_presence().begin(), f->rich_presence().end());
std::map<std::string, std::string> map2(msg->friend_().rich_presence().begin(), msg->friend_().rich_presence().end());
if (map1 != map2) {
//The App ID of the game. This should always be the current game.
if (isSameAppId(f)) {
rich_presence_updated((uint64)msg->friend_().id(), (uint64)msg->friend_().appid());
}
}
//TODO: callbacks?
*f = msg->friend_();
}
}
if (msg->has_friend_messages()) {
if (msg->friend_messages().type() == Friend_Messages::LOBBY_INVITE) {
PRINT_DEBUG("Steam_Friends Got Lobby Invite\n");
//TODO: the user should accept the invite first but we auto accept it because there's no gui yet
GameLobbyJoinRequested_t data;
data.m_steamIDLobby = CSteamID((uint64)msg->friend_messages().lobby_id());
data.m_steamIDFriend = CSteamID((uint64)msg->source_id());
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
}
};

View File

@ -0,0 +1,110 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Game_Coordinator :
public ISteamGameCoordinator
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
public:
static void steam_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_gamecoordinator_callback\n");
Steam_Game_Coordinator *steam_gamecoordinator = (Steam_Game_Coordinator *)object;
steam_gamecoordinator->Callback(msg);
}
static void steam_run_every_runcb(void *object)
{
PRINT_DEBUG("steam_gamecoordinator_run_every_runcb\n");
Steam_Game_Coordinator *steam_gamecoordinator = (Steam_Game_Coordinator *)object;
steam_gamecoordinator->RunCallbacks();
}
Steam_Game_Coordinator(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->run_every_runcb = run_every_runcb;
//this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Game_Coordinator::steam_callback, this);
this->run_every_runcb->add(&Steam_Game_Coordinator::steam_run_every_runcb, this);
this->callback_results = callback_results;
this->callbacks = callbacks;
}
~Steam_Game_Coordinator()
{
//TODO rm network callbacks
this->run_every_runcb->remove(&Steam_Game_Coordinator::steam_run_every_runcb, this);
}
// sends a message to the Game Coordinator
EGCResults SendMessage_( uint32 unMsgType, const void *pubData, uint32 cubData )
{
PRINT_DEBUG("Steam_Game_Coordinator::SendMessage %u\n", unMsgType);
return k_EGCResultOK;
}
// returns true if there is a message waiting from the game coordinator
bool IsMessageAvailable( uint32 *pcubMsgSize )
{
PRINT_DEBUG("Steam_Game_Coordinator::IsMessageAvailable\n");
return false;
}
// fills the provided buffer with the first message in the queue and returns k_EGCResultOK or
// returns k_EGCResultNoMessage if there is no message waiting. pcubMsgSize is filled with the message size.
// If the provided buffer is not large enough to fit the entire message, k_EGCResultBufferTooSmall is returned
// and the message remains at the head of the queue.
EGCResults RetrieveMessage( uint32 *punMsgType, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize )
{
PRINT_DEBUG("Steam_Game_Coordinator::RetrieveMessage\n");
return k_EGCResultNoMessage;
}
void RunCallbacks()
{
}
void Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::CONNECT) {
}
if (msg->low_level().type() == Low_Level::DISCONNECT) {
}
}
if (msg->has_networking_sockets()) {
}
}
};

201
dll/steam_gamesearch.h Normal file
View File

@ -0,0 +1,201 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Game_Search :
public ISteamGameSearch
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
FSteamNetworkingSocketsDebugOutput debug_function;
public:
static void steam_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_gamesearch_callback\n");
Steam_Game_Search *steam_gamesearch = (Steam_Game_Search *)object;
steam_gamesearch->Callback(msg);
}
static void steam_run_every_runcb(void *object)
{
PRINT_DEBUG("steam_gamesearch_run_every_runcb\n");
Steam_Game_Search *steam_gamesearch = (Steam_Game_Search *)object;
steam_gamesearch->RunCallbacks();
}
Steam_Game_Search(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->run_every_runcb = run_every_runcb;
//this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Game_Search::steam_callback, this);
this->run_every_runcb->add(&Steam_Game_Search::steam_run_every_runcb, this);
this->callback_results = callback_results;
this->callbacks = callbacks;
}
~Steam_Game_Search()
{
//TODO rm network callbacks
this->run_every_runcb->remove(&Steam_Game_Search::steam_run_every_runcb, this);
}
// =============================================================================================
// Game Player APIs
// a keyname and a list of comma separated values: one of which is must be found in order for the match to qualify
// fails if a search is currently in progress
EGameSearchErrorCode_t AddGameSearchParams( const char *pchKeyToFind, const char *pchValuesToFind )
{
PRINT_DEBUG("Steam_Game_Search::AddGameSearchParams\n");
}
// all players in lobby enter the queue and await a SearchForGameNotificationCallback_t callback. fails if another search is currently in progress
// if not the owner of the lobby or search already in progress this call fails
// periodic callbacks will be sent as queue time estimates change
EGameSearchErrorCode_t SearchForGameWithLobby( CSteamID steamIDLobby, int nPlayerMin, int nPlayerMax )
{
PRINT_DEBUG("Steam_Game_Search::SearchForGameWithLobby\n");
}
// user enter the queue and await a SearchForGameNotificationCallback_t callback. fails if another search is currently in progress
// periodic callbacks will be sent as queue time estimates change
EGameSearchErrorCode_t SearchForGameSolo( int nPlayerMin, int nPlayerMax )
{
PRINT_DEBUG("Steam_Game_Search::SearchForGameSolo\n");
}
// after receiving SearchForGameResultCallback_t, accept or decline the game
// multiple SearchForGameResultCallback_t will follow as players accept game until the host starts or cancels the game
EGameSearchErrorCode_t AcceptGame()
{
PRINT_DEBUG("Steam_Game_Search::AcceptGame\n");
}
EGameSearchErrorCode_t DeclineGame()
{
PRINT_DEBUG("Steam_Game_Search::DeclineGame\n");
}
// after receiving GameStartedByHostCallback_t get connection details to server
EGameSearchErrorCode_t RetrieveConnectionDetails( CSteamID steamIDHost, char *pchConnectionDetails, int cubConnectionDetails )
{
PRINT_DEBUG("Steam_Game_Search::RetrieveConnectionDetails\n");
}
// leaves queue if still waiting
EGameSearchErrorCode_t EndGameSearch()
{
PRINT_DEBUG("Steam_Game_Search::EndGameSearch\n");
}
// =============================================================================================
// Game Host APIs
// a keyname and a list of comma separated values: all the values you allow
EGameSearchErrorCode_t SetGameHostParams( const char *pchKey, const char *pchValue )
{
PRINT_DEBUG("Steam_Game_Search::SetGameHostParams\n");
}
// set connection details for players once game is found so they can connect to this server
EGameSearchErrorCode_t SetConnectionDetails( const char *pchConnectionDetails, int cubConnectionDetails )
{
PRINT_DEBUG("Steam_Game_Search::SetConnectionDetails\n");
}
// mark server as available for more players with nPlayerMin,nPlayerMax desired
// accept no lobbies with playercount greater than nMaxTeamSize
// the set of lobbies returned must be partitionable into teams of no more than nMaxTeamSize
// RequestPlayersForGameNotificationCallback_t callback will be sent when the search has started
// multple RequestPlayersForGameResultCallback_t callbacks will follow when players are found
EGameSearchErrorCode_t RequestPlayersForGame( int nPlayerMin, int nPlayerMax, int nMaxTeamSize )
{
PRINT_DEBUG("Steam_Game_Search::RequestPlayersForGame\n");
}
// accept the player list and release connection details to players
// players will only be given connection details and host steamid when this is called
// ( allows host to accept after all players confirm, some confirm, or none confirm. decision is entirely up to the host )
EGameSearchErrorCode_t HostConfirmGameStart( uint64 ullUniqueGameID )
{
PRINT_DEBUG("Steam_Game_Search::HostConfirmGameStart\n");
}
// cancel request and leave the pool of game hosts looking for players
// if a set of players has already been sent to host, all players will receive SearchForGameHostFailedToConfirm_t
EGameSearchErrorCode_t CancelRequestPlayersForGame()
{
PRINT_DEBUG("Steam_Game_Search::CancelRequestPlayersForGame\n");
}
// submit a result for one player. does not end the game. ullUniqueGameID continues to describe this game
EGameSearchErrorCode_t SubmitPlayerResult( uint64 ullUniqueGameID, CSteamID steamIDPlayer, EPlayerResult_t EPlayerResult )
{
PRINT_DEBUG("Steam_Game_Search::SubmitPlayerResult\n");
}
// ends the game. no further SubmitPlayerResults for ullUniqueGameID will be accepted
// any future requests will provide a new ullUniqueGameID
EGameSearchErrorCode_t EndGame( uint64 ullUniqueGameID )
{
PRINT_DEBUG("Steam_Game_Search::EndGame\n");
}
void RunCallbacks()
{
}
void Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::CONNECT) {
}
if (msg->low_level().type() == Low_Level::DISCONNECT) {
}
}
if (msg->has_networking_sockets()) {
}
}
};

599
dll/steam_gameserver.cpp Normal file
View File

@ -0,0 +1,599 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "steam_gameserver.h"
#define SEND_SERVER_RATE 5.0
Steam_GameServer::Steam_GameServer(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks)
{
this->network = network;
this->settings = settings;
server_data.set_id(settings->get_local_steam_id().ConvertToUint64());
this->callbacks = callbacks;
ticket_manager = new Auth_Ticket_Manager(settings, network, callbacks);
}
Steam_GameServer::~Steam_GameServer()
{
delete ticket_manager;
}
//
// Basic server data. These properties, if set, must be set before before calling LogOn. They
// may not be changed after logged in.
//
/// This is called by SteamGameServer_Init, and you will usually not need to call it directly
bool Steam_GameServer::InitGameServer( uint32 unIP, uint16 usGamePort, uint16 usQueryPort, uint32 unFlags, AppId_t nGameAppId, const char *pchVersionString )
{
PRINT_DEBUG("InitGameServer\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (logged_in) return false; // may not be changed after logged in.
std::string version(pchVersionString);
version.erase(std::remove(version.begin(), version.end(), ' '), version.end());
version.erase(std::remove(version.begin(), version.end(), '.'), version.end());
server_data.set_version(stoi(version));
server_data.set_ip(unIP);
server_data.set_port(usGamePort);
server_data.set_query_port(usQueryPort);
server_data.set_offline(false);
if (!settings->get_local_game_id().AppID()) settings->set_game_id(CGameID(nGameAppId));
flags = unFlags;
policy_response_called = false;
call_servers_connected = false;
call_servers_disconnected = false;
return true;
}
/// Game product identifier. This is currently used by the master server for version checking purposes.
/// It's a required field, but will eventually will go away, and the AppID will be used for this purpose.
void Steam_GameServer::SetProduct( const char *pszProduct )
{
PRINT_DEBUG("SetProduct\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_product(pszProduct);
}
/// Description of the game. This is a required field and is displayed in the steam server browser....for now.
/// This is a required field, but it will go away eventually, as the data should be determined from the AppID.
void Steam_GameServer::SetGameDescription( const char *pszGameDescription )
{
PRINT_DEBUG("SetGameDescription\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_game_description(pszGameDescription);
}
/// If your game is a "mod," pass the string that identifies it. The default is an empty string, meaning
/// this application is the original game, not a mod.
///
/// @see k_cbMaxGameServerGameDir
void Steam_GameServer::SetModDir( const char *pszModDir )
{
PRINT_DEBUG("SetModDir\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_mod_dir(pszModDir);
}
/// Is this is a dedicated server? The default value is false.
void Steam_GameServer::SetDedicatedServer( bool bDedicated )
{
PRINT_DEBUG("SetDedicatedServer\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_dedicated_server(bDedicated);
}
//
// Login
//
/// Begin process to login to a persistent game server account
///
/// You need to register for callbacks to determine the result of this operation.
/// @see SteamServersConnected_t
/// @see SteamServerConnectFailure_t
/// @see SteamServersDisconnected_t
void Steam_GameServer::LogOn( const char *pszToken )
{
PRINT_DEBUG("LogOn %s\n", pszToken);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
call_servers_connected = true;
logged_in = true;
}
/// Login to a generic, anonymous account.
///
/// Note: in previous versions of the SDK, this was automatically called within SteamGameServer_Init,
/// but this is no longer the case.
void Steam_GameServer::LogOnAnonymous()
{
PRINT_DEBUG("LogOnAnonymous\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
call_servers_connected = true;
logged_in = true;
}
/// Begin process of logging game server out of steam
void Steam_GameServer::LogOff()
{
PRINT_DEBUG("LogOff\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (logged_in) {
call_servers_disconnected = true;
}
logged_in = false;
}
// status functions
bool Steam_GameServer::BLoggedOn()
{
PRINT_DEBUG("BLoggedOn\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return logged_in;
}
bool Steam_GameServer::BSecure()
{
PRINT_DEBUG("BSecure\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return flags == eServerModeAuthenticationAndSecure;
}
CSteamID Steam_GameServer::GetSteamID()
{
PRINT_DEBUG("Steam_GameServer::GetSteamID\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return settings->get_local_steam_id();
}
/// Returns true if the master server has requested a restart.
/// Only returns true once per request.
bool Steam_GameServer::WasRestartRequested()
{
PRINT_DEBUG("WasRestartRequested\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
//
// Server state. These properties may be changed at any time.
//
/// Max player count that will be reported to server browser and client queries
void Steam_GameServer::SetMaxPlayerCount( int cPlayersMax )
{
PRINT_DEBUG("SetMaxPlayerCount\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_max_player_count(cPlayersMax);
}
/// Number of bots. Default value is zero
void Steam_GameServer::SetBotPlayerCount( int cBotplayers )
{
PRINT_DEBUG("SetBotPlayerCount\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_bot_player_count(cBotplayers);
}
/// Set the name of server as it will appear in the server browser
///
/// @see k_cbMaxGameServerName
void Steam_GameServer::SetServerName( const char *pszServerName )
{
PRINT_DEBUG("SetServerName\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_server_name(pszServerName);
}
/// Set name of map to report in the server browser
///
/// @see k_cbMaxGameServerName
void Steam_GameServer::SetMapName( const char *pszMapName )
{
PRINT_DEBUG("SetMapName\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_map_name(pszMapName);
}
/// Let people know if your server will require a password
void Steam_GameServer::SetPasswordProtected( bool bPasswordProtected )
{
PRINT_DEBUG("SetPasswordProtected\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_password_protected(bPasswordProtected);
}
/// Spectator server. The default value is zero, meaning the service
/// is not used.
void Steam_GameServer::SetSpectatorPort( uint16 unSpectatorPort )
{
PRINT_DEBUG("SetSpectatorPort\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_spectator_port(unSpectatorPort);
}
/// Name of the spectator server. (Only used if spectator port is nonzero.)
///
/// @see k_cbMaxGameServerMapName
void Steam_GameServer::SetSpectatorServerName( const char *pszSpectatorServerName )
{
PRINT_DEBUG("SetSpectatorServerName\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_spectator_server_name(pszSpectatorServerName);
}
/// Call this to clear the whole list of key/values that are sent in rules queries.
void Steam_GameServer::ClearAllKeyValues()
{
PRINT_DEBUG("ClearAllKeyValues\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.clear_values();
}
/// Call this to add/update a key/value pair.
void Steam_GameServer::SetKeyValue( const char *pKey, const char *pValue )
{
PRINT_DEBUG("SetKeyValue\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
(*server_data.mutable_values())[std::string(pKey)] = std::string(pValue);
}
/// Sets a string defining the "gametags" for this server, this is optional, but if it is set
/// it allows users to filter in the matchmaking/server-browser interfaces based on the value
///
/// @see k_cbMaxGameServerTags
void Steam_GameServer::SetGameTags( const char *pchGameTags )
{
PRINT_DEBUG("SetGameTags\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_tags(pchGameTags);
}
/// Sets a string defining the "gamedata" for this server, this is optional, but if it is set
/// it allows users to filter in the matchmaking/server-browser interfaces based on the value
/// don't set this unless it actually changes, its only uploaded to the master once (when
/// acknowledged)
///
/// @see k_cbMaxGameServerGameData
void Steam_GameServer::SetGameData( const char *pchGameData )
{
PRINT_DEBUG("SetGameData\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_gamedata(pchGameData);
}
/// Region identifier. This is an optional field, the default value is empty, meaning the "world" region
void Steam_GameServer::SetRegion( const char *pszRegion )
{
PRINT_DEBUG("SetRegion\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_region(pszRegion);
}
//
// Player list management / authentication
//
// Handles receiving a new connection from a Steam user. This call will ask the Steam
// servers to validate the users identity, app ownership, and VAC status. If the Steam servers
// are off-line, then it will validate the cached ticket itself which will validate app ownership
// and identity. The AuthBlob here should be acquired on the game client using SteamUser()->InitiateGameConnection()
// and must then be sent up to the game server for authentication.
//
// Return Value: returns true if the users ticket passes basic checks. pSteamIDUser will contain the Steam ID of this user. pSteamIDUser must NOT be NULL
// If the call succeeds then you should expect a GSClientApprove_t or GSClientDeny_t callback which will tell you whether authentication
// for the user has succeeded or failed (the steamid in the callback will match the one returned by this call)
bool Steam_GameServer::SendUserConnectAndAuthenticate( uint32 unIPClient, const void *pvAuthBlob, uint32 cubAuthBlobSize, CSteamID *pSteamIDUser )
{
PRINT_DEBUG("SendUserConnectAndAuthenticate %u %u\n", unIPClient, cubAuthBlobSize);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return ticket_manager->SendUserConnectAndAuthenticate(unIPClient, pvAuthBlob, cubAuthBlobSize, pSteamIDUser);
}
// Creates a fake user (ie, a bot) which will be listed as playing on the server, but skips validation.
//
// Return Value: Returns a SteamID for the user to be tracked with, you should call HandleUserDisconnect()
// when this user leaves the server just like you would for a real user.
CSteamID Steam_GameServer::CreateUnauthenticatedUserConnection()
{
PRINT_DEBUG("CreateUnauthenticatedUserConnection\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return ticket_manager->fakeUser();
}
// Should be called whenever a user leaves our game server, this lets Steam internally
// track which users are currently on which servers for the purposes of preventing a single
// account being logged into multiple servers, showing who is currently on a server, etc.
void Steam_GameServer::SendUserDisconnect( CSteamID steamIDUser )
{
PRINT_DEBUG("SendUserDisconnect\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
ticket_manager->endAuth(steamIDUser);
}
// Update the data to be displayed in the server browser and matchmaking interfaces for a user
// currently connected to the server. For regular users you must call this after you receive a
// GSUserValidationSuccess callback.
//
// Return Value: true if successful, false if failure (ie, steamIDUser wasn't for an active player)
bool Steam_GameServer::BUpdateUserData( CSteamID steamIDUser, const char *pchPlayerName, uint32 uScore )
{
PRINT_DEBUG("BUpdateUserData\n");
return true;
}
// New auth system APIs - do not mix with the old auth system APIs.
// ----------------------------------------------------------------
// Retrieve ticket to be sent to the entity who wishes to authenticate you ( using BeginAuthSession API ).
// pcbTicket retrieves the length of the actual ticket.
HAuthTicket Steam_GameServer::GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
{
PRINT_DEBUG("Steam_GameServer::GetAuthSessionTicket\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return ticket_manager->getTicket(pTicket, cbMaxTicket, pcbTicket);
}
// Authenticate ticket ( from GetAuthSessionTicket ) from entity steamID to be sure it is valid and isnt reused
// Registers for callbacks if the entity goes offline or cancels the ticket ( see ValidateAuthTicketResponse_t callback and EAuthSessionResponse )
EBeginAuthSessionResult Steam_GameServer::BeginAuthSession( const void *pAuthTicket, int cbAuthTicket, CSteamID steamID )
{
PRINT_DEBUG("Steam_GameServer::BeginAuthSession %i %llu\n", cbAuthTicket, steamID.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return ticket_manager->beginAuth(pAuthTicket, cbAuthTicket, steamID );
}
// Stop tracking started by BeginAuthSession - called when no longer playing game with this entity
void Steam_GameServer::EndAuthSession( CSteamID steamID )
{
PRINT_DEBUG("Steam_GameServer::EndAuthSession %llu\n", steamID.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
ticket_manager->endAuth(steamID);
}
// Cancel auth ticket from GetAuthSessionTicket, called when no longer playing game with the entity you gave the ticket to
void Steam_GameServer::CancelAuthTicket( HAuthTicket hAuthTicket )
{
PRINT_DEBUG("Steam_GameServer::CancelAuthTicket\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
ticket_manager->cancelTicket(hAuthTicket);
}
// After receiving a user's authentication data, and passing it to SendUserConnectAndAuthenticate, use this function
// to determine if the user owns downloadable content specified by the provided AppID.
EUserHasLicenseForAppResult Steam_GameServer::UserHasLicenseForApp( CSteamID steamID, AppId_t appID )
{
PRINT_DEBUG("Steam_GameServer::UserHasLicenseForApp\n");
return k_EUserHasLicenseResultHasLicense;
}
// Ask if a user in in the specified group, results returns async by GSUserGroupStatus_t
// returns false if we're not connected to the steam servers and thus cannot ask
bool Steam_GameServer::RequestUserGroupStatus( CSteamID steamIDUser, CSteamID steamIDGroup )
{
PRINT_DEBUG("RequestUserGroupStatus\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
// these two functions s are deprecated, and will not return results
// they will be removed in a future version of the SDK
void Steam_GameServer::GetGameplayStats( )
{
PRINT_DEBUG("GetGameplayStats\n");
}
STEAM_CALL_RESULT( GSReputation_t )
SteamAPICall_t Steam_GameServer::GetServerReputation()
{
PRINT_DEBUG("GetServerReputation\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Returns the public IP of the server according to Steam, useful when the server is
// behind NAT and you want to advertise its IP in a lobby for other clients to directly
// connect to
uint32 Steam_GameServer::GetPublicIP()
{
PRINT_DEBUG("GetPublicIP\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
uint32 ip = network->getOwnIP();
PRINT_DEBUG("%X\n", ip);
return ip;
}
// These are in GameSocketShare mode, where instead of ISteamGameServer creating its own
// socket to talk to the master server on, it lets the game use its socket to forward messages
// back and forth. This prevents us from requiring server ops to open up yet another port
// in their firewalls.
//
// the IP address and port should be in host order, i.e 127.0.0.1 == 0x7f000001
// These are used when you've elected to multiplex the game server's UDP socket
// rather than having the master server updater use its own sockets.
//
// Source games use this to simplify the job of the server admins, so they
// don't have to open up more ports on their firewalls.
// Call this when a packet that starts with 0xFFFFFFFF comes in. That means
// it's for us.
bool Steam_GameServer::HandleIncomingPacket( const void *pData, int cbData, uint32 srcIP, uint16 srcPort )
{
PRINT_DEBUG("HandleIncomingPacket %i %X %i\n", cbData, srcIP, srcPort);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
// AFTER calling HandleIncomingPacket for any packets that came in that frame, call this.
// This gets a packet that the master server updater needs to send out on UDP.
// It returns the length of the packet it wants to send, or 0 if there are no more packets to send.
// Call this each frame until it returns 0.
int Steam_GameServer::GetNextOutgoingPacket( void *pOut, int cbMaxOut, uint32 *pNetAdr, uint16 *pPort )
{
PRINT_DEBUG("GetNextOutgoingPacket\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (outgoing_packets.size() == 0) return 0;
if (outgoing_packets.back().data.size() < cbMaxOut) cbMaxOut = outgoing_packets.back().data.size();
if (pOut) memcpy(pOut, outgoing_packets.back().data.data(), cbMaxOut);
if (pNetAdr) *pNetAdr = outgoing_packets.back().ip;
if (pPort) *pPort = outgoing_packets.back().port;
outgoing_packets.pop_back();
return cbMaxOut;
}
//
// Control heartbeats / advertisement with master server
//
// Call this as often as you like to tell the master server updater whether or not
// you want it to be active (default: off).
void Steam_GameServer::EnableHeartbeats( bool bActive )
{
PRINT_DEBUG("EnableHeartbeats\n");
}
// You usually don't need to modify this.
// Pass -1 to use the default value for iHeartbeatInterval.
// Some mods change this.
void Steam_GameServer::SetHeartbeatInterval( int iHeartbeatInterval )
{
PRINT_DEBUG("SetHeartbeatInterval\n");
}
// Force a heartbeat to steam at the next opportunity
void Steam_GameServer::ForceHeartbeat()
{
PRINT_DEBUG("ForceHeartbeat\n");
}
// associate this game server with this clan for the purposes of computing player compat
STEAM_CALL_RESULT( AssociateWithClanResult_t )
SteamAPICall_t Steam_GameServer::AssociateWithClan( CSteamID steamIDClan )
{
PRINT_DEBUG("AssociateWithClan\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// ask if any of the current players dont want to play with this new player - or vice versa
STEAM_CALL_RESULT( ComputeNewPlayerCompatibilityResult_t )
SteamAPICall_t Steam_GameServer::ComputeNewPlayerCompatibility( CSteamID steamIDNewPlayer )
{
PRINT_DEBUG("ComputeNewPlayerCompatibility\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
void Steam_GameServer::RunCallbacks()
{
bool temp_call_servers_connected = call_servers_connected;
bool temp_call_servers_disconnected = call_servers_disconnected;
call_servers_disconnected = call_servers_connected = false;
if (temp_call_servers_connected) {
PRINT_DEBUG("Steam_GameServer::SteamServersConnected_t\n");
SteamServersConnected_t data;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
if (logged_in && !policy_response_called) {
PRINT_DEBUG("Steam_GameServer::GSPolicyResponse_t\n");
GSPolicyResponse_t data;
data.m_bSecure = flags == eServerModeAuthenticationAndSecure;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
policy_response_called = true;
}
if (logged_in && check_timedout(last_sent_server_info, SEND_SERVER_RATE)) {
PRINT_DEBUG("Steam_GameServer Sending Gameserver\n");
Common_Message msg;
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
server_data.set_appid(settings->get_local_game_id().AppID());
msg.set_allocated_gameserver(new Gameserver(server_data));
msg.mutable_gameserver()->set_num_players(ticket_manager->countInboundAuth());
network->sendToAllIndividuals(&msg, true);
last_sent_server_info = std::chrono::high_resolution_clock::now();
}
if (temp_call_servers_disconnected) {
SteamServersDisconnected_t data;
data.m_eResult = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
if (!logged_in) {
Common_Message msg;
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
msg.set_allocated_gameserver(new Gameserver(server_data));
msg.mutable_gameserver()->set_offline(true);
network->sendToAllIndividuals(&msg, true);
}
}
}

284
dll/steam_gameserver.h Normal file
View File

@ -0,0 +1,284 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
//-----------------------------------------------------------------------------
// Purpose: Functions for authenticating users via Steam to play on a game server
//-----------------------------------------------------------------------------
struct Gameserver_Outgoing_Packet {
std::string data;
uint32 ip;
uint16 port;
};
class Steam_GameServer : public ISteamGameServer
{
class Settings *settings;
class Networking *network;
class SteamCallBacks *callbacks;
CSteamID steam_id;
bool call_servers_connected = false;
bool logged_in = false;
bool call_servers_disconnected = false;
Gameserver server_data;
uint32 flags;
bool policy_response_called;
std::chrono::high_resolution_clock::time_point last_sent_server_info;
Auth_Ticket_Manager *ticket_manager;
std::vector<struct Gameserver_Outgoing_Packet> outgoing_packets;
public:
Steam_GameServer(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks);
~Steam_GameServer();
//
// Basic server data. These properties, if set, must be set before before calling LogOn. They
// may not be changed after logged in.
//
/// This is called by SteamGameServer_Init, and you will usually not need to call it directly
bool InitGameServer( uint32 unIP, uint16 usGamePort, uint16 usQueryPort, uint32 unFlags, AppId_t nGameAppId, const char *pchVersionString );
/// Game product identifier. This is currently used by the master server for version checking purposes.
/// It's a required field, but will eventually will go away, and the AppID will be used for this purpose.
void SetProduct( const char *pszProduct );
/// Description of the game. This is a required field and is displayed in the steam server browser....for now.
/// This is a required field, but it will go away eventually, as the data should be determined from the AppID.
void SetGameDescription( const char *pszGameDescription );
/// If your game is a "mod," pass the string that identifies it. The default is an empty string, meaning
/// this application is the original game, not a mod.
///
/// @see k_cbMaxGameServerGameDir
void SetModDir( const char *pszModDir );
/// Is this is a dedicated server? The default value is false.
void SetDedicatedServer( bool bDedicated );
//
// Login
//
/// Begin process to login to a persistent game server account
///
/// You need to register for callbacks to determine the result of this operation.
/// @see SteamServersConnected_t
/// @see SteamServerConnectFailure_t
/// @see SteamServersDisconnected_t
void LogOn( const char *pszToken );
/// Login to a generic, anonymous account.
///
/// Note: in previous versions of the SDK, this was automatically called within SteamGameServer_Init,
/// but this is no longer the case.
void LogOnAnonymous();
/// Begin process of logging game server out of steam
void LogOff();
// status functions
bool BLoggedOn();
bool BSecure();
CSteamID GetSteamID();
/// Returns true if the master server has requested a restart.
/// Only returns true once per request.
bool WasRestartRequested();
//
// Server state. These properties may be changed at any time.
//
/// Max player count that will be reported to server browser and client queries
void SetMaxPlayerCount( int cPlayersMax );
/// Number of bots. Default value is zero
void SetBotPlayerCount( int cBotplayers );
/// Set the name of server as it will appear in the server browser
///
/// @see k_cbMaxGameServerName
void SetServerName( const char *pszServerName );
/// Set name of map to report in the server browser
///
/// @see k_cbMaxGameServerName
void SetMapName( const char *pszMapName );
/// Let people know if your server will require a password
void SetPasswordProtected( bool bPasswordProtected );
/// Spectator server. The default value is zero, meaning the service
/// is not used.
void SetSpectatorPort( uint16 unSpectatorPort );
/// Name of the spectator server. (Only used if spectator port is nonzero.)
///
/// @see k_cbMaxGameServerMapName
void SetSpectatorServerName( const char *pszSpectatorServerName );
/// Call this to clear the whole list of key/values that are sent in rules queries.
void ClearAllKeyValues();
/// Call this to add/update a key/value pair.
void SetKeyValue( const char *pKey, const char *pValue );
/// Sets a string defining the "gametags" for this server, this is optional, but if it is set
/// it allows users to filter in the matchmaking/server-browser interfaces based on the value
///
/// @see k_cbMaxGameServerTags
void SetGameTags( const char *pchGameTags );
/// Sets a string defining the "gamedata" for this server, this is optional, but if it is set
/// it allows users to filter in the matchmaking/server-browser interfaces based on the value
/// don't set this unless it actually changes, its only uploaded to the master once (when
/// acknowledged)
///
/// @see k_cbMaxGameServerGameData
void SetGameData( const char *pchGameData );
/// Region identifier. This is an optional field, the default value is empty, meaning the "world" region
void SetRegion( const char *pszRegion );
//
// Player list management / authentication
//
// Handles receiving a new connection from a Steam user. This call will ask the Steam
// servers to validate the users identity, app ownership, and VAC status. If the Steam servers
// are off-line, then it will validate the cached ticket itself which will validate app ownership
// and identity. The AuthBlob here should be acquired on the game client using SteamUser()->InitiateGameConnection()
// and must then be sent up to the game server for authentication.
//
// Return Value: returns true if the users ticket passes basic checks. pSteamIDUser will contain the Steam ID of this user. pSteamIDUser must NOT be NULL
// If the call succeeds then you should expect a GSClientApprove_t or GSClientDeny_t callback which will tell you whether authentication
// for the user has succeeded or failed (the steamid in the callback will match the one returned by this call)
bool SendUserConnectAndAuthenticate( uint32 unIPClient, const void *pvAuthBlob, uint32 cubAuthBlobSize, CSteamID *pSteamIDUser );
// Creates a fake user (ie, a bot) which will be listed as playing on the server, but skips validation.
//
// Return Value: Returns a SteamID for the user to be tracked with, you should call HandleUserDisconnect()
// when this user leaves the server just like you would for a real user.
CSteamID CreateUnauthenticatedUserConnection();
// Should be called whenever a user leaves our game server, this lets Steam internally
// track which users are currently on which servers for the purposes of preventing a single
// account being logged into multiple servers, showing who is currently on a server, etc.
void SendUserDisconnect( CSteamID steamIDUser );
// Update the data to be displayed in the server browser and matchmaking interfaces for a user
// currently connected to the server. For regular users you must call this after you receive a
// GSUserValidationSuccess callback.
//
// Return Value: true if successful, false if failure (ie, steamIDUser wasn't for an active player)
bool BUpdateUserData( CSteamID steamIDUser, const char *pchPlayerName, uint32 uScore );
// New auth system APIs - do not mix with the old auth system APIs.
// ----------------------------------------------------------------
// Retrieve ticket to be sent to the entity who wishes to authenticate you ( using BeginAuthSession API ).
// pcbTicket retrieves the length of the actual ticket.
HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket );
// Authenticate ticket ( from GetAuthSessionTicket ) from entity steamID to be sure it is valid and isnt reused
// Registers for callbacks if the entity goes offline or cancels the ticket ( see ValidateAuthTicketResponse_t callback and EAuthSessionResponse )
EBeginAuthSessionResult BeginAuthSession( const void *pAuthTicket, int cbAuthTicket, CSteamID steamID );
// Stop tracking started by BeginAuthSession - called when no longer playing game with this entity
void EndAuthSession( CSteamID steamID );
// Cancel auth ticket from GetAuthSessionTicket, called when no longer playing game with the entity you gave the ticket to
void CancelAuthTicket( HAuthTicket hAuthTicket );
// After receiving a user's authentication data, and passing it to SendUserConnectAndAuthenticate, use this function
// to determine if the user owns downloadable content specified by the provided AppID.
EUserHasLicenseForAppResult UserHasLicenseForApp( CSteamID steamID, AppId_t appID );
// Ask if a user in in the specified group, results returns async by GSUserGroupStatus_t
// returns false if we're not connected to the steam servers and thus cannot ask
bool RequestUserGroupStatus( CSteamID steamIDUser, CSteamID steamIDGroup );
// these two functions s are deprecated, and will not return results
// they will be removed in a future version of the SDK
void GetGameplayStats( );
STEAM_CALL_RESULT( GSReputation_t )
SteamAPICall_t GetServerReputation();
// Returns the public IP of the server according to Steam, useful when the server is
// behind NAT and you want to advertise its IP in a lobby for other clients to directly
// connect to
uint32 GetPublicIP();
// These are in GameSocketShare mode, where instead of ISteamGameServer creating its own
// socket to talk to the master server on, it lets the game use its socket to forward messages
// back and forth. This prevents us from requiring server ops to open up yet another port
// in their firewalls.
//
// the IP address and port should be in host order, i.e 127.0.0.1 == 0x7f000001
// These are used when you've elected to multiplex the game server's UDP socket
// rather than having the master server updater use its own sockets.
//
// Source games use this to simplify the job of the server admins, so they
// don't have to open up more ports on their firewalls.
// Call this when a packet that starts with 0xFFFFFFFF comes in. That means
// it's for us.
bool HandleIncomingPacket( const void *pData, int cbData, uint32 srcIP, uint16 srcPort );
// AFTER calling HandleIncomingPacket for any packets that came in that frame, call this.
// This gets a packet that the master server updater needs to send out on UDP.
// It returns the length of the packet it wants to send, or 0 if there are no more packets to send.
// Call this each frame until it returns 0.
int GetNextOutgoingPacket( void *pOut, int cbMaxOut, uint32 *pNetAdr, uint16 *pPort );
//
// Control heartbeats / advertisement with master server
//
// Call this as often as you like to tell the master server updater whether or not
// you want it to be active (default: off).
void EnableHeartbeats( bool bActive );
// You usually don't need to modify this.
// Pass -1 to use the default value for iHeartbeatInterval.
// Some mods change this.
void SetHeartbeatInterval( int iHeartbeatInterval );
// Force a heartbeat to steam at the next opportunity
void ForceHeartbeat();
// associate this game server with this clan for the purposes of computing player compat
STEAM_CALL_RESULT( AssociateWithClanResult_t )
SteamAPICall_t AssociateWithClan( CSteamID steamIDClan );
// ask if any of the current players dont want to play with this new player - or vice versa
STEAM_CALL_RESULT( ComputeNewPlayerCompatibilityResult_t )
SteamAPICall_t ComputeNewPlayerCompatibility( CSteamID steamIDNewPlayer );
//
void RunCallbacks();
};

View File

@ -0,0 +1,118 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "steam_gameserverstats.h"
Steam_GameServerStats::Steam_GameServerStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
{
this->settings = settings;
this->network = network;
this->callback_results = callback_results;
this->callbacks = callbacks;
}
// downloads stats for the user
// returns a GSStatsReceived_t callback when completed
// if the user has no stats, GSStatsReceived_t.m_eResult will be set to k_EResultFail
// these stats will only be auto-updated for clients playing on the server. For other
// users you'll need to call RequestUserStats() again to refresh any data
STEAM_CALL_RESULT( GSStatsReceived_t )
SteamAPICall_t Steam_GameServerStats::RequestUserStats( CSteamID steamIDUser )
{
PRINT_DEBUG("RequestUserStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
GSStatsReceived_t data;
data.m_eResult = k_EResultFail;//k_EResultOK;
data.m_steamIDUser = steamIDUser;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// requests stat information for a user, usable after a successful call to RequestUserStats()
bool Steam_GameServerStats::GetUserStat( CSteamID steamIDUser, const char *pchName, int32 *pData )
{
PRINT_DEBUG("GetUserStat\n");
return false;
}
bool Steam_GameServerStats::GetUserStat( CSteamID steamIDUser, const char *pchName, float *pData )
{
PRINT_DEBUG("GetUserStat\n");
return false;
}
bool Steam_GameServerStats::GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchieved )
{
PRINT_DEBUG("GetUserAchievement\n");
return false;
}
// Set / update stats and achievements.
// Note: These updates will work only on stats game servers are allowed to edit and only for
// game servers that have been declared as officially controlled by the game creators.
// Set the IP range of your official servers on the Steamworks page
bool Steam_GameServerStats::SetUserStat( CSteamID steamIDUser, const char *pchName, int32 nData )
{
PRINT_DEBUG("SetUserStat\n");
return false;
}
bool Steam_GameServerStats::SetUserStat( CSteamID steamIDUser, const char *pchName, float fData )
{
PRINT_DEBUG("SetUserStat\n");
return false;
}
bool Steam_GameServerStats::UpdateUserAvgRateStat( CSteamID steamIDUser, const char *pchName, float flCountThisSession, double dSessionLength )
{
PRINT_DEBUG("UpdateUserAvgRateStat\n");
return false;
}
bool Steam_GameServerStats::SetUserAchievement( CSteamID steamIDUser, const char *pchName )
{
PRINT_DEBUG("SetUserAchievement\n");
return false;
}
bool Steam_GameServerStats::ClearUserAchievement( CSteamID steamIDUser, const char *pchName )
{
PRINT_DEBUG("ClearUserAchievement\n");
return false;
}
// Store the current data on the server, will get a GSStatsStored_t callback when set.
//
// If the callback has a result of k_EResultInvalidParam, one or more stats
// uploaded has been rejected, either because they broke constraints
// or were out of date. In this case the server sends back updated values.
// The stats should be re-iterated to keep in sync.
STEAM_CALL_RESULT( GSStatsStored_t )
SteamAPICall_t Steam_GameServerStats::StoreUserStats( CSteamID steamIDUser )
{
PRINT_DEBUG("StoreUserStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
GSStatsStored_t data;
data.m_eResult = k_EResultOK;
data.m_steamIDUser = steamIDUser;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}

View File

@ -0,0 +1,63 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
//-----------------------------------------------------------------------------
// Purpose: Functions for authenticating users via Steam to play on a game server
//-----------------------------------------------------------------------------
class Steam_GameServerStats : public ISteamGameServerStats
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
public:
Steam_GameServerStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks);
// downloads stats for the user
// returns a GSStatsReceived_t callback when completed
// if the user has no stats, GSStatsReceived_t.m_eResult will be set to k_EResultFail
// these stats will only be auto-updated for clients playing on the server. For other
// users you'll need to call RequestUserStats() again to refresh any data
STEAM_CALL_RESULT( GSStatsReceived_t )
SteamAPICall_t RequestUserStats( CSteamID steamIDUser );
// requests stat information for a user, usable after a successful call to RequestUserStats()
bool GetUserStat( CSteamID steamIDUser, const char *pchName, int32 *pData );
bool GetUserStat( CSteamID steamIDUser, const char *pchName, float *pData );
bool GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchieved );
// Set / update stats and achievements.
// Note: These updates will work only on stats game servers are allowed to edit and only for
// game servers that have been declared as officially controlled by the game creators.
// Set the IP range of your official servers on the Steamworks page
bool SetUserStat( CSteamID steamIDUser, const char *pchName, int32 nData );
bool SetUserStat( CSteamID steamIDUser, const char *pchName, float fData );
bool UpdateUserAvgRateStat( CSteamID steamIDUser, const char *pchName, float flCountThisSession, double dSessionLength );
bool SetUserAchievement( CSteamID steamIDUser, const char *pchName );
bool ClearUserAchievement( CSteamID steamIDUser, const char *pchName );
// Store the current data on the server, will get a GSStatsStored_t callback when set.
//
// If the callback has a result of k_EResultInvalidParam, one or more stats
// uploaded has been rejected, either because they broke constraints
// or were out of date. In this case the server sends back updated values.
// The stats should be re-iterated to keep in sync.
STEAM_CALL_RESULT( GSStatsStored_t )
SteamAPICall_t StoreUserStats( CSteamID steamIDUser );
};

362
dll/steam_http.cpp Normal file
View File

@ -0,0 +1,362 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "steam_http.h"
Steam_HTTP::Steam_HTTP(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
{
this->settings = settings;
this->network = network;
this->callback_results = callback_results;
this->callbacks = callbacks;
}
Steam_Http_Request *Steam_HTTP::get_request(HTTPRequestHandle hRequest)
{
auto conn = std::find_if(requests.begin(), requests.end(), [&hRequest](struct Steam_Http_Request const& conn) { return conn.handle == hRequest;});
if (conn == requests.end()) return NULL;
return &(*conn);
}
// Initializes a new HTTP request, returning a handle to use in further operations on it. Requires
// the method (GET or POST) and the absolute URL for the request. Both http and https are supported,
// so this string must start with http:// or https:// and should look like http://store.steampowered.com/app/250/
// or such.
HTTPRequestHandle Steam_HTTP::CreateHTTPRequest( EHTTPMethod eHTTPRequestMethod, const char *pchAbsoluteURL )
{
PRINT_DEBUG("CreateHTTPRequest %i %s\n", eHTTPRequestMethod, pchAbsoluteURL);
static HTTPRequestHandle h;
++h;
struct Steam_Http_Request request;
request.handle = h;
request.context_value = 0;
requests.push_back(request);
return request.handle;
}
// Set a context value for the request, which will be returned in the HTTPRequestCompleted_t callback after
// sending the request. This is just so the caller can easily keep track of which callbacks go with which request data.
bool Steam_HTTP::SetHTTPRequestContextValue( HTTPRequestHandle hRequest, uint64 ulContextValue )
{
PRINT_DEBUG("SetHTTPRequestContextValue\n");
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
request->context_value = ulContextValue;
return true;
}
// Set a timeout in seconds for the HTTP request, must be called prior to sending the request. Default
// timeout is 60 seconds if you don't call this. Returns false if the handle is invalid, or the request
// has already been sent.
bool Steam_HTTP::SetHTTPRequestNetworkActivityTimeout( HTTPRequestHandle hRequest, uint32 unTimeoutSeconds )
{
PRINT_DEBUG("SetHTTPRequestNetworkActivityTimeout\n");
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
return true;
}
// Set a request header value for the request, must be called prior to sending the request. Will
// return false if the handle is invalid or the request is already sent.
bool Steam_HTTP::SetHTTPRequestHeaderValue( HTTPRequestHandle hRequest, const char *pchHeaderName, const char *pchHeaderValue )
{
PRINT_DEBUG("SetHTTPRequestHeaderValue\n");
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
return true;
}
// Set a GET or POST parameter value on the request, which is set will depend on the EHTTPMethod specified
// when creating the request. Must be called prior to sending the request. Will return false if the
// handle is invalid or the request is already sent.
bool Steam_HTTP::SetHTTPRequestGetOrPostParameter( HTTPRequestHandle hRequest, const char *pchParamName, const char *pchParamValue )
{
PRINT_DEBUG("SetHTTPRequestGetOrPostParameter\n");
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
return true;
}
// Sends the HTTP request, will return false on a bad handle, otherwise use SteamCallHandle to wait on
// asynchronous response via callback.
//
// Note: If the user is in offline mode in Steam, then this will add a only-if-cached cache-control
// header and only do a local cache lookup rather than sending any actual remote request.
bool Steam_HTTP::SendHTTPRequest( HTTPRequestHandle hRequest, SteamAPICall_t *pCallHandle )
{
PRINT_DEBUG("SendHTTPRequest\n");
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
struct HTTPRequestCompleted_t data;
data.m_hRequest = request->handle;
data.m_ulContextValue = request->context_value;
data.m_bRequestSuccessful = false;
data.m_eStatusCode = k_EHTTPStatusCode404NotFound;
data.m_unBodySize = request->response.size();
if (pCallHandle) {
*pCallHandle = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1);
}
return true;
}
// Sends the HTTP request, will return false on a bad handle, otherwise use SteamCallHandle to wait on
// asynchronous response via callback for completion, and listen for HTTPRequestHeadersReceived_t and
// HTTPRequestDataReceived_t callbacks while streaming.
bool Steam_HTTP::SendHTTPRequestAndStreamResponse( HTTPRequestHandle hRequest, SteamAPICall_t *pCallHandle )
{
PRINT_DEBUG("SendHTTPRequestAndStreamResponse\n");
return false;
}
// Defers a request you have sent, the actual HTTP client code may have many requests queued, and this will move
// the specified request to the tail of the queue. Returns false on invalid handle, or if the request is not yet sent.
bool Steam_HTTP::DeferHTTPRequest( HTTPRequestHandle hRequest )
{
PRINT_DEBUG("DeferHTTPRequest\n");
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
return true;
}
// Prioritizes a request you have sent, the actual HTTP client code may have many requests queued, and this will move
// the specified request to the head of the queue. Returns false on invalid handle, or if the request is not yet sent.
bool Steam_HTTP::PrioritizeHTTPRequest( HTTPRequestHandle hRequest )
{
PRINT_DEBUG("PrioritizeHTTPRequest\n");
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
return true;
}
// Checks if a response header is present in a HTTP response given a handle from HTTPRequestCompleted_t, also
// returns the size of the header value if present so the caller and allocate a correctly sized buffer for
// GetHTTPResponseHeaderValue.
bool Steam_HTTP::GetHTTPResponseHeaderSize( HTTPRequestHandle hRequest, const char *pchHeaderName, uint32 *unResponseHeaderSize )
{
PRINT_DEBUG("GetHTTPResponseHeaderSize\n");
return false;
}
// Gets header values from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the
// header is not present or if your buffer is too small to contain it's value. You should first call
// BGetHTTPResponseHeaderSize to check for the presence of the header and to find out the size buffer needed.
bool Steam_HTTP::GetHTTPResponseHeaderValue( HTTPRequestHandle hRequest, const char *pchHeaderName, uint8 *pHeaderValueBuffer, uint32 unBufferSize )
{
PRINT_DEBUG("GetHTTPResponseHeaderValue\n");
return false;
}
// Gets the size of the body data from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the
// handle is invalid.
bool Steam_HTTP::GetHTTPResponseBodySize( HTTPRequestHandle hRequest, uint32 *unBodySize )
{
PRINT_DEBUG("GetHTTPResponseBodySize\n");
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
if (unBodySize) *unBodySize = request->response.size();
return true;
}
// Gets the body data from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the
// handle is invalid or is to a streaming response, or if the provided buffer is not the correct size. Use BGetHTTPResponseBodySize first to find out
// the correct buffer size to use.
bool Steam_HTTP::GetHTTPResponseBodyData( HTTPRequestHandle hRequest, uint8 *pBodyDataBuffer, uint32 unBufferSize )
{
PRINT_DEBUG("GetHTTPResponseBodyData\n");
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
if (unBufferSize < request->response.size()) {
return false;
}
if (pBodyDataBuffer) memcpy(pBodyDataBuffer, request->response.data(), request->response.size());
return true;
}
// Gets the body data from a streaming HTTP response given a handle from HTTPRequestDataReceived_t. Will return false if the
// handle is invalid or is to a non-streaming response (meaning it wasn't sent with SendHTTPRequestAndStreamResponse), or if the buffer size and offset
// do not match the size and offset sent in HTTPRequestDataReceived_t.
bool Steam_HTTP::GetHTTPStreamingResponseBodyData( HTTPRequestHandle hRequest, uint32 cOffset, uint8 *pBodyDataBuffer, uint32 unBufferSize )
{
PRINT_DEBUG("GetHTTPStreamingResponseBodyData\n");
return false;
}
// Releases an HTTP response handle, should always be called to free resources after receiving a HTTPRequestCompleted_t
// callback and finishing using the response.
bool Steam_HTTP::ReleaseHTTPRequest( HTTPRequestHandle hRequest )
{
PRINT_DEBUG("ReleaseHTTPRequest\n");
auto c = std::begin(requests);
while (c != std::end(requests)) {
if (c->handle == hRequest) {
c = requests.erase(c);
return true;
}
}
return false;
}
// Gets progress on downloading the body for the request. This will be zero unless a response header has already been
// received which included a content-length field. For responses that contain no content-length it will report
// zero for the duration of the request as the size is unknown until the connection closes.
bool Steam_HTTP::GetHTTPDownloadProgressPct( HTTPRequestHandle hRequest, float *pflPercentOut )
{
PRINT_DEBUG("GetHTTPDownloadProgressPct\n");
return false;
}
// Sets the body for an HTTP Post request. Will fail and return false on a GET request, and will fail if POST params
// have already been set for the request. Setting this raw body makes it the only contents for the post, the pchContentType
// parameter will set the content-type header for the request so the server may know how to interpret the body.
bool Steam_HTTP::SetHTTPRequestRawPostBody( HTTPRequestHandle hRequest, const char *pchContentType, uint8 *pubBody, uint32 unBodyLen )
{
PRINT_DEBUG("SetHTTPRequestRawPostBody\n");
return false;
}
// Creates a cookie container handle which you must later free with ReleaseCookieContainer(). If bAllowResponsesToModify=true
// than any response to your requests using this cookie container may add new cookies which may be transmitted with
// future requests. If bAllowResponsesToModify=false than only cookies you explicitly set will be sent. This API is just for
// during process lifetime, after steam restarts no cookies are persisted and you have no way to access the cookie container across
// repeat executions of your process.
HTTPCookieContainerHandle Steam_HTTP::CreateCookieContainer( bool bAllowResponsesToModify )
{
PRINT_DEBUG("CreateCookieContainer\n");
return false;
}
// Release a cookie container you are finished using, freeing it's memory
bool Steam_HTTP::ReleaseCookieContainer( HTTPCookieContainerHandle hCookieContainer )
{
PRINT_DEBUG("ReleaseCookieContainer\n");
return false;
}
// Adds a cookie to the specified cookie container that will be used with future requests.
bool Steam_HTTP::SetCookie( HTTPCookieContainerHandle hCookieContainer, const char *pchHost, const char *pchUrl, const char *pchCookie )
{
PRINT_DEBUG("SetCookie\n");
return false;
}
// Set the cookie container to use for a HTTP request
bool Steam_HTTP::SetHTTPRequestCookieContainer( HTTPRequestHandle hRequest, HTTPCookieContainerHandle hCookieContainer )
{
PRINT_DEBUG("SetHTTPRequestCookieContainer\n");
return false;
}
// Set the extra user agent info for a request, this doesn't clobber the normal user agent, it just adds the extra info on the end
bool Steam_HTTP::SetHTTPRequestUserAgentInfo( HTTPRequestHandle hRequest, const char *pchUserAgentInfo )
{
PRINT_DEBUG("SetHTTPRequestUserAgentInfo\n");
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
return true;
}
// Set that https request should require verified SSL certificate via machines certificate trust store
bool Steam_HTTP::SetHTTPRequestRequiresVerifiedCertificate( HTTPRequestHandle hRequest, bool bRequireVerifiedCertificate )
{
PRINT_DEBUG("SetHTTPRequestRequiresVerifiedCertificate\n");
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
return true;
}
// Set an absolute timeout on the HTTP request, this is just a total time timeout different than the network activity timeout
// which can bump everytime we get more data
bool Steam_HTTP::SetHTTPRequestAbsoluteTimeoutMS( HTTPRequestHandle hRequest, uint32 unMilliseconds )
{
PRINT_DEBUG("SetHTTPRequestAbsoluteTimeoutMS\n");
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
return true;
}
// Check if the reason the request failed was because we timed it out (rather than some harder failure)
bool Steam_HTTP::GetHTTPRequestWasTimedOut( HTTPRequestHandle hRequest, bool *pbWasTimedOut )
{
PRINT_DEBUG("GetHTTPRequestWasTimedOut\n");
return false;
}

151
dll/steam_http.h Normal file
View File

@ -0,0 +1,151 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
struct Steam_Http_Request {
HTTPRequestHandle handle;
uint64 context_value;
std::string response;
};
class Steam_HTTP : public ISteamHTTP
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
std::vector<Steam_Http_Request> requests;
Steam_Http_Request *get_request(HTTPRequestHandle hRequest);
public:
Steam_HTTP(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks);
// Initializes a new HTTP request, returning a handle to use in further operations on it. Requires
// the method (GET or POST) and the absolute URL for the request. Both http and https are supported,
// so this string must start with http:// or https:// and should look like http://store.steampowered.com/app/250/
// or such.
HTTPRequestHandle CreateHTTPRequest( EHTTPMethod eHTTPRequestMethod, const char *pchAbsoluteURL );
// Set a context value for the request, which will be returned in the HTTPRequestCompleted_t callback after
// sending the request. This is just so the caller can easily keep track of which callbacks go with which request data.
bool SetHTTPRequestContextValue( HTTPRequestHandle hRequest, uint64 ulContextValue );
// Set a timeout in seconds for the HTTP request, must be called prior to sending the request. Default
// timeout is 60 seconds if you don't call this. Returns false if the handle is invalid, or the request
// has already been sent.
bool SetHTTPRequestNetworkActivityTimeout( HTTPRequestHandle hRequest, uint32 unTimeoutSeconds );
// Set a request header value for the request, must be called prior to sending the request. Will
// return false if the handle is invalid or the request is already sent.
bool SetHTTPRequestHeaderValue( HTTPRequestHandle hRequest, const char *pchHeaderName, const char *pchHeaderValue );
// Set a GET or POST parameter value on the request, which is set will depend on the EHTTPMethod specified
// when creating the request. Must be called prior to sending the request. Will return false if the
// handle is invalid or the request is already sent.
bool SetHTTPRequestGetOrPostParameter( HTTPRequestHandle hRequest, const char *pchParamName, const char *pchParamValue );
// Sends the HTTP request, will return false on a bad handle, otherwise use SteamCallHandle to wait on
// asynchronous response via callback.
//
// Note: If the user is in offline mode in Steam, then this will add a only-if-cached cache-control
// header and only do a local cache lookup rather than sending any actual remote request.
bool SendHTTPRequest( HTTPRequestHandle hRequest, SteamAPICall_t *pCallHandle );
// Sends the HTTP request, will return false on a bad handle, otherwise use SteamCallHandle to wait on
// asynchronous response via callback for completion, and listen for HTTPRequestHeadersReceived_t and
// HTTPRequestDataReceived_t callbacks while streaming.
bool SendHTTPRequestAndStreamResponse( HTTPRequestHandle hRequest, SteamAPICall_t *pCallHandle );
// Defers a request you have sent, the actual HTTP client code may have many requests queued, and this will move
// the specified request to the tail of the queue. Returns false on invalid handle, or if the request is not yet sent.
bool DeferHTTPRequest( HTTPRequestHandle hRequest );
// Prioritizes a request you have sent, the actual HTTP client code may have many requests queued, and this will move
// the specified request to the head of the queue. Returns false on invalid handle, or if the request is not yet sent.
bool PrioritizeHTTPRequest( HTTPRequestHandle hRequest );
// Checks if a response header is present in a HTTP response given a handle from HTTPRequestCompleted_t, also
// returns the size of the header value if present so the caller and allocate a correctly sized buffer for
// GetHTTPResponseHeaderValue.
bool GetHTTPResponseHeaderSize( HTTPRequestHandle hRequest, const char *pchHeaderName, uint32 *unResponseHeaderSize );
// Gets header values from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the
// header is not present or if your buffer is too small to contain it's value. You should first call
// BGetHTTPResponseHeaderSize to check for the presence of the header and to find out the size buffer needed.
bool GetHTTPResponseHeaderValue( HTTPRequestHandle hRequest, const char *pchHeaderName, uint8 *pHeaderValueBuffer, uint32 unBufferSize );
// Gets the size of the body data from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the
// handle is invalid.
bool GetHTTPResponseBodySize( HTTPRequestHandle hRequest, uint32 *unBodySize );
// Gets the body data from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the
// handle is invalid or is to a streaming response, or if the provided buffer is not the correct size. Use BGetHTTPResponseBodySize first to find out
// the correct buffer size to use.
bool GetHTTPResponseBodyData( HTTPRequestHandle hRequest, uint8 *pBodyDataBuffer, uint32 unBufferSize );
// Gets the body data from a streaming HTTP response given a handle from HTTPRequestDataReceived_t. Will return false if the
// handle is invalid or is to a non-streaming response (meaning it wasn't sent with SendHTTPRequestAndStreamResponse), or if the buffer size and offset
// do not match the size and offset sent in HTTPRequestDataReceived_t.
bool GetHTTPStreamingResponseBodyData( HTTPRequestHandle hRequest, uint32 cOffset, uint8 *pBodyDataBuffer, uint32 unBufferSize );
// Releases an HTTP response handle, should always be called to free resources after receiving a HTTPRequestCompleted_t
// callback and finishing using the response.
bool ReleaseHTTPRequest( HTTPRequestHandle hRequest );
// Gets progress on downloading the body for the request. This will be zero unless a response header has already been
// received which included a content-length field. For responses that contain no content-length it will report
// zero for the duration of the request as the size is unknown until the connection closes.
bool GetHTTPDownloadProgressPct( HTTPRequestHandle hRequest, float *pflPercentOut );
// Sets the body for an HTTP Post request. Will fail and return false on a GET request, and will fail if POST params
// have already been set for the request. Setting this raw body makes it the only contents for the post, the pchContentType
// parameter will set the content-type header for the request so the server may know how to interpret the body.
bool SetHTTPRequestRawPostBody( HTTPRequestHandle hRequest, const char *pchContentType, uint8 *pubBody, uint32 unBodyLen );
// Creates a cookie container handle which you must later free with ReleaseCookieContainer(). If bAllowResponsesToModify=true
// than any response to your requests using this cookie container may add new cookies which may be transmitted with
// future requests. If bAllowResponsesToModify=false than only cookies you explicitly set will be sent. This API is just for
// during process lifetime, after steam restarts no cookies are persisted and you have no way to access the cookie container across
// repeat executions of your process.
HTTPCookieContainerHandle CreateCookieContainer( bool bAllowResponsesToModify );
// Release a cookie container you are finished using, freeing it's memory
bool ReleaseCookieContainer( HTTPCookieContainerHandle hCookieContainer );
// Adds a cookie to the specified cookie container that will be used with future requests.
bool SetCookie( HTTPCookieContainerHandle hCookieContainer, const char *pchHost, const char *pchUrl, const char *pchCookie );
// Set the cookie container to use for a HTTP request
bool SetHTTPRequestCookieContainer( HTTPRequestHandle hRequest, HTTPCookieContainerHandle hCookieContainer );
// Set the extra user agent info for a request, this doesn't clobber the normal user agent, it just adds the extra info on the end
bool SetHTTPRequestUserAgentInfo( HTTPRequestHandle hRequest, const char *pchUserAgentInfo );
// Set that https request should require verified SSL certificate via machines certificate trust store
bool SetHTTPRequestRequiresVerifiedCertificate( HTTPRequestHandle hRequest, bool bRequireVerifiedCertificate );
// Set an absolute timeout on the HTTP request, this is just a total time timeout different than the network activity timeout
// which can bump everytime we get more data
bool SetHTTPRequestAbsoluteTimeoutMS( HTTPRequestHandle hRequest, uint32 unMilliseconds );
// Check if the reason the request failed was because we timed it out (rather than some harder failure)
bool GetHTTPRequestWasTimedOut( HTTPRequestHandle hRequest, bool *pbWasTimedOut );
};

654
dll/steam_inventory.h Normal file
View File

@ -0,0 +1,654 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
struct Steam_Inventory_Requests {
double timeout = 0.1;
SteamInventoryResult_t inventory_result;
std::chrono::system_clock::time_point time_created;
std::vector<SteamItemInstanceID_t> instance_ids;
bool result_done() {
return std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::system_clock::now() - time_created).count() > timeout;
}
uint32 timestamp() {
return std::chrono::duration_cast<std::chrono::duration<uint32>>(time_created.time_since_epoch()).count();
}
};
class Steam_Inventory :
public ISteamInventory001,
public ISteamInventory002,
public ISteamInventory
{
class Settings *settings;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
std::vector<struct Steam_Inventory_Requests> inventory_requests;
SteamInventoryResult_t new_inventory_result(const SteamItemInstanceID_t *pInstanceIDs=NULL, uint32 unCountInstanceIDs=0)
{
static SteamInventoryResult_t result;
++result;
struct Steam_Inventory_Requests request;
request.inventory_result = result;
if (pInstanceIDs && unCountInstanceIDs) {
for (int i = 0; i < unCountInstanceIDs; ++i)
request.instance_ids.push_back(pInstanceIDs[i]);
}
request.time_created = std::chrono::system_clock::now();
inventory_requests.push_back(request);
return request.inventory_result;
}
struct Steam_Inventory_Requests *get_inventory_result(SteamInventoryResult_t resultHandle)
{
auto request = std::find_if(inventory_requests.begin(), inventory_requests.end(), [&resultHandle](struct Steam_Inventory_Requests const& item) { return item.inventory_result == resultHandle; });
if (inventory_requests.end() == request)
return NULL;
return &(*request);
}
public:
Steam_Inventory(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
{
this->settings = settings;
this->callbacks = callbacks;
this->callback_results = callback_results;
}
// INVENTORY ASYNC RESULT MANAGEMENT
//
// Asynchronous inventory queries always output a result handle which can be used with
// GetResultStatus, GetResultItems, etc. A SteamInventoryResultReady_t callback will
// be triggered when the asynchronous result becomes ready (or fails).
//
// Find out the status of an asynchronous inventory result handle. Possible values:
// k_EResultPending - still in progress
// k_EResultOK - done, result ready
// k_EResultExpired - done, result ready, maybe out of date (see DeserializeResult)
// k_EResultInvalidParam - ERROR: invalid API call parameters
// k_EResultServiceUnavailable - ERROR: service temporarily down, you may retry later
// k_EResultLimitExceeded - ERROR: operation would exceed per-user inventory limits
// k_EResultFail - ERROR: unknown / generic error
STEAM_METHOD_DESC(Find out the status of an asynchronous inventory result handle.)
EResult GetResultStatus( SteamInventoryResult_t resultHandle )
{
PRINT_DEBUG("GetResultStatus\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Steam_Inventory_Requests *request = get_inventory_result(resultHandle);
if (!request) return k_EResultInvalidParam;
if (!request->result_done()) return k_EResultPending;
return k_EResultOK;
}
// Copies the contents of a result set into a flat array. The specific
// contents of the result set depend on which query which was used.
STEAM_METHOD_DESC(Copies the contents of a result set into a flat array. The specific contents of the result set depend on which query which was used.)
bool GetResultItems( SteamInventoryResult_t resultHandle,
STEAM_OUT_ARRAY_COUNT( punOutItemsArraySize,Output array) SteamItemDetails_t *pOutItemsArray,
uint32 *punOutItemsArraySize )
{
PRINT_DEBUG("GetResultItems\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Steam_Inventory_Requests *request = get_inventory_result(resultHandle);
if (!request) return false;
if (!request->result_done()) return false;
if (punOutItemsArraySize) *punOutItemsArraySize = 0;
return true;
}
// In combination with GetResultItems, you can use GetResultItemProperty to retrieve
// dynamic string properties for a given item returned in the result set.
//
// Property names are always composed of ASCII letters, numbers, and/or underscores.
//
// Pass a NULL pointer for pchPropertyName to get a comma - separated list of available
// property names.
//
// If pchValueBuffer is NULL, *punValueBufferSize will contain the
// suggested buffer size. Otherwise it will be the number of bytes actually copied
// to pchValueBuffer. If the results do not fit in the given buffer, partial
// results may be copied.
bool GetResultItemProperty( SteamInventoryResult_t resultHandle,
uint32 unItemIndex,
const char *pchPropertyName,
STEAM_OUT_STRING_COUNT( punValueBufferSizeOut ) char *pchValueBuffer, uint32 *punValueBufferSizeOut )
{
PRINT_DEBUG("GetResultItemProperty\n");
//TODO
return false;
}
// Returns the server time at which the result was generated. Compare against
// the value of IClientUtils::GetServerRealTime() to determine age.
STEAM_METHOD_DESC(Returns the server time at which the result was generated. Compare against the value of IClientUtils::GetServerRealTime() to determine age.)
uint32 GetResultTimestamp( SteamInventoryResult_t resultHandle )
{
PRINT_DEBUG("GetResultTimestamp\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Steam_Inventory_Requests *request = get_inventory_result(resultHandle);
if (!request || !request->result_done()) return 0;
return request->timestamp();
}
// Returns true if the result belongs to the target steam ID, false if the
// result does not. This is important when using DeserializeResult, to verify
// that a remote player is not pretending to have a different user's inventory.
STEAM_METHOD_DESC(Returns true if the result belongs to the target steam ID or false if the result does not. This is important when using DeserializeResult to verify that a remote player is not pretending to have a different users inventory.)
bool CheckResultSteamID( SteamInventoryResult_t resultHandle, CSteamID steamIDExpected )
{
PRINT_DEBUG("CheckResultSteamID\n");
//TODO
return true;
}
// Destroys a result handle and frees all associated memory.
STEAM_METHOD_DESC(Destroys a result handle and frees all associated memory.)
void DestroyResult( SteamInventoryResult_t resultHandle )
{
PRINT_DEBUG("DestroyResult\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto request = std::find_if(inventory_requests.begin(), inventory_requests.end(), [&resultHandle](struct Steam_Inventory_Requests const& item) { return item.inventory_result == resultHandle; });
if (inventory_requests.end() == request)
return;
inventory_requests.erase(request);
}
// INVENTORY ASYNC QUERY
//
// Captures the entire state of the current user's Steam inventory.
// You must call DestroyResult on this handle when you are done with it.
// Returns false and sets *pResultHandle to zero if inventory is unavailable.
// Note: calls to this function are subject to rate limits and may return
// cached results if called too frequently. It is suggested that you call
// this function only when you are about to display the user's full inventory,
// or if you expect that the inventory may have changed.
STEAM_METHOD_DESC(Captures the entire state of the current users Steam inventory.)
bool GetAllItems( SteamInventoryResult_t *pResultHandle )
{
PRINT_DEBUG("GetAllItems\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pResultHandle) {
SteamInventoryResult_t handle = new_inventory_result();
{
// SteamInventoryFullUpdate_t callbacks are triggered when GetAllItems
// successfully returns a result which is newer / fresher than the last
// known result.
//TODO: should this always be returned for each get all item calls?
struct SteamInventoryFullUpdate_t data;
data.m_handle = handle;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
{
struct SteamInventoryResultReady_t data;
data.m_handle = handle;
data.m_result = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
*pResultHandle = handle;
return true;
}
return false;
}
// Captures the state of a subset of the current user's Steam inventory,
// identified by an array of item instance IDs. The results from this call
// can be serialized and passed to other players to "prove" that the current
// user owns specific items, without exposing the user's entire inventory.
// For example, you could call GetItemsByID with the IDs of the user's
// currently equipped cosmetic items and serialize this to a buffer, and
// then transmit this buffer to other players upon joining a game.
STEAM_METHOD_DESC(Captures the state of a subset of the current users Steam inventory identified by an array of item instance IDs.)
bool GetItemsByID( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT( unCountInstanceIDs ) const SteamItemInstanceID_t *pInstanceIDs, uint32 unCountInstanceIDs )
{
PRINT_DEBUG("GetItemsByID\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pResultHandle) {
SteamInventoryResult_t handle = new_inventory_result(pInstanceIDs, unCountInstanceIDs);
{
struct SteamInventoryResultReady_t data;
data.m_handle = handle;
data.m_result = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
*pResultHandle = handle;
return true;
}
return false;
}
// RESULT SERIALIZATION AND AUTHENTICATION
//
// Serialized result sets contain a short signature which can't be forged
// or replayed across different game sessions. A result set can be serialized
// on the local client, transmitted to other players via your game networking,
// and deserialized by the remote players. This is a secure way of preventing
// hackers from lying about posessing rare/high-value items.
// Serializes a result set with signature bytes to an output buffer. Pass
// NULL as an output buffer to get the required size via punOutBufferSize.
// The size of a serialized result depends on the number items which are being
// serialized. When securely transmitting items to other players, it is
// recommended to use "GetItemsByID" first to create a minimal result set.
// Results have a built-in timestamp which will be considered "expired" after
// an hour has elapsed. See DeserializeResult for expiration handling.
bool SerializeResult( SteamInventoryResult_t resultHandle, STEAM_OUT_BUFFER_COUNT(punOutBufferSize) void *pOutBuffer, uint32 *punOutBufferSize )
{
PRINT_DEBUG("SerializeResult %i\n", resultHandle);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO
struct Steam_Inventory_Requests *request = get_inventory_result(resultHandle);
if (!request) return false;
if (!request->result_done()) return false;
uint8 buffer[8 + 128] = {};
memset(buffer, 0x5F, sizeof(buffer));
if (!punOutBufferSize) return false;
PRINT_DEBUG("Size %u\n", *punOutBufferSize);
if (!pOutBuffer) {
*punOutBufferSize = sizeof(buffer);
return true;
}
if (*punOutBufferSize < sizeof(buffer)) {
*punOutBufferSize = sizeof(buffer);
return false; //??
}
memcpy(pOutBuffer, buffer, sizeof(buffer));
*punOutBufferSize = sizeof(buffer);
return true;
}
// Deserializes a result set and verifies the signature bytes. Returns false
// if bRequireFullOnlineVerify is set but Steam is running in Offline mode.
// Otherwise returns true and then delivers error codes via GetResultStatus.
//
// The bRESERVED_MUST_BE_FALSE flag is reserved for future use and should not
// be set to true by your game at this time.
//
// DeserializeResult has a potential soft-failure mode where the handle status
// is set to k_EResultExpired. GetResultItems() still succeeds in this mode.
// The "expired" result could indicate that the data may be out of date - not
// just due to timed expiration (one hour), but also because one of the items
// in the result set may have been traded or consumed since the result set was
// generated. You could compare the timestamp from GetResultTimestamp() to
// ISteamUtils::GetServerRealTime() to determine how old the data is. You could
// simply ignore the "expired" result code and continue as normal, or you
// could challenge the player with expired data to send an updated result set.
bool DeserializeResult( SteamInventoryResult_t *pOutResultHandle, STEAM_BUFFER_COUNT(punOutBufferSize) const void *pBuffer, uint32 unBufferSize, bool bRESERVED_MUST_BE_FALSE)
{
PRINT_DEBUG("DeserializeResult\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO
if (pOutResultHandle) {
SteamInventoryResult_t handle = new_inventory_result();
{
struct SteamInventoryResultReady_t data;
data.m_handle = handle;
data.m_result = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
*pOutResultHandle = handle;
return true;
}
return false;
}
// INVENTORY ASYNC MODIFICATION
//
// GenerateItems() creates one or more items and then generates a SteamInventoryCallback_t
// notification with a matching nCallbackContext parameter. This API is only intended
// for prototyping - it is only usable by Steam accounts that belong to the publisher group
// for your game.
// If punArrayQuantity is not NULL, it should be the same length as pArrayItems and should
// describe the quantity of each item to generate.
bool GenerateItems( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, STEAM_ARRAY_COUNT(unArrayLength) const uint32 *punArrayQuantity, uint32 unArrayLength )
{
PRINT_DEBUG("GenerateItems\n");
}
// GrantPromoItems() checks the list of promotional items for which the user may be eligible
// and grants the items (one time only). On success, the result set will include items which
// were granted, if any. If no items were granted because the user isn't eligible for any
// promotions, this is still considered a success.
STEAM_METHOD_DESC(GrantPromoItems() checks the list of promotional items for which the user may be eligible and grants the items (one time only).)
bool GrantPromoItems( SteamInventoryResult_t *pResultHandle )
{
PRINT_DEBUG("GrantPromoItems\n");
return true;
}
// AddPromoItem() / AddPromoItems() are restricted versions of GrantPromoItems(). Instead of
// scanning for all eligible promotional items, the check is restricted to a single item
// definition or set of item definitions. This can be useful if your game has custom UI for
// showing a specific promo item to the user.
bool AddPromoItem( SteamInventoryResult_t *pResultHandle, SteamItemDef_t itemDef )
{
PRINT_DEBUG("AddPromoItem\n");
return false;
}
bool AddPromoItems( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, uint32 unArrayLength )
{
PRINT_DEBUG("AddPromoItems\n");
return false;
}
// ConsumeItem() removes items from the inventory, permanently. They cannot be recovered.
// Not for the faint of heart - if your game implements item removal at all, a high-friction
// UI confirmation process is highly recommended.
STEAM_METHOD_DESC(ConsumeItem() removes items from the inventory permanently.)
bool ConsumeItem( SteamInventoryResult_t *pResultHandle, SteamItemInstanceID_t itemConsume, uint32 unQuantity )
{
PRINT_DEBUG("ConsumeItem\n");
}
// ExchangeItems() is an atomic combination of item generation and consumption.
// It can be used to implement crafting recipes or transmutations, or items which unpack
// themselves into other items (e.g., a chest).
// Exchange recipes are defined in the ItemDef, and explicitly list the required item
// types and resulting generated type.
// Exchange recipes are evaluated atomically by the Inventory Service; if the supplied
// components do not match the recipe, or do not contain sufficient quantity, the
// exchange will fail.
bool ExchangeItems( SteamInventoryResult_t *pResultHandle,
STEAM_ARRAY_COUNT(unArrayGenerateLength) const SteamItemDef_t *pArrayGenerate, STEAM_ARRAY_COUNT(unArrayGenerateLength) const uint32 *punArrayGenerateQuantity, uint32 unArrayGenerateLength,
STEAM_ARRAY_COUNT(unArrayDestroyLength) const SteamItemInstanceID_t *pArrayDestroy, STEAM_ARRAY_COUNT(unArrayDestroyLength) const uint32 *punArrayDestroyQuantity, uint32 unArrayDestroyLength )
{
PRINT_DEBUG("ExchangeItems\n");
}
// TransferItemQuantity() is intended for use with items which are "stackable" (can have
// quantity greater than one). It can be used to split a stack into two, or to transfer
// quantity from one stack into another stack of identical items. To split one stack into
// two, pass k_SteamItemInstanceIDInvalid for itemIdDest and a new item will be generated.
bool TransferItemQuantity( SteamInventoryResult_t *pResultHandle, SteamItemInstanceID_t itemIdSource, uint32 unQuantity, SteamItemInstanceID_t itemIdDest )
{
PRINT_DEBUG("TransferItemQuantity\n");
}
// TIMED DROPS AND PLAYTIME CREDIT
//
// Deprecated. Calling this method is not required for proper playtime accounting.
STEAM_METHOD_DESC( Deprecated method. Playtime accounting is performed on the Steam servers. )
void SendItemDropHeartbeat()
{
PRINT_DEBUG("SendItemDropHeartbeat\n");
}
// Playtime credit must be consumed and turned into item drops by your game. Only item
// definitions which are marked as "playtime item generators" can be spawned. The call
// will return an empty result set if there is not enough playtime credit for a drop.
// Your game should call TriggerItemDrop at an appropriate time for the user to receive
// new items, such as between rounds or while the player is dead. Note that players who
// hack their clients could modify the value of "dropListDefinition", so do not use it
// to directly control rarity.
// See your Steamworks configuration to set playtime drop rates for individual itemdefs.
// The client library will suppress too-frequent calls to this method.
STEAM_METHOD_DESC(Playtime credit must be consumed and turned into item drops by your game.)
bool TriggerItemDrop( SteamInventoryResult_t *pResultHandle, SteamItemDef_t dropListDefinition )
{
PRINT_DEBUG("TriggerItemDrop\n");
//TODO: if gameserver return false
return true;
}
// Deprecated. This method is not supported.
bool TradeItems( SteamInventoryResult_t *pResultHandle, CSteamID steamIDTradePartner,
STEAM_ARRAY_COUNT(nArrayGiveLength) const SteamItemInstanceID_t *pArrayGive, STEAM_ARRAY_COUNT(nArrayGiveLength) const uint32 *pArrayGiveQuantity, uint32 nArrayGiveLength,
STEAM_ARRAY_COUNT(nArrayGetLength) const SteamItemInstanceID_t *pArrayGet, STEAM_ARRAY_COUNT(nArrayGetLength) const uint32 *pArrayGetQuantity, uint32 nArrayGetLength )
{
PRINT_DEBUG("TradeItems\n");
return false;
}
// ITEM DEFINITIONS
//
// Item definitions are a mapping of "definition IDs" (integers between 1 and 1000000)
// to a set of string properties. Some of these properties are required to display items
// on the Steam community web site. Other properties can be defined by applications.
// Use of these functions is optional; there is no reason to call LoadItemDefinitions
// if your game hardcodes the numeric definition IDs (eg, purple face mask = 20, blue
// weapon mod = 55) and does not allow for adding new item types without a client patch.
//
// LoadItemDefinitions triggers the automatic load and refresh of item definitions.
// Every time new item definitions are available (eg, from the dynamic addition of new
// item types while players are still in-game), a SteamInventoryDefinitionUpdate_t
// callback will be fired.
STEAM_METHOD_DESC(LoadItemDefinitions triggers the automatic load and refresh of item definitions.)
bool LoadItemDefinitions()
{
PRINT_DEBUG("LoadItemDefinitions\n");
return true;
}
// GetItemDefinitionIDs returns the set of all defined item definition IDs (which are
// defined via Steamworks configuration, and not necessarily contiguous integers).
// If pItemDefIDs is null, the call will return true and *punItemDefIDsArraySize will
// contain the total size necessary for a subsequent call. Otherwise, the call will
// return false if and only if there is not enough space in the output array.
bool GetItemDefinitionIDs(
STEAM_OUT_ARRAY_COUNT(punItemDefIDsArraySize,List of item definition IDs) SteamItemDef_t *pItemDefIDs,
STEAM_DESC(Size of array is passed in and actual size used is returned in this param) uint32 *punItemDefIDsArraySize )
{
PRINT_DEBUG("GetItemDefinitionIDs\n");
if (!punItemDefIDsArraySize) {
return false;
}
PRINT_DEBUG("array_size %u\n", *punItemDefIDsArraySize);
/*
if (pItemDefIDs) {
*pItemDefIDs = 0;
}
*/
//*punItemDefIDsArraySize = 0;
return false;
}
// GetItemDefinitionProperty returns a string property from a given item definition.
// Note that some properties (for example, "name") may be localized and will depend
// on the current Steam language settings (see ISteamApps::GetCurrentGameLanguage).
// Property names are always composed of ASCII letters, numbers, and/or underscores.
// Pass a NULL pointer for pchPropertyName to get a comma - separated list of available
// property names. If pchValueBuffer is NULL, *punValueBufferSize will contain the
// suggested buffer size. Otherwise it will be the number of bytes actually copied
// to pchValueBuffer. If the results do not fit in the given buffer, partial
// results may be copied.
bool GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPropertyName,
STEAM_OUT_STRING_COUNT(punValueBufferSizeOut) char *pchValueBuffer, uint32 *punValueBufferSizeOut )
{
PRINT_DEBUG("GetItemDefinitionProperty\n");
}
// Request the list of "eligible" promo items that can be manually granted to the given
// user. These are promo items of type "manual" that won't be granted automatically.
// An example usage of this is an item that becomes available every week.
STEAM_CALL_RESULT( SteamInventoryEligiblePromoItemDefIDs_t )
SteamAPICall_t RequestEligiblePromoItemDefinitionsIDs( CSteamID steamID )
{
PRINT_DEBUG("RequestEligiblePromoItemDefinitionsIDs\n");
}
// After handling a SteamInventoryEligiblePromoItemDefIDs_t call result, use this
// function to pull out the list of item definition ids that the user can be
// manually granted via the AddPromoItems() call.
bool GetEligiblePromoItemDefinitionIDs(
CSteamID steamID,
STEAM_OUT_ARRAY_COUNT(punItemDefIDsArraySize,List of item definition IDs) SteamItemDef_t *pItemDefIDs,
STEAM_DESC(Size of array is passed in and actual size used is returned in this param) uint32 *punItemDefIDsArraySize )
{
PRINT_DEBUG("GetEligiblePromoItemDefinitionIDs\n");
}
// Starts the purchase process for the given item definitions. The callback SteamInventoryStartPurchaseResult_t
// will be posted if Steam was able to initialize the transaction.
//
// Once the purchase has been authorized and completed by the user, the callback SteamInventoryResultReady_t
// will be posted.
STEAM_CALL_RESULT( SteamInventoryStartPurchaseResult_t )
SteamAPICall_t StartPurchase( STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, STEAM_ARRAY_COUNT(unArrayLength) const uint32 *punArrayQuantity, uint32 unArrayLength )
{
PRINT_DEBUG("StartPurchase\n");
}
// Request current prices for all applicable item definitions
STEAM_CALL_RESULT( SteamInventoryRequestPricesResult_t )
SteamAPICall_t RequestPrices()
{
PRINT_DEBUG("RequestPrices\n");
return 0;
}
// Returns the number of items with prices. Need to call RequestPrices() first.
uint32 GetNumItemsWithPrices()
{
PRINT_DEBUG("GetNumItemsWithPrices\n");
}
bool GetItemsWithPrices( STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(pArrayItemDefs, Items with prices) SteamItemDef_t *pArrayItemDefs,
STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(pPrices, List of prices for the given item defs) uint64 *pCurrentPrices,
STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(pPrices, List of prices for the given item defs) uint64 *pBasePrices,
uint32 unArrayLength )
{
PRINT_DEBUG("GetItemsWithPrices\n");
}
// Returns item definition ids and their prices in the user's local currency.
// Need to call RequestPrices() first.
bool GetItemsWithPrices( STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(pArrayItemDefs, Items with prices) SteamItemDef_t *pArrayItemDefs,
STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(pPrices, List of prices for the given item defs) uint64 *pPrices,
uint32 unArrayLength )
{
PRINT_DEBUG("GetItemsWithPrices old\n");
return GetItemsWithPrices(pArrayItemDefs, pPrices, NULL, unArrayLength);
}
bool GetItemPrice( SteamItemDef_t iDefinition, uint64 *pCurrentPrice, uint64 *pBasePrice )
{
PRINT_DEBUG("GetItemPrice\n");
}
// Retrieves the price for the item definition id
// Returns false if there is no price stored for the item definition.
bool GetItemPrice( SteamItemDef_t iDefinition, uint64 *pPrice )
{
PRINT_DEBUG("GetItemPrice old\n");
return GetItemPrice(iDefinition, pPrice, NULL);
}
// Create a request to update properties on items
SteamInventoryUpdateHandle_t StartUpdateProperties()
{
PRINT_DEBUG("StartUpdateProperties\n");
}
// Remove the property on the item
bool RemoveProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName )
{
PRINT_DEBUG("RemoveProperty\n");
}
// Accessor methods to set properties on items
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, const char *pchPropertyValue )
{
PRINT_DEBUG("SetProperty\n");
}
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, bool bValue )
{
PRINT_DEBUG("SetProperty\n");
}
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, int64 nValue )
{
PRINT_DEBUG("SetProperty\n");
}
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, float flValue )
{
PRINT_DEBUG("SetProperty\n");
}
// Submit the update request by handle
bool SubmitUpdateProperties( SteamInventoryUpdateHandle_t handle, SteamInventoryResult_t * pResultHandle )
{
PRINT_DEBUG("SubmitUpdateProperties\n");
}
};

View File

@ -0,0 +1,219 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Masterserver_Updater :
public ISteamMasterServerUpdater
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
public:
static void steam_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_masterserverupdater_callback\n");
Steam_Masterserver_Updater *steam_masterserverupdater = (Steam_Masterserver_Updater *)object;
steam_masterserverupdater->Callback(msg);
}
static void steam_run_every_runcb(void *object)
{
PRINT_DEBUG("steam_masterserverupdater_run_every_runcb\n");
Steam_Masterserver_Updater *steam_masterserverupdater = (Steam_Masterserver_Updater *)object;
steam_masterserverupdater->RunCallbacks();
}
Steam_Masterserver_Updater(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->run_every_runcb = run_every_runcb;
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Masterserver_Updater::steam_callback, this);
this->run_every_runcb->add(&Steam_Masterserver_Updater::steam_run_every_runcb, this);
this->callback_results = callback_results;
this->callbacks = callbacks;
}
~Steam_Masterserver_Updater()
{
//TODO rm network callbacks
this->run_every_runcb->remove(&Steam_Masterserver_Updater::steam_run_every_runcb, this);
}
// Call this as often as you like to tell the master server updater whether or not
// you want it to be active (default: off).
void SetActive( bool bActive )
{
PRINT_DEBUG("Steam_Masterserver_Updater::SetActive\n");
}
// You usually don't need to modify this.
// Pass -1 to use the default value for iHeartbeatInterval.
// Some mods change this.
void SetHeartbeatInterval( int iHeartbeatInterval )
{
PRINT_DEBUG("Steam_Masterserver_Updater::SetHeartbeatInterval\n");
}
// These are in GameSocketShare mode, where instead of ISteamMasterServerUpdater creating its own
// socket to talk to the master server on, it lets the game use its socket to forward messages
// back and forth. This prevents us from requiring server ops to open up yet another port
// in their firewalls.
//
// the IP address and port should be in host order, i.e 127.0.0.1 == 0x7f000001
// These are used when you've elected to multiplex the game server's UDP socket
// rather than having the master server updater use its own sockets.
//
// Source games use this to simplify the job of the server admins, so they
// don't have to open up more ports on their firewalls.
// Call this when a packet that starts with 0xFFFFFFFF comes in. That means
// it's for us.
bool HandleIncomingPacket( const void *pData, int cbData, uint32 srcIP, uint16 srcPort )
{
PRINT_DEBUG("Steam_Masterserver_Updater::HandleIncomingPacket\n");
return true;
}
// AFTER calling HandleIncomingPacket for any packets that came in that frame, call this.
// This gets a packet that the master server updater needs to send out on UDP.
// It returns the length of the packet it wants to send, or 0 if there are no more packets to send.
// Call this each frame until it returns 0.
int GetNextOutgoingPacket( void *pOut, int cbMaxOut, uint32 *pNetAdr, uint16 *pPort )
{
PRINT_DEBUG("Steam_Masterserver_Updater::GetNextOutgoingPacket\n");
return 0;
}
// Functions to set various fields that are used to respond to queries.
// Call this to set basic data that is passed to the server browser.
void SetBasicServerData(
unsigned short nProtocolVersion,
bool bDedicatedServer,
const char *pRegionName,
const char *pProductName,
unsigned short nMaxReportedClients,
bool bPasswordProtected,
const char *pGameDescription )
{
PRINT_DEBUG("Steam_Masterserver_Updater::SetBasicServerData\n");
}
// Call this to clear the whole list of key/values that are sent in rules queries.
void ClearAllKeyValues()
{
PRINT_DEBUG("Steam_Masterserver_Updater::ClearAllKeyValues\n");
}
// Call this to add/update a key/value pair.
void SetKeyValue( const char *pKey, const char *pValue )
{
PRINT_DEBUG("Steam_Masterserver_Updater::SetKeyValue\n");
}
// You can call this upon shutdown to clear out data stored for this game server and
// to tell the master servers that this server is going away.
void NotifyShutdown()
{
PRINT_DEBUG("Steam_Masterserver_Updater::NotifyShutdown\n");
}
// Returns true if the master server has requested a restart.
// Only returns true once per request.
bool WasRestartRequested()
{
PRINT_DEBUG("Steam_Masterserver_Updater::WasRestartRequested\n");
return false;
}
// Force it to request a heartbeat from the master servers.
void ForceHeartbeat()
{
PRINT_DEBUG("Steam_Masterserver_Updater::ForceHeartbeat\n");
}
// Manually edit and query the master server list.
// It will provide name resolution and use the default master server port if none is provided.
bool AddMasterServer( const char *pServerAddress )
{
PRINT_DEBUG("Steam_Masterserver_Updater::AddMasterServer\n");
return true;
}
bool RemoveMasterServer( const char *pServerAddress )
{
PRINT_DEBUG("Steam_Masterserver_Updater::RemoveMasterServer\n");
return true;
}
int GetNumMasterServers()
{
PRINT_DEBUG("Steam_Masterserver_Updater::GetNumMasterServers\n");
return 0;
}
// Returns the # of bytes written to pOut.
int GetMasterServerAddress( int iServer, char *pOut, int outBufferSize )
{
PRINT_DEBUG("Steam_Masterserver_Updater::GetMasterServerAddress\n");
return 0;
}
void RunCallbacks()
{
}
void Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::CONNECT) {
}
if (msg->low_level().type() == Low_Level::DISCONNECT) {
}
}
}
};

1399
dll/steam_matchmaking.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,514 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "steam_matchmaking_servers.h"
static void network_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_matchmaking_servers_callback\n");
Steam_Matchmaking_Servers *obj = (Steam_Matchmaking_Servers *)object;
obj->Callback(msg);
}
Steam_Matchmaking_Servers::Steam_Matchmaking_Servers(class Settings *settings, class Networking *network)
{
this->settings = settings;
this->network = network;
this->network->setCallback(CALLBACK_ID_GAMESERVER, (uint64) 0, &network_callback, this);
}
static int server_list_request;
// Request a new list of servers of a particular type. These calls each correspond to one of the EMatchMakingType values.
// Each call allocates a new asynchronous request object.
// Request object must be released by calling ReleaseRequest( hServerListRequest )
HServerListRequest Steam_Matchmaking_Servers::RequestInternetServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse )
{
PRINT_DEBUG("RequestInternetServerList\n");
//TODO
return RequestLANServerList(iApp, pRequestServersResponse);
}
HServerListRequest Steam_Matchmaking_Servers::RequestLANServerList( AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse )
{
PRINT_DEBUG("RequestLANServerList %u\n", iApp);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Steam_Matchmaking_Request request;
request.appid = iApp;
request.callbacks = pRequestServersResponse;
request.cancelled = false;
request.completed = false;
requests.push_back(request);
++server_list_request;
requests[requests.size() - 1].id = (void *)server_list_request;
HServerListRequest id = requests[requests.size() - 1].id;
PRINT_DEBUG("request id: %p\n", id);
return id;
}
HServerListRequest Steam_Matchmaking_Servers::RequestFriendsServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse )
{
PRINT_DEBUG("RequestFriendsServerList\n");
//TODO
return RequestLANServerList(iApp, pRequestServersResponse);
}
HServerListRequest Steam_Matchmaking_Servers::RequestFavoritesServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse )
{
PRINT_DEBUG("RequestFavoritesServerList\n");
//TODO
return RequestLANServerList(iApp, pRequestServersResponse);
}
HServerListRequest Steam_Matchmaking_Servers::RequestHistoryServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse )
{
PRINT_DEBUG("RequestHistoryServerList\n");
//TODO
return RequestLANServerList(iApp, pRequestServersResponse);
}
HServerListRequest Steam_Matchmaking_Servers::RequestSpectatorServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse )
{
PRINT_DEBUG("RequestSpectatorServerList\n");
//TODO
return RequestLANServerList(iApp, pRequestServersResponse);
}
// Releases the asynchronous request object and cancels any pending query on it if there's a pending query in progress.
// RefreshComplete callback is not posted when request is released.
void Steam_Matchmaking_Servers::ReleaseRequest( HServerListRequest hServerListRequest )
{
PRINT_DEBUG("ReleaseRequest %p\n", hServerListRequest);
auto g = std::begin(requests);
while (g != std::end(requests)) {
if (g->id == hServerListRequest) {
//NOTE: some garbage games release the request before getting server details from it.
// g = requests.erase(g);
// } else {
//TODO: eventually delete the released request.
g->cancelled = true;
g->released = true;
}
++g;
}
}
/* the filter operation codes that go in the key part of MatchMakingKeyValuePair_t should be one of these:
"map"
- Server passes the filter if the server is playing the specified map.
"gamedataand"
- Server passes the filter if the server's game data (ISteamGameServer::SetGameData) contains all of the
specified strings. The value field is a comma-delimited list of strings to match.
"gamedataor"
- Server passes the filter if the server's game data (ISteamGameServer::SetGameData) contains at least one of the
specified strings. The value field is a comma-delimited list of strings to match.
"gamedatanor"
- Server passes the filter if the server's game data (ISteamGameServer::SetGameData) does not contain any
of the specified strings. The value field is a comma-delimited list of strings to check.
"gametagsand"
- Server passes the filter if the server's game tags (ISteamGameServer::SetGameTags) contains all
of the specified strings. The value field is a comma-delimited list of strings to check.
"gametagsnor"
- Server passes the filter if the server's game tags (ISteamGameServer::SetGameTags) does not contain any
of the specified strings. The value field is a comma-delimited list of strings to check.
"and" (x1 && x2 && ... && xn)
"or" (x1 || x2 || ... || xn)
"nand" !(x1 && x2 && ... && xn)
"nor" !(x1 || x2 || ... || xn)
- Performs Boolean operation on the following filters. The operand to this filter specifies
the "size" of the Boolean inputs to the operation, in Key/value pairs. (The keyvalue
pairs must immediately follow, i.e. this is a prefix logical operator notation.)
In the simplest case where Boolean expressions are not nested, this is simply
the number of operands.
For example, to match servers on a particular map or with a particular tag, would would
use these filters.
( server.map == "cp_dustbowl" || server.gametags.contains("payload") )
"or", "2"
"map", "cp_dustbowl"
"gametagsand", "payload"
If logical inputs are nested, then the operand specifies the size of the entire
"length" of its operands, not the number of immediate children.
( server.map == "cp_dustbowl" || ( server.gametags.contains("payload") && !server.gametags.contains("payloadrace") ) )
"or", "4"
"map", "cp_dustbowl"
"and", "2"
"gametagsand", "payload"
"gametagsnor", "payloadrace"
Unary NOT can be achieved using either "nand" or "nor" with a single operand.
"addr"
- Server passes the filter if the server's query address matches the specified IP or IP:port.
"gameaddr"
- Server passes the filter if the server's game address matches the specified IP or IP:port.
The following filter operations ignore the "value" part of MatchMakingKeyValuePair_t
"dedicated"
- Server passes the filter if it passed true to SetDedicatedServer.
"secure"
- Server passes the filter if the server is VAC-enabled.
"notfull"
- Server passes the filter if the player count is less than the reported max player count.
"hasplayers"
- Server passes the filter if the player count is greater than zero.
"noplayers"
- Server passes the filter if it doesn't have any players.
"linux"
- Server passes the filter if it's a linux server
*/
void Steam_Matchmaking_Servers::server_details(Gameserver *g, gameserveritem_t *server)
{
uint16 query_port = g->query_port();
if (g->query_port() == 0xFFFF) {
query_port = g->port();
}
server->m_NetAdr.Init(g->ip(), query_port, g->port());
server->m_nPing = 10; //TODO
server->m_bHadSuccessfulResponse = true;
server->m_bDoNotRefresh = false;
strncpy(server->m_szGameDir, g->mod_dir().c_str(), k_cbMaxGameServerGameDir - 1);
strncpy(server->m_szMap, g->map_name().c_str(), k_cbMaxGameServerMapName - 1);
strncpy(server->m_szGameDescription, g->game_description().c_str(), k_cbMaxGameServerGameDescription - 1);
server->m_szGameDir[k_cbMaxGameServerGameDir - 1] = 0;
server->m_szMap[k_cbMaxGameServerMapName - 1] = 0;
server->m_szGameDescription[k_cbMaxGameServerGameDescription - 1] = 0;
server->m_nAppID = g->appid();
server->m_nPlayers = g->num_players();
server->m_nMaxPlayers = g->max_player_count();
server->m_nBotPlayers = g->bot_player_count();
server->m_bPassword = g->password_protected();
server->m_bSecure = g->secure();
server->m_ulTimeLastPlayed = 0;
server->m_nServerVersion = g->version();
server->SetName(g->server_name().c_str());
server->m_steamID = CSteamID((uint64)g->id());
PRINT_DEBUG("server_details %llu\n", g->id());
strncpy(server->m_szGameTags, g->tags().c_str(), k_cbMaxGameServerTags - 1);
server->m_szGameTags[k_cbMaxGameServerTags - 1] = 0;
}
// Get details on a given server in the list, you can get the valid range of index
// values by calling GetServerCount(). You will also receive index values in
// ISteamMatchmakingServerListResponse::ServerResponded() callbacks
gameserveritem_t *Steam_Matchmaking_Servers::GetServerDetails( HServerListRequest hRequest, int iServer )
{
PRINT_DEBUG("GetServerDetails %p %i\n", hRequest, iServer);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
std::vector <struct Steam_Matchmaking_Servers_Gameserver> gameservers_filtered;
auto g = std::begin(requests);
while (g != std::end(requests)) {
PRINT_DEBUG("equal? %p %p\n", hRequest, g->id);
if (g->id == hRequest) {
gameservers_filtered = g->gameservers_filtered;
PRINT_DEBUG("found %u\n", gameservers_filtered.size());
break;
}
++g;
}
if (iServer >= gameservers_filtered.size() || iServer < 0) {
return NULL;
}
Gameserver *gs = &gameservers_filtered[iServer].server;
gameserveritem_t *server = new gameserveritem_t(); //TODO: is the new here ok?
server_details(gs, server);
PRINT_DEBUG("Returned server details\n");
return server;
}
// Cancel an request which is operation on the given list type. You should call this to cancel
// any in-progress requests before destructing a callback object that may have been passed
// to one of the above list request calls. Not doing so may result in a crash when a callback
// occurs on the destructed object.
// Canceling a query does not release the allocated request handle.
// The request handle must be released using ReleaseRequest( hRequest )
void Steam_Matchmaking_Servers::CancelQuery( HServerListRequest hRequest )
{
PRINT_DEBUG("CancelQuery %p\n", hRequest);
auto g = std::begin(requests);
while (g != std::end(requests)) {
if (g->id == hRequest) {
g->cancelled = true;
}
++g;
}
}
// Ping every server in your list again but don't update the list of servers
// Query callback installed when the server list was requested will be used
// again to post notifications and RefreshComplete, so the callback must remain
// valid until another RefreshComplete is called on it or the request
// is released with ReleaseRequest( hRequest )
void Steam_Matchmaking_Servers::RefreshQuery( HServerListRequest hRequest )
{
PRINT_DEBUG("RefreshQuery %p\n", hRequest);
}
// Returns true if the list is currently refreshing its server list
bool Steam_Matchmaking_Servers::IsRefreshing( HServerListRequest hRequest )
{
PRINT_DEBUG("IsRefreshing %p\n", hRequest);
return false;
}
// How many servers in the given list, GetServerDetails above takes 0... GetServerCount() - 1
int Steam_Matchmaking_Servers::GetServerCount( HServerListRequest hRequest )
{
PRINT_DEBUG("GetServerCount %p\n", hRequest);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
int size = 0;
auto g = std::begin(requests);
while (g != std::end(requests)) {
if (g->id == hRequest) {
size = g->gameservers_filtered.size();
break;
}
++g;
}
return size;
}
// Refresh a single server inside of a query (rather than all the servers )
void Steam_Matchmaking_Servers::RefreshServer( HServerListRequest hRequest, int iServer )
{
PRINT_DEBUG("RefreshServer %p\n", hRequest);
//TODO
}
static HServerQuery new_server_query()
{
static int a;
++a;
if (!a) ++a;
return a;
}
//-----------------------------------------------------------------------------
// Queries to individual servers directly via IP/Port
//-----------------------------------------------------------------------------
// Request updated ping time and other details from a single server
HServerQuery Steam_Matchmaking_Servers::PingServer( uint32 unIP, uint16 usPort, ISteamMatchmakingPingResponse *pRequestServersResponse )
{
PRINT_DEBUG("PingServer %hhu.%hhu.%hhu.%hhu:%hu\n", ((unsigned char *)&unIP)[3], ((unsigned char *)&unIP)[2], ((unsigned char *)&unIP)[1], ((unsigned char *)&unIP)[0], usPort);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
Steam_Matchmaking_Servers_Direct_IP_Request r;
r.id = new_server_query();
r.ip = unIP;
r.port = usPort;
r.ping_response = pRequestServersResponse;
direct_ip_requests.push_back(r);
return r.id;
}
// Request the list of players currently playing on a server
HServerQuery Steam_Matchmaking_Servers::PlayerDetails( uint32 unIP, uint16 usPort, ISteamMatchmakingPlayersResponse *pRequestServersResponse )
{
PRINT_DEBUG("PlayerDetails %hhu.%hhu.%hhu.%hhu:%hu\n", ((unsigned char *)&unIP)[3], ((unsigned char *)&unIP)[2], ((unsigned char *)&unIP)[1], ((unsigned char *)&unIP)[0], usPort);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
Steam_Matchmaking_Servers_Direct_IP_Request r;
r.id = new_server_query();
r.ip = unIP;
r.port = usPort;
r.players_response = pRequestServersResponse;
direct_ip_requests.push_back(r);
return r.id;
}
// Request the list of rules that the server is running (See ISteamGameServer::SetKeyValue() to set the rules server side)
HServerQuery Steam_Matchmaking_Servers::ServerRules( uint32 unIP, uint16 usPort, ISteamMatchmakingRulesResponse *pRequestServersResponse )
{
PRINT_DEBUG("ServerRules %hhu.%hhu.%hhu.%hhu:%hu\n", ((unsigned char *)&unIP)[3], ((unsigned char *)&unIP)[2], ((unsigned char *)&unIP)[1], ((unsigned char *)&unIP)[0], usPort);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
Steam_Matchmaking_Servers_Direct_IP_Request r;
r.id = new_server_query();
r.ip = unIP;
r.port = usPort;
r.rules_response = pRequestServersResponse;
direct_ip_requests.push_back(r);
return r.id;
}
// Cancel an outstanding Ping/Players/Rules query from above. You should call this to cancel
// any in-progress requests before destructing a callback object that may have been passed
// to one of the above calls to avoid crashing when callbacks occur.
void Steam_Matchmaking_Servers::CancelServerQuery( HServerQuery hServerQuery )
{
PRINT_DEBUG("CancelServerQuery\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto r = std::find_if(direct_ip_requests.begin(), direct_ip_requests.end(), [&hServerQuery](Steam_Matchmaking_Servers_Direct_IP_Request const& item) { return item.id == hServerQuery; });
if (direct_ip_requests.end() == r) return;
direct_ip_requests.erase(r);
}
void Steam_Matchmaking_Servers::RunCallbacks()
{
PRINT_DEBUG("Steam_Matchmaking_Servers::RunCallbacks\n");
{
auto g = std::begin(gameservers);
while (g != std::end(gameservers)) {
if (check_timedout(g->last_recv, SERVER_TIMEOUT)) {
g = gameservers.erase(g);
PRINT_DEBUG("SERVER TIMEOUT\n");
} else {
++g;
}
}
}
PRINT_DEBUG("REQUESTS %zu gs: %zu\n", requests.size(), gameservers.size());
for (auto &r : requests) {
if (r.cancelled || r.completed) continue;
r.gameservers_filtered.clear();
for (auto &g : gameservers) {
if (g.server.appid() == r.appid) {
PRINT_DEBUG("REQUESTS server found\n");
r.gameservers_filtered.push_back(g);
}
}
}
std::vector <struct Steam_Matchmaking_Request> requests_temp(requests);
PRINT_DEBUG("REQUESTS_TEMP %zu\n", requests_temp.size());
for (auto &r : requests) {
r.completed = true;
}
for (auto &r : requests_temp) {
if (r.cancelled || r.completed) continue;
int i = 0;
for (auto &g : r.gameservers_filtered) {
PRINT_DEBUG("REQUESTS server responded cb %p\n", r.id);
r.callbacks->ServerResponded(r.id, i);
++i;
}
if (i) {
r.callbacks->RefreshComplete(r.id, eServerResponded);
} else {
r.callbacks->RefreshComplete(r.id, eNoServersListedOnMasterServer);
}
}
std::vector <struct Steam_Matchmaking_Servers_Direct_IP_Request> direct_ip_requests_temp = direct_ip_requests;
direct_ip_requests.clear();
for (auto &r : direct_ip_requests_temp) {
PRINT_DEBUG("dip request: %lu:%hu\n", r.ip, r.port);
for (auto &g : gameservers) {
PRINT_DEBUG("server: %lu:%hu\n", g.server.ip(), g.server.query_port());
uint16 query_port = g.server.query_port();
if (query_port == 0xFFFF) {
query_port = g.server.port();
}
if (query_port == r.port && g.server.ip() == r.ip) {
if (r.rules_response) {
int number_rules = g.server.values().size();
PRINT_DEBUG("rules: %lu\n", number_rules);
auto rule = g.server.values().begin();
for (int i = 0; i < number_rules; ++i) {
PRINT_DEBUG("RULE %s %s\n", rule->first.c_str(), rule->second.c_str());
r.rules_response->RulesResponded(rule->first.c_str(), rule->second.c_str());
++rule;
}
r.rules_response->RulesRefreshComplete();
r.rules_response = NULL;
}
if (r.ping_response) {
gameserveritem_t server;
server_details(&(g.server), &server);
r.ping_response->ServerResponded(server);
r.ping_response = NULL;
}
//TODO: players response
}
}
if (r.rules_response) r.rules_response->RulesRefreshComplete();
//TODO: player response
if (r.players_response) r.players_response->PlayersRefreshComplete();
if (r.ping_response) r.ping_response->ServerFailedToRespond();
}
}
void Steam_Matchmaking_Servers::Callback(Common_Message *msg)
{
if (msg->has_gameserver()) {
if (msg->gameserver().offline()) {
for (auto &g : gameservers) {
if (g.server.id() == msg->gameserver().id()) {
g.last_recv = std::chrono::high_resolution_clock::time_point();
}
}
} else {
bool already = false;
for (auto &g : gameservers) {
if (g.server.id() == msg->gameserver().id()) {
g.last_recv = std::chrono::high_resolution_clock::now();
g.server = msg->gameserver();
g.server.set_ip(msg->source_ip());
already = true;
}
}
if (!already) {
struct Steam_Matchmaking_Servers_Gameserver g;
g.last_recv = std::chrono::high_resolution_clock::now();
g.server = msg->gameserver();
g.server.set_ip(msg->source_ip());
gameservers.push_back(g);
PRINT_DEBUG("SERVER ADDED\n");
}
}
}
}

View File

@ -0,0 +1,191 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
#define SERVER_TIMEOUT 10.0
struct Steam_Matchmaking_Servers_Direct_IP_Request {
HServerQuery id;
uint32 ip;
uint16 port;
ISteamMatchmakingRulesResponse *rules_response = NULL;
ISteamMatchmakingPlayersResponse *players_response = NULL;
ISteamMatchmakingPingResponse *ping_response = NULL;
};
struct Steam_Matchmaking_Servers_Gameserver {
Gameserver server;
std::chrono::high_resolution_clock::time_point last_recv;
};
struct Steam_Matchmaking_Request {
AppId_t appid;
HServerListRequest id;
ISteamMatchmakingServerListResponse *callbacks;
bool completed, cancelled, released;
std::vector <struct Steam_Matchmaking_Servers_Gameserver> gameservers_filtered;
};
class Steam_Matchmaking_Servers : public ISteamMatchmakingServers
{
class Settings *settings;
class Networking *network;
std::vector <struct Steam_Matchmaking_Servers_Gameserver> gameservers;
std::vector <struct Steam_Matchmaking_Request> requests;
std::vector <struct Steam_Matchmaking_Servers_Direct_IP_Request> direct_ip_requests;
public:
Steam_Matchmaking_Servers(class Settings *settings, class Networking *network);
// Request a new list of servers of a particular type. These calls each correspond to one of the EMatchMakingType values.
// Each call allocates a new asynchronous request object.
// Request object must be released by calling ReleaseRequest( hServerListRequest )
HServerListRequest RequestInternetServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse );
HServerListRequest RequestLANServerList( AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse );
HServerListRequest RequestFriendsServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse );
HServerListRequest RequestFavoritesServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse );
HServerListRequest RequestHistoryServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse );
HServerListRequest RequestSpectatorServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse );
// Releases the asynchronous request object and cancels any pending query on it if there's a pending query in progress.
// RefreshComplete callback is not posted when request is released.
void ReleaseRequest( HServerListRequest hServerListRequest );
/* the filter operation codes that go in the key part of MatchMakingKeyValuePair_t should be one of these:
"map"
- Server passes the filter if the server is playing the specified map.
"gamedataand"
- Server passes the filter if the server's game data (ISteamGameServer::SetGameData) contains all of the
specified strings. The value field is a comma-delimited list of strings to match.
"gamedataor"
- Server passes the filter if the server's game data (ISteamGameServer::SetGameData) contains at least one of the
specified strings. The value field is a comma-delimited list of strings to match.
"gamedatanor"
- Server passes the filter if the server's game data (ISteamGameServer::SetGameData) does not contain any
of the specified strings. The value field is a comma-delimited list of strings to check.
"gametagsand"
- Server passes the filter if the server's game tags (ISteamGameServer::SetGameTags) contains all
of the specified strings. The value field is a comma-delimited list of strings to check.
"gametagsnor"
- Server passes the filter if the server's game tags (ISteamGameServer::SetGameTags) does not contain any
of the specified strings. The value field is a comma-delimited list of strings to check.
"and" (x1 && x2 && ... && xn)
"or" (x1 || x2 || ... || xn)
"nand" !(x1 && x2 && ... && xn)
"nor" !(x1 || x2 || ... || xn)
- Performs Boolean operation on the following filters. The operand to this filter specifies
the "size" of the Boolean inputs to the operation, in Key/value pairs. (The keyvalue
pairs must immediately follow, i.e. this is a prefix logical operator notation.)
In the simplest case where Boolean expressions are not nested, this is simply
the number of operands.
For example, to match servers on a particular map or with a particular tag, would would
use these filters.
( server.map == "cp_dustbowl" || server.gametags.contains("payload") )
"or", "2"
"map", "cp_dustbowl"
"gametagsand", "payload"
If logical inputs are nested, then the operand specifies the size of the entire
"length" of its operands, not the number of immediate children.
( server.map == "cp_dustbowl" || ( server.gametags.contains("payload") && !server.gametags.contains("payloadrace") ) )
"or", "4"
"map", "cp_dustbowl"
"and", "2"
"gametagsand", "payload"
"gametagsnor", "payloadrace"
Unary NOT can be achieved using either "nand" or "nor" with a single operand.
"addr"
- Server passes the filter if the server's query address matches the specified IP or IP:port.
"gameaddr"
- Server passes the filter if the server's game address matches the specified IP or IP:port.
The following filter operations ignore the "value" part of MatchMakingKeyValuePair_t
"dedicated"
- Server passes the filter if it passed true to SetDedicatedServer.
"secure"
- Server passes the filter if the server is VAC-enabled.
"notfull"
- Server passes the filter if the player count is less than the reported max player count.
"hasplayers"
- Server passes the filter if the player count is greater than zero.
"noplayers"
- Server passes the filter if it doesn't have any players.
"linux"
- Server passes the filter if it's a linux server
*/
// Get details on a given server in the list, you can get the valid range of index
// values by calling GetServerCount(). You will also receive index values in
// ISteamMatchmakingServerListResponse::ServerResponded() callbacks
gameserveritem_t *GetServerDetails( HServerListRequest hRequest, int iServer );
// Cancel an request which is operation on the given list type. You should call this to cancel
// any in-progress requests before destructing a callback object that may have been passed
// to one of the above list request calls. Not doing so may result in a crash when a callback
// occurs on the destructed object.
// Canceling a query does not release the allocated request handle.
// The request handle must be released using ReleaseRequest( hRequest )
void CancelQuery( HServerListRequest hRequest );
// Ping every server in your list again but don't update the list of servers
// Query callback installed when the server list was requested will be used
// again to post notifications and RefreshComplete, so the callback must remain
// valid until another RefreshComplete is called on it or the request
// is released with ReleaseRequest( hRequest )
void RefreshQuery( HServerListRequest hRequest );
// Returns true if the list is currently refreshing its server list
bool IsRefreshing( HServerListRequest hRequest );
// How many servers in the given list, GetServerDetails above takes 0... GetServerCount() - 1
int GetServerCount( HServerListRequest hRequest );
// Refresh a single server inside of a query (rather than all the servers )
void RefreshServer( HServerListRequest hRequest, int iServer );
//-----------------------------------------------------------------------------
// Queries to individual servers directly via IP/Port
//-----------------------------------------------------------------------------
// Request updated ping time and other details from a single server
HServerQuery PingServer( uint32 unIP, uint16 usPort, ISteamMatchmakingPingResponse *pRequestServersResponse );
// Request the list of players currently playing on a server
HServerQuery PlayerDetails( uint32 unIP, uint16 usPort, ISteamMatchmakingPlayersResponse *pRequestServersResponse );
// Request the list of rules that the server is running (See ISteamGameServer::SetKeyValue() to set the rules server side)
HServerQuery ServerRules( uint32 unIP, uint16 usPort, ISteamMatchmakingRulesResponse *pRequestServersResponse );
// Cancel an outstanding Ping/Players/Rules query from above. You should call this to cancel
// any in-progress requests before destructing a callback object that may have been passed
// to one of the above calls to avoid crashing when callbacks occur.
void CancelServerQuery( HServerQuery hServerQuery );
//
void RunCallbacks();
void Callback(Common_Message *msg);
void server_details(Gameserver *g, gameserveritem_t *server);
};

116
dll/steam_music.cpp Normal file
View File

@ -0,0 +1,116 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "steam_music.h"
Steam_Music::Steam_Music(class SteamCallBacks *callbacks)
{
playing = 0;
volume = 1.0;
this->callbacks = callbacks;
}
void Steam_Music::change_playstate(int new_playing)
{
if (new_playing != playing) {
PlaybackStatusHasChanged_t data;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
playing = new_playing;
}
bool Steam_Music::BIsEnabled()
{
PRINT_DEBUG("Steam_Music::BIsEnabled\n");
return true;
}
bool Steam_Music::BIsPlaying()
{
PRINT_DEBUG("Steam_Music::BIsPlaying\n");
return playing > 0;
}
AudioPlayback_Status Steam_Music::GetPlaybackStatus()
{
PRINT_DEBUG("Steam_Music::GetPlaybackStatus\n");
if (playing == 0) {
return AudioPlayback_Idle;
}
if (playing == 1) {
return AudioPlayback_Paused;
}
if (playing == 2) {
return AudioPlayback_Playing;
}
return AudioPlayback_Idle;
}
void Steam_Music::Play()
{
PRINT_DEBUG("Steam_Music::Play\n");
change_playstate(2);
}
void Steam_Music::Pause()
{
PRINT_DEBUG("Steam_Music::Pause\n");
change_playstate(1);
}
void Steam_Music::PlayPrevious()
{
PRINT_DEBUG("Steam_Music::PlayPrevious\n");
change_playstate(2);
}
void Steam_Music::PlayNext()
{
PRINT_DEBUG("Steam_Music::PlayNext\n");
change_playstate(2);
}
// volume is between 0.0 and 1.0
void Steam_Music::SetVolume( float flVolume )
{
PRINT_DEBUG("Steam_Music::SetVolume\n");
if (flVolume > 1.0)
flVolume = 1.0;
if (flVolume < 0.0)
flVolume = 0.0;
if (flVolume != volume) {
VolumeHasChanged_t data;
data.m_flNewVolume = volume;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
volume = flVolume;
}
float Steam_Music::GetVolume()
{
PRINT_DEBUG("Steam_Music::GetVolume\n");
return volume;
}

43
dll/steam_music.h Normal file
View File

@ -0,0 +1,43 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Music : public ISteamMusic
{
int playing;
float volume;
void change_playstate(int new_playing);
class SteamCallBacks *callbacks;
public:
Steam_Music(class SteamCallBacks *callbacks);
bool BIsEnabled();
bool BIsPlaying();
AudioPlayback_Status GetPlaybackStatus();
void Play();
void Pause();
void PlayPrevious();
void PlayNext();
// volume is between 0.0 and 1.0
void SetVolume( float flVolume );
float GetVolume();
};

190
dll/steam_musicremote.cpp Normal file
View File

@ -0,0 +1,190 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "steam_musicremote.h"
// Service Definition
bool Steam_MusicRemote::RegisterSteamMusicRemote( const char *pchName )
{
PRINT_DEBUG("RegisterSteamMusicRemote\n");
}
bool Steam_MusicRemote::DeregisterSteamMusicRemote()
{
PRINT_DEBUG("DeregisterSteamMusicRemote\n");
}
bool Steam_MusicRemote::BIsCurrentMusicRemote()
{
PRINT_DEBUG("BIsCurrentMusicRemote\n");
}
bool Steam_MusicRemote::BActivationSuccess( bool bValue )
{
PRINT_DEBUG("BActivationSuccess\n");
}
bool Steam_MusicRemote::SetDisplayName( const char *pchDisplayName )
{
PRINT_DEBUG("SetDisplayName\n");
}
bool Steam_MusicRemote::SetPNGIcon_64x64( void *pvBuffer, uint32 cbBufferLength )
{
PRINT_DEBUG("SetPNGIcon_64x64\n");
}
// Abilities for the user interface
bool Steam_MusicRemote::EnablePlayPrevious(bool bValue)
{
PRINT_DEBUG("EnablePlayPrevious\n");
}
bool Steam_MusicRemote::EnablePlayNext( bool bValue )
{
PRINT_DEBUG("EnablePlayNext\n");
}
bool Steam_MusicRemote::EnableShuffled( bool bValue )
{
PRINT_DEBUG("EnableShuffled\n");
}
bool Steam_MusicRemote::EnableLooped( bool bValue )
{
PRINT_DEBUG("EnableLooped\n");
}
bool Steam_MusicRemote::EnableQueue( bool bValue )
{
PRINT_DEBUG("EnableQueue\n");
}
bool Steam_MusicRemote::EnablePlaylists( bool bValue )
{
PRINT_DEBUG("EnablePlaylists\n");
}
// Status
bool Steam_MusicRemote::UpdatePlaybackStatus( AudioPlayback_Status nStatus )
{
PRINT_DEBUG("UpdatePlaybackStatus\n");
}
bool Steam_MusicRemote::UpdateShuffled( bool bValue )
{
PRINT_DEBUG("UpdateShuffled\n");
}
bool Steam_MusicRemote::UpdateLooped( bool bValue )
{
PRINT_DEBUG("UpdateLooped\n");
}
bool Steam_MusicRemote::UpdateVolume( float flValue )
{
PRINT_DEBUG("UpdateVolume\n");
}
// volume is between 0.0 and 1.0
// Current Entry
bool Steam_MusicRemote::CurrentEntryWillChange()
{
PRINT_DEBUG("CurrentEntryWillChange\n");
}
bool Steam_MusicRemote::CurrentEntryIsAvailable( bool bAvailable )
{
PRINT_DEBUG("CurrentEntryIsAvailable\n");
}
bool Steam_MusicRemote::UpdateCurrentEntryText( const char *pchText )
{
PRINT_DEBUG("UpdateCurrentEntryText\n");
}
bool Steam_MusicRemote::UpdateCurrentEntryElapsedSeconds( int nValue )
{
PRINT_DEBUG("UpdateCurrentEntryElapsedSeconds\n");
}
bool Steam_MusicRemote::UpdateCurrentEntryCoverArt( void *pvBuffer, uint32 cbBufferLength )
{
PRINT_DEBUG("UpdateCurrentEntryCoverArt\n");
}
bool Steam_MusicRemote::CurrentEntryDidChange()
{
PRINT_DEBUG("CurrentEntryDidChange\n");
}
// Queue
bool Steam_MusicRemote::QueueWillChange()
{
PRINT_DEBUG("QueueWillChange\n");
}
bool Steam_MusicRemote::ResetQueueEntries()
{
PRINT_DEBUG("ResetQueueEntries\n");
}
bool Steam_MusicRemote::SetQueueEntry( int nID, int nPosition, const char *pchEntryText )
{
PRINT_DEBUG("SetQueueEntry\n");
}
bool Steam_MusicRemote::SetCurrentQueueEntry( int nID )
{
PRINT_DEBUG("SetCurrentQueueEntry\n");
}
bool Steam_MusicRemote::QueueDidChange()
{
PRINT_DEBUG("QueueDidChange\n");
}
// Playlist
bool Steam_MusicRemote::PlaylistWillChange()
{
PRINT_DEBUG("PlaylistWillChange\n");
}
bool Steam_MusicRemote::ResetPlaylistEntries()
{
PRINT_DEBUG("ResetPlaylistEntries\n");
}
bool Steam_MusicRemote::SetPlaylistEntry( int nID, int nPosition, const char *pchEntryText )
{
PRINT_DEBUG("SetPlaylistEntry\n");
}
bool Steam_MusicRemote::SetCurrentPlaylistEntry( int nID )
{
PRINT_DEBUG("SetCurrentPlaylistEntry\n");
}
bool Steam_MusicRemote::PlaylistDidChange()
{
PRINT_DEBUG("PlaylistDidChange\n");
}

67
dll/steam_musicremote.h Normal file
View File

@ -0,0 +1,67 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_MusicRemote : public ISteamMusicRemote
{
public:
// Service Definition
bool RegisterSteamMusicRemote( const char *pchName );
bool DeregisterSteamMusicRemote();
bool BIsCurrentMusicRemote();
bool BActivationSuccess( bool bValue );
bool SetDisplayName( const char *pchDisplayName );
bool SetPNGIcon_64x64( void *pvBuffer, uint32 cbBufferLength );
// Abilities for the user interface
bool EnablePlayPrevious(bool bValue);
bool EnablePlayNext( bool bValue );
bool EnableShuffled( bool bValue );
bool EnableLooped( bool bValue );
bool EnableQueue( bool bValue );
bool EnablePlaylists( bool bValue );
// Status
bool UpdatePlaybackStatus( AudioPlayback_Status nStatus );
bool UpdateShuffled( bool bValue );
bool UpdateLooped( bool bValue );
bool UpdateVolume( float flValue ); // volume is between 0.0 and 1.0
// Current Entry
bool CurrentEntryWillChange();
bool CurrentEntryIsAvailable( bool bAvailable );
bool UpdateCurrentEntryText( const char *pchText );
bool UpdateCurrentEntryElapsedSeconds( int nValue );
bool UpdateCurrentEntryCoverArt( void *pvBuffer, uint32 cbBufferLength );
bool CurrentEntryDidChange();
// Queue
bool QueueWillChange();
bool ResetQueueEntries();
bool SetQueueEntry( int nID, int nPosition, const char *pchEntryText );
bool SetCurrentQueueEntry( int nID );
bool QueueDidChange();
// Playlist
bool PlaylistWillChange();
bool ResetPlaylistEntries();
bool SetPlaylistEntry( int nID, int nPosition, const char *pchEntryText );
bool SetCurrentPlaylistEntry( int nID );
bool PlaylistDidChange();
};

904
dll/steam_networking.h Normal file
View File

@ -0,0 +1,904 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
//packet timeout in seconds for non connections
#define ORPHANED_PACKET_TIMEOUT (20)
#define OLD_CHANNEL_NUMBER 1
struct Steam_Networking_Connection {
CSteamID remote;
std::vector<int> open_channels;
};
struct steam_listen_socket {
SNetListenSocket_t id;
int nVirtualP2PPort;
uint32 nIP;
uint16 nPort;
};
enum steam_socket_connection_status {
SOCKET_CONNECTING,
SOCKET_CONNECTED,
SOCKET_DISCONNECTED,
SOCKET_KILLED,
};
struct steam_connection_socket {
SNetSocket_t id;
SNetListenSocket_t listen_id;
enum steam_socket_connection_status status;
CSteamID target;
int nVirtualPort;
uint32 nIP;
uint16 nPort;
SNetSocket_t other_id;
std::vector<Network_Old> data_packets;
};
class Steam_Networking :
public ISteamNetworking001,
public ISteamNetworking002,
public ISteamNetworking003,
public ISteamNetworking004,
public ISteamNetworking
{
class Settings *settings;
class Networking *network;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
std::vector<Common_Message> messages;
std::vector<struct Steam_Networking_Connection> connections;
std::vector<struct steam_listen_socket> listen_sockets;
std::vector<struct steam_connection_socket> connection_sockets;
bool connection_exists(CSteamID id)
{
return std::find_if(connections.begin(), connections.end(), [&id](struct Steam_Networking_Connection const& conn) { return conn.remote == id;}) != connections.end();
}
struct Steam_Networking_Connection *get_or_create_connection(CSteamID id)
{
auto conn = std::find_if(connections.begin(), connections.end(), [&id](struct Steam_Networking_Connection const& conn) { return conn.remote == id;});
if (connections.end() == conn) {
struct Steam_Networking_Connection connection;
connection.remote = id;
connections.push_back(connection);
return &(connections[connections.size() - 1]);
} else {
return &(*conn);
}
}
void remove_connection(CSteamID id)
{
auto conn = std::begin(connections);
while (conn != std::end(connections)) {
if (conn->remote == id) {
conn = connections.erase(conn);
} else {
++conn;
}
}
//pretty sure steam also clears the entire queue of messages for that connection
auto msg = std::begin(messages);
while (msg != std::end(messages)) {
if (msg->source_id() == id.ConvertToUint64()) {
msg = messages.erase(msg);
} else {
++msg;
}
}
}
SNetSocket_t create_connection_socket(CSteamID target, int nVirtualPort, uint32 nIP, uint16 nPort, SNetListenSocket_t id=0, enum steam_socket_connection_status status=SOCKET_CONNECTING, SNetSocket_t other_id=0)
{
static SNetSocket_t socket_number = 0;
bool found = 0;
do {
++socket_number;
for (auto & c: connection_sockets) {
if (c.id == socket_number || socket_number == 0) {
found = true;
break;
}
}
} while (found);
struct steam_connection_socket socket;
socket.id = socket_number;
socket.listen_id = id;
socket.status = status;
socket.target = target;
socket.nVirtualPort = nVirtualPort;
socket.nIP = nIP;
socket.nPort = nPort;
socket.other_id = other_id;
connection_sockets.push_back(socket);
Common_Message msg;
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
msg.set_dest_id(target.ConvertToUint64());
msg.set_allocated_network_old(new Network_Old);
if (nPort) {
msg.mutable_network_old()->set_type(Network_Old::CONNECTION_REQUEST_IP);
msg.mutable_network_old()->set_port(nPort);
} else {
msg.mutable_network_old()->set_type(Network_Old::CONNECTION_REQUEST_STEAMID);
msg.mutable_network_old()->set_port(nVirtualPort);
}
if (socket.status == SOCKET_CONNECTED) {
msg.mutable_network_old()->set_type(Network_Old::CONNECTION_ACCEPTED);
}
msg.mutable_network_old()->set_connection_id(socket.other_id);
msg.mutable_network_old()->set_connection_id_from(socket.id);
if (target.IsValid()) {
network->sendTo(&msg, true);
} else if (nIP) {
network->sendToIPPort(&msg, nIP, nPort, true);
}
return socket.id;
}
struct steam_connection_socket *get_connection_socket(SNetSocket_t id)
{
auto conn = std::find_if(connection_sockets.begin(), connection_sockets.end(), [&id](struct steam_connection_socket const& conn) { return conn.id == id;});
if (conn == connection_sockets.end()) return NULL;
return &(*conn);
}
void remove_killed_connection_sockets()
{
auto socket = std::begin(connection_sockets);
while (socket != std::end(connection_sockets)) {
if (socket->status == SOCKET_KILLED || socket->status == SOCKET_DISCONNECTED) {
socket = connection_sockets.erase(socket);
} else {
++socket;
}
}
}
public:
static void steam_networking_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_networking_callback\n");
Steam_Networking *steam_networking = (Steam_Networking *)object;
steam_networking->Callback(msg);
}
static void steam_networking_run_every_runcp(void *object)
{
PRINT_DEBUG("steam_networking_run_every_runcp\n");
Steam_Networking *steam_networking = (Steam_Networking *)object;
steam_networking->RunCallbacks();
}
Steam_Networking(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->run_every_runcb = run_every_runcb;
this->network->setCallback(CALLBACK_ID_NETWORKING, settings->get_local_steam_id(), &Steam_Networking::steam_networking_callback, this);
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking::steam_networking_callback, this);
this->run_every_runcb->add(&Steam_Networking::steam_networking_run_every_runcp, this);
this->callbacks = callbacks;
PRINT_DEBUG("steam_networking_contructor %llu messages: %p\n", settings->get_local_steam_id().ConvertToUint64(), &messages);
}
~Steam_Networking()
{
//TODO rm network callbacks
this->run_every_runcb->remove(&Steam_Networking::steam_networking_run_every_runcp, this);
}
////////////////////////////////////////////////////////////////////////////////////////////
// Session-less connection functions
// automatically establishes NAT-traversing or Relay server connections
// Sends a P2P packet to the specified user
// UDP-like, unreliable and a max packet size of 1200 bytes
// the first packet send may be delayed as the NAT-traversal code runs
// if we can't get through to the user, an error will be posted via the callback P2PSessionConnectFail_t
// see EP2PSend enum above for the descriptions of the different ways of sending packets
//
// nChannel is a routing number you can use to help route message to different systems - you'll have to call ReadP2PPacket()
// with the same channel number in order to retrieve the data on the other end
// using different channels to talk to the same user will still use the same underlying p2p connection, saving on resources
bool SendP2PPacket( CSteamID steamIDRemote, const void *pubData, uint32 cubData, EP2PSend eP2PSendType, int nChannel)
{
PRINT_DEBUG("Steam_Networking::SendP2PPacket len %u sendtype: %u channel: %u to: %llu\n", cubData, eP2PSendType, nChannel, steamIDRemote.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
bool reliable = false;
if (eP2PSendType == k_EP2PSendReliable || eP2PSendType == k_EP2PSendReliableWithBuffering) reliable = true;
Common_Message msg;
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
msg.set_dest_id(steamIDRemote.ConvertToUint64());
msg.set_allocated_network(new Network);
if (!connection_exists(steamIDRemote)) {
msg.mutable_network()->set_type(Network::CLEAR_BEFORE);
network->sendTo(&msg, true);
}
msg.mutable_network()->set_channel(nChannel);
msg.mutable_network()->set_data(pubData, cubData);
msg.mutable_network()->set_type(Network::DATA);
struct Steam_Networking_Connection *conn = get_or_create_connection(steamIDRemote);
PUSH_BACK_IF_NOT_IN(conn->open_channels, nChannel);
bool ret = network->sendTo(&msg, reliable);
PRINT_DEBUG("Sent message with size: %zu %u\n", msg.network().data().size(), ret);
return ret;
}
bool SendP2PPacket( CSteamID steamIDRemote, const void *pubData, uint32 cubData, EP2PSend eP2PSendType ) {
PRINT_DEBUG("Steam_Networking::SendP2PPacket old\n");
return SendP2PPacket(steamIDRemote, pubData, cubData, eP2PSendType, OLD_CHANNEL_NUMBER);
}
// returns true if any data is available for read, and the amount of data that will need to be read
bool IsP2PPacketAvailable( uint32 *pcubMsgSize, int nChannel)
{
PRINT_DEBUG("Steam_Networking::IsP2PPacketAvailable channel: %i\n", nChannel);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//Not sure if this should be here because it slightly screws up games that don't like such low "pings"
//Commenting it out for now because it looks like it causes a bug where 20xx gets stuck in an infinite receive packet loop
//this->network->Run();
//RunCallbacks();
PRINT_DEBUG("Messages %zu %p\n", messages.size(), &messages);
for (auto &msg : messages) {
if (connection_exists((uint64)msg.source_id()) && msg.mutable_network()->channel() == nChannel && msg.network().processed()) {
uint32 size = msg.mutable_network()->data().size();
if (pcubMsgSize) *pcubMsgSize = size;
PRINT_DEBUG("available with size: %lu\n", size);
return true;
}
}
PRINT_DEBUG("Not available\n");
return false;
}
bool IsP2PPacketAvailable( uint32 *pcubMsgSize)
{
PRINT_DEBUG("Steam_Networking::IsP2PPacketAvailable old\n");
return IsP2PPacketAvailable(pcubMsgSize, OLD_CHANNEL_NUMBER);
}
// reads in a packet that has been sent from another user via SendP2PPacket()
// returns the size of the message and the steamID of the user who sent it in the last two parameters
// if the buffer passed in is too small, the message will be truncated
// this call is not blocking, and will return false if no data is available
bool ReadP2PPacket( void *pubDest, uint32 cubDest, uint32 *pcubMsgSize, CSteamID *psteamIDRemote, int nChannel)
{
PRINT_DEBUG("Steam_Networking::ReadP2PPacket %u %i\n", cubDest, nChannel);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//Not sure if this should be here because it slightly screws up games that don't like such low "pings"
//Commenting it out for now because it looks like it causes a bug where 20xx gets stuck in an infinite receive packet loop
//this->network->Run();
//RunCallbacks();
bool read = false;
PRINT_DEBUG("Number messages %zu\n", messages.size());
auto msg = std::begin(messages);
while (msg != std::end(messages)) {
if (connection_exists((uint64)msg->source_id()) && msg->network().channel() == nChannel && msg->network().processed()) {
uint32 msg_size = msg->network().data().size();
if (msg_size > cubDest) msg_size = cubDest;
if (pcubMsgSize) *pcubMsgSize = msg_size;
memcpy(pubDest, msg->network().data().data(), msg_size);
#ifndef EMU_RELEASE_BUILD
for (int i = 0; i < msg_size; ++i) {
PRINT_DEBUG("%02hhX", ((char*)pubDest)[i]);
}PRINT_DEBUG("\n");
#endif
*psteamIDRemote = CSteamID((uint64)msg->source_id());
PRINT_DEBUG("Steam_Networking::ReadP2PPacket len %u channel: %u from: %llu\n", msg_size, nChannel, msg->source_id());
msg = messages.erase(msg);
return true;
}
++msg;
}
return false;
}
bool ReadP2PPacket( void *pubDest, uint32 cubDest, uint32 *pcubMsgSize, CSteamID *psteamIDRemote)
{
PRINT_DEBUG("Steam_Networking::ReadP2PPacket old\n");
return ReadP2PPacket(pubDest, cubDest, pcubMsgSize, psteamIDRemote, OLD_CHANNEL_NUMBER);
}
// AcceptP2PSessionWithUser() should only be called in response to a P2PSessionRequest_t callback
// P2PSessionRequest_t will be posted if another user tries to send you a packet that you haven't talked to yet
// if you don't want to talk to the user, just ignore the request
// if the user continues to send you packets, another P2PSessionRequest_t will be posted periodically
// this may be called multiple times for a single user
// (if you've called SendP2PPacket() on the other user, this implicitly accepts the session request)
bool AcceptP2PSessionWithUser( CSteamID steamIDRemote )
{
PRINT_DEBUG("Steam_Networking::AcceptP2PSessionWithUser %llu\n", steamIDRemote.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Steam_Networking_Connection *conn = get_or_create_connection(steamIDRemote);
return !!conn;
}
// call CloseP2PSessionWithUser() when you're done talking to a user, will free up resources under-the-hood
// if the remote user tries to send data to you again, another P2PSessionRequest_t callback will be posted
bool CloseP2PSessionWithUser( CSteamID steamIDRemote )
{
PRINT_DEBUG("Steam_Networking::CloseP2PSessionWithUser %llu\n", steamIDRemote.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!connection_exists(steamIDRemote)) {
return false;
}
remove_connection(steamIDRemote);
return true;
}
// call CloseP2PChannelWithUser() when you're done talking to a user on a specific channel. Once all channels
// open channels to a user have been closed, the open session to the user will be closed and new data from this
// user will trigger a P2PSessionRequest_t callback
bool CloseP2PChannelWithUser( CSteamID steamIDRemote, int nChannel )
{
PRINT_DEBUG("Steam_Networking::CloseP2PChannelWithUser\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!connection_exists(steamIDRemote)) {
return false;
}
struct Steam_Networking_Connection *conn = get_or_create_connection(steamIDRemote);
conn->open_channels.erase(std::remove(conn->open_channels.begin(), conn->open_channels.end(), nChannel), conn->open_channels.end());
if (conn->open_channels.size() == 0) {
remove_connection(steamIDRemote);
}
return true;
}
// fills out P2PSessionState_t structure with details about the underlying connection to the user
// should only needed for debugging purposes
// returns false if no connection exists to the specified user
bool GetP2PSessionState( CSteamID steamIDRemote, P2PSessionState_t *pConnectionState )
{
PRINT_DEBUG("Steam_Networking::GetP2PSessionState %llu\n", steamIDRemote.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!connection_exists(steamIDRemote) && (steamIDRemote != settings->get_local_steam_id())) {
if (pConnectionState) {
pConnectionState->m_bConnectionActive = false;
pConnectionState->m_bConnecting = false;
pConnectionState->m_eP2PSessionError = 0;
pConnectionState->m_bUsingRelay = false;
pConnectionState->m_nBytesQueuedForSend = 0;
pConnectionState->m_nPacketsQueuedForSend = 0;
pConnectionState->m_nRemoteIP = 0;
pConnectionState->m_nRemotePort = 0;
}
PRINT_DEBUG("No Connection\n");
return false;
}
if (pConnectionState) {
pConnectionState->m_bConnectionActive = true;
pConnectionState->m_bConnecting = false;
pConnectionState->m_eP2PSessionError = 0;
pConnectionState->m_bUsingRelay = false;
pConnectionState->m_nBytesQueuedForSend = 0;
pConnectionState->m_nPacketsQueuedForSend = 0;
//TODO ip?
pConnectionState->m_nRemoteIP = 0;
pConnectionState->m_nRemotePort = 0;
}
PRINT_DEBUG("Connection\n");
return true;
}
// Allow P2P connections to fall back to being relayed through the Steam servers if a direct connection
// or NAT-traversal cannot be established. Only applies to connections created after setting this value,
// or to existing connections that need to automatically reconnect after this value is set.
//
// P2P packet relay is allowed by default
bool AllowP2PPacketRelay( bool bAllow )
{
PRINT_DEBUG("Steam_Networking::AllowP2PPacketRelay\n");
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////
// LISTEN / CONNECT style interface functions
//
// This is an older set of functions designed around the Berkeley TCP sockets model
// it's preferential that you use the above P2P functions, they're more robust
// and these older functions will be removed eventually
//
////////////////////////////////////////////////////////////////////////////////////////////
SNetListenSocket_t socket_number = 0;
// creates a socket and listens others to connect
// will trigger a SocketStatusCallback_t callback on another client connecting
// nVirtualP2PPort is the unique ID that the client will connect to, in case you have multiple ports
// this can usually just be 0 unless you want multiple sets of connections
// unIP is the local IP address to bind to
// pass in 0 if you just want the default local IP
// unPort is the port to use
// pass in 0 if you don't want users to be able to connect via IP/Port, but expect to be always peer-to-peer connections only
SNetListenSocket_t CreateListenSocket( int nVirtualP2PPort, uint32 nIP, uint16 nPort, bool bAllowUseOfPacketRelay )
{
PRINT_DEBUG("Steam_Networking::CreateListenSocket %i %u %hu %u\n", nVirtualP2PPort, nIP, nPort, bAllowUseOfPacketRelay);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
for (auto & c : listen_sockets) {
if (c.nVirtualP2PPort == nVirtualP2PPort || c.nPort == nPort)
return 0;
}
++socket_number;
if (!socket_number) ++socket_number;
struct steam_listen_socket socket;
socket.id = socket_number;
socket.nVirtualP2PPort = nVirtualP2PPort;
socket.nIP = nIP;
socket.nPort = nPort;
listen_sockets.push_back(socket);
return socket.id;
}
SNetListenSocket_t CreateListenSocket( int nVirtualP2PPort, uint32 nIP, uint16 nPort )
{
PRINT_DEBUG("Steam_Networking::CreateListenSocket old\n");
return CreateListenSocket(nVirtualP2PPort, nIP, nPort, true);
}
// creates a socket and begin connection to a remote destination
// can connect via a known steamID (client or game server), or directly to an IP
// on success will trigger a SocketStatusCallback_t callback
// on failure or timeout will trigger a SocketStatusCallback_t callback with a failure code in m_eSNetSocketState
SNetSocket_t CreateP2PConnectionSocket( CSteamID steamIDTarget, int nVirtualPort, int nTimeoutSec, bool bAllowUseOfPacketRelay )
{
PRINT_DEBUG("Steam_Networking::CreateP2PConnectionSocket %llu %i %i %u\n", steamIDTarget.ConvertToUint64(), nVirtualPort, nTimeoutSec, bAllowUseOfPacketRelay);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO: nTimeoutSec
return create_connection_socket(steamIDTarget, nVirtualPort, 0, 0);
}
SNetSocket_t CreateP2PConnectionSocket( CSteamID steamIDTarget, int nVirtualPort, int nTimeoutSec )
{
PRINT_DEBUG("Steam_Networking::CreateP2PConnectionSocket old\n");
return CreateP2PConnectionSocket(steamIDTarget, nVirtualPort, nTimeoutSec, true);
}
SNetSocket_t CreateConnectionSocket( uint32 nIP, uint16 nPort, int nTimeoutSec )
{
PRINT_DEBUG("Steam_Networking::CreateConnectionSocket %u %hu %i\n", nIP, nPort, nTimeoutSec);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO: nTimeoutSec
return create_connection_socket((uint64)0, 0, nIP, nPort);
}
// disconnects the connection to the socket, if any, and invalidates the handle
// any unread data on the socket will be thrown away
// if bNotifyRemoteEnd is set, socket will not be completely destroyed until the remote end acknowledges the disconnect
bool DestroySocket( SNetSocket_t hSocket, bool bNotifyRemoteEnd )
{
PRINT_DEBUG("Steam_Networking::DestroySocket\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct steam_connection_socket *socket = get_connection_socket(hSocket);
if (!socket || socket->status == SOCKET_KILLED) return false;
socket->status = SOCKET_KILLED;
return true;
}
// destroying a listen socket will automatically kill all the regular sockets generated from it
bool DestroyListenSocket( SNetListenSocket_t hSocket, bool bNotifyRemoteEnd )
{
PRINT_DEBUG("Steam_Networking::DestroyListenSocket\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto c = std::begin(listen_sockets);
while (c != std::end(listen_sockets)) {
if (c->id == hSocket) {
c = listen_sockets.erase(c);
for (auto & socket : connection_sockets) {
if (socket.listen_id == hSocket) {
socket.status = SOCKET_KILLED;
}
}
return true;
} else {
++c;
}
}
return false;
}
// sending data
// must be a handle to a connected socket
// data is all sent via UDP, and thus send sizes are limited to 1200 bytes; after this, many routers will start dropping packets
// use the reliable flag with caution; although the resend rate is pretty aggressive,
// it can still cause stalls in receiving data (like TCP)
bool SendDataOnSocket( SNetSocket_t hSocket, void *pubData, uint32 cubData, bool bReliable )
{
PRINT_DEBUG("Steam_Networking::SendDataOnSocket\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct steam_connection_socket *socket = get_connection_socket(hSocket);
if (!socket || socket->status != SOCKET_CONNECTED) return false;
Common_Message msg;
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
msg.set_dest_id(socket->target.ConvertToUint64());
msg.set_allocated_network_old(new Network_Old);
msg.mutable_network_old()->set_type(Network_Old::DATA);
msg.mutable_network_old()->set_connection_id(socket->other_id);
msg.mutable_network_old()->set_data(pubData, cubData);
return network->sendTo(&msg, bReliable);
}
// receiving data
// returns false if there is no data remaining
// fills out *pcubMsgSize with the size of the next message, in bytes
bool IsDataAvailableOnSocket( SNetSocket_t hSocket, uint32 *pcubMsgSize )
{
PRINT_DEBUG("Steam_Networking::IsDataAvailableOnSocket\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct steam_connection_socket *socket = get_connection_socket(hSocket);
if (!socket) return false;
if (socket->data_packets.size() == 0) return false;
if (pcubMsgSize) *pcubMsgSize = socket->data_packets[0].data().size();
return true;
}
// fills in pubDest with the contents of the message
// messages are always complete, of the same size as was sent (i.e. packetized, not streaming)
// if *pcubMsgSize < cubDest, only partial data is written
// returns false if no data is available
bool RetrieveDataFromSocket( SNetSocket_t hSocket, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize )
{
PRINT_DEBUG("Steam_Networking::RetrieveDataFromSocket\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct steam_connection_socket *socket = get_connection_socket(hSocket);
if (!socket || socket->data_packets.size() == 0) return false;
auto msg = std::begin(socket->data_packets);
if (msg != std::end(socket->data_packets)) {
uint32 msg_size = msg->data().size();
if (msg_size > cubDest) msg_size = cubDest;
if (pcubMsgSize) *pcubMsgSize = msg_size;
memcpy(pubDest, msg->data().data(), msg_size);
msg = socket->data_packets.erase(msg);
return true;
}
return false;
}
// checks for data from any socket that has been connected off this listen socket
// returns false if there is no data remaining
// fills out *pcubMsgSize with the size of the next message, in bytes
// fills out *phSocket with the socket that data is available on
bool IsDataAvailable( SNetListenSocket_t hListenSocket, uint32 *pcubMsgSize, SNetSocket_t *phSocket )
{
PRINT_DEBUG("Steam_Networking::IsDataAvailable\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!hListenSocket) return false;
for (auto & socket : connection_sockets) {
if (socket.listen_id == hListenSocket && socket.data_packets.size()) {
if (pcubMsgSize) *pcubMsgSize = socket.data_packets[0].data().size();
if (phSocket) *phSocket = socket.id;
return true;
}
}
return false;
}
// retrieves data from any socket that has been connected off this listen socket
// fills in pubDest with the contents of the message
// messages are always complete, of the same size as was sent (i.e. packetized, not streaming)
// if *pcubMsgSize < cubDest, only partial data is written
// returns false if no data is available
// fills out *phSocket with the socket that data is available on
bool RetrieveData( SNetListenSocket_t hListenSocket, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize, SNetSocket_t *phSocket )
{
PRINT_DEBUG("Steam_Networking::RetrieveData\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!hListenSocket) return false;
for (auto & socket : connection_sockets) {
if (socket.listen_id == hListenSocket && socket.data_packets.size()) {
auto msg = std::begin(socket.data_packets);
if (msg != std::end(socket.data_packets)) {
uint32 msg_size = msg->data().size();
if (msg_size > cubDest) msg_size = cubDest;
if (pcubMsgSize) *pcubMsgSize = msg_size;
if (phSocket) *phSocket = socket.id;
memcpy(pubDest, msg->data().data(), msg_size);
msg = socket.data_packets.erase(msg);
return true;
}
}
}
return false;
}
// returns information about the specified socket, filling out the contents of the pointers
bool GetSocketInfo( SNetSocket_t hSocket, CSteamID *pSteamIDRemote, int *peSocketStatus, uint32 *punIPRemote, uint16 *punPortRemote )
{
PRINT_DEBUG("Steam_Networking::GetSocketInfo\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct steam_connection_socket *socket = get_connection_socket(hSocket);
if (!socket) return false;
if (pSteamIDRemote) *pSteamIDRemote = socket->target;
if (peSocketStatus) {
//TODO: I'm not sure what peSocketStatus is supposed to be but I'm guessing it's ESNetSocketState
if (socket->status == SOCKET_CONNECTED) {
*peSocketStatus = k_ESNetSocketStateConnected;
} else if (socket->status == SOCKET_CONNECTING) {
*peSocketStatus = k_ESNetSocketStateInitiated;
} else if (socket->status == SOCKET_DISCONNECTED) {
*peSocketStatus = k_ESNetSocketStateDisconnecting;
} else if (socket->status == SOCKET_KILLED) {
*peSocketStatus = k_ESNetSocketStateConnectionBroken;
} else {
*peSocketStatus = k_ESNetSocketStateInvalid;
}
}
if (punIPRemote) *punIPRemote = socket->nIP;
if (punPortRemote) *punPortRemote = socket->nPort;
return true;
}
// returns which local port the listen socket is bound to
// *pnIP and *pnPort will be 0 if the socket is set to listen for P2P connections only
bool GetListenSocketInfo( SNetListenSocket_t hListenSocket, uint32 *pnIP, uint16 *pnPort )
{
PRINT_DEBUG("Steam_Networking::GetListenSocketInfo\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto conn = std::find_if(listen_sockets.begin(), listen_sockets.end(), [&hListenSocket](struct steam_listen_socket const& conn) { return conn.id == hListenSocket;});
if (conn == listen_sockets.end()) return false;
if (pnIP) *pnIP = conn->nIP;
if (pnPort) *pnPort = conn->nPort;
return true;
}
// returns true to describe how the socket ended up connecting
ESNetSocketConnectionType GetSocketConnectionType( SNetSocket_t hSocket )
{
PRINT_DEBUG("Steam_Networking::GetSocketConnectionType\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct steam_connection_socket *socket = get_connection_socket(hSocket);
if (!socket || socket->status != SOCKET_CONNECTED) return k_ESNetSocketConnectionTypeNotConnected;
else return k_ESNetSocketConnectionTypeUDP;
}
// max packet size, in bytes
int GetMaxPacketSize( SNetSocket_t hSocket )
{
PRINT_DEBUG("Steam_Networking::GetMaxPacketSize\n");
return 1500;
}
void RunCallbacks()
{
uint64 current_time = std::chrono::duration_cast<std::chrono::duration<uint64>>(std::chrono::system_clock::now().time_since_epoch()).count();
for (auto &msg : messages) {
CSteamID source_id((uint64)msg.source_id());
if (!msg.network().processed()) {
if (!connection_exists(source_id)) {
P2PSessionRequest_t data;
memset(&data, 0, sizeof(data));
data.m_steamIDRemote = CSteamID(source_id);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), true);
} else {
struct Steam_Networking_Connection *conn = get_or_create_connection(source_id);
PUSH_BACK_IF_NOT_IN(conn->open_channels, msg.network().channel());
}
msg.mutable_network()->set_processed(true);
msg.mutable_network()->set_time_processed(current_time);
}
}
auto msg = std::begin(messages);
while (msg != std::end(messages)) {
bool deleted = false;
if (msg->network().processed()) {
if (!connection_exists((uint64)msg->source_id())) {
if (msg->network().time_processed() + ORPHANED_PACKET_TIMEOUT < current_time) {
deleted = true;
}
}
}
if (deleted) {
msg = messages.erase(msg);
} else {
++msg;
}
}
//TODO: not sure if sockets should be wiped right away
remove_killed_connection_sockets();
}
void Callback(Common_Message *msg)
{
if (msg->has_network()) {
#ifndef EMU_RELEASE_BUILD
PRINT_DEBUG("Steam_Networking: got msg from: %llu to: %llu size %zu | messages %p: %zu\n", msg->source_id(), msg->dest_id(), msg->network().data().size(), &messages, messages.size());
for (int i = 0; i < msg->network().data().size(); ++i) {
PRINT_DEBUG("%02hhX", msg->network().data().data()[i]);
}PRINT_DEBUG("\n");
#endif
if (msg->network().type() == Network::DATA) {
messages.push_back(Common_Message(*msg));
}
if (msg->network().type() == Network::CLEAR_BEFORE) {
auto msg_temp = std::begin(messages);
while (msg_temp != std::end(messages)) {
//only delete processed to handle unreliable message arriving at the same time.
if (msg_temp->source_id() == msg->source_id() && msg->network().processed()) {
msg_temp = messages.erase(msg_temp);
} else {
++msg_temp;
}
}
}
}
if (msg->has_network_old()) {
PRINT_DEBUG("Steam_Networking: got network socket msg %u\n", msg->network_old().type());
if (msg->network_old().type() == Network_Old::CONNECTION_REQUEST_IP) {
for (auto & listen : listen_sockets) {
if (listen.nPort == msg->network_old().port()) {
SNetSocket_t new_sock = create_connection_socket((uint64)msg->source_id(), 0, 0, msg->network_old().port(), listen.id, SOCKET_CONNECTED, msg->network_old().connection_id_from());
if (new_sock) {
struct SocketStatusCallback_t data;
data.m_hSocket = new_sock;
data.m_hListenSocket = listen.id;
data.m_steamIDRemote = (uint64)msg->source_id();
data.m_eSNetSocketState = k_ESNetSocketStateConnected; //TODO is this the right state?
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
}
} else if (msg->network_old().type() == Network_Old::CONNECTION_REQUEST_STEAMID) {
for (auto & listen : listen_sockets) {
if (listen.nVirtualP2PPort == msg->network_old().port()) {
SNetSocket_t new_sock = create_connection_socket((uint64)msg->source_id(), msg->network_old().port(), 0, 0, listen.id, SOCKET_CONNECTED, msg->network_old().connection_id_from());
if (new_sock) {
struct SocketStatusCallback_t data;
data.m_hSocket = new_sock;
data.m_hListenSocket = listen.id;
data.m_steamIDRemote = (uint64)msg->source_id();
data.m_eSNetSocketState = k_ESNetSocketStateConnected; //TODO is this the right state?
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
}
} else if (msg->network_old().type() == Network_Old::CONNECTION_ACCEPTED) {
struct steam_connection_socket *socket = get_connection_socket(msg->network_old().connection_id());
if (socket && socket->nPort && socket->status == SOCKET_CONNECTING && !socket->target.IsValid()) {
socket->target = (uint64)msg->source_id();
}
if (socket && socket->status == SOCKET_CONNECTING && msg->source_id() == socket->target.ConvertToUint64()) {
socket->status = SOCKET_CONNECTED;
socket->other_id = msg->network_old().connection_id_from();
struct SocketStatusCallback_t data;
data.m_hSocket = socket->id;
data.m_hListenSocket = socket->listen_id;
data.m_steamIDRemote = socket->target;
data.m_eSNetSocketState = k_ESNetSocketStateConnected; //TODO is this the right state?
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
} else if (msg->network_old().type() == Network_Old::CONNECTION_END) {
struct steam_connection_socket *socket = get_connection_socket(msg->network_old().connection_id());
if (socket && socket->status == SOCKET_CONNECTED && msg->source_id() == socket->target.ConvertToUint64()) {
struct SocketStatusCallback_t data;
socket->status = SOCKET_DISCONNECTED;
data.m_hSocket = socket->id;
data.m_hListenSocket = socket->listen_id;
data.m_steamIDRemote = socket->target;
data.m_eSNetSocketState = k_ESNetSocketStateRemoteEndDisconnected; //TODO is this the right state?
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
} else if (msg->network_old().type() == Network_Old::DATA) {
struct steam_connection_socket *socket = get_connection_socket(msg->network_old().connection_id());
if (socket && socket->status == SOCKET_CONNECTED && msg->source_id() == socket->target.ConvertToUint64()) {
socket->data_packets.push_back(msg->network_old());
}
}
}
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::DISCONNECT) {
CSteamID source_id((uint64)msg->source_id());
if (connection_exists(source_id)) {
P2PSessionConnectFail_t data;
data.m_steamIDRemote = source_id;
data.m_eP2PSessionError = k_EP2PSessionErrorDestinationNotLoggedIn;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
for (auto & socket : connection_sockets) {
if (socket.target.ConvertToUint64() == msg->source_id()) {
struct SocketStatusCallback_t data;
socket.status = SOCKET_DISCONNECTED;
data.m_hSocket = socket.id;
data.m_hListenSocket = socket.listen_id;
data.m_steamIDRemote = socket.target;
data.m_eSNetSocketState = k_ESNetSocketStateConnectionBroken; //TODO is this the right state?
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
} else
if (msg->low_level().type() == Low_Level::CONNECT) {
}
}
}
};

View File

@ -0,0 +1,837 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
struct Listen_Socket {
HSteamListenSocket socket_id;
int virtual_port;
uint32 ip;
uint16 port;
};
class Steam_Networking_Sockets :
public ISteamNetworkingSockets001,
public ISteamNetworkingSockets
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
std::vector<struct Listen_Socket> listen_sockets;
public:
static void steam_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_networkingsockets_callback\n");
Steam_Networking_Sockets *steam_networkingsockets = (Steam_Networking_Sockets *)object;
steam_networkingsockets->Callback(msg);
}
static void steam_run_every_runcb(void *object)
{
PRINT_DEBUG("steam_networkingsockets_run_every_runcb\n");
Steam_Networking_Sockets *steam_networkingsockets = (Steam_Networking_Sockets *)object;
steam_networkingsockets->RunCallbacks();
}
Steam_Networking_Sockets(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->run_every_runcb = run_every_runcb;
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Sockets::steam_callback, this);
this->network->setCallback(CALLBACK_ID_NETWORKING_SOCKETS, settings->get_local_steam_id(), &Steam_Networking_Sockets::steam_callback, this);
this->run_every_runcb->add(&Steam_Networking_Sockets::steam_run_every_runcb, this);
this->callback_results = callback_results;
this->callbacks = callbacks;
}
~Steam_Networking_Sockets()
{
//TODO rm network callbacks
this->run_every_runcb->remove(&Steam_Networking_Sockets::steam_run_every_runcb, this);
}
HSteamListenSocket new_listen_socket(int nSteamConnectVirtualPort, uint32 nIP, uint16 nPort)
{
static HSteamListenSocket socket_id;
++socket_id;
struct Listen_Socket listen_socket;
listen_socket.socket_id = socket_id;
listen_socket.virtual_port = nSteamConnectVirtualPort;
listen_socket.ip = nIP;
listen_socket.port = nPort;
listen_sockets.push_back(listen_socket);
return socket_id;
}
struct Listen_Socket *get_connection_socket(HSteamListenSocket id)
{
auto conn = std::find_if(listen_sockets.begin(), listen_sockets.end(), [&id](struct Listen_Socket const& conn) { return conn.socket_id == id;});
if (conn == listen_sockets.end()) return NULL;
return &(*conn);
}
/// Creates a "server" socket that listens for clients to connect to, either by calling
/// ConnectSocketBySteamID or ConnectSocketByIPv4Address.
///
/// nSteamConnectVirtualPort specifies how clients can connect to this socket using
/// ConnectBySteamID. A negative value indicates that this functionality is
/// disabled and clients must connect by IP address. It's very common for applications
/// to only have one listening socket; in that case, use zero. If you need to open
/// multiple listen sockets and have clients be able to connect to one or the other, then
/// nSteamConnectVirtualPort should be a small integer constant unique to each listen socket
/// you create.
///
/// In the open-source version of this API, you must pass -1 for nSteamConnectVirtualPort
///
/// If you want clients to connect to you by your IPv4 addresses using
/// ConnectByIPv4Address, then you must set nPort to be nonzero. Steam will
/// bind a UDP socket to the specified local port, and clients will send packets using
/// ordinary IP routing. It's up to you to take care of NAT, protecting your server
/// from DoS, etc. If you don't need clients to connect to you by IP, then set nPort=0.
/// Use nIP if you wish to bind to a particular local interface. Typically you will use 0,
/// which means to listen on all interfaces, and accept the default outbound IP address.
/// If nPort is zero, then nIP must also be zero.
///
/// A SocketStatusCallback_t callback when another client attempts a connection.
HSteamListenSocket CreateListenSocket( int nSteamConnectVirtualPort, uint32 nIP, uint16 nPort )
{
PRINT_DEBUG("Steam_Networking_Sockets::CreateListenSocket %i %u %u\n", nSteamConnectVirtualPort, nIP, nPort);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return new_listen_socket(nSteamConnectVirtualPort, nIP, nPort);
}
/// Creates a "server" socket that listens for clients to connect to by
/// calling ConnectByIPAddress, over ordinary UDP (IPv4 or IPv6)
///
/// You must select a specific local port to listen on and set it
/// the port field of the local address.
///
/// Usually you wil set the IP portion of the address to zero, (SteamNetworkingIPAddr::Clear()).
/// This means that you will not bind to any particular local interface. In addition,
/// if possible the socket will be bound in "dual stack" mode, which means that it can
/// accept both IPv4 and IPv6 clients. If you wish to bind a particular interface, then
/// set the local address to the appropriate IPv4 or IPv6 IP.
///
/// When a client attempts to connect, a SteamNetConnectionStatusChangedCallback_t
/// will be posted. The connection will be in the connecting state.
HSteamListenSocket CreateListenSocketIP( const SteamNetworkingIPAddr &localAddress )
{
PRINT_DEBUG("Steam_Networking_Sockets::CreateListenSocketIP\n");
}
/// Creates a connection and begins talking to a "server" over UDP at the
/// given IPv4 or IPv6 address. The remote host must be listening with a
/// matching call to CreateListenSocketIP on the specified port.
///
/// A SteamNetConnectionStatusChangedCallback_t callback will be triggered when we start
/// connecting, and then another one on either timeout or successful connection.
///
/// If the server does not have any identity configured, then their network address
/// will be the only identity in use. Or, the network host may provide a platform-specific
/// identity with or without a valid certificate to authenticate that identity. (These
/// details will be contained in the SteamNetConnectionStatusChangedCallback_t.) It's
/// up to your application to decide whether to allow the connection.
///
/// By default, all connections will get basic encryption sufficient to prevent
/// casual eavesdropping. But note that without certificates (or a shared secret
/// distributed through some other out-of-band mechanism), you don't have any
/// way of knowing who is actually on the other end, and thus are vulnerable to
/// man-in-the-middle attacks.
HSteamNetConnection ConnectByIPAddress( const SteamNetworkingIPAddr &address )
{
PRINT_DEBUG("Steam_Networking_Sockets::ConnectByIPAddress\n");
}
/// Like CreateListenSocketIP, but clients will connect using ConnectP2P
///
/// nVirtualPort specifies how clients can connect to this socket using
/// ConnectP2P. It's very common for applications to only have one listening socket;
/// in that case, use zero. If you need to open multiple listen sockets and have clients
/// be able to connect to one or the other, then nVirtualPort should be a small integer (<1000)
/// unique to each listen socket you create.
///
/// If you use this, you probably want to call ISteamNetworkingUtils::InitializeRelayNetworkAccess()
/// when your app initializes
HSteamListenSocket CreateListenSocketP2P( int nVirtualPort )
{
PRINT_DEBUG("Steam_Networking_Sockets::CreateListenSocketP2P\n");
}
/// Begin connecting to a server that is identified using a platform-specific identifier.
/// This requires some sort of third party rendezvous service, and will depend on the
/// platform and what other libraries and services you are integrating with.
///
/// At the time of this writing, there is only one supported rendezvous service: Steam.
/// Set the SteamID (whether "user" or "gameserver") and Steam will determine if the
/// client is online and facilitate a relay connection. Note that all P2P connections on
/// Steam are currently relayed.
///
/// If you use this, you probably want to call ISteamNetworkingUtils::InitializeRelayNetworkAccess()
/// when your app initializes
HSteamNetConnection ConnectP2P( const SteamNetworkingIdentity &identityRemote, int nVirtualPort )
{
PRINT_DEBUG("Steam_Networking_Sockets::ConnectP2P\n");
}
/// Creates a connection and begins talking to a remote destination. The remote host
/// must be listening with a matching call to CreateListenSocket.
///
/// Use ConnectBySteamID to connect using the SteamID (client or game server) as the network address.
/// Use ConnectByIPv4Address to connect by IP address.
///
/// A SteamNetConnectionStatusChangedCallback_t callback will be triggered when we start connecting,
/// and then another one on timeout or successful connection
//#ifndef STEAMNETWORKINGSOCKETS_OPENSOURCE
HSteamNetConnection ConnectBySteamID( CSteamID steamIDTarget, int nVirtualPort )
{
PRINT_DEBUG("Steam_Networking_Sockets::ConnectBySteamID\n");
}
//#endif
HSteamNetConnection ConnectByIPv4Address( uint32 nIP, uint16 nPort )
{
PRINT_DEBUG("Steam_Networking_Sockets::ConnectByIPv4Address\n");
}
/// Accept an incoming connection that has been received on a listen socket.
///
/// When a connection attempt is received (perhaps after a few basic handshake
/// packets have been exchanged to prevent trivial spoofing), a connection interface
/// object is created in the k_ESteamNetworkingConnectionState_Connecting state
/// and a SteamNetConnectionStatusChangedCallback_t is posted. At this point, your
/// application MUST either accept or close the connection. (It may not ignore it.)
/// Accepting the connection will transition it either into the connected state,
/// of the finding route state, depending on the connection type.
///
/// You should take action within a second or two, because accepting the connection is
/// what actually sends the reply notifying the client that they are connected. If you
/// delay taking action, from the client's perspective it is the same as the network
/// being unresponsive, and the client may timeout the connection attempt. In other
/// words, the client cannot distinguish between a delay caused by network problems
/// and a delay caused by the application.
///
/// This means that if your application goes for more than a few seconds without
/// processing callbacks (for example, while loading a map), then there is a chance
/// that a client may attempt to connect in that interval and fail due to timeout.
///
/// If the application does not respond to the connection attempt in a timely manner,
/// and we stop receiving communication from the client, the connection attempt will
/// be timed out locally, transitioning the connection to the
/// k_ESteamNetworkingConnectionState_ProblemDetectedLocally state. The client may also
/// close the connection before it is accepted, and a transition to the
/// k_ESteamNetworkingConnectionState_ClosedByPeer is also possible depending the exact
/// sequence of events.
///
/// Returns k_EResultInvalidParam if the handle is invalid.
/// Returns k_EResultInvalidState if the connection is not in the appropriate state.
/// (Remember that the connection state could change in between the time that the
/// notification being posted to the queue and when it is received by the application.)
EResult AcceptConnection( HSteamNetConnection hConn )
{
PRINT_DEBUG("Steam_Networking_Sockets::AcceptConnection\n");
}
/// Disconnects from the remote host and invalidates the connection handle.
/// Any unread data on the connection is discarded.
///
/// nReason is an application defined code that will be received on the other
/// end and recorded (when possible) in backend analytics. The value should
/// come from a restricted range. (See ESteamNetConnectionEnd.) If you don't need
/// to communicate any information to the remote host, and do not want analytics to
/// be able to distinguish "normal" connection terminations from "exceptional" ones,
/// You may pass zero, in which case the generic value of
/// k_ESteamNetConnectionEnd_App_Generic will be used.
///
/// pszDebug is an optional human-readable diagnostic string that will be received
/// by the remote host and recorded (when possible) in backend analytics.
///
/// If you wish to put the socket into a "linger" state, where an attempt is made to
/// flush any remaining sent data, use bEnableLinger=true. Otherwise reliable data
/// is not flushed.
///
/// If the connection has already ended and you are just freeing up the
/// connection interface, the reason code, debug string, and linger flag are
/// ignored.
bool CloseConnection( HSteamNetConnection hPeer, int nReason, const char *pszDebug, bool bEnableLinger )
{
PRINT_DEBUG("Steam_Networking_Sockets::CloseConnection\n");
}
/// Destroy a listen socket, and all the client sockets generated by accepting connections
/// on the listen socket.
///
/// pszNotifyRemoteReason determines what cleanup actions are performed on the client
/// sockets being destroyed. (See DestroySocket for more details.)
///
/// Note that if cleanup is requested and you have requested the listen socket bound to a
/// particular local port to facilitate direct UDP/IPv4 connections, then the underlying UDP
/// socket must remain open until all clients have been cleaned up.
bool CloseListenSocket( HSteamListenSocket hSocket, const char *pszNotifyRemoteReason )
{
PRINT_DEBUG("Steam_Networking_Sockets::CloseListenSocket old\n");
}
/// Destroy a listen socket. All the connections that were accepting on the listen
/// socket are closed ungracefully.
bool CloseListenSocket( HSteamListenSocket hSocket )
{
PRINT_DEBUG("Steam_Networking_Sockets::CloseListenSocket\n");
}
/// Set connection user data. Returns false if the handle is invalid.
bool SetConnectionUserData( HSteamNetConnection hPeer, int64 nUserData )
{
PRINT_DEBUG("Steam_Networking_Sockets::SetConnectionUserData\n");
}
/// Fetch connection user data. Returns -1 if handle is invalid
/// or if you haven't set any userdata on the connection.
int64 GetConnectionUserData( HSteamNetConnection hPeer )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetConnectionUserData\n");
}
/// Set a name for the connection, used mostly for debugging
void SetConnectionName( HSteamNetConnection hPeer, const char *pszName )
{
PRINT_DEBUG("Steam_Networking_Sockets::SetConnectionName\n");
}
/// Fetch connection name. Returns false if handle is invalid
bool GetConnectionName( HSteamNetConnection hPeer, char *pszName, int nMaxLen )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetConnectionName\n");
}
/// Send a message to the remote host on the connected socket.
///
/// eSendType determines the delivery guarantees that will be provided,
/// when data should be buffered, etc.
///
/// Note that the semantics we use for messages are not precisely
/// the same as the semantics of a standard "stream" socket.
/// (SOCK_STREAM) For an ordinary stream socket, the boundaries
/// between chunks are not considered relevant, and the sizes of
/// the chunks of data written will not necessarily match up to
/// the sizes of the chunks that are returned by the reads on
/// the other end. The remote host might read a partial chunk,
/// or chunks might be coalesced. For the message semantics
/// used here, however, the sizes WILL match. Each send call
/// will match a successful read call on the remote host
/// one-for-one. If you are porting existing stream-oriented
/// code to the semantics of reliable messages, your code should
/// work the same, since reliable message semantics are more
/// strict than stream semantics. The only caveat is related to
/// performance: there is per-message overhead to retain the
/// messages sizes, and so if your code sends many small chunks
/// of data, performance will suffer. Any code based on stream
/// sockets that does not write excessively small chunks will
/// work without any changes.
EResult SendMessageToConnection( HSteamNetConnection hConn, const void *pData, uint32 cbData, ESteamNetworkingSendType eSendType )
{
PRINT_DEBUG("Steam_Networking_Sockets::SendMessageToConnection old\n");
}
/// Send a message to the remote host on the specified connection.
///
/// nSendFlags determines the delivery guarantees that will be provided,
/// when data should be buffered, etc. E.g. k_nSteamNetworkingSend_Unreliable
///
/// Note that the semantics we use for messages are not precisely
/// the same as the semantics of a standard "stream" socket.
/// (SOCK_STREAM) For an ordinary stream socket, the boundaries
/// between chunks are not considered relevant, and the sizes of
/// the chunks of data written will not necessarily match up to
/// the sizes of the chunks that are returned by the reads on
/// the other end. The remote host might read a partial chunk,
/// or chunks might be coalesced. For the message semantics
/// used here, however, the sizes WILL match. Each send call
/// will match a successful read call on the remote host
/// one-for-one. If you are porting existing stream-oriented
/// code to the semantics of reliable messages, your code should
/// work the same, since reliable message semantics are more
/// strict than stream semantics. The only caveat is related to
/// performance: there is per-message overhead to retain the
/// message sizes, and so if your code sends many small chunks
/// of data, performance will suffer. Any code based on stream
/// sockets that does not write excessively small chunks will
/// work without any changes.
///
/// Returns:
/// - k_EResultInvalidParam: invalid connection handle, or the individual message is too big.
/// (See k_cbMaxSteamNetworkingSocketsMessageSizeSend)
/// - k_EResultInvalidState: connection is in an invalid state
/// - k_EResultNoConnection: connection has ended
/// - k_EResultIgnored: You used k_nSteamNetworkingSend_NoDelay, and the message was dropped because
/// we were not ready to send it.
/// - k_EResultLimitExceeded: there was already too much data queued to be sent.
/// (See k_ESteamNetworkingConfig_SendBufferSize)
virtual EResult SendMessageToConnection( HSteamNetConnection hConn, const void *pData, uint32 cbData, int nSendFlags )
{
PRINT_DEBUG("Steam_Networking_Sockets::SendMessageToConnection\n");
}
/// If Nagle is enabled (its on by default) then when calling
/// SendMessageToConnection the message will be queued up the Nagle time
/// before being sent to merge small messages into the same packet.
///
/// Call this function to flush any queued messages and send them immediately
/// on the next transmission time (often that means right now).
EResult FlushMessagesOnConnection( HSteamNetConnection hConn )
{
PRINT_DEBUG("Steam_Networking_Sockets::FlushMessagesOnConnection\n");
}
/// Fetch the next available message(s) from the connection, if any.
/// Returns the number of messages returned into your array, up to nMaxMessages.
/// If the connection handle is invalid, -1 is returned.
///
/// The order of the messages returned in the array is relevant.
/// Reliable messages will be received in the order they were sent (and with the
/// same sizes --- see SendMessageToConnection for on this subtle difference from a stream socket).
///
/// Unreliable messages may be dropped, or delivered out of order withrespect to
/// each other or with respect to reliable messages. The same unreliable message
/// may be received multiple times.
///
/// If any messages are returned, you MUST call SteamNetworkingMessage_t::Release() on each
/// of them free up resources after you are done. It is safe to keep the object alive for
/// a little while (put it into some queue, etc), and you may call Release() from any thread.
int ReceiveMessagesOnConnection( HSteamNetConnection hConn, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages )
{
PRINT_DEBUG("Steam_Networking_Sockets::ReceiveMessagesOnConnection\n");
}
/// Same as ReceiveMessagesOnConnection, but will return the next message available
/// on any connection that was accepted through the specified listen socket. Examine
/// SteamNetworkingMessage_t::m_conn to know which client connection.
///
/// Delivery order of messages among different clients is not defined. They may
/// be returned in an order different from what they were actually received. (Delivery
/// order of messages from the same client is well defined, and thus the order of the
/// messages is relevant!)
int ReceiveMessagesOnListenSocket( HSteamListenSocket hSocket, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages )
{
PRINT_DEBUG("Steam_Networking_Sockets::ReceiveMessagesOnListenSocket\n");
}
/// Returns basic information about the high-level state of the connection.
bool GetConnectionInfo( HSteamNetConnection hConn, SteamNetConnectionInfo_t *pInfo )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetConnectionInfo\n");
}
/// Fetch the next available message(s) from the socket, if any.
/// Returns the number of messages returned into your array, up to nMaxMessages.
/// If the connection handle is invalid, -1 is returned.
///
/// The order of the messages returned in the array is relevant.
/// Reliable messages will be received in the order they were sent (and with the
/// same sizes --- see SendMessageToConnection for on this subtle difference from a stream socket).
///
/// FIXME - We're still debating the exact set of guarantees for unreliable, so this might change.
/// Unreliable messages may not be received. The order of delivery of unreliable messages
/// is NOT specified. They may be received out of order with respect to each other or
/// reliable messages. They may be received multiple times!
///
/// If any messages are returned, you MUST call Release() to each of them free up resources
/// after you are done. It is safe to keep the object alive for a little while (put it
/// into some queue, etc), and you may call Release() from any thread.
int ReceiveMessagesOnConnection( HSteamNetConnection hConn, SteamNetworkingMessage001_t **ppOutMessages, int nMaxMessages )
{
PRINT_DEBUG("Steam_Networking_Sockets::ReceiveMessagesOnConnection\n");
}
/// Same as ReceiveMessagesOnConnection, but will return the next message available
/// on any client socket that was accepted through the specified listen socket. Examine
/// SteamNetworkingMessage_t::m_conn to know which client connection.
///
/// Delivery order of messages among different clients is not defined. They may
/// be returned in an order different from what they were actually received. (Delivery
/// order of messages from the same client is well defined, and thus the order of the
/// messages is relevant!)
int ReceiveMessagesOnListenSocket( HSteamListenSocket hSocket, SteamNetworkingMessage001_t **ppOutMessages, int nMaxMessages )
{
PRINT_DEBUG("Steam_Networking_Sockets::ReceiveMessagesOnListenSocket\n");
}
/// Returns information about the specified connection.
bool GetConnectionInfo( HSteamNetConnection hConn, SteamNetConnectionInfo001_t *pInfo )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetConnectionInfo\n");
}
/// Returns brief set of connection status that you might want to display
/// to the user in game.
bool GetQuickConnectionStatus( HSteamNetConnection hConn, SteamNetworkingQuickConnectionStatus *pStats )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetQuickConnectionStatus\n");
}
/// Returns detailed connection stats in text format. Useful
/// for dumping to a log, etc.
///
/// Returns:
/// -1 failure (bad connection handle)
/// 0 OK, your buffer was filled in and '\0'-terminated
/// >0 Your buffer was either nullptr, or it was too small and the text got truncated. Try again with a buffer of at least N bytes.
int GetDetailedConnectionStatus( HSteamNetConnection hConn, char *pszBuf, int cbBuf )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetDetailedConnectionStatus\n");
}
/// Returns local IP and port that a listen socket created using CreateListenSocketIP is bound to.
///
/// An IPv6 address of ::0 means "any IPv4 or IPv6"
/// An IPv6 address of ::ffff:0000:0000 means "any IPv4"
bool GetListenSocketAddress( HSteamListenSocket hSocket, SteamNetworkingIPAddr *address )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetListenSocketAddress\n");
}
/// Returns information about the listen socket.
///
/// *pnIP and *pnPort will be 0 if the socket is set to listen for connections based
/// on SteamID only. If your listen socket accepts connections on IPv4, then both
/// fields will return nonzero, even if you originally passed a zero IP. However,
/// note that the address returned may be a private address (e.g. 10.0.0.x or 192.168.x.x),
/// and may not be reachable by a general host on the Internet.
bool GetListenSocketInfo( HSteamListenSocket hSocket, uint32 *pnIP, uint16 *pnPort )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetListenSocketInfo\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Listen_Socket *socket = get_connection_socket(hSocket);
if (!socket) return false;
if (pnIP) *pnIP = socket->ip;
if (pnPort) *pnPort = socket->port;
return true;
}
/// Create a pair of connections that are talking to each other, e.g. a loopback connection.
/// This is very useful for testing, or so that your client/server code can work the same
/// even when you are running a local "server".
///
/// The two connections will immediately be placed into the connected state, and no callbacks
/// will be posted immediately. After this, if you close either connection, the other connection
/// will receive a callback, exactly as if they were communicating over the network. You must
/// close *both* sides in order to fully clean up the resources!
///
/// By default, internal buffers are used, completely bypassing the network, the chopping up of
/// messages into packets, encryption, copying the payload, etc. This means that loopback
/// packets, by default, will not simulate lag or loss. Passing true for bUseNetworkLoopback will
/// cause the socket pair to send packets through the local network loopback device (127.0.0.1)
/// on ephemeral ports. Fake lag and loss are supported in this case, and CPU time is expended
/// to encrypt and decrypt.
///
/// The SteamID assigned to both ends of the connection will be the SteamID of this interface.
bool CreateSocketPair( HSteamNetConnection *pOutConnection1, HSteamNetConnection *pOutConnection2, bool bUseNetworkLoopback )
{
PRINT_DEBUG("Steam_Networking_Sockets::CreateSocketPair old\n");
}
/// Create a pair of connections that are talking to each other, e.g. a loopback connection.
/// This is very useful for testing, or so that your client/server code can work the same
/// even when you are running a local "server".
///
/// The two connections will immediately be placed into the connected state, and no callbacks
/// will be posted immediately. After this, if you close either connection, the other connection
/// will receive a callback, exactly as if they were communicating over the network. You must
/// close *both* sides in order to fully clean up the resources!
///
/// By default, internal buffers are used, completely bypassing the network, the chopping up of
/// messages into packets, encryption, copying the payload, etc. This means that loopback
/// packets, by default, will not simulate lag or loss. Passing true for bUseNetworkLoopback will
/// cause the socket pair to send packets through the local network loopback device (127.0.0.1)
/// on ephemeral ports. Fake lag and loss are supported in this case, and CPU time is expended
/// to encrypt and decrypt.
///
/// If you wish to assign a specific identity to either connection, you may pass a particular
/// identity. Otherwise, if you pass nullptr, the respective connection will assume a generic
/// "localhost" identity. If you use real network loopback, this might be translated to the
/// actual bound loopback port. Otherwise, the port will be zero.
bool CreateSocketPair( HSteamNetConnection *pOutConnection1, HSteamNetConnection *pOutConnection2, bool bUseNetworkLoopback, const SteamNetworkingIdentity *pIdentity1, const SteamNetworkingIdentity *pIdentity2 )
{
PRINT_DEBUG("Steam_Networking_Sockets::CreateSocketPair\n");
}
/// Get the identity assigned to this interface.
/// E.g. on Steam, this is the user's SteamID, or for the gameserver interface, the SteamID assigned
/// to the gameserver. Returns false and sets the result to an invalid identity if we don't know
/// our identity yet. (E.g. GameServer has not logged in. On Steam, the user will know their SteamID
/// even if they are not signed into Steam.)
bool GetIdentity( SteamNetworkingIdentity *pIdentity )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetIdentity\n");
}
//#ifndef STEAMNETWORKINGSOCKETS_OPENSOURCE
//
// Clients connecting to dedicated servers hosted in a data center,
// using central-authority-granted tickets.
//
/// Called when we receive a ticket from our central matchmaking system. Puts the
/// ticket into a persistent cache, and optionally returns the parsed ticket.
///
/// See stamdatagram_ticketgen.h for more details.
bool ReceivedRelayAuthTicket( const void *pvTicket, int cbTicket, SteamDatagramRelayAuthTicket *pOutParsedTicket )
{
PRINT_DEBUG("Steam_Networking_Sockets::ReceivedRelayAuthTicket\n");
}
/// Search cache for a ticket to talk to the server on the specified virtual port.
/// If found, returns the number of second until the ticket expires, and optionally
/// the complete cracked ticket. Returns 0 if we don't have a ticket.
///
/// Typically this is useful just to confirm that you have a ticket, before you
/// call ConnectToHostedDedicatedServer to connect to the server.
int FindRelayAuthTicketForServer( CSteamID steamID, int nVirtualPort, SteamDatagramRelayAuthTicket *pOutParsedTicket )
{
PRINT_DEBUG("Steam_Networking_Sockets::FindRelayAuthTicketForServer old\n");
}
/// Search cache for a ticket to talk to the server on the specified virtual port.
/// If found, returns the number of seconds until the ticket expires, and optionally
/// the complete cracked ticket. Returns 0 if we don't have a ticket.
///
/// Typically this is useful just to confirm that you have a ticket, before you
/// call ConnectToHostedDedicatedServer to connect to the server.
int FindRelayAuthTicketForServer( const SteamNetworkingIdentity &identityGameServer, int nVirtualPort, SteamDatagramRelayAuthTicket *pOutParsedTicket )
{
PRINT_DEBUG("Steam_Networking_Sockets::FindRelayAuthTicketForServer\n");
}
/// Client call to connect to a server hosted in a Valve data center, on the specified virtual
/// port. You must have placed a ticket for this server into the cache, or else this connect attempt will fail!
///
/// You may wonder why tickets are stored in a cache, instead of simply being passed as an argument
/// here. The reason is to make reconnection to a gameserver robust, even if the client computer loses
/// connection to Steam or the central backend, or the app is restarted or crashes, etc.
///
/// If you use this, you probably want to call ISteamNetworkingUtils::InitializeRelayNetworkAccess()
/// when your app initializes
HSteamNetConnection ConnectToHostedDedicatedServer( const SteamNetworkingIdentity &identityTarget, int nVirtualPort )
{
PRINT_DEBUG("Steam_Networking_Sockets::ConnectToHostedDedicatedServer\n");
}
/// Client call to connect to a server hosted in a Valve data center, on the specified virtual
/// port. You should have received a ticket for this server, or else this connect call will fail!
///
/// You may wonder why tickets are stored in a cache, instead of simply being passed as an argument
/// here. The reason is to make reconnection to a gameserver robust, even if the client computer loses
/// connection to Steam or the central backend, or the app is restarted or crashes, etc.
HSteamNetConnection ConnectToHostedDedicatedServer( CSteamID steamIDTarget, int nVirtualPort )
{
PRINT_DEBUG("Steam_Networking_Sockets::ConnectToHostedDedicatedServer old\n");
}
//
// Servers hosted in Valve data centers
//
/// Returns the value of the SDR_LISTEN_PORT environment variable.
uint16 GetHostedDedicatedServerPort()
{
PRINT_DEBUG("Steam_Networking_Sockets::GetHostedDedicatedServerPort\n");
//TODO?
return 27054;
}
/// If you are running in a production data center, this will return the data
/// center code. Returns 0 otherwise.
SteamNetworkingPOPID GetHostedDedicatedServerPOPID()
{
PRINT_DEBUG("Steam_Networking_Sockets::GetHostedDedicatedServerPOPID\n");
}
/// Return info about the hosted server. You will need to send this information to your
/// backend, and put it in tickets, so that the relays will know how to forward traffic from
/// clients to your server. See SteamDatagramRelayAuthTicket for more info.
///
/// NOTE ABOUT DEVELOPMENT ENVIRONMENTS:
/// In production in our data centers, these parameters are configured via environment variables.
/// In development, the only one you need to set is SDR_LISTEN_PORT, which is the local port you
/// want to listen on. Furthermore, if you are running your server behind a corporate firewall,
/// you probably will not be able to put the routing information returned by this function into
/// tickets. Instead, it should be a public internet address that the relays can use to send
/// data to your server. So you might just end up hardcoding a public address and setup port
/// forwarding on your corporate firewall. In that case, the port you put into the ticket
/// needs to be the public-facing port opened on your firewall, if it is different from the
/// actual server port.
///
/// This function will fail if SteamDatagramServer_Init has not been called.
///
/// Returns false if the SDR_LISTEN_PORT environment variable is not set.
bool GetHostedDedicatedServerAddress( SteamDatagramHostedAddress *pRouting )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetHostedDedicatedServerAddress %p\n", pRouting);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
pRouting->SetDevAddress(network->getOwnIP(), 27054);
return true;
}
/// Create a listen socket on the specified virtual port. The physical UDP port to use
/// will be determined by the SDR_LISTEN_PORT environment variable. If a UDP port is not
/// configured, this call will fail.
///
/// Note that this call MUST be made through the SteamNetworkingSocketsGameServer() interface
HSteamListenSocket CreateHostedDedicatedServerListenSocket( int nVirtualPort )
{
PRINT_DEBUG("Steam_Networking_Sockets::CreateHostedDedicatedServerListenSocket %i\n", nVirtualPort);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return new_listen_socket(nVirtualPort, 0, 0);
}
//#endif // #ifndef STEAMNETWORKINGSOCKETS_OPENSOURCE
//
// Gets some debug text from the connection
//
bool GetConnectionDebugText( HSteamNetConnection hConn, char *pOut, int nOutCCH )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetConnectionDebugText\n");
}
//
// Set and get configuration values, see ESteamNetworkingConfigurationValue for individual descriptions.
//
// Returns the value or -1 is eConfigValue is invalid
int32 GetConfigurationValue( ESteamNetworkingConfigurationValue eConfigValue )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetConfigurationValue\n");
}
// Returns true if successfully set
bool SetConfigurationValue( ESteamNetworkingConfigurationValue eConfigValue, int32 nValue )
{
PRINT_DEBUG("Steam_Networking_Sockets::SetConfigurationValue %i: %i\n", eConfigValue, nValue);
return true;
}
// Return the name of an int configuration value, or NULL if config value isn't known
const char *GetConfigurationValueName( ESteamNetworkingConfigurationValue eConfigValue )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetConfigurationValueName\n");
}
//
// Set and get configuration strings, see ESteamNetworkingConfigurationString for individual descriptions.
//
// Get the configuration string, returns length of string needed if pDest is nullpr or destSize is 0
// returns -1 if the eConfigValue is invalid
int32 GetConfigurationString( ESteamNetworkingConfigurationString eConfigString, char *pDest, int32 destSize )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetConfigurationString\n");
}
bool SetConfigurationString( ESteamNetworkingConfigurationString eConfigString, const char *pString )
{
PRINT_DEBUG("Steam_Networking_Sockets::SetConfigurationString\n");
}
// Return the name of a string configuration value, or NULL if config value isn't known
const char *GetConfigurationStringName( ESteamNetworkingConfigurationString eConfigString )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetConfigurationStringName\n");
}
//
// Set and get configuration values, see ESteamNetworkingConnectionConfigurationValue for individual descriptions.
//
// Returns the value or -1 is eConfigValue is invalid
int32 GetConnectionConfigurationValue( HSteamNetConnection hConn, ESteamNetworkingConnectionConfigurationValue eConfigValue )
{
PRINT_DEBUG("Steam_Networking_Sockets::GetConnectionConfigurationValue\n");
}
// Returns true if successfully set
bool SetConnectionConfigurationValue( HSteamNetConnection hConn, ESteamNetworkingConnectionConfigurationValue eConfigValue, int32 nValue )
{
PRINT_DEBUG("Steam_Networking_Sockets::SetConnectionConfigurationValue\n");
}
// TEMP KLUDGE Call to invoke all queued callbacks.
// Eventually this function will go away, and callwacks will be ordinary Steamworks callbacks.
// You should call this at the same time you call SteamAPI_RunCallbacks and SteamGameServer_RunCallbacks
// to minimize potential changes in timing when that change happens.
void RunCallbacks( ISteamNetworkingSocketsCallbacks *pCallbacks )
{
PRINT_DEBUG("Steam_Networking_Sockets:RunCallbacks\n");
}
void RunCallbacks()
{
}
void Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::CONNECT) {
}
if (msg->low_level().type() == Low_Level::DISCONNECT) {
}
}
if (msg->has_networking_sockets()) {
}
}
};

View File

@ -0,0 +1,137 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Networking_Sockets_Serialized :
public ISteamNetworkingSocketsSerialized002,
public ISteamNetworkingSocketsSerialized003
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
public:
static void steam_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_networkingsockets_callback\n");
Steam_Networking_Sockets_Serialized *steam_networkingsockets = (Steam_Networking_Sockets_Serialized *)object;
steam_networkingsockets->Callback(msg);
}
static void steam_run_every_runcb(void *object)
{
PRINT_DEBUG("steam_networkingsockets_run_every_runcb\n");
Steam_Networking_Sockets_Serialized *steam_networkingsockets = (Steam_Networking_Sockets_Serialized *)object;
steam_networkingsockets->RunCallbacks();
}
Steam_Networking_Sockets_Serialized(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->run_every_runcb = run_every_runcb;
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Sockets_Serialized::steam_callback, this);
this->run_every_runcb->add(&Steam_Networking_Sockets_Serialized::steam_run_every_runcb, this);
this->callback_results = callback_results;
this->callbacks = callbacks;
}
~Steam_Networking_Sockets_Serialized()
{
//TODO rm network callbacks
this->run_every_runcb->remove(&Steam_Networking_Sockets_Serialized::steam_run_every_runcb, this);
}
void SendP2PRendezvous( CSteamID steamIDRemote, uint32 unConnectionIDSrc, const void *pMsgRendezvous, uint32 cbRendezvous )
{
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::SendP2PRendezvous\n");
}
void SendP2PConnectionFailure( CSteamID steamIDRemote, uint32 unConnectionIDDest, uint32 nReason, const char *pszReason )
{
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::SendP2PConnectionFailure\n");
}
SteamAPICall_t GetCertAsync()
{
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::GetCertAsync\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct SteamNetworkingSocketsCert_t data = {};
data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
int GetNetworkConfigJSON( void *buf, uint32 cbBuf, const char *pszLauncherPartner )
{
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::GetNetworkConfigJSON %s\n", pszLauncherPartner);
return 0;
}
int GetNetworkConfigJSON( void *buf, uint32 cbBuf )
{
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::GetNetworkConfigJSON old\n");
return GetNetworkConfigJSON(buf, cbBuf, "");
}
void CacheRelayTicket( const void *pTicket, uint32 cbTicket )
{
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::CacheRelayTicket\n");
}
uint32 GetCachedRelayTicketCount()
{
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::GetCachedRelayTicketCount\n");
return 0;
}
int GetCachedRelayTicket( uint32 idxTicket, void *buf, uint32 cbBuf )
{
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::GetCachedRelayTicket\n");
return 0;
}
void PostConnectionStateMsg( const void *pMsg, uint32 cbMsg )
{
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::PostConnectionStateMsg\n");
}
void RunCallbacks()
{
}
void Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::CONNECT) {
}
if (msg->low_level().type() == Low_Level::DISCONNECT) {
}
}
}
};

View File

@ -0,0 +1,301 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Networking_Utils :
public ISteamNetworkingUtils
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
FSteamNetworkingSocketsDebugOutput debug_function;
public:
static void steam_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_networkingutils_callback\n");
Steam_Networking_Utils *steam_networkingutils = (Steam_Networking_Utils *)object;
steam_networkingutils->Callback(msg);
}
static void steam_run_every_runcb(void *object)
{
PRINT_DEBUG("steam_networkingutils_run_every_runcb\n");
Steam_Networking_Utils *steam_networkingutils = (Steam_Networking_Utils *)object;
steam_networkingutils->RunCallbacks();
}
Steam_Networking_Utils(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->run_every_runcb = run_every_runcb;
//this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Utils::steam_callback, this);
this->run_every_runcb->add(&Steam_Networking_Utils::steam_run_every_runcb, this);
this->callback_results = callback_results;
this->callbacks = callbacks;
}
~Steam_Networking_Utils()
{
//TODO rm network callbacks
this->run_every_runcb->remove(&Steam_Networking_Utils::steam_run_every_runcb, this);
}
bool InitializeRelayAccess()
{
PRINT_DEBUG("Steam_Networking_Utils::InitializeRelayAccess\n");
return true;
}
float GetLocalPingLocation( SteamNetworkPingLocation_t &result )
{
PRINT_DEBUG("Steam_Networking_Utils::GetLocalPingLocation\n");
return -1;
}
int EstimatePingTimeBetweenTwoLocations( const SteamNetworkPingLocation_t &location1, const SteamNetworkPingLocation_t &location2 )
{
PRINT_DEBUG("Steam_Networking_Utils::EstimatePingTimeBetweenTwoLocations\n");
return k_nSteamNetworkingPing_Unknown ;
}
int EstimatePingTimeFromLocalHost( const SteamNetworkPingLocation_t &remoteLocation )
{
PRINT_DEBUG("Steam_Networking_Utils::EstimatePingTimeFromLocalHost\n");
}
void ConvertPingLocationToString( const SteamNetworkPingLocation_t &location, char *pszBuf, int cchBufSize )
{
PRINT_DEBUG("Steam_Networking_Utils::ConvertPingLocationToString\n");
}
bool ParsePingLocationString( const char *pszString, SteamNetworkPingLocation_t &result )
{
PRINT_DEBUG("Steam_Networking_Utils::ParsePingLocationString\n");
}
bool CheckPingDataUpToDate( float flMaxAgeSeconds )
{
PRINT_DEBUG("Steam_Networking_Utils::CheckPingDataUpToDate %f\n", flMaxAgeSeconds);
return true;
}
bool IsPingMeasurementInProgress()
{
PRINT_DEBUG("Steam_Networking_Utils::IsPingMeasurementInProgress\n");
}
int GetPingToDataCenter( SteamNetworkingPOPID popID, SteamNetworkingPOPID *pViaRelayPoP )
{
PRINT_DEBUG("Steam_Networking_Utils::GetPingToDataCenter\n");
}
int GetDirectPingToPOP( SteamNetworkingPOPID popID )
{
PRINT_DEBUG("Steam_Networking_Utils::GetDirectPingToPOP\n");
}
int GetPOPCount()
{
PRINT_DEBUG("Steam_Networking_Utils::GetPOPCount\n");
}
int GetPOPList( SteamNetworkingPOPID *list, int nListSz )
{
PRINT_DEBUG("Steam_Networking_Utils::GetPOPList\n");
}
//
// Misc
//
/// Fetch current timestamp. This timer has the following properties:
///
/// - Monotonicity is guaranteed.
/// - The initial value will be at least 24*3600*30*1e6, i.e. about
/// 30 days worth of microseconds. In this way, the timestamp value of
/// 0 will always be at least "30 days ago". Also, negative numbers
/// will never be returned.
/// - Wraparound / overflow is not a practical concern.
///
/// If you are running under the debugger and stop the process, the clock
/// might not advance the full wall clock time that has elapsed between
/// calls. If the process is not blocked from normal operation, the
/// timestamp values will track wall clock time, even if you don't call
/// the function frequently.
///
/// The value is only meaningful for this run of the process. Don't compare
/// it to values obtained on another computer, or other runs of the same process.
SteamNetworkingMicroseconds GetLocalTimestamp()
{
PRINT_DEBUG("Steam_Networking_Utils::GetLocalTimestamp\n");
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - initialized_time).count() + (SteamNetworkingMicroseconds)24*3600*30*1e6;
}
/// Set a function to receive network-related information that is useful for debugging.
/// This can be very useful during development, but it can also be useful for troubleshooting
/// problems with tech savvy end users. If you have a console or other log that customers
/// can examine, these log messages can often be helpful to troubleshoot network issues.
/// (Especially any warning/error messages.)
///
/// The detail level indicates what message to invoke your callback on. Lower numeric
/// value means more important, and the value you pass is the lowest priority (highest
/// numeric value) you wish to receive callbacks for.
///
/// Except when debugging, you should only use k_ESteamNetworkingSocketsDebugOutputType_Msg
/// or k_ESteamNetworkingSocketsDebugOutputType_Warning. For best performance, do NOT
/// request a high detail level and then filter out messages in your callback. Instead,
/// call function function to adjust the desired level of detail.
///
/// IMPORTANT: This may be called from a service thread, while we own a mutex, etc.
/// Your output function must be threadsafe and fast! Do not make any other
/// Steamworks calls from within the handler.
void SetDebugOutputFunction( ESteamNetworkingSocketsDebugOutputType eDetailLevel, FSteamNetworkingSocketsDebugOutput pfnFunc )
{
PRINT_DEBUG("Steam_Networking_Utils::SetDebugOutputFunction %i\n", eDetailLevel);
if (eDetailLevel != k_ESteamNetworkingSocketsDebugOutputType_None) {
debug_function = pfnFunc;
}
}
//
// Set and get configuration values, see ESteamNetworkingConfigValue for individual descriptions.
//
// Shortcuts for common cases. (Implemented as inline functions below)
/*
bool SetGlobalConfigValueInt32( ESteamNetworkingConfigValue eValue, int32 val );
bool SetGlobalConfigValueFloat( ESteamNetworkingConfigValue eValue, float val );
bool SetGlobalConfigValueString( ESteamNetworkingConfigValue eValue, const char *val );
bool SetConnectionConfigValueInt32( HSteamNetConnection hConn, ESteamNetworkingConfigValue eValue, int32 val );
bool SetConnectionConfigValueFloat( HSteamNetConnection hConn, ESteamNetworkingConfigValue eValue, float val );
bool SetConnectionConfigValueString( HSteamNetConnection hConn, ESteamNetworkingConfigValue eValue, const char *val );
*/
/// Set a configuration value.
/// - eValue: which value is being set
/// - eScope: Onto what type of object are you applying the setting?
/// - scopeArg: Which object you want to change? (Ignored for global scope). E.g. connection handle, listen socket handle, interface pointer, etc.
/// - eDataType: What type of data is in the buffer at pValue? This must match the type of the variable exactly!
/// - pArg: Value to set it to. You can pass NULL to remove a non-global sett at this scope,
/// causing the value for that object to use global defaults. Or at global scope, passing NULL
/// will reset any custom value and restore it to the system default.
/// NOTE: When setting callback functions, do not pass the function pointer directly.
/// Your argument should be a pointer to a function pointer.
bool SetConfigValue( ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigScope eScopeType, intptr_t scopeObj,
ESteamNetworkingConfigDataType eDataType, const void *pArg )
{
PRINT_DEBUG("Steam_Networking_Utils::SetConfigValue\n");
}
/// Get a configuration value.
/// - eValue: which value to fetch
/// - eScopeType: query setting on what type of object
/// - eScopeArg: the object to query the setting for
/// - pOutDataType: If non-NULL, the data type of the value is returned.
/// - pResult: Where to put the result. Pass NULL to query the required buffer size. (k_ESteamNetworkingGetConfigValue_BufferTooSmall will be returned.)
/// - cbResult: IN: the size of your buffer. OUT: the number of bytes filled in or required.
ESteamNetworkingGetConfigValueResult GetConfigValue( ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigScope eScopeType, intptr_t scopeObj,
ESteamNetworkingConfigDataType *pOutDataType, void *pResult, size_t *cbResult )
{
PRINT_DEBUG("Steam_Networking_Utils::GetConfigValue\n");
}
/// Returns info about a configuration value. Returns false if the value does not exist.
/// pOutNextValue can be used to iterate through all of the known configuration values.
/// (Use GetFirstConfigValue() to begin the iteration, will be k_ESteamNetworkingConfig_Invalid on the last value)
/// Any of the output parameters can be NULL if you do not need that information.
bool GetConfigValueInfo( ESteamNetworkingConfigValue eValue, const char **pOutName, ESteamNetworkingConfigDataType *pOutDataType, ESteamNetworkingConfigScope *pOutScope, ESteamNetworkingConfigValue *pOutNextValue )
{
PRINT_DEBUG("Steam_Networking_Utils::GetConfigValueInfo\n");
}
/// Return the lowest numbered configuration value available in the current environment.
ESteamNetworkingConfigValue GetFirstConfigValue()
{
PRINT_DEBUG("Steam_Networking_Utils::GetFirstConfigValue\n");
}
// String conversions. You'll usually access these using the respective
// inline methods.
void SteamNetworkingIPAddr_ToString( const SteamNetworkingIPAddr &addr, char *buf, size_t cbBuf, bool bWithPort )
{
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIPAddr_ToString\n");
}
bool SteamNetworkingIPAddr_ParseString( SteamNetworkingIPAddr *pAddr, const char *pszStr )
{
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIPAddr_ParseString\n");
}
void SteamNetworkingIdentity_ToString( const SteamNetworkingIdentity &identity, char *buf, size_t cbBuf )
{
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIdentity_ToString\n");
}
bool SteamNetworkingIdentity_ParseString( SteamNetworkingIdentity *pIdentity, const char *pszStr )
{
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIdentity_ParseString\n");
}
void RunCallbacks()
{
}
void Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::CONNECT) {
}
if (msg->low_level().type() == Low_Level::DISCONNECT) {
}
}
if (msg->has_networking_sockets()) {
}
}
};

56
dll/steam_parental.cpp Normal file
View File

@ -0,0 +1,56 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "steam_parental.h"
bool Steam_Parental::BIsParentalLockEnabled()
{
PRINT_DEBUG("BIsParentalLockEnabled\n");
return false;
}
bool Steam_Parental::BIsParentalLockLocked()
{
PRINT_DEBUG("BIsParentalLockLocked\n");
return false;
}
bool Steam_Parental::BIsAppBlocked( AppId_t nAppID )
{
PRINT_DEBUG("BIsAppBlocked\n");
return false;
}
bool Steam_Parental::BIsAppInBlockList( AppId_t nAppID )
{
PRINT_DEBUG("BIsAppInBlockList\n");
return false;
}
bool Steam_Parental::BIsFeatureBlocked( EParentalFeature eFeature )
{
PRINT_DEBUG("BIsFeatureBlocked\n");
return false;
}
bool Steam_Parental::BIsFeatureInBlockList( EParentalFeature eFeature )
{
PRINT_DEBUG("BIsFeatureInBlockList\n");
return false;
}

31
dll/steam_parental.h Normal file
View File

@ -0,0 +1,31 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Parental : public ISteamParentalSettings
{
public:
bool BIsParentalLockEnabled();
bool BIsParentalLockLocked();
bool BIsAppBlocked( AppId_t nAppID );
bool BIsAppInBlockList( AppId_t nAppID );
bool BIsFeatureBlocked( EParentalFeature eFeature );
bool BIsFeatureInBlockList( EParentalFeature eFeature );
};

184
dll/steam_parties.h Normal file
View File

@ -0,0 +1,184 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Parties :
public ISteamParties
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
FSteamNetworkingSocketsDebugOutput debug_function;
public:
static void steam_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_parties_callback\n");
Steam_Parties *steam_parties = (Steam_Parties *)object;
steam_parties->Callback(msg);
}
static void steam_run_every_runcb(void *object)
{
PRINT_DEBUG("steam_parties_run_every_runcb\n");
Steam_Parties *steam_parties = (Steam_Parties *)object;
steam_parties->RunCallbacks();
}
Steam_Parties(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->run_every_runcb = run_every_runcb;
//this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Parties::steam_callback, this);
this->run_every_runcb->add(&Steam_Parties::steam_run_every_runcb, this);
this->callback_results = callback_results;
this->callbacks = callbacks;
}
~Steam_Parties()
{
//TODO rm network callbacks
this->run_every_runcb->remove(&Steam_Parties::steam_run_every_runcb, this);
}
// =============================================================================================
// Party Client APIs
// Enumerate any active beacons for parties you may wish to join
uint32 GetNumActiveBeacons()
{
PRINT_DEBUG("Steam_Parties::GetNumActiveBeacons\n");
}
PartyBeaconID_t GetBeaconByIndex( uint32 unIndex )
{
PRINT_DEBUG("Steam_Parties::GetBeaconByIndex\n");
}
bool GetBeaconDetails( PartyBeaconID_t ulBeaconID, CSteamID *pSteamIDBeaconOwner, STEAM_OUT_STRUCT() SteamPartyBeaconLocation_t *pLocation, STEAM_OUT_STRING_COUNT(cchMetadata) char *pchMetadata, int cchMetadata )
{
PRINT_DEBUG("Steam_Parties::GetBeaconDetails\n");
}
// Join an open party. Steam will reserve one beacon slot for your SteamID,
// and return the necessary JoinGame string for you to use to connect
STEAM_CALL_RESULT( JoinPartyCallback_t )
SteamAPICall_t JoinParty( PartyBeaconID_t ulBeaconID )
{
PRINT_DEBUG("Steam_Parties::JoinParty\n");
}
// =============================================================================================
// Party Host APIs
// Get a list of possible beacon locations
bool GetNumAvailableBeaconLocations( uint32 *puNumLocations )
{
PRINT_DEBUG("Steam_Parties::GetNumAvailableBeaconLocations\n");
}
bool GetAvailableBeaconLocations( SteamPartyBeaconLocation_t *pLocationList, uint32 uMaxNumLocations )
{
PRINT_DEBUG("Steam_Parties::GetAvailableBeaconLocations\n");
}
// Create a new party beacon and activate it in the selected location.
// unOpenSlots is the maximum number of users that Steam will send to you.
// When people begin responding to your beacon, Steam will send you
// PartyReservationCallback_t callbacks to let you know who is on the way.
STEAM_CALL_RESULT( CreateBeaconCallback_t )
SteamAPICall_t CreateBeacon( uint32 unOpenSlots, SteamPartyBeaconLocation_t *pBeaconLocation, const char *pchConnectString, const char *pchMetadata )
{
PRINT_DEBUG("Steam_Parties::CreateBeacon\n");
}
// Call this function when a user that had a reservation (see callback below)
// has successfully joined your party.
// Steam will manage the remaining open slots automatically.
void OnReservationCompleted( PartyBeaconID_t ulBeacon, CSteamID steamIDUser )
{
PRINT_DEBUG("Steam_Parties::OnReservationCompleted\n");
}
// To cancel a reservation (due to timeout or user input), call this.
// Steam will open a new reservation slot.
// Note: The user may already be in-flight to your game, so it's possible they will still connect and try to join your party.
void CancelReservation( PartyBeaconID_t ulBeacon, CSteamID steamIDUser )
{
PRINT_DEBUG("Steam_Parties::CancelReservation\n");
}
// Change the number of open beacon reservation slots.
// Call this if, for example, someone without a reservation joins your party (eg a friend, or via your own matchmaking system).
STEAM_CALL_RESULT( ChangeNumOpenSlotsCallback_t )
SteamAPICall_t ChangeNumOpenSlots( PartyBeaconID_t ulBeacon, uint32 unOpenSlots )
{
PRINT_DEBUG("Steam_Parties::ChangeNumOpenSlots\n");
}
// Turn off the beacon.
bool DestroyBeacon( PartyBeaconID_t ulBeacon )
{
PRINT_DEBUG("Steam_Parties::DestroyBeacon\n");
}
// Utils
bool GetBeaconLocationData( SteamPartyBeaconLocation_t BeaconLocation, ESteamPartyBeaconLocationData eData, STEAM_OUT_STRING_COUNT(cchDataStringOut) char *pchDataStringOut, int cchDataStringOut )
{
PRINT_DEBUG("Steam_Parties::GetBeaconLocationData\n");
}
void RunCallbacks()
{
}
void Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::CONNECT) {
}
if (msg->low_level().type() == Low_Level::DISCONNECT) {
}
}
if (msg->has_networking_sockets()) {
}
}
};

668
dll/steam_remote_storage.h Normal file
View File

@ -0,0 +1,668 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
struct Async_Read {
SteamAPICall_t api_call;
uint32 offset;
uint32 to_read;
uint32 size;
std::string file_name;
};
struct Stream_Write {
std::string file_name;
UGCFileWriteStreamHandle_t write_stream_handle;
std::vector<char> file_data;
};
class Steam_Remote_Storage :
public ISteamRemoteStorage001,
public ISteamRemoteStorage002,
public ISteamRemoteStorage003,
public ISteamRemoteStorage004,
public ISteamRemoteStorage005,
public ISteamRemoteStorage006,
public ISteamRemoteStorage007,
public ISteamRemoteStorage008,
public ISteamRemoteStorage009,
public ISteamRemoteStorage010,
public ISteamRemoteStorage011,
public ISteamRemoteStorage012,
public ISteamRemoteStorage013,
public ISteamRemoteStorage
{
private:
Local_Storage *local_storage;
class SteamCallResults *callback_results;
bool steam_cloud_enabled;
std::vector<struct Async_Read> async_reads;
std::vector<struct Stream_Write> stream_writes;
public:
Steam_Remote_Storage(Local_Storage *local_storage, class SteamCallResults *callback_results)
{
this->local_storage = local_storage;
this->callback_results = callback_results;
steam_cloud_enabled = true;
local_storage->update_save_filenames(REMOTE_STORAGE_FOLDER);
}
// NOTE
//
// Filenames are case-insensitive, and will be converted to lowercase automatically.
// So "foo.bar" and "Foo.bar" are the same file, and if you write "Foo.bar" then
// iterate the files, the filename returned will be "foo.bar".
//
// file operations
bool FileWrite( const char *pchFile, const void *pvData, int32 cubData )
{
PRINT_DEBUG("Steam_Remote_Storage::FileWrite %s %u\n", pchFile, cubData);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
int data_stored = local_storage->store_data(REMOTE_STORAGE_FOLDER, pchFile, (char* )pvData, cubData);
PRINT_DEBUG("Steam_Remote_Storage::Stored %i, %u\n", data_stored, data_stored == cubData);
return data_stored == cubData;
}
int32 FileRead( const char *pchFile, void *pvData, int32 cubDataToRead )
{
PRINT_DEBUG("Steam_Remote_Storage::FileRead %s %i\n", pchFile, cubDataToRead);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
int read_data = local_storage->get_data(REMOTE_STORAGE_FOLDER, pchFile, (char* )pvData, cubDataToRead);
if (read_data < 0) read_data = 0;
PRINT_DEBUG("Read %i\n", read_data);
return read_data;
}
STEAM_CALL_RESULT( RemoteStorageFileWriteAsyncComplete_t )
SteamAPICall_t FileWriteAsync( const char *pchFile, const void *pvData, uint32 cubData )
{
PRINT_DEBUG("Steam_Remote_Storage::FileWriteAsync\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
bool success = local_storage->store_data(REMOTE_STORAGE_FOLDER, pchFile, (char* )pvData, cubData) == cubData;
RemoteStorageFileWriteAsyncComplete_t data;
data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
STEAM_CALL_RESULT( RemoteStorageFileReadAsyncComplete_t )
SteamAPICall_t FileReadAsync( const char *pchFile, uint32 nOffset, uint32 cubToRead )
{
PRINT_DEBUG("Steam_Remote_Storage::FileReadAsync\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
unsigned int size = local_storage->file_size(REMOTE_STORAGE_FOLDER, pchFile);
RemoteStorageFileReadAsyncComplete_t data;
if (size <= nOffset) {
return k_uAPICallInvalid;
}
if ((size - nOffset) < cubToRead) cubToRead = size - nOffset;
struct Async_Read a_read;
data.m_eResult = k_EResultOK;
a_read.offset = data.m_nOffset = nOffset;
a_read.api_call = data.m_hFileReadAsync = callback_results->reserveCallResult();
a_read.to_read = data.m_cubRead = cubToRead;
a_read.file_name = std::string(pchFile);
a_read.size = size;
async_reads.push_back(a_read);
callback_results->addCallResult(data.m_hFileReadAsync, data.k_iCallback, &data, sizeof(data));
return data.m_hFileReadAsync;
}
bool FileReadAsyncComplete( SteamAPICall_t hReadCall, void *pvBuffer, uint32 cubToRead )
{
PRINT_DEBUG("Steam_Remote_Storage::FileReadAsyncComplete\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto a_read = std::find_if(async_reads.begin(), async_reads.end(), [&hReadCall](Async_Read const& item) { return item.api_call == hReadCall; });
if (async_reads.end() == a_read)
return false;
if (cubToRead < a_read->to_read)
return false;
char *temp = new char[a_read->size];
int read_data = local_storage->get_data(REMOTE_STORAGE_FOLDER, a_read->file_name, (char* )temp, a_read->size);
if (read_data < a_read->to_read + a_read->offset) {
delete[] temp;
return false;
}
memcpy(pvBuffer, temp + a_read->offset, a_read->to_read);
delete[] temp;
async_reads.erase(a_read);
return true;
}
bool FileForget( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::FileForget\n");
return true;
}
bool FileDelete( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::FileDelete\n");
return local_storage->file_delete(REMOTE_STORAGE_FOLDER, pchFile);
}
STEAM_CALL_RESULT( RemoteStorageFileShareResult_t )
SteamAPICall_t FileShare( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::FileShare\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
RemoteStorageFileShareResult_t data = {};
data.m_eResult = k_EResultOK;
data.m_hFile = generate_steam_api_call_id();
strncpy(data.m_rgchFilename, pchFile, sizeof(data.m_rgchFilename) - 1);
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
bool SetSyncPlatforms( const char *pchFile, ERemoteStoragePlatform eRemoteStoragePlatform )
{
PRINT_DEBUG("Steam_Remote_Storage::SetSyncPlatforms\n");
return true;
}
// file operations that cause network IO
UGCFileWriteStreamHandle_t FileWriteStreamOpen( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::FileWriteStreamOpen\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
static UGCFileWriteStreamHandle_t handle;
++handle;
struct Stream_Write stream_write;
stream_write.file_name = std::string(pchFile);
stream_write.write_stream_handle = handle;
stream_writes.push_back(stream_write);
return stream_write.write_stream_handle;
}
bool FileWriteStreamWriteChunk( UGCFileWriteStreamHandle_t writeHandle, const void *pvData, int32 cubData )
{
PRINT_DEBUG("Steam_Remote_Storage::FileWriteStreamWriteChunk\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto request = std::find_if(stream_writes.begin(), stream_writes.end(), [&writeHandle](struct Stream_Write const& item) { return item.write_stream_handle == writeHandle; });
if (stream_writes.end() == request)
return false;
std::copy((char *)pvData, (char *)pvData + cubData, std::back_inserter(request->file_data));
return true;
}
bool FileWriteStreamClose( UGCFileWriteStreamHandle_t writeHandle )
{
PRINT_DEBUG("Steam_Remote_Storage::FileWriteStreamClose\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto request = std::find_if(stream_writes.begin(), stream_writes.end(), [&writeHandle](struct Stream_Write const& item) { return item.write_stream_handle == writeHandle; });
if (stream_writes.end() == request)
return false;
local_storage->store_data(REMOTE_STORAGE_FOLDER, request->file_name, request->file_data.data(), request->file_data.size());
stream_writes.erase(request);
return true;
}
bool FileWriteStreamCancel( UGCFileWriteStreamHandle_t writeHandle )
{
PRINT_DEBUG("Steam_Remote_Storage::FileWriteStreamCancel\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto request = std::find_if(stream_writes.begin(), stream_writes.end(), [&writeHandle](struct Stream_Write const& item) { return item.write_stream_handle == writeHandle; });
if (stream_writes.end() == request)
return false;
stream_writes.erase(request);
return true;
}
// file information
bool FileExists( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::FileExists %s\n", pchFile);
return local_storage->file_exists(REMOTE_STORAGE_FOLDER, pchFile);
}
bool FilePersisted( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::FilePersisted\n");
return local_storage->file_exists(REMOTE_STORAGE_FOLDER, pchFile);
}
int32 GetFileSize( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::GetFileSize %s\n", pchFile);
return local_storage->file_size(REMOTE_STORAGE_FOLDER, pchFile);
}
int64 GetFileTimestamp( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::GetFileTimestamp\n");
return local_storage->file_timestamp(REMOTE_STORAGE_FOLDER, pchFile);
}
ERemoteStoragePlatform GetSyncPlatforms( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::GetSyncPlatforms\n");
return k_ERemoteStoragePlatformAll;
}
// iteration
int32 GetFileCount()
{
PRINT_DEBUG("Steam_Remote_Storage::GetFileCount\n");
int32 num = local_storage->count_files(REMOTE_STORAGE_FOLDER);
PRINT_DEBUG("Steam_Remote_Storage::File count: %i\n", num);
return num;
}
const char *GetFileNameAndSize( int iFile, int32 *pnFileSizeInBytes )
{
PRINT_DEBUG("Steam_Remote_Storage::GetFileNameAndSize %i\n", iFile);
static char output_filename[MAX_FILENAME_LENGTH];
if (local_storage->iterate_file(REMOTE_STORAGE_FOLDER, iFile, output_filename, pnFileSizeInBytes)) {
PRINT_DEBUG("Steam_Remote_Storage::Name: |%s|, size: %i\n", output_filename, pnFileSizeInBytes ? *pnFileSizeInBytes : 0);
return output_filename;
} else {
return "";
}
}
// configuration management
bool GetQuota( uint64 *pnTotalBytes, uint64 *puAvailableBytes )
{
PRINT_DEBUG("Steam_Remote_Storage::GetQuota\n");
uint64 quota = 2 << 26;
*pnTotalBytes = quota;
*puAvailableBytes = (quota);
return true;
}
bool GetQuota( int32 *pnTotalBytes, int32 *puAvailableBytes )
{
PRINT_DEBUG("Steam_Remote_Storage::GetQuota\n");
uint64 quota = 2 << 26;
*pnTotalBytes = quota;
*puAvailableBytes = (quota);
return true;
}
bool IsCloudEnabledForAccount()
{
PRINT_DEBUG("Steam_Remote_Storage::IsCloudEnabledForAccount\n");
return true;
}
bool IsCloudEnabledForApp()
{
PRINT_DEBUG("Steam_Remote_Storage::IsCloudEnabledForApp\n");
return steam_cloud_enabled;
}
bool IsCloudEnabledThisApp()
{
PRINT_DEBUG("Steam_Remote_Storage::IsCloudEnabledThisApp\n");
return steam_cloud_enabled;
}
void SetCloudEnabledForApp( bool bEnabled )
{
PRINT_DEBUG("Steam_Remote_Storage::SetCloudEnabledForApp\n");
steam_cloud_enabled = bEnabled;
}
bool SetCloudEnabledThisApp( bool bEnabled )
{
PRINT_DEBUG("Steam_Remote_Storage::SetCloudEnabledThisApp\n");
steam_cloud_enabled = bEnabled;
return true;
}
// user generated content
// Downloads a UGC file. A priority value of 0 will download the file immediately,
// otherwise it will wait to download the file until all downloads with a lower priority
// value are completed. Downloads with equal priority will occur simultaneously.
STEAM_CALL_RESULT( RemoteStorageDownloadUGCResult_t )
SteamAPICall_t UGCDownload( UGCHandle_t hContent, uint32 unPriority )
{
PRINT_DEBUG("Steam_Remote_Storage::UGCDownload\n");
}
STEAM_CALL_RESULT( RemoteStorageDownloadUGCResult_t )
SteamAPICall_t UGCDownload( UGCHandle_t hContent )
{
PRINT_DEBUG("Steam_Remote_Storage::UGCDownload old\n");
}
// Gets the amount of data downloaded so far for a piece of content. pnBytesExpected can be 0 if function returns false
// or if the transfer hasn't started yet, so be careful to check for that before dividing to get a percentage
bool GetUGCDownloadProgress( UGCHandle_t hContent, int32 *pnBytesDownloaded, int32 *pnBytesExpected )
{
PRINT_DEBUG("Steam_Remote_Storage::GetUGCDownloadProgress\n");
}
bool GetUGCDownloadProgress( UGCHandle_t hContent, uint32 *pnBytesDownloaded, uint32 *pnBytesExpected )
{
PRINT_DEBUG("Steam_Remote_Storage::GetUGCDownloadProgress old\n");
}
// Gets metadata for a file after it has been downloaded. This is the same metadata given in the RemoteStorageDownloadUGCResult_t call result
bool GetUGCDetails( UGCHandle_t hContent, AppId_t *pnAppID, STEAM_OUT_STRING() char **ppchName, int32 *pnFileSizeInBytes, STEAM_OUT_STRUCT() CSteamID *pSteamIDOwner )
{
PRINT_DEBUG("Steam_Remote_Storage::GetUGCDetails\n");
}
// After download, gets the content of the file.
// Small files can be read all at once by calling this function with an offset of 0 and cubDataToRead equal to the size of the file.
// Larger files can be read in chunks to reduce memory usage (since both sides of the IPC client and the game itself must allocate
// enough memory for each chunk). Once the last byte is read, the file is implicitly closed and further calls to UGCRead will fail
// unless UGCDownload is called again.
// For especially large files (anything over 100MB) it is a requirement that the file is read in chunks.
int32 UGCRead( UGCHandle_t hContent, void *pvData, int32 cubDataToRead, uint32 cOffset, EUGCReadAction eAction )
{
PRINT_DEBUG("Steam_Remote_Storage::UGCRead\n");
}
int32 UGCRead( UGCHandle_t hContent, void *pvData, int32 cubDataToRead )
{
PRINT_DEBUG("Steam_Remote_Storage::UGCRead old\n");
}
int32 UGCRead( UGCHandle_t hContent, void *pvData, int32 cubDataToRead, uint32 cOffset)
{
PRINT_DEBUG("Steam_Remote_Storage::UGCRead old\n");
}
// Functions to iterate through UGC that has finished downloading but has not yet been read via UGCRead()
int32 GetCachedUGCCount()
{
PRINT_DEBUG("Steam_Remote_Storage::GetCachedUGCCount\n");
}
UGCHandle_t GetCachedUGCHandle( int32 iCachedContent )
{
PRINT_DEBUG("Steam_Remote_Storage::GetCachedUGCHandle\n");
}
// The following functions are only necessary on the Playstation 3. On PC & Mac, the Steam client will handle these operations for you
// On Playstation 3, the game controls which files are stored in the cloud, via FilePersist, FileFetch, and FileForget.
#if defined(_PS3) || defined(_SERVER)
// Connect to Steam and get a list of files in the Cloud - results in a RemoteStorageAppSyncStatusCheck_t callback
void GetFileListFromServer()
{
PRINT_DEBUG("Steam_Remote_Storage::GetFileListFromServer\n");
}
// Indicate this file should be downloaded in the next sync
bool FileFetch( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::FileFetch\n");
return true;
}
// Indicate this file should be persisted in the next sync
bool FilePersist( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::FilePersist\n");
return true;
}
// Pull any requested files down from the Cloud - results in a RemoteStorageAppSyncedClient_t callback
bool SynchronizeToClient()
{
PRINT_DEBUG("Steam_Remote_Storage::SynchronizeToClient\n");
}
// Upload any requested files to the Cloud - results in a RemoteStorageAppSyncedServer_t callback
bool SynchronizeToServer()
{
PRINT_DEBUG("Steam_Remote_Storage::SynchronizeToServer\n");
}
// Reset any fetch/persist/etc requests
bool ResetFileRequestState()
{
PRINT_DEBUG("Steam_Remote_Storage::ResetFileRequestState\n");
}
#endif
// publishing UGC
STEAM_CALL_RESULT( RemoteStoragePublishFileProgress_t )
SteamAPICall_t PublishWorkshopFile( const char *pchFile, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags, EWorkshopFileType eWorkshopFileType )
{
PRINT_DEBUG("Steam_Remote_Storage::PublishWorkshopFile\n");
}
PublishedFileUpdateHandle_t CreatePublishedFileUpdateRequest( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::CreatePublishedFileUpdateRequest\n");
}
bool UpdatePublishedFileFile( PublishedFileUpdateHandle_t updateHandle, const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileFile\n");
}
SteamAPICall_t PublishFile( const char *pchFile, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags )
{
PRINT_DEBUG("Steam_Remote_Storage::PublishFile\n");
}
SteamAPICall_t PublishWorkshopFile( const char *pchFile, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, SteamParamStringArray_t *pTags )
{
PRINT_DEBUG("Steam_Remote_Storage::PublishWorkshopFile old\n");
}
SteamAPICall_t UpdatePublishedFile( RemoteStorageUpdatePublishedFileRequest_t updatePublishedFileRequest )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFile\n");
}
bool UpdatePublishedFilePreviewFile( PublishedFileUpdateHandle_t updateHandle, const char *pchPreviewFile )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFilePreviewFile\n");
}
bool UpdatePublishedFileTitle( PublishedFileUpdateHandle_t updateHandle, const char *pchTitle )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileTitle\n");
}
bool UpdatePublishedFileDescription( PublishedFileUpdateHandle_t updateHandle, const char *pchDescription )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileDescription\n");
}
bool UpdatePublishedFileVisibility( PublishedFileUpdateHandle_t updateHandle, ERemoteStoragePublishedFileVisibility eVisibility )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileVisibility\n");
}
bool UpdatePublishedFileTags( PublishedFileUpdateHandle_t updateHandle, SteamParamStringArray_t *pTags )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileTags\n");
}
STEAM_CALL_RESULT( RemoteStorageUpdatePublishedFileResult_t )
SteamAPICall_t CommitPublishedFileUpdate( PublishedFileUpdateHandle_t updateHandle )
{
PRINT_DEBUG("Steam_Remote_Storage::CommitPublishedFileUpdate\n");
}
// Gets published file details for the given publishedfileid. If unMaxSecondsOld is greater than 0,
// cached data may be returned, depending on how long ago it was cached. A value of 0 will force a refresh.
// A value of k_WorkshopForceLoadPublishedFileDetailsFromCache will use cached data if it exists, no matter how old it is.
STEAM_CALL_RESULT( RemoteStorageGetPublishedFileDetailsResult_t )
SteamAPICall_t GetPublishedFileDetails( PublishedFileId_t unPublishedFileId, uint32 unMaxSecondsOld )
{
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedFileDetails\n");
}
STEAM_CALL_RESULT( RemoteStorageGetPublishedFileDetailsResult_t )
SteamAPICall_t GetPublishedFileDetails( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedFileDetails old\n");
}
STEAM_CALL_RESULT( RemoteStorageDeletePublishedFileResult_t )
SteamAPICall_t DeletePublishedFile( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::DeletePublishedFile\n");
}
// enumerate the files that the current user published with this app
STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t )
SteamAPICall_t EnumerateUserPublishedFiles( uint32 unStartIndex )
{
PRINT_DEBUG("Steam_Remote_Storage::EnumerateUserPublishedFiles\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
RemoteStorageEnumerateUserPublishedFilesResult_t data;
data.m_eResult = k_EResultOK;
data.m_nResultsReturned = 0;
data.m_nTotalResultCount = 0;
//data.m_rgPublishedFileId;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
STEAM_CALL_RESULT( RemoteStorageSubscribePublishedFileResult_t )
SteamAPICall_t SubscribePublishedFile( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::SubscribePublishedFile\n");
}
STEAM_CALL_RESULT( RemoteStorageEnumerateUserSubscribedFilesResult_t )
SteamAPICall_t EnumerateUserSubscribedFiles( uint32 unStartIndex )
{
PRINT_DEBUG("Steam_Remote_Storage::EnumerateUserSubscribedFiles\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
RemoteStorageEnumerateUserSubscribedFilesResult_t data;
data.m_eResult = k_EResultOK;
data.m_nResultsReturned = 0;
data.m_nTotalResultCount = 0;
//data.m_rgPublishedFileId;
//data.m_rgRTimeSubscribed;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
STEAM_CALL_RESULT( RemoteStorageUnsubscribePublishedFileResult_t )
SteamAPICall_t UnsubscribePublishedFile( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::UnsubscribePublishedFile\n");
}
bool UpdatePublishedFileSetChangeDescription( PublishedFileUpdateHandle_t updateHandle, const char *pchChangeDescription )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileSetChangeDescription\n");
}
STEAM_CALL_RESULT( RemoteStorageGetPublishedItemVoteDetailsResult_t )
SteamAPICall_t GetPublishedItemVoteDetails( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedItemVoteDetails\n");
}
STEAM_CALL_RESULT( RemoteStorageUpdateUserPublishedItemVoteResult_t )
SteamAPICall_t UpdateUserPublishedItemVote( PublishedFileId_t unPublishedFileId, bool bVoteUp )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdateUserPublishedItemVote\n");
}
STEAM_CALL_RESULT( RemoteStorageGetPublishedItemVoteDetailsResult_t )
SteamAPICall_t GetUserPublishedItemVoteDetails( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::GetUserPublishedItemVoteDetails\n");
}
STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t )
SteamAPICall_t EnumerateUserSharedWorkshopFiles( CSteamID steamId, uint32 unStartIndex, SteamParamStringArray_t *pRequiredTags, SteamParamStringArray_t *pExcludedTags )
{
PRINT_DEBUG("Steam_Remote_Storage::EnumerateUserSharedWorkshopFiles\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
RemoteStorageEnumerateUserPublishedFilesResult_t data;
data.m_eResult = k_EResultOK;
data.m_nResultsReturned = 0;
data.m_nTotalResultCount = 0;
//data.m_rgPublishedFileId;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t )
SteamAPICall_t EnumerateUserSharedWorkshopFiles(AppId_t nAppId, CSteamID steamId, uint32 unStartIndex, SteamParamStringArray_t *pRequiredTags, SteamParamStringArray_t *pExcludedTags )
{
PRINT_DEBUG("Steam_Remote_Storage::EnumerateUserSharedWorkshopFiles old\n");
return EnumerateUserSharedWorkshopFiles(steamId, unStartIndex, pRequiredTags, pExcludedTags);
}
STEAM_CALL_RESULT( RemoteStoragePublishFileProgress_t )
SteamAPICall_t PublishVideo( EWorkshopVideoProvider eVideoProvider, const char *pchVideoAccount, const char *pchVideoIdentifier, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags )
{
PRINT_DEBUG("Steam_Remote_Storage::PublishVideo\n");
}
STEAM_CALL_RESULT( RemoteStoragePublishFileProgress_t )
SteamAPICall_t PublishVideo(const char *pchFileName, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags )
{
PRINT_DEBUG("Steam_Remote_Storage::PublishVideo old\n");
}
STEAM_CALL_RESULT( RemoteStorageSetUserPublishedFileActionResult_t )
SteamAPICall_t SetUserPublishedFileAction( PublishedFileId_t unPublishedFileId, EWorkshopFileAction eAction )
{
PRINT_DEBUG("Steam_Remote_Storage::SetUserPublishedFileAction\n");
}
STEAM_CALL_RESULT( RemoteStorageEnumeratePublishedFilesByUserActionResult_t )
SteamAPICall_t EnumeratePublishedFilesByUserAction( EWorkshopFileAction eAction, uint32 unStartIndex )
{
PRINT_DEBUG("Steam_Remote_Storage::EnumeratePublishedFilesByUserAction\n");
}
// this method enumerates the public view of workshop files
STEAM_CALL_RESULT( RemoteStorageEnumerateWorkshopFilesResult_t )
SteamAPICall_t EnumeratePublishedWorkshopFiles( EWorkshopEnumerationType eEnumerationType, uint32 unStartIndex, uint32 unCount, uint32 unDays, SteamParamStringArray_t *pTags, SteamParamStringArray_t *pUserTags )
{
PRINT_DEBUG("Steam_Remote_Storage::EnumeratePublishedWorkshopFiles\n");
}
STEAM_CALL_RESULT( RemoteStorageDownloadUGCResult_t )
SteamAPICall_t UGCDownloadToLocation( UGCHandle_t hContent, const char *pchLocation, uint32 unPriority )
{
PRINT_DEBUG("Steam_Remote_Storage::UGCDownloadToLocation\n");
}
};

99
dll/steam_screenshots.cpp Normal file
View File

@ -0,0 +1,99 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "steam_screenshots.h"
// Writes a screenshot to the user's screenshot library given the raw image data, which must be in RGB format.
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
ScreenshotHandle Steam_Screenshots::WriteScreenshot( void *pubRGB, uint32 cubRGB, int nWidth, int nHeight )
{
PRINT_DEBUG("WriteScreenshot\n");
return INVALID_SCREENSHOT_HANDLE;
}
// Adds a screenshot to the user's screenshot library from disk. If a thumbnail is provided, it must be 200 pixels wide and the same aspect ratio
// as the screenshot, otherwise a thumbnail will be generated if the user uploads the screenshot. The screenshots must be in either JPEG or TGA format.
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
// JPEG, TGA, and PNG formats are supported.
ScreenshotHandle Steam_Screenshots::AddScreenshotToLibrary( const char *pchFilename, const char *pchThumbnailFilename, int nWidth, int nHeight )
{
PRINT_DEBUG("AddScreenshotToLibrary\n");
return INVALID_SCREENSHOT_HANDLE;
}
// Causes the Steam overlay to take a screenshot. If screenshots are being hooked by the game then a ScreenshotRequested_t callback is sent back to the game instead.
void Steam_Screenshots::TriggerScreenshot()
{
PRINT_DEBUG("TriggerScreenshot\n");
}
// Toggles whether the overlay handles screenshots when the user presses the screenshot hotkey, or the game handles them. If the game is hooking screenshots,
// then the ScreenshotRequested_t callback will be sent if the user presses the hotkey, and the game is expected to call WriteScreenshot or AddScreenshotToLibrary
// in response.
void Steam_Screenshots::HookScreenshots( bool bHook )
{
PRINT_DEBUG("HookScreenshots\n");
hooked = bHook;
}
// Sets metadata about a screenshot's location (for example, the name of the map)
bool Steam_Screenshots::SetLocation( ScreenshotHandle hScreenshot, const char *pchLocation )
{
PRINT_DEBUG("SetLocation\n");
return false;
}
// Tags a user as being visible in the screenshot
bool Steam_Screenshots::TagUser( ScreenshotHandle hScreenshot, CSteamID steamID )
{
PRINT_DEBUG("TagUser\n");
return false;
}
// Tags a published file as being visible in the screenshot
bool Steam_Screenshots::TagPublishedFile( ScreenshotHandle hScreenshot, PublishedFileId_t unPublishedFileID )
{
PRINT_DEBUG("TagPublishedFile\n");
return false;
}
// Returns true if the app has hooked the screenshot
bool Steam_Screenshots::IsScreenshotsHooked()
{
PRINT_DEBUG("IsScreenshotsHooked\n");
return hooked;
}
// Adds a VR screenshot to the user's screenshot library from disk in the supported type.
// pchFilename should be the normal 2D image used in the library view
// pchVRFilename should contain the image that matches the correct type
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
// JPEG, TGA, and PNG formats are supported.
ScreenshotHandle Steam_Screenshots::AddVRScreenshotToLibrary( EVRScreenshotType eType, const char *pchFilename, const char *pchVRFilename )
{
PRINT_DEBUG("AddVRScreenshotToLibrary\n");
return INVALID_SCREENSHOT_HANDLE;
}

60
dll/steam_screenshots.h Normal file
View File

@ -0,0 +1,60 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Screenshots : public ISteamScreenshots
{
bool hooked = false;
public:
// Writes a screenshot to the user's screenshot library given the raw image data, which must be in RGB format.
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
ScreenshotHandle WriteScreenshot( void *pubRGB, uint32 cubRGB, int nWidth, int nHeight );
// Adds a screenshot to the user's screenshot library from disk. If a thumbnail is provided, it must be 200 pixels wide and the same aspect ratio
// as the screenshot, otherwise a thumbnail will be generated if the user uploads the screenshot. The screenshots must be in either JPEG or TGA format.
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
// JPEG, TGA, and PNG formats are supported.
ScreenshotHandle AddScreenshotToLibrary( const char *pchFilename, const char *pchThumbnailFilename, int nWidth, int nHeight );
// Causes the Steam overlay to take a screenshot. If screenshots are being hooked by the game then a ScreenshotRequested_t callback is sent back to the game instead.
void TriggerScreenshot();
// Toggles whether the overlay handles screenshots when the user presses the screenshot hotkey, or the game handles them. If the game is hooking screenshots,
// then the ScreenshotRequested_t callback will be sent if the user presses the hotkey, and the game is expected to call WriteScreenshot or AddScreenshotToLibrary
// in response.
void HookScreenshots( bool bHook );
// Sets metadata about a screenshot's location (for example, the name of the map)
bool SetLocation( ScreenshotHandle hScreenshot, const char *pchLocation );
// Tags a user as being visible in the screenshot
bool TagUser( ScreenshotHandle hScreenshot, CSteamID steamID );
// Tags a published file as being visible in the screenshot
bool TagPublishedFile( ScreenshotHandle hScreenshot, PublishedFileId_t unPublishedFileID );
// Returns true if the app has hooked the screenshot
bool IsScreenshotsHooked();
// Adds a VR screenshot to the user's screenshot library from disk in the supported type.
// pchFilename should be the normal 2D image used in the library view
// pchVRFilename should contain the image that matches the correct type
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
// JPEG, TGA, and PNG formats are supported.
ScreenshotHandle AddVRScreenshotToLibrary( EVRScreenshotType eType, const char *pchFilename, const char *pchVRFilename );
};

822
dll/steam_ugc.h Normal file
View File

@ -0,0 +1,822 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
struct UGC_query {
UGCQueryHandle_t handle;
std::set<PublishedFileId_t> return_only;
bool return_all_subscribed;
std::set<PublishedFileId_t> results;
};
class Steam_UGC :
public ISteamUGC001,
public ISteamUGC002,
public ISteamUGC003,
public ISteamUGC004,
public ISteamUGC005,
public ISteamUGC006,
public ISteamUGC007,
public ISteamUGC008,
public ISteamUGC009,
public ISteamUGC010,
public ISteamUGC
{
class Settings *settings;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
std::set<PublishedFileId_t> subscribed;
UGCQueryHandle_t handle = 0;
std::vector<struct UGC_query> ugc_queries;
UGCQueryHandle_t new_ugc_query(bool return_all_subscribed = false, std::set<PublishedFileId_t> return_only = std::set<PublishedFileId_t>())
{
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct UGC_query query;
++handle;
query.handle = handle;
query.return_all_subscribed = return_all_subscribed;
query.return_only = return_only;
ugc_queries.push_back(query);
return query.handle;
}
void set_details(PublishedFileId_t id, SteamUGCDetails_t *pDetails)
{
if (pDetails) {
if (settings->isModInstalled(id)) {
pDetails->m_eResult = k_EResultOK;
pDetails->m_nPublishedFileId = id;
pDetails->m_eFileType = k_EWorkshopFileTypeCommunity;
pDetails->m_nCreatorAppID = settings->get_local_game_id().AppID();
pDetails->m_nConsumerAppID = settings->get_local_game_id().AppID();
snprintf(pDetails->m_rgchTitle, sizeof(pDetails->m_rgchDescription), "%s", settings->getMod(id).title.c_str());
//TODO
} else {
pDetails->m_nPublishedFileId = id;
pDetails->m_eResult = k_EResultFail;
}
}
}
public:
Steam_UGC(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
{
this->settings = settings;
this->callbacks = callbacks;
this->callback_results = callback_results;
subscribed = settings->modSet();
}
// Query UGC associated with a user. Creator app id or consumer app id must be valid and be set to the current running app. unPage should start at 1.
UGCQueryHandle_t CreateQueryUserUGCRequest( AccountID_t unAccountID, EUserUGCList eListType, EUGCMatchingUGCType eMatchingUGCType, EUserUGCListSortOrder eSortOrder, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint32 unPage )
{
PRINT_DEBUG("Steam_UGC::CreateQueryUserUGCRequest %u %i %i %i %u %u %u\n", unAccountID, eListType, eMatchingUGCType, eSortOrder, nCreatorAppID, nConsumerAppID, unPage);
//TODO
return new_ugc_query(eListType == k_EUserUGCList_Subscribed || eListType == k_EUserUGCList_Published);
}
// Query for all matching UGC. Creator app id or consumer app id must be valid and be set to the current running app. unPage should start at 1.
UGCQueryHandle_t CreateQueryAllUGCRequest( EUGCQuery eQueryType, EUGCMatchingUGCType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint32 unPage )
{
PRINT_DEBUG("Steam_UGC::CreateQueryAllUGCRequest\n");
//TODO
return new_ugc_query();
}
// Query for all matching UGC using the new deep paging interface. Creator app id or consumer app id must be valid and be set to the current running app. pchCursor should be set to NULL or "*" to get the first result set.
UGCQueryHandle_t CreateQueryAllUGCRequest( EUGCQuery eQueryType, EUGCMatchingUGCType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, const char *pchCursor = NULL )
{
PRINT_DEBUG("Steam_UGC::CreateQueryAllUGCRequest other\n");
//TODO
return new_ugc_query();
}
// Query for the details of the given published file ids (the RequestUGCDetails call is deprecated and replaced with this)
UGCQueryHandle_t CreateQueryUGCDetailsRequest( PublishedFileId_t *pvecPublishedFileID, uint32 unNumPublishedFileIDs )
{
PRINT_DEBUG("Steam_UGC::CreateQueryUGCDetailsRequest\n");
std::set<PublishedFileId_t> only(pvecPublishedFileID, pvecPublishedFileID + unNumPublishedFileIDs);
return new_ugc_query(false, only);
}
// Send the query to Steam
STEAM_CALL_RESULT( SteamUGCQueryCompleted_t )
SteamAPICall_t SendQueryUGCRequest( UGCQueryHandle_t handle )
{
PRINT_DEBUG("Steam_UGC::SendQueryUGCRequest\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto request = std::find_if(ugc_queries.begin(), ugc_queries.end(), [&handle](struct UGC_query const& item) { return item.handle == handle; });
if (ugc_queries.end() == request)
return 0;
if (request->return_all_subscribed) {
request->results = subscribed;
}
if (request->return_only.size()) {
for (auto & s : request->return_only) {
if (subscribed.count(s)) {
request->results.insert(s);
}
}
}
SteamUGCQueryCompleted_t data = {};
data.m_handle = handle;
data.m_eResult = k_EResultOK;
data.m_unNumResultsReturned = request->results.size();
data.m_unTotalMatchingResults = request->results.size();
data.m_bCachedData = false;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Retrieve an individual result after receiving the callback for querying UGC
bool GetQueryUGCResult( UGCQueryHandle_t handle, uint32 index, SteamUGCDetails_t *pDetails )
{
PRINT_DEBUG("Steam_UGC::GetQueryUGCResult %u\n", index);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pDetails) {
memset(pDetails, 0, sizeof(SteamUGCDetails_t));
pDetails->m_eResult = k_EResultFail;
}
auto request = std::find_if(ugc_queries.begin(), ugc_queries.end(), [&handle](struct UGC_query const& item) { return item.handle == handle; });
if (ugc_queries.end() == request) {
return false;
}
if (index >= request->results.size()) {
return false;
}
auto it = request->results.begin();
std::advance(it, index);
set_details(*it, pDetails);
return true;
}
bool GetQueryUGCPreviewURL( UGCQueryHandle_t handle, uint32 index, STEAM_OUT_STRING_COUNT(cchURLSize) char *pchURL, uint32 cchURLSize )
{
PRINT_DEBUG("Steam_UGC::GetQueryUGCPreviewURL\n");
return false;
}
bool GetQueryUGCMetadata( UGCQueryHandle_t handle, uint32 index, STEAM_OUT_STRING_COUNT(cchMetadatasize) char *pchMetadata, uint32 cchMetadatasize )
{
PRINT_DEBUG("Steam_UGC::GetQueryUGCMetadata\n");
return false;
}
bool GetQueryUGCChildren( UGCQueryHandle_t handle, uint32 index, PublishedFileId_t* pvecPublishedFileID, uint32 cMaxEntries )
{
PRINT_DEBUG("Steam_UGC::GetQueryUGCChildren\n");
return false;
}
bool GetQueryUGCStatistic( UGCQueryHandle_t handle, uint32 index, EItemStatistic eStatType, uint64 *pStatValue )
{
PRINT_DEBUG("Steam_UGC::GetQueryUGCStatistic\n");
return false;
}
bool GetQueryUGCStatistic( UGCQueryHandle_t handle, uint32 index, EItemStatistic eStatType, uint32 *pStatValue )
{
PRINT_DEBUG("Steam_UGC::GetQueryUGCStatistic old\n");
return false;
}
uint32 GetQueryUGCNumAdditionalPreviews( UGCQueryHandle_t handle, uint32 index )
{
PRINT_DEBUG("Steam_UGC::GetQueryUGCNumAdditionalPreviews\n");
return 0;
}
bool GetQueryUGCAdditionalPreview( UGCQueryHandle_t handle, uint32 index, uint32 previewIndex, STEAM_OUT_STRING_COUNT(cchURLSize) char *pchURLOrVideoID, uint32 cchURLSize, STEAM_OUT_STRING_COUNT(cchURLSize) char *pchOriginalFileName, uint32 cchOriginalFileNameSize, EItemPreviewType *pPreviewType )
{
PRINT_DEBUG("Steam_UGC::GetQueryUGCAdditionalPreview\n");
return false;
}
bool GetQueryUGCAdditionalPreview( UGCQueryHandle_t handle, uint32 index, uint32 previewIndex, char *pchURLOrVideoID, uint32 cchURLSize, bool *hz )
{
PRINT_DEBUG("Steam_UGC::GetQueryUGCAdditionalPreview old\n");
return false;
}
uint32 GetQueryUGCNumKeyValueTags( UGCQueryHandle_t handle, uint32 index )
{
PRINT_DEBUG("Steam_UGC::GetQueryUGCNumKeyValueTags\n");
return 0;
}
bool GetQueryUGCKeyValueTag( UGCQueryHandle_t handle, uint32 index, uint32 keyValueTagIndex, STEAM_OUT_STRING_COUNT(cchKeySize) char *pchKey, uint32 cchKeySize, STEAM_OUT_STRING_COUNT(cchValueSize) char *pchValue, uint32 cchValueSize )
{
PRINT_DEBUG("Steam_UGC::GetQueryUGCKeyValueTag\n");
return false;
}
// Release the request to free up memory, after retrieving results
bool ReleaseQueryUGCRequest( UGCQueryHandle_t handle )
{
PRINT_DEBUG("Steam_UGC::ReleaseQueryUGCRequest\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto request = std::find_if(ugc_queries.begin(), ugc_queries.end(), [&handle](struct UGC_query const& item) { return item.handle == handle; });
if (ugc_queries.end() == request)
return false;
ugc_queries.erase(request);
return true;
}
// Options to set for querying UGC
bool AddRequiredTag( UGCQueryHandle_t handle, const char *pTagName )
{
PRINT_DEBUG("Steam_UGC::AddRequiredTag\n");
return true;
}
bool AddExcludedTag( UGCQueryHandle_t handle, const char *pTagName )
{
PRINT_DEBUG("Steam_UGC::AddExcludedTag\n");
return true;
}
bool SetReturnOnlyIDs( UGCQueryHandle_t handle, bool bReturnOnlyIDs )
{
PRINT_DEBUG("Steam_UGC::SetReturnOnlyIDs\n");
return true;
}
bool SetReturnKeyValueTags( UGCQueryHandle_t handle, bool bReturnKeyValueTags )
{
PRINT_DEBUG("Steam_UGC::SetReturnKeyValueTags\n");
return true;
}
bool SetReturnLongDescription( UGCQueryHandle_t handle, bool bReturnLongDescription )
{
PRINT_DEBUG("Steam_UGC::SetReturnLongDescription\n");
return true;
}
bool SetReturnMetadata( UGCQueryHandle_t handle, bool bReturnMetadata )
{
PRINT_DEBUG("Steam_UGC::SetReturnMetadata\n");
return true;
}
bool SetReturnChildren( UGCQueryHandle_t handle, bool bReturnChildren )
{
PRINT_DEBUG("Steam_UGC::SetReturnChildren\n");
return true;
}
bool SetReturnAdditionalPreviews( UGCQueryHandle_t handle, bool bReturnAdditionalPreviews )
{
PRINT_DEBUG("Steam_UGC::SetReturnAdditionalPreviews\n");
return true;
}
bool SetReturnTotalOnly( UGCQueryHandle_t handle, bool bReturnTotalOnly )
{
PRINT_DEBUG("Steam_UGC::SetReturnTotalOnly\n");
return true;
}
bool SetReturnPlaytimeStats( UGCQueryHandle_t handle, uint32 unDays )
{
PRINT_DEBUG("Steam_UGC::SetReturnPlaytimeStats\n");
return true;
}
bool SetLanguage( UGCQueryHandle_t handle, const char *pchLanguage )
{
PRINT_DEBUG("Steam_UGC::SetLanguage\n");
return true;
}
bool SetAllowCachedResponse( UGCQueryHandle_t handle, uint32 unMaxAgeSeconds )
{
PRINT_DEBUG("Steam_UGC::SetAllowCachedResponse\n");
return true;
}
// Options only for querying user UGC
bool SetCloudFileNameFilter( UGCQueryHandle_t handle, const char *pMatchCloudFileName )
{
PRINT_DEBUG("Steam_UGC::SetCloudFileNameFilter\n");
return true;
}
// Options only for querying all UGC
bool SetMatchAnyTag( UGCQueryHandle_t handle, bool bMatchAnyTag )
{
PRINT_DEBUG("Steam_UGC::SetMatchAnyTag\n");
return true;
}
bool SetSearchText( UGCQueryHandle_t handle, const char *pSearchText )
{
PRINT_DEBUG("Steam_UGC::SetSearchText\n");
return true;
}
bool SetRankedByTrendDays( UGCQueryHandle_t handle, uint32 unDays )
{
PRINT_DEBUG("Steam_UGC::SetRankedByTrendDays\n");
return true;
}
bool AddRequiredKeyValueTag( UGCQueryHandle_t handle, const char *pKey, const char *pValue )
{
PRINT_DEBUG("Steam_UGC::AddRequiredKeyValueTag\n");
return true;
}
// DEPRECATED - Use CreateQueryUGCDetailsRequest call above instead!
SteamAPICall_t RequestUGCDetails( PublishedFileId_t nPublishedFileID, uint32 unMaxAgeSeconds )
{
PRINT_DEBUG("Steam_UGC::RequestUGCDetails\n");
SteamUGCRequestUGCDetailsResult_t data = {};
data.m_bCachedData = false;
set_details(nPublishedFileID, &(data.m_details));
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
SteamAPICall_t RequestUGCDetails( PublishedFileId_t nPublishedFileID )
{
PRINT_DEBUG("Steam_UGC::RequestUGCDetails old\n");
return RequestUGCDetails(nPublishedFileID, 0);
}
// Steam Workshop Creator API
STEAM_CALL_RESULT( CreateItemResult_t )
SteamAPICall_t CreateItem( AppId_t nConsumerAppId, EWorkshopFileType eFileType )
{
PRINT_DEBUG("Steam_UGC::CreateItem\n");
return 0;
}
// create new item for this app with no content attached yet
UGCUpdateHandle_t StartItemUpdate( AppId_t nConsumerAppId, PublishedFileId_t nPublishedFileID )
{
PRINT_DEBUG("Steam_UGC::StartItemUpdate\n");
return 0;
}
// start an UGC item update. Set changed properties before commiting update with CommitItemUpdate()
bool SetItemTitle( UGCUpdateHandle_t handle, const char *pchTitle )
{
PRINT_DEBUG("Steam_UGC::SetItemTitle\n");
return false;
}
// change the title of an UGC item
bool SetItemDescription( UGCUpdateHandle_t handle, const char *pchDescription )
{
PRINT_DEBUG("Steam_UGC::SetItemDescription\n");
return false;
}
// change the description of an UGC item
bool SetItemUpdateLanguage( UGCUpdateHandle_t handle, const char *pchLanguage )
{
PRINT_DEBUG("Steam_UGC::SetItemUpdateLanguage\n");
return false;
}
// specify the language of the title or description that will be set
bool SetItemMetadata( UGCUpdateHandle_t handle, const char *pchMetaData )
{
PRINT_DEBUG("Steam_UGC::SetItemMetadata\n");
return false;
}
// change the metadata of an UGC item (max = k_cchDeveloperMetadataMax)
bool SetItemVisibility( UGCUpdateHandle_t handle, ERemoteStoragePublishedFileVisibility eVisibility )
{
PRINT_DEBUG("Steam_UGC::SetItemVisibility\n");
return false;
}
// change the visibility of an UGC item
bool SetItemTags( UGCUpdateHandle_t updateHandle, const SteamParamStringArray_t *pTags )
{
PRINT_DEBUG("Steam_UGC::SetItemTags\n");
return false;
}
// change the tags of an UGC item
bool SetItemContent( UGCUpdateHandle_t handle, const char *pszContentFolder )
{
PRINT_DEBUG("Steam_UGC::SetItemContent\n");
return false;
}
// update item content from this local folder
bool SetItemPreview( UGCUpdateHandle_t handle, const char *pszPreviewFile )
{
PRINT_DEBUG("Steam_UGC::SetItemPreview\n");
return false;
}
// change preview image file for this item. pszPreviewFile points to local image file, which must be under 1MB in size
bool SetAllowLegacyUpload( UGCUpdateHandle_t handle, bool bAllowLegacyUpload )
{
PRINT_DEBUG("Steam_UGC::SetAllowLegacyUpload\n");
return false;
}
bool RemoveItemKeyValueTags( UGCUpdateHandle_t handle, const char *pchKey )
{
PRINT_DEBUG("Steam_UGC::RemoveItemKeyValueTags\n");
return false;
}
// remove any existing key-value tags with the specified key
bool AddItemKeyValueTag( UGCUpdateHandle_t handle, const char *pchKey, const char *pchValue )
{
PRINT_DEBUG("Steam_UGC::AddItemKeyValueTag\n");
return false;
}
// add new key-value tags for the item. Note that there can be multiple values for a tag.
bool AddItemPreviewFile( UGCUpdateHandle_t handle, const char *pszPreviewFile, EItemPreviewType type )
{
PRINT_DEBUG("Steam_UGC::AddItemPreviewFile\n");
return false;
}
// add preview file for this item. pszPreviewFile points to local file, which must be under 1MB in size
bool AddItemPreviewVideo( UGCUpdateHandle_t handle, const char *pszVideoID )
{
PRINT_DEBUG("Steam_UGC::AddItemPreviewVideo\n");
return false;
}
// add preview video for this item
bool UpdateItemPreviewFile( UGCUpdateHandle_t handle, uint32 index, const char *pszPreviewFile )
{
PRINT_DEBUG("Steam_UGC::UpdateItemPreviewFile\n");
return false;
}
// updates an existing preview file for this item. pszPreviewFile points to local file, which must be under 1MB in size
bool UpdateItemPreviewVideo( UGCUpdateHandle_t handle, uint32 index, const char *pszVideoID )
{
PRINT_DEBUG("Steam_UGC::UpdateItemPreviewVideo\n");
return false;
}
// updates an existing preview video for this item
bool RemoveItemPreview( UGCUpdateHandle_t handle, uint32 index )
{
PRINT_DEBUG("Steam_UGC::RemoveItemPreview %llu %u\n", handle, index);
return false;
}
// remove a preview by index starting at 0 (previews are sorted)
STEAM_CALL_RESULT( SubmitItemUpdateResult_t )
SteamAPICall_t SubmitItemUpdate( UGCUpdateHandle_t handle, const char *pchChangeNote )
{
PRINT_DEBUG("Steam_UGC::SubmitItemUpdate\n");
return 0;
}
// commit update process started with StartItemUpdate()
EItemUpdateStatus GetItemUpdateProgress( UGCUpdateHandle_t handle, uint64 *punBytesProcessed, uint64* punBytesTotal )
{
PRINT_DEBUG("Steam_UGC::GetItemUpdateProgress\n");
return k_EItemUpdateStatusInvalid;
}
// Steam Workshop Consumer API
STEAM_CALL_RESULT( SetUserItemVoteResult_t )
SteamAPICall_t SetUserItemVote( PublishedFileId_t nPublishedFileID, bool bVoteUp )
{
PRINT_DEBUG("Steam_UGC::SetUserItemVote\n");
return 0;
}
STEAM_CALL_RESULT( GetUserItemVoteResult_t )
SteamAPICall_t GetUserItemVote( PublishedFileId_t nPublishedFileID )
{
PRINT_DEBUG("Steam_UGC::GetUserItemVote\n");
return 0;
}
STEAM_CALL_RESULT( UserFavoriteItemsListChanged_t )
SteamAPICall_t AddItemToFavorites( AppId_t nAppId, PublishedFileId_t nPublishedFileID )
{
PRINT_DEBUG("Steam_UGC::AddItemToFavorites\n");
return 0;
}
STEAM_CALL_RESULT( UserFavoriteItemsListChanged_t )
SteamAPICall_t RemoveItemFromFavorites( AppId_t nAppId, PublishedFileId_t nPublishedFileID )
{
PRINT_DEBUG("Steam_UGC::RemoveItemFromFavorites\n");
return 0;
}
STEAM_CALL_RESULT( RemoteStorageSubscribePublishedFileResult_t )
SteamAPICall_t SubscribeItem( PublishedFileId_t nPublishedFileID )
{
PRINT_DEBUG("Steam_UGC::SubscribeItem %llu\n", nPublishedFileID);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
subscribed.insert(nPublishedFileID);
RemoteStorageSubscribePublishedFileResult_t data;
if (settings->isModInstalled(nPublishedFileID)) {
data.m_eResult = k_EResultOK;
} else {
data.m_eResult = k_EResultFail;
}
data.m_nPublishedFileId = nPublishedFileID;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// subscribe to this item, will be installed ASAP
STEAM_CALL_RESULT( RemoteStorageUnsubscribePublishedFileResult_t )
SteamAPICall_t UnsubscribeItem( PublishedFileId_t nPublishedFileID )
{
PRINT_DEBUG("Steam_UGC::UnsubscribeItem %llu\n", nPublishedFileID);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
RemoteStorageUnsubscribePublishedFileResult_t data;
data.m_eResult = k_EResultOK;
if (subscribed.count(nPublishedFileID) == 0) {
data.m_eResult = k_EResultFail; //TODO: check if this is accurate
}
subscribed.erase(nPublishedFileID);
data.m_nPublishedFileId = nPublishedFileID;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// unsubscribe from this item, will be uninstalled after game quits
uint32 GetNumSubscribedItems()
{
PRINT_DEBUG("Steam_UGC::GetNumSubscribedItems\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return subscribed.size();
}
// number of subscribed items
uint32 GetSubscribedItems( PublishedFileId_t* pvecPublishedFileID, uint32 cMaxEntries )
{
PRINT_DEBUG("Steam_UGC::GetSubscribedItems\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (cMaxEntries > subscribed.size()) {
cMaxEntries = subscribed.size();
}
std::copy_n(subscribed.begin(), cMaxEntries, pvecPublishedFileID);
return cMaxEntries;
}
// all subscribed item PublishFileIDs
// get EItemState flags about item on this client
uint32 GetItemState( PublishedFileId_t nPublishedFileID )
{
PRINT_DEBUG("Steam_UGC::GetItemState\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (subscribed.count(nPublishedFileID)) {
if (settings->isModInstalled(nPublishedFileID)) {
return k_EItemStateInstalled | k_EItemStateSubscribed;
}
return k_EItemStateSubscribed;
}
return k_EItemStateNone;
}
// get info about currently installed content on disc for items that have k_EItemStateInstalled set
// if k_EItemStateLegacyItem is set, pchFolder contains the path to the legacy file itself (not a folder)
bool GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *punSizeOnDisk, STEAM_OUT_STRING_COUNT( cchFolderSize ) char *pchFolder, uint32 cchFolderSize, uint32 *punTimeStamp )
{
PRINT_DEBUG("Steam_UGC::GetItemInstallInfo\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!settings->isModInstalled(nPublishedFileID)) {
return false;
}
if (punSizeOnDisk) *punSizeOnDisk = 1000000;
if (punTimeStamp) *punTimeStamp = 1554997000;
if (pchFolder && cchFolderSize) {
snprintf(pchFolder, cchFolderSize, "%s", settings->getMod(nPublishedFileID).path.c_str());
}
return true;
}
// get info about pending update for items that have k_EItemStateNeedsUpdate set. punBytesTotal will be valid after download started once
bool GetItemDownloadInfo( PublishedFileId_t nPublishedFileID, uint64 *punBytesDownloaded, uint64 *punBytesTotal )
{
PRINT_DEBUG("Steam_UGC::GetItemDownloadInfo\n");
return false;
}
bool GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *punSizeOnDisk, char *pchFolder, uint32 cchFolderSize, bool *pbLegacyItem ) // returns true if item is installed
{
PRINT_DEBUG("Steam_UGC::GetItemInstallInfo old\n");
return false;
}
bool GetItemUpdateInfo( PublishedFileId_t nPublishedFileID, bool *pbNeedsUpdate, bool *pbIsDownloading, uint64 *punBytesDownloaded, uint64 *punBytesTotal )
{
PRINT_DEBUG("Steam_UGC::GetItemDownloadInfo old\n");
return false;
}
bool GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *punSizeOnDisk, char *pchFolder, uint32 cchFolderSize ) // returns true if item is installed
{
PRINT_DEBUG("Steam_UGC::GetItemInstallInfo older\n");
return false;
}
// download new or update already installed item. If function returns true, wait for DownloadItemResult_t. If the item is already installed,
// then files on disk should not be used until callback received. If item is not subscribed to, it will be cached for some time.
// If bHighPriority is set, any other item download will be suspended and this item downloaded ASAP.
bool DownloadItem( PublishedFileId_t nPublishedFileID, bool bHighPriority )
{
PRINT_DEBUG("Steam_UGC::DownloadItem\n");
return false;
}
// game servers can set a specific workshop folder before issuing any UGC commands.
// This is helpful if you want to support multiple game servers running out of the same install folder
bool BInitWorkshopForGameServer( DepotId_t unWorkshopDepotID, const char *pszFolder )
{
PRINT_DEBUG("Steam_UGC::BInitWorkshopForGameServer\n");
return false;
}
// SuspendDownloads( true ) will suspend all workshop downloads until SuspendDownloads( false ) is called or the game ends
void SuspendDownloads( bool bSuspend )
{
PRINT_DEBUG("Steam_UGC::SuspendDownloads\n");
}
// usage tracking
STEAM_CALL_RESULT( StartPlaytimeTrackingResult_t )
SteamAPICall_t StartPlaytimeTracking( PublishedFileId_t *pvecPublishedFileID, uint32 unNumPublishedFileIDs )
{
PRINT_DEBUG("Steam_UGC::StartPlaytimeTracking\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
StopPlaytimeTrackingResult_t data;
data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
STEAM_CALL_RESULT( StopPlaytimeTrackingResult_t )
SteamAPICall_t StopPlaytimeTracking( PublishedFileId_t *pvecPublishedFileID, uint32 unNumPublishedFileIDs )
{
PRINT_DEBUG("Steam_UGC::StopPlaytimeTracking\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
StopPlaytimeTrackingResult_t data;
data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
STEAM_CALL_RESULT( StopPlaytimeTrackingResult_t )
SteamAPICall_t StopPlaytimeTrackingForAllItems()
{
PRINT_DEBUG("Steam_UGC::StopPlaytimeTrackingForAllItems\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
StopPlaytimeTrackingResult_t data;
data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// parent-child relationship or dependency management
STEAM_CALL_RESULT( AddUGCDependencyResult_t )
SteamAPICall_t AddDependency( PublishedFileId_t nParentPublishedFileID, PublishedFileId_t nChildPublishedFileID )
{
PRINT_DEBUG("Steam_UGC::AddDependency\n");
return 0;
}
STEAM_CALL_RESULT( RemoveUGCDependencyResult_t )
SteamAPICall_t RemoveDependency( PublishedFileId_t nParentPublishedFileID, PublishedFileId_t nChildPublishedFileID )
{
PRINT_DEBUG("Steam_UGC::RemoveDependency\n");
return 0;
}
// add/remove app dependence/requirements (usually DLC)
STEAM_CALL_RESULT( AddAppDependencyResult_t )
SteamAPICall_t AddAppDependency( PublishedFileId_t nPublishedFileID, AppId_t nAppID )
{
PRINT_DEBUG("Steam_UGC::AddAppDependency\n");
return 0;
}
STEAM_CALL_RESULT( RemoveAppDependencyResult_t )
SteamAPICall_t RemoveAppDependency( PublishedFileId_t nPublishedFileID, AppId_t nAppID )
{
PRINT_DEBUG("Steam_UGC::RemoveAppDependency\n");
return 0;
}
// request app dependencies. note that whatever callback you register for GetAppDependenciesResult_t may be called multiple times
// until all app dependencies have been returned
STEAM_CALL_RESULT( GetAppDependenciesResult_t )
SteamAPICall_t GetAppDependencies( PublishedFileId_t nPublishedFileID )
{
PRINT_DEBUG("Steam_UGC::GetAppDependencies\n");
return 0;
}
// delete the item without prompting the user
STEAM_CALL_RESULT( DeleteItemResult_t )
SteamAPICall_t DeleteItem( PublishedFileId_t nPublishedFileID )
{
PRINT_DEBUG("Steam_UGC::DeleteItem\n");
return 0;
}
};

View File

@ -0,0 +1,123 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Unified_Messages :
public ISteamUnifiedMessages
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
public:
static void steam_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_steamunifiedmessages_callback\n");
Steam_Unified_Messages *steam_steamunifiedmessages = (Steam_Unified_Messages *)object;
steam_steamunifiedmessages->Callback(msg);
}
static void steam_run_every_runcb(void *object)
{
PRINT_DEBUG("steam_steamunifiedmessages_run_every_runcb\n");
Steam_Unified_Messages *steam_steamunifiedmessages = (Steam_Unified_Messages *)object;
steam_steamunifiedmessages->RunCallbacks();
}
Steam_Unified_Messages(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->run_every_runcb = run_every_runcb;
// this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Unified_Messages::steam_callback, this);
// this->run_every_runcb->add(&Steam_Unified_Messages::steam_run_every_runcb, this);
this->callback_results = callback_results;
this->callbacks = callbacks;
}
~Steam_Unified_Messages()
{
//TODO rm network callbacks
// this->run_every_runcb->remove(&Steam_Unified_Messages::steam_run_every_runcb, this);
}
// Sends a service method (in binary serialized form) using the Steam Client.
// Returns a unified message handle (k_InvalidUnifiedMessageHandle if could not send the message).
ClientUnifiedMessageHandle SendMethod( const char *pchServiceMethod, const void *pRequestBuffer, uint32 unRequestBufferSize, uint64 unContext )
{
PRINT_DEBUG("Steam_Unified_Messages::SendMethod\n");
return ISteamUnifiedMessages::k_InvalidUnifiedMessageHandle;
}
// Gets the size of the response and the EResult. Returns false if the response is not ready yet.
bool GetMethodResponseInfo( ClientUnifiedMessageHandle hHandle, uint32 *punResponseSize, EResult *peResult )
{
PRINT_DEBUG("Steam_Unified_Messages::GetMethodResponseInfo\n");
return false;
}
// Gets a response in binary serialized form (and optionally release the corresponding allocated memory).
bool GetMethodResponseData( ClientUnifiedMessageHandle hHandle, void *pResponseBuffer, uint32 unResponseBufferSize, bool bAutoRelease )
{
PRINT_DEBUG("Steam_Unified_Messages::GetMethodResponseData\n");
return false;
}
// Releases the message and its corresponding allocated memory.
bool ReleaseMethod( ClientUnifiedMessageHandle hHandle )
{
PRINT_DEBUG("Steam_Unified_Messages::ReleaseMethod\n");
return false;
}
// Sends a service notification (in binary serialized form) using the Steam Client.
// Returns true if the notification was sent successfully.
bool SendNotification( const char *pchServiceNotification, const void *pNotificationBuffer, uint32 unNotificationBufferSize )
{
PRINT_DEBUG("Steam_Unified_Messages::SendNotification\n");
return false;
}
void RunCallbacks()
{
}
void Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::CONNECT) {
}
if (msg->low_level().type() == Low_Level::DISCONNECT) {
}
}
}
};

451
dll/steam_user.h Normal file
View File

@ -0,0 +1,451 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_User :
public ISteamUser009,
public ISteamUser010,
public ISteamUser011,
public ISteamUser012,
public ISteamUser013,
public ISteamUser014,
public ISteamUser015,
public ISteamUser016,
public ISteamUser017,
public ISteamUser018,
public ISteamUser019,
public ISteamUser
{
Settings *settings;
class Networking *network;
class SteamCallBacks *callbacks;
class SteamCallResults *callback_results;
Local_Storage *local_storage;
bool recording = false;
std::chrono::high_resolution_clock::time_point last_get_voice;
std::string encrypted_app_ticket;
Auth_Ticket_Manager *ticket_manager;
public:
Steam_User(Settings *settings, Local_Storage *local_storage, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
{
this->settings = settings;
this->local_storage = local_storage;
this->network = network;
this->callbacks = callbacks;
this->callback_results = callback_results;
recording = false;
ticket_manager = new Auth_Ticket_Manager(settings, network, callbacks);
}
~Steam_User()
{
delete ticket_manager;
}
// returns the HSteamUser this interface represents
// this is only used internally by the API, and by a few select interfaces that support multi-user
HSteamUser GetHSteamUser()
{
PRINT_DEBUG("GetHSteamUser\n");
return CLIENT_HSTEAMUSER;
}
// returns true if the Steam client current has a live connection to the Steam servers.
// If false, it means there is no active connection due to either a networking issue on the local machine, or the Steam server is down/busy.
// The Steam client will automatically be trying to recreate the connection as often as possible.
bool BLoggedOn()
{
PRINT_DEBUG("BLoggedOn\n");
return true;
}
// returns the CSteamID of the account currently logged into the Steam client
// a CSteamID is a unique identifier for an account, and used to differentiate users in all parts of the Steamworks API
CSteamID GetSteamID()
{
PRINT_DEBUG("Steam_User::GetSteamID\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
CSteamID id = settings->get_local_steam_id();
return id;
}
// Multiplayer Authentication functions
// InitiateGameConnection() starts the state machine for authenticating the game client with the game server
// It is the client portion of a three-way handshake between the client, the game server, and the steam servers
//
// Parameters:
// void *pAuthBlob - a pointer to empty memory that will be filled in with the authentication token.
// int cbMaxAuthBlob - the number of bytes of allocated memory in pBlob. Should be at least 2048 bytes.
// CSteamID steamIDGameServer - the steamID of the game server, received from the game server by the client
// CGameID gameID - the ID of the current game. For games without mods, this is just CGameID( <appID> )
// uint32 unIPServer, uint16 usPortServer - the IP address of the game server
// bool bSecure - whether or not the client thinks that the game server is reporting itself as secure (i.e. VAC is running)
//
// return value - returns the number of bytes written to pBlob. If the return is 0, then the buffer passed in was too small, and the call has failed
// The contents of pBlob should then be sent to the game server, for it to use to complete the authentication process.
//steam returns 206 bytes
#define INITIATE_GAME_CONNECTION_TICKET_SIZE 206
int InitiateGameConnection( void *pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer, bool bSecure )
{
PRINT_DEBUG("InitiateGameConnection %i %llu %u %u %u\n", cbMaxAuthBlob, steamIDGameServer.ConvertToUint64(), unIPServer, usPortServer, bSecure);
if (cbMaxAuthBlob < INITIATE_GAME_CONNECTION_TICKET_SIZE) return 0;
uint32 out_size = INITIATE_GAME_CONNECTION_TICKET_SIZE;
ticket_manager->getTicketData(pAuthBlob, INITIATE_GAME_CONNECTION_TICKET_SIZE, &out_size);
return out_size;
}
int InitiateGameConnection( void *pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, CGameID gameID, uint32 unIPServer, uint16 usPortServer, bool bSecure )
{
PRINT_DEBUG("InitiateGameConnection old\n");
return InitiateGameConnection(pAuthBlob, cbMaxAuthBlob, steamIDGameServer, unIPServer, usPortServer, bSecure);
}
// notify of disconnect
// needs to occur when the game client leaves the specified game server, needs to match with the InitiateGameConnection() call
void TerminateGameConnection( uint32 unIPServer, uint16 usPortServer )
{
PRINT_DEBUG("TerminateGameConnection\n");
}
// Legacy functions
// used by only a few games to track usage events
void TrackAppUsageEvent( CGameID gameID, int eAppUsageEvent, const char *pchExtraInfo)
{
PRINT_DEBUG("TrackAppUsageEvent\n");
}
void RefreshSteam2Login()
{
PRINT_DEBUG("RefreshSteam2Login\n");
}
// get the local storage folder for current Steam account to write application data, e.g. save games, configs etc.
// this will usually be something like "C:\Progam Files\Steam\userdata\<SteamID>\<AppID>\local"
bool GetUserDataFolder( char *pchBuffer, int cubBuffer )
{
PRINT_DEBUG("GetUserDataFolder\n");
if (!cubBuffer) return false;
std::string user_data = local_storage->get_path(USER_DATA_FOLDER);
strncpy(pchBuffer, user_data.c_str(), cubBuffer - 1);
pchBuffer[cubBuffer - 1] = 0;
return true;
}
// Starts voice recording. Once started, use GetVoice() to get the data
void StartVoiceRecording( )
{
PRINT_DEBUG("StartVoiceRecording\n");
last_get_voice = std::chrono::high_resolution_clock::now();
recording = true;
//TODO:fix
recording = false;
}
// Stops voice recording. Because people often release push-to-talk keys early, the system will keep recording for
// a little bit after this function is called. GetVoice() should continue to be called until it returns
// k_eVoiceResultNotRecording
void StopVoiceRecording( )
{
PRINT_DEBUG("StopVoiceRecording\n");
recording = false;
}
// Determine the size of captured audio data that is available from GetVoice.
// Most applications will only use compressed data and should ignore the other
// parameters, which exist primarily for backwards compatibility. See comments
// below for further explanation of "uncompressed" data.
EVoiceResult GetAvailableVoice( uint32 *pcbCompressed, uint32 *pcbUncompressed_Deprecated, uint32 nUncompressedVoiceDesiredSampleRate_Deprecated )
{
PRINT_DEBUG("GetAvailableVoice\n");
if (pcbCompressed) *pcbCompressed = 0;
if (pcbUncompressed_Deprecated) *pcbUncompressed_Deprecated = 0;
if (!recording) return k_EVoiceResultNotRecording;
double seconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - last_get_voice).count();
if (pcbCompressed) *pcbCompressed = seconds * 1024.0 * 64.0 / 8.0;
if (pcbUncompressed_Deprecated) *pcbUncompressed_Deprecated = seconds * (double)nUncompressedVoiceDesiredSampleRate_Deprecated * 2.0;
return k_EVoiceResultOK;
}
EVoiceResult GetAvailableVoice(uint32 *pcbCompressed, uint32 *pcbUncompressed)
{
PRINT_DEBUG("GetAvailableVoice old\n");
return GetAvailableVoice(pcbCompressed, pcbUncompressed, 11025);
}
// ---------------------------------------------------------------------------
// NOTE: "uncompressed" audio is a deprecated feature and should not be used
// by most applications. It is raw single-channel 16-bit PCM wave data which
// may have been run through preprocessing filters and/or had silence removed,
// so the uncompressed audio could have a shorter duration than you expect.
// There may be no data at all during long periods of silence. Also, fetching
// uncompressed audio will cause GetVoice to discard any leftover compressed
// audio, so you must fetch both types at once. Finally, GetAvailableVoice is
// not precisely accurate when the uncompressed size is requested. So if you
// really need to use uncompressed audio, you should call GetVoice frequently
// with two very large (20kb+) output buffers instead of trying to allocate
// perfectly-sized buffers. But most applications should ignore all of these
// details and simply leave the "uncompressed" parameters as NULL/zero.
// ---------------------------------------------------------------------------
// Read captured audio data from the microphone buffer. This should be called
// at least once per frame, and preferably every few milliseconds, to keep the
// microphone input delay as low as possible. Most applications will only use
// compressed data and should pass NULL/zero for the "uncompressed" parameters.
// Compressed data can be transmitted by your application and decoded into raw
// using the DecompressVoice function below.
EVoiceResult GetVoice( bool bWantCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten, bool bWantUncompressed_Deprecated, void *pUncompressedDestBuffer_Deprecated , uint32 cbUncompressedDestBufferSize_Deprecated , uint32 *nUncompressBytesWritten_Deprecated , uint32 nUncompressedVoiceDesiredSampleRate_Deprecated )
{
PRINT_DEBUG("GetVoice\n");
if (!recording) return k_EVoiceResultNotRecording;
double seconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - last_get_voice).count();
if (bWantCompressed) {
uint32 towrite = seconds * 1024.0 * 64.0 / 8.0;
if (cbDestBufferSize < towrite) towrite = cbDestBufferSize;
if (pDestBuffer) memset(pDestBuffer, 0, towrite);
if (nBytesWritten) *nBytesWritten = towrite;
}
if (bWantUncompressed_Deprecated) {
PRINT_DEBUG("Wanted Uncompressed\n");
}
last_get_voice = std::chrono::high_resolution_clock::now();
return k_EVoiceResultOK;
}
EVoiceResult GetVoice( bool bWantCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten, bool bWantUncompressed, void *pUncompressedDestBuffer, uint32 cbUncompressedDestBufferSize, uint32 *nUncompressBytesWritten )
{
PRINT_DEBUG("GetVoice old\n");
return GetVoice(bWantCompressed, pDestBuffer, cbDestBufferSize, nBytesWritten, bWantUncompressed, pUncompressedDestBuffer, cbUncompressedDestBufferSize, nUncompressBytesWritten, 11025);
}
EVoiceResult GetCompressedVoice( void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten )
{
PRINT_DEBUG("GetCompressedVoice\n");
return GetVoice(true, pDestBuffer, cbDestBufferSize, nBytesWritten, false, NULL, 0, NULL);
}
// Decodes the compressed voice data returned by GetVoice. The output data is
// raw single-channel 16-bit PCM audio. The decoder supports any sample rate
// from 11025 to 48000; see GetVoiceOptimalSampleRate() below for details.
// If the output buffer is not large enough, then *nBytesWritten will be set
// to the required buffer size, and k_EVoiceResultBufferTooSmall is returned.
// It is suggested to start with a 20kb buffer and reallocate as necessary.
EVoiceResult DecompressVoice( const void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten, uint32 nDesiredSampleRate )
{
PRINT_DEBUG("DecompressVoice\n");
if (!recording) return k_EVoiceResultNotRecording;
uint32 uncompressed = (double)cbCompressed * ((double)nDesiredSampleRate / 8192.0);
if(nBytesWritten) *nBytesWritten = uncompressed;
if (uncompressed > cbDestBufferSize) uncompressed = cbDestBufferSize;
if (pDestBuffer) memset(pDestBuffer, 0, uncompressed);
return k_EVoiceResultOK;
}
EVoiceResult DecompressVoice( const void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten )
{
PRINT_DEBUG("DecompressVoice old\n");
return DecompressVoice(pCompressed, cbCompressed, pDestBuffer, cbDestBufferSize, nBytesWritten, 11025);
}
EVoiceResult DecompressVoice( void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten )
{
PRINT_DEBUG("DecompressVoice older\n");
return DecompressVoice(pCompressed, cbCompressed, pDestBuffer, cbDestBufferSize, nBytesWritten, 11025);
}
// This returns the native sample rate of the Steam voice decompressor
// this sample rate for DecompressVoice will perform the least CPU processing.
// However, the final audio quality will depend on how well the audio device
// (and/or your application's audio output SDK) deals with lower sample rates.
// You may find that you get the best audio output quality when you ignore
// this function and use the native sample rate of your audio output device,
// which is usually 48000 or 44100.
uint32 GetVoiceOptimalSampleRate()
{
PRINT_DEBUG("GetVoiceOptimalSampleRate\n");
return 48000;
}
// Retrieve ticket to be sent to the entity who wishes to authenticate you.
// pcbTicket retrieves the length of the actual ticket.
HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
{
PRINT_DEBUG("Steam_User::GetAuthSessionTicket %i\n", cbMaxTicket);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return ticket_manager->getTicket(pTicket, cbMaxTicket, pcbTicket);
}
// Authenticate ticket from entity steamID to be sure it is valid and isnt reused
// Registers for callbacks if the entity goes offline or cancels the ticket ( see ValidateAuthTicketResponse_t callback and EAuthSessionResponse )
EBeginAuthSessionResult BeginAuthSession( const void *pAuthTicket, int cbAuthTicket, CSteamID steamID )
{
PRINT_DEBUG("Steam_User::BeginAuthSession %i %llu\n", cbAuthTicket, steamID.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return ticket_manager->beginAuth(pAuthTicket, cbAuthTicket, steamID);
}
// Stop tracking started by BeginAuthSession - called when no longer playing game with this entity
void EndAuthSession( CSteamID steamID )
{
PRINT_DEBUG("Steam_User::EndAuthSession\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
ticket_manager->endAuth(steamID);
}
// Cancel auth ticket from GetAuthSessionTicket, called when no longer playing game with the entity you gave the ticket to
void CancelAuthTicket( HAuthTicket hAuthTicket )
{
PRINT_DEBUG("Steam_User::CancelAuthTicket\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
ticket_manager->cancelTicket(hAuthTicket);
}
// After receiving a user's authentication data, and passing it to BeginAuthSession, use this function
// to determine if the user owns downloadable content specified by the provided AppID.
EUserHasLicenseForAppResult UserHasLicenseForApp( CSteamID steamID, AppId_t appID )
{
PRINT_DEBUG("Steam_User::UserHasLicenseForApp\n");
return k_EUserHasLicenseResultHasLicense;
}
// returns true if this users looks like they are behind a NAT device. Only valid once the user has connected to steam
// (i.e a SteamServersConnected_t has been issued) and may not catch all forms of NAT.
bool BIsBehindNAT()
{
PRINT_DEBUG("BIsBehindNAT\n");
return false;
}
// set data to be replicated to friends so that they can join your game
// CSteamID steamIDGameServer - the steamID of the game server, received from the game server by the client
// uint32 unIPServer, uint16 usPortServer - the IP address of the game server
void AdvertiseGame( CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer )
{
PRINT_DEBUG("AdvertiseGame\n");
}
// Requests a ticket encrypted with an app specific shared key
// pDataToInclude, cbDataToInclude will be encrypted into the ticket
// ( This is asynchronous, you must wait for the ticket to be completed by the server )
STEAM_CALL_RESULT( EncryptedAppTicketResponse_t )
SteamAPICall_t RequestEncryptedAppTicket( void *pDataToInclude, int cbDataToInclude )
{
PRINT_DEBUG("Steam_User::RequestEncryptedAppTicket\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
EncryptedAppTicketResponse_t data;
data.m_eResult = k_EResultOK;
encrypted_app_ticket = std::string((char *)pDataToInclude, cbDataToInclude);
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// retrieve a finished ticket
bool GetEncryptedAppTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
{
PRINT_DEBUG("Steam_User::GetEncryptedAppTicket\n");
if (!pcbTicket || !pTicket) return false;
if (encrypted_app_ticket.size() < cbMaxTicket) cbMaxTicket = encrypted_app_ticket.size();
memset(pTicket, 'g', cbMaxTicket);
*pcbTicket = cbMaxTicket;
return true;
}
// Trading Card badges data access
// if you only have one set of cards, the series will be 1
// the user has can have two different badges for a series; the regular (max level 5) and the foil (max level 1)
int GetGameBadgeLevel( int nSeries, bool bFoil )
{
PRINT_DEBUG("GetGameBadgeLevel\n");
return 0;
}
// gets the Steam Level of the user, as shown on their profile
int GetPlayerSteamLevel()
{
PRINT_DEBUG("GetPlayerSteamLevel\n");
return 100;
}
// Requests a URL which authenticates an in-game browser for store check-out,
// and then redirects to the specified URL. As long as the in-game browser
// accepts and handles session cookies, Steam microtransaction checkout pages
// will automatically recognize the user instead of presenting a login page.
// The result of this API call will be a StoreAuthURLResponse_t callback.
// NOTE: The URL has a very short lifetime to prevent history-snooping attacks,
// so you should only call this API when you are about to launch the browser,
// or else immediately navigate to the result URL using a hidden browser window.
// NOTE 2: The resulting authorization cookie has an expiration time of one day,
// so it would be a good idea to request and visit a new auth URL every 12 hours.
STEAM_CALL_RESULT( StoreAuthURLResponse_t )
SteamAPICall_t RequestStoreAuthURL( const char *pchRedirectURL )
{
PRINT_DEBUG("RequestStoreAuthURL\n");
return 0;
}
// gets whether the users phone number is verified
bool BIsPhoneVerified()
{
PRINT_DEBUG("BIsPhoneVerified\n");
return true;
}
// gets whether the user has two factor enabled on their account
bool BIsTwoFactorEnabled()
{
PRINT_DEBUG("BIsTwoFactorEnabled\n");
return true;
}
// gets whether the users phone number is identifying
bool BIsPhoneIdentifying()
{
PRINT_DEBUG("BIsPhoneIdentifying\n");
return false;
}
// gets whether the users phone number is awaiting (re)verification
bool BIsPhoneRequiringVerification()
{
PRINT_DEBUG("BIsPhoneRequiringVerification\n");
return false;
}
STEAM_CALL_RESULT( MarketEligibilityResponse_t )
SteamAPICall_t GetMarketEligibility()
{
PRINT_DEBUG("GetMarketEligibility\n");
return 0;
}
};

621
dll/steam_user_stats.h Normal file
View File

@ -0,0 +1,621 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
struct Steam_Leaderboard {
std::string name;
ELeaderboardSortMethod sort_method;
ELeaderboardDisplayType display_type;
};
class Steam_User_Stats :
public ISteamUserStats003,
public ISteamUserStats004,
public ISteamUserStats005,
public ISteamUserStats006,
public ISteamUserStats007,
public ISteamUserStats008,
public ISteamUserStats009,
public ISteamUserStats010,
public ISteamUserStats
{
Local_Storage *local_storage;
Settings *settings;
SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
std::vector<struct Steam_Leaderboard> leaderboards;
unsigned int find_leaderboard(std::string name)
{
unsigned index = 1;
for (auto &leaderboard : leaderboards) {
if (leaderboard.name == name) return index;
++index;
}
return 0;
}
public:
Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
{
this->local_storage = local_storage;
this->settings = settings;
this->callback_results = callback_results;
this->callbacks = callbacks;
}
// Ask the server to send down this user's data and achievements for this game
STEAM_CALL_BACK( UserStatsReceived_t )
bool RequestCurrentStats()
{
PRINT_DEBUG("Steam_User_Stats::RequestCurrentStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
UserStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
data.m_steamIDUser = settings->get_local_steam_id();
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1);
return true;
}
// Data accessors
bool GetStat( const char *pchName, int32 *pData )
{
PRINT_DEBUG("GetStat int32 %s\n", pchName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
int read_data = local_storage->get_data(STATS_STORAGE_FOLDER, pchName, (char* )pData, sizeof(*pData));
if (read_data == sizeof(int32))
return true;
return false;
}
bool GetStat( const char *pchName, float *pData )
{
PRINT_DEBUG("GetStat float %s\n", pchName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
int read_data = local_storage->get_data(STATS_STORAGE_FOLDER, pchName, (char* )pData, sizeof(*pData));
if (read_data == sizeof(int32))
return true;
return false;
}
// Set / update data
bool SetStat( const char *pchName, int32 nData )
{
PRINT_DEBUG("SetStat int32 %s\n", pchName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return local_storage->store_data(STATS_STORAGE_FOLDER, pchName, (char* )&nData, sizeof(nData)) == sizeof(nData);
}
bool SetStat( const char *pchName, float fData )
{
PRINT_DEBUG("SetStat float %s\n", pchName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return local_storage->store_data(STATS_STORAGE_FOLDER, pchName, (char* )&fData, sizeof(fData)) == sizeof(fData);
}
bool UpdateAvgRateStat( const char *pchName, float flCountThisSession, double dSessionLength )
{
PRINT_DEBUG("UpdateAvgRateStat %s\n", pchName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
char data[sizeof(float) + sizeof(float) + sizeof(double)];
int read_data = local_storage->get_data(STATS_STORAGE_FOLDER, pchName, (char* )data, sizeof(*data));
float oldcount = 0;
double oldsessionlength = 0;
if (read_data == sizeof(data)) {
memcpy(&oldcount, data + sizeof(float), sizeof(oldcount));
memcpy(&oldsessionlength, data + sizeof(float) + sizeof(double), sizeof(oldsessionlength));
}
oldcount += flCountThisSession;
oldsessionlength += dSessionLength;
float average = oldcount / oldsessionlength;
memcpy(data, &average, sizeof(average));
memcpy(data + sizeof(float), &oldcount, sizeof(oldcount));
memcpy(data + sizeof(float) * 2, &oldsessionlength, sizeof(oldsessionlength));
return local_storage->store_data(STATS_STORAGE_FOLDER, pchName, data, sizeof(data)) == sizeof(data);
}
// Achievement flag accessors
bool GetAchievement( const char *pchName, bool *pbAchieved )
{
//TODO: these achievement functions need to load a list of achievements from somewhere, return false so that kf2 doesn't loop endlessly
PRINT_DEBUG("GetAchievement %s\n", pchName);
*pbAchieved = false;
return false;
}
bool SetAchievement( const char *pchName )
{
PRINT_DEBUG("SetAchievement %s\n", pchName);
return false;
}
bool ClearAchievement( const char *pchName )
{
PRINT_DEBUG("ClearAchievement %s\n", pchName);
return false;
}
// Get the achievement status, and the time it was unlocked if unlocked.
// If the return value is true, but the unlock time is zero, that means it was unlocked before Steam
// began tracking achievement unlock times (December 2009). Time is seconds since January 1, 1970.
bool GetAchievementAndUnlockTime( const char *pchName, bool *pbAchieved, uint32 *punUnlockTime )
{
PRINT_DEBUG("GetAchievementAndUnlockTime\n");
*pbAchieved = false;
return true;
}
// Store the current data on the server, will get a callback when set
// And one callback for every new achievement
//
// If the callback has a result of k_EResultInvalidParam, one or more stats
// uploaded has been rejected, either because they broke constraints
// or were out of date. In this case the server sends back updated values.
// The stats should be re-iterated to keep in sync.
bool StoreStats()
{
PRINT_DEBUG("StoreStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
UserStatsStored_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return true;
}
// Achievement / GroupAchievement metadata
// Gets the icon of the achievement, which is a handle to be used in ISteamUtils::GetImageRGBA(), or 0 if none set.
// A return value of 0 may indicate we are still fetching data, and you can wait for the UserAchievementIconFetched_t callback
// which will notify you when the bits are ready. If the callback still returns zero, then there is no image set for the
// specified achievement.
int GetAchievementIcon( const char *pchName )
{
PRINT_DEBUG("GetAchievementIcon\n");
return 0;
}
// Get general attributes for an achievement. Accepts the following keys:
// - "name" and "desc" for retrieving the localized achievement name and description (returned in UTF8)
// - "hidden" for retrieving if an achievement is hidden (returns "0" when not hidden, "1" when hidden)
const char * GetAchievementDisplayAttribute( const char *pchName, const char *pchKey )
{
PRINT_DEBUG("GetAchievementDisplayAttribute %s %s\n", pchName, pchKey);
return ""; //TODO
if (strcmp (pchKey, "name") == 0) {
return "Achievement Name";
}
if (strcmp (pchKey, "desc") == 0) {
return "Achievement Description";
}
if (strcmp (pchKey, "hidden") == 0) {
return "0";
}
return "";
}
// Achievement progress - triggers an AchievementProgress callback, that is all.
// Calling this w/ N out of N progress will NOT set the achievement, the game must still do that.
bool IndicateAchievementProgress( const char *pchName, uint32 nCurProgress, uint32 nMaxProgress )
{
PRINT_DEBUG("IndicateAchievementProgress\n");
}
// Used for iterating achievements. In general games should not need these functions because they should have a
// list of existing achievements compiled into them
uint32 GetNumAchievements()
{
PRINT_DEBUG("GetNumAchievements\n");
return 0;
}
// Get achievement name iAchievement in [0,GetNumAchievements)
const char * GetAchievementName( uint32 iAchievement )
{
PRINT_DEBUG("GetAchievementName\n");
return "";
}
// Friends stats & achievements
// downloads stats for the user
// returns a UserStatsReceived_t received when completed
// if the other user has no stats, UserStatsReceived_t.m_eResult will be set to k_EResultFail
// these stats won't be auto-updated; you'll need to call RequestUserStats() again to refresh any data
STEAM_CALL_RESULT( UserStatsReceived_t )
SteamAPICall_t RequestUserStats( CSteamID steamIDUser )
{
PRINT_DEBUG("Steam_User_Stats::RequestUserStats %llu\n", steamIDUser.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
UserStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
data.m_steamIDUser = steamIDUser;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1);
}
// requests stat information for a user, usable after a successful call to RequestUserStats()
bool GetUserStat( CSteamID steamIDUser, const char *pchName, int32 *pData )
{
PRINT_DEBUG("GetUserStat %s %llu\n", pchName, steamIDUser.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
GetStat(pchName, pData);
} else {
*pData = 0;
}
return true;
}
bool GetUserStat( CSteamID steamIDUser, const char *pchName, float *pData )
{
PRINT_DEBUG("GetUserStat %s %llu\n", pchName, steamIDUser.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
GetStat(pchName, pData);
} else {
*pData = 0;
}
return true;
}
bool GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchieved )
{
PRINT_DEBUG("GetUserAchievement %s\n", pchName);
return false;
}
// See notes for GetAchievementAndUnlockTime above
bool GetUserAchievementAndUnlockTime( CSteamID steamIDUser, const char *pchName, bool *pbAchieved, uint32 *punUnlockTime )
{
PRINT_DEBUG("GetUserAchievementAndUnlockTime %s\n", pchName);
return false;
}
// Reset stats
bool ResetAllStats( bool bAchievementsToo )
{
PRINT_DEBUG("ResetAllStats\n");
//TODO
return true;
}
// Leaderboard functions
// asks the Steam back-end for a leaderboard by name, and will create it if it's not yet
// This call is asynchronous, with the result returned in LeaderboardFindResult_t
STEAM_CALL_RESULT(LeaderboardFindResult_t)
SteamAPICall_t FindOrCreateLeaderboard( const char *pchLeaderboardName, ELeaderboardSortMethod eLeaderboardSortMethod, ELeaderboardDisplayType eLeaderboardDisplayType )
{
PRINT_DEBUG("FindOrCreateLeaderboard %s\n", pchLeaderboardName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
unsigned int leader = find_leaderboard(pchLeaderboardName);
if (!leader) {
struct Steam_Leaderboard leaderboard;
leaderboard.name = std::string(pchLeaderboardName);
leaderboard.sort_method = eLeaderboardSortMethod;
leaderboard.display_type = eLeaderboardDisplayType;
leaderboards.push_back(leaderboard);
leader = leaderboards.size();
}
LeaderboardFindResult_t data;
data.m_hSteamLeaderboard = leader;
data.m_bLeaderboardFound = 1;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// as above, but won't create the leaderboard if it's not found
// This call is asynchronous, with the result returned in LeaderboardFindResult_t
STEAM_CALL_RESULT( LeaderboardFindResult_t )
SteamAPICall_t FindLeaderboard( const char *pchLeaderboardName )
{
PRINT_DEBUG("FindLeaderboard %s\n", pchLeaderboardName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO: figure out a way to get real leaderboard info
/*
LeaderboardFindResult_t data;
data.m_hSteamLeaderboard = find_leaderboard(pchLeaderboardName);;
data.m_bLeaderboardFound = !!data.m_hSteamLeaderboard;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
*/
return FindOrCreateLeaderboard(pchLeaderboardName, k_ELeaderboardSortMethodDescending, k_ELeaderboardDisplayTypeNumeric);
}
// returns the name of a leaderboard
const char * GetLeaderboardName( SteamLeaderboard_t hSteamLeaderboard )
{
PRINT_DEBUG("GetLeaderboardName\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return "";
return leaderboards[hSteamLeaderboard - 1].name.c_str();
}
// returns the total number of entries in a leaderboard, as of the last request
int GetLeaderboardEntryCount( SteamLeaderboard_t hSteamLeaderboard )
{
PRINT_DEBUG("GetLeaderboardEntryCount\n");
return 0;
}
// returns the sort method of the leaderboard
ELeaderboardSortMethod GetLeaderboardSortMethod( SteamLeaderboard_t hSteamLeaderboard )
{
PRINT_DEBUG("GetLeaderboardSortMethod\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_ELeaderboardSortMethodNone;
return leaderboards[hSteamLeaderboard - 1].sort_method;
}
// returns the display type of the leaderboard
ELeaderboardDisplayType GetLeaderboardDisplayType( SteamLeaderboard_t hSteamLeaderboard )
{
PRINT_DEBUG("GetLeaderboardDisplayType\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_ELeaderboardDisplayTypeNone;
return leaderboards[hSteamLeaderboard - 1].display_type;
}
// Asks the Steam back-end for a set of rows in the leaderboard.
// This call is asynchronous, with the result returned in LeaderboardScoresDownloaded_t
// LeaderboardScoresDownloaded_t will contain a handle to pull the results from GetDownloadedLeaderboardEntries() (below)
// You can ask for more entries than exist, and it will return as many as do exist.
// k_ELeaderboardDataRequestGlobal requests rows in the leaderboard from the full table, with nRangeStart & nRangeEnd in the range [1, TotalEntries]
// k_ELeaderboardDataRequestGlobalAroundUser requests rows around the current user, nRangeStart being negate
// e.g. DownloadLeaderboardEntries( hLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -3, 3 ) will return 7 rows, 3 before the user, 3 after
// k_ELeaderboardDataRequestFriends requests all the rows for friends of the current user
STEAM_CALL_RESULT( LeaderboardScoresDownloaded_t )
SteamAPICall_t DownloadLeaderboardEntries( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardDataRequest eLeaderboardDataRequest, int nRangeStart, int nRangeEnd )
{
PRINT_DEBUG("DownloadLeaderboardEntries %llu %i %i %i\n", hSteamLeaderboard, eLeaderboardDataRequest, nRangeStart, nRangeEnd);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoresDownloaded_t data;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_hSteamLeaderboardEntries = 123;
data.m_cEntryCount = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// as above, but downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers
// if a user doesn't have a leaderboard entry, they won't be included in the result
// a max of 100 users can be downloaded at a time, with only one outstanding call at a time
STEAM_METHOD_DESC(Downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers)
STEAM_CALL_RESULT( LeaderboardScoresDownloaded_t )
SteamAPICall_t DownloadLeaderboardEntriesForUsers( SteamLeaderboard_t hSteamLeaderboard,
STEAM_ARRAY_COUNT_D(cUsers, Array of users to retrieve) CSteamID *prgUsers, int cUsers )
{
PRINT_DEBUG("DownloadLeaderboardEntriesForUsers\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoresDownloaded_t data;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_hSteamLeaderboardEntries = 123;
data.m_cEntryCount = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Returns data about a single leaderboard entry
// use a for loop from 0 to LeaderboardScoresDownloaded_t::m_cEntryCount to get all the downloaded entries
// e.g.
// void OnLeaderboardScoresDownloaded( LeaderboardScoresDownloaded_t *pLeaderboardScoresDownloaded )
// {
// for ( int index = 0; index < pLeaderboardScoresDownloaded->m_cEntryCount; index++ )
// {
// LeaderboardEntry_t leaderboardEntry;
// int32 details[3]; // we know this is how many we've stored previously
// GetDownloadedLeaderboardEntry( pLeaderboardScoresDownloaded->m_hSteamLeaderboardEntries, index, &leaderboardEntry, details, 3 );
// assert( leaderboardEntry.m_cDetails == 3 );
// ...
// }
// once you've accessed all the entries, the data will be free'd, and the SteamLeaderboardEntries_t handle will become invalid
bool GetDownloadedLeaderboardEntry( SteamLeaderboardEntries_t hSteamLeaderboardEntries, int index, LeaderboardEntry_t *pLeaderboardEntry, int32 *pDetails, int cDetailsMax )
{
PRINT_DEBUG("GetDownloadedLeaderboardEntry\n");
return false;
}
// Uploads a user score to the Steam back-end.
// This call is asynchronous, with the result returned in LeaderboardScoreUploaded_t
// Details are extra game-defined information regarding how the user got that score
// pScoreDetails points to an array of int32's, cScoreDetailsCount is the number of int32's in the list
STEAM_CALL_RESULT( LeaderboardScoreUploaded_t )
SteamAPICall_t UploadLeaderboardScore( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32 *pScoreDetails, int cScoreDetailsCount )
{
PRINT_DEBUG("UploadLeaderboardScore\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoreUploaded_t data;
data.m_bSuccess = 1; //needs to be success or DOA6 freezes when uploading score.
//data.m_bSuccess = 0;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_nScore = nScore;
//data.m_bScoreChanged = 1;
data.m_bScoreChanged = 0;
//data.m_nGlobalRankNew = 1;
data.m_nGlobalRankNew = 0;
data.m_nGlobalRankPrevious = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
SteamAPICall_t UploadLeaderboardScore( SteamLeaderboard_t hSteamLeaderboard, int32 nScore, int32 *pScoreDetails, int cScoreDetailsCount )
{
PRINT_DEBUG("UploadLeaderboardScore old\n");
return UploadLeaderboardScore(hSteamLeaderboard, k_ELeaderboardUploadScoreMethodKeepBest, nScore, pScoreDetails, cScoreDetailsCount);
}
// Attaches a piece of user generated content the user's entry on a leaderboard.
// hContent is a handle to a piece of user generated content that was shared using ISteamUserRemoteStorage::FileShare().
// This call is asynchronous, with the result returned in LeaderboardUGCSet_t.
STEAM_CALL_RESULT( LeaderboardUGCSet_t )
SteamAPICall_t AttachLeaderboardUGC( SteamLeaderboard_t hSteamLeaderboard, UGCHandle_t hUGC )
{
PRINT_DEBUG("AttachLeaderboardUGC\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardUGCSet_t data = {};
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) {
data.m_eResult = k_EResultFail;
} else {
data.m_eResult = k_EResultOK;
}
data.m_hSteamLeaderboard = hSteamLeaderboard;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Retrieves the number of players currently playing your game (online + offline)
// This call is asynchronous, with the result returned in NumberOfCurrentPlayers_t
STEAM_CALL_RESULT( NumberOfCurrentPlayers_t )
SteamAPICall_t GetNumberOfCurrentPlayers()
{
PRINT_DEBUG("GetNumberOfCurrentPlayers\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
NumberOfCurrentPlayers_t data;
data.m_bSuccess = 1;
data.m_cPlayers = 69;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Requests that Steam fetch data on the percentage of players who have received each achievement
// for the game globally.
// This call is asynchronous, with the result returned in GlobalAchievementPercentagesReady_t.
STEAM_CALL_RESULT( GlobalAchievementPercentagesReady_t )
SteamAPICall_t RequestGlobalAchievementPercentages()
{
PRINT_DEBUG("RequestGlobalAchievementPercentages\n");
}
// Get the info on the most achieved achievement for the game, returns an iterator index you can use to fetch
// the next most achieved afterwards. Will return -1 if there is no data on achievement
// percentages (ie, you haven't called RequestGlobalAchievementPercentages and waited on the callback).
int GetMostAchievedAchievementInfo( char *pchName, uint32 unNameBufLen, float *pflPercent, bool *pbAchieved )
{
PRINT_DEBUG("GetMostAchievedAchievementInfo\n");
}
// Get the info on the next most achieved achievement for the game. Call this after GetMostAchievedAchievementInfo or another
// GetNextMostAchievedAchievementInfo call passing the iterator from the previous call. Returns -1 after the last
// achievement has been iterated.
int GetNextMostAchievedAchievementInfo( int iIteratorPrevious, char *pchName, uint32 unNameBufLen, float *pflPercent, bool *pbAchieved )
{
PRINT_DEBUG("GetNextMostAchievedAchievementInfo\n");
}
// Returns the percentage of users who have achieved the specified achievement.
bool GetAchievementAchievedPercent( const char *pchName, float *pflPercent )
{
PRINT_DEBUG("GetAchievementAchievedPercent\n");
}
// Requests global stats data, which is available for stats marked as "aggregated".
// This call is asynchronous, with the results returned in GlobalStatsReceived_t.
// nHistoryDays specifies how many days of day-by-day history to retrieve in addition
// to the overall totals. The limit is 60.
STEAM_CALL_RESULT( GlobalStatsReceived_t )
SteamAPICall_t RequestGlobalStats( int nHistoryDays )
{
PRINT_DEBUG("RequestGlobalStats %i\n", nHistoryDays);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
GlobalStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Gets the lifetime totals for an aggregated stat
bool GetGlobalStat( const char *pchStatName, int64 *pData )
{
PRINT_DEBUG("GetGlobalStat %s\n", pchStatName);
return false;
}
bool GetGlobalStat( const char *pchStatName, double *pData )
{
PRINT_DEBUG("GetGlobalStat %s\n", pchStatName);
return false;
}
// Gets history for an aggregated stat. pData will be filled with daily values, starting with today.
// So when called, pData[0] will be today, pData[1] will be yesterday, and pData[2] will be two days ago,
// etc. cubData is the size in bytes of the pubData buffer. Returns the number of
// elements actually set.
int32 GetGlobalStatHistory( const char *pchStatName, STEAM_ARRAY_COUNT(cubData) int64 *pData, uint32 cubData )
{
PRINT_DEBUG("GetGlobalStatHistory int64 %s\n", pchStatName);
return 0;
}
int32 GetGlobalStatHistory( const char *pchStatName, STEAM_ARRAY_COUNT(cubData) double *pData, uint32 cubData )
{
PRINT_DEBUG("GetGlobalStatHistory double %s\n", pchStatName);
return 0;
}
};

345
dll/steam_utils.h Normal file
View File

@ -0,0 +1,345 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
#include "local_storage.h"
static std::chrono::time_point<std::chrono::steady_clock> app_initialized_time = std::chrono::steady_clock::now();
class Steam_Utils :
public ISteamUtils002,
public ISteamUtils003,
public ISteamUtils004,
public ISteamUtils005,
public ISteamUtils006,
public ISteamUtils007,
public ISteamUtils008,
public ISteamUtils
{
private:
Settings *settings;
class SteamCallResults *callback_results;
public:
Steam_Utils(Settings *settings, class SteamCallResults *callback_results)
{
this->settings = settings;
this->callback_results = callback_results;
}
// return the number of seconds since the user
uint32 GetSecondsSinceAppActive()
{
PRINT_DEBUG("GetSecondsSinceAppActive\n");
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - app_initialized_time).count();
}
uint32 GetSecondsSinceComputerActive()
{
PRINT_DEBUG("GetSecondsSinceComputerActive\n");
return GetSecondsSinceAppActive() + 2000;
}
// the universe this client is connecting to
EUniverse GetConnectedUniverse()
{
PRINT_DEBUG("GetConnectedUniverse\n");
return k_EUniversePublic;
}
// Steam server time. Number of seconds since January 1, 1970, GMT (i.e unix time)
uint32 GetServerRealTime()
{
PRINT_DEBUG("GetServerRealTime\n");
uint32 server_time = std::chrono::duration_cast<std::chrono::duration<uint32>>(std::chrono::system_clock::now().time_since_epoch()).count();
PRINT_DEBUG("Time %lu\n", server_time);
return server_time;
}
// returns the 2 digit ISO 3166-1-alpha-2 format country code this client is running in (as looked up via an IP-to-location database)
// e.g "US" or "UK".
const char *GetIPCountry()
{
PRINT_DEBUG("GetIPCountry\n");
return "US";
}
static uint32 width_image(int iImage)
{
if ((iImage % 3) == 1) return 32;
if ((iImage % 3) == 2) return 64;
return 184;
}
// returns true if the image exists, and valid sizes were filled out
bool GetImageSize( int iImage, uint32 *pnWidth, uint32 *pnHeight )
{
PRINT_DEBUG("GetImageSize\n");
if (!iImage || !pnWidth || !pnHeight) return false;
*pnWidth = width_image(iImage);
*pnHeight = width_image(iImage);;
return true;
}
// returns true if the image exists, and the buffer was successfully filled out
// results are returned in RGBA format
// the destination buffer size should be 4 * height * width * sizeof(char)
bool GetImageRGBA( int iImage, uint8 *pubDest, int nDestBufferSize )
{
PRINT_DEBUG("GetImageRGBA\n");
if (!iImage || !pubDest || !nDestBufferSize) return false;
unsigned size = width_image(iImage) * width_image(iImage) * 4;
if (nDestBufferSize < size) size = nDestBufferSize;
memset(pubDest, 0xFF, size);
return true;
}
// returns the IP of the reporting server for valve - currently only used in Source engine games
bool GetCSERIPPort( uint32 *unIP, uint16 *usPort )
{
PRINT_DEBUG("GetCSERIPPort\n");
return false;
}
// return the amount of battery power left in the current system in % [0..100], 255 for being on AC power
uint8 GetCurrentBatteryPower()
{
PRINT_DEBUG("GetCurrentBatteryPower\n");
return 255;
}
// returns the appID of the current process
uint32 GetAppID()
{
PRINT_DEBUG("GetAppID\n");
return settings->get_local_game_id().AppID();
}
// Sets the position where the overlay instance for the currently calling game should show notifications.
// This position is per-game and if this function is called from outside of a game context it will do nothing.
void SetOverlayNotificationPosition( ENotificationPosition eNotificationPosition )
{
PRINT_DEBUG("SetOverlayNotificationPosition\n");
}
// API asynchronous call results
// can be used directly, but more commonly used via the callback dispatch API (see steam_api.h)
bool IsAPICallCompleted( SteamAPICall_t hSteamAPICall, bool *pbFailed )
{
PRINT_DEBUG("IsAPICallCompleted: %llu\n", hSteamAPICall);
if (hSteamAPICall == 1) { //bug ? soul calibur 6 calls this function with the return value 1 of Steam_User_Stats::RequestCurrentStats and expects this function to return true
if (pbFailed) *pbFailed = true;
return true;
}
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!callback_results->exists(hSteamAPICall)) return false;
if (pbFailed) *pbFailed = false;
return true; //all api calls "complete" right away
}
ESteamAPICallFailure GetAPICallFailureReason( SteamAPICall_t hSteamAPICall )
{
PRINT_DEBUG("GetAPICallFailureReason\n");
return k_ESteamAPICallFailureNone;
}
bool GetAPICallResult( SteamAPICall_t hSteamAPICall, void *pCallback, int cubCallback, int iCallbackExpected, bool *pbFailed )
{
PRINT_DEBUG("GetAPICallResult %llu %i %i\n", hSteamAPICall, cubCallback, iCallbackExpected);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (callback_results->callback_result(hSteamAPICall, pCallback, cubCallback)) {
*pbFailed = false;
PRINT_DEBUG("GetAPICallResult Succeeded\n");
return true;
} else {
return false;
}
}
// Deprecated. Applications should use SteamAPI_RunCallbacks() instead. Game servers do not need to call this function.
STEAM_PRIVATE_API( void RunFrame()
{
PRINT_DEBUG("RunFrame\n");
}
)
// returns the number of IPC calls made since the last time this function was called
// Used for perf debugging so you can understand how many IPC calls your game makes per frame
// Every IPC call is at minimum a thread context switch if not a process one so you want to rate
// control how often you do them.
uint32 GetIPCCallCount()
{
PRINT_DEBUG("GetIPCCallCount\n");
static int i = 0;
i += 123;
return i; //TODO
}
// API warning handling
// 'int' is the severity; 0 for msg, 1 for warning
// 'const char *' is the text of the message
// callbacks will occur directly after the API function is called that generated the warning or message
void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction )
{
PRINT_DEBUG("Steam_Utils::SetWarningMessageHook\n");
}
// Returns true if the overlay is running & the user can access it. The overlay process could take a few seconds to
// start & hook the game process, so this function will initially return false while the overlay is loading.
bool IsOverlayEnabled()
{
PRINT_DEBUG("IsOverlayEnabled\n");
//TODO
return false;
}
// Normally this call is unneeded if your game has a constantly running frame loop that calls the
// D3D Present API, or OGL SwapBuffers API every frame.
//
// However, if you have a game that only refreshes the screen on an event driven basis then that can break
// the overlay, as it uses your Present/SwapBuffers calls to drive it's internal frame loop and it may also
// need to Present() to the screen any time an even needing a notification happens or when the overlay is
// brought up over the game by a user. You can use this API to ask the overlay if it currently need a present
// in that case, and then you can check for this periodically (roughly 33hz is desirable) and make sure you
// refresh the screen with Present or SwapBuffers to allow the overlay to do it's work.
bool BOverlayNeedsPresent()
{
PRINT_DEBUG("BOverlayNeedsPresent\n");
return false;
}
// Asynchronous call to check if an executable file has been signed using the public key set on the signing tab
// of the partner site, for example to refuse to load modified executable files.
// The result is returned in CheckFileSignature_t.
// k_ECheckFileSignatureNoSignaturesFoundForThisApp - This app has not been configured on the signing tab of the partner site to enable this function.
// k_ECheckFileSignatureNoSignaturesFoundForThisFile - This file is not listed on the signing tab for the partner site.
// k_ECheckFileSignatureFileNotFound - The file does not exist on disk.
// k_ECheckFileSignatureInvalidSignature - The file exists, and the signing tab has been set for this file, but the file is either not signed or the signature does not match.
// k_ECheckFileSignatureValidSignature - The file is signed and the signature is valid.
STEAM_CALL_RESULT( CheckFileSignature_t )
SteamAPICall_t CheckFileSignature( const char *szFileName )
{
PRINT_DEBUG("CheckFileSignature\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
CheckFileSignature_t data;
data.m_eCheckFileSignature = k_ECheckFileSignatureValidSignature;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Activates the Big Picture text input dialog which only supports gamepad input
bool ShowGamepadTextInput( EGamepadTextInputMode eInputMode, EGamepadTextInputLineMode eLineInputMode, const char *pchDescription, uint32 unCharMax, const char *pchExistingText )
{
PRINT_DEBUG("ShowGamepadTextInput\n");
return false;
}
bool ShowGamepadTextInput( EGamepadTextInputMode eInputMode, EGamepadTextInputLineMode eLineInputMode, const char *pchDescription, uint32 unCharMax )
{
PRINT_DEBUG("ShowGamepadTextInput old\n");
return ShowGamepadTextInput(eInputMode, eLineInputMode, pchDescription, unCharMax, NULL);
}
// Returns previously entered text & length
uint32 GetEnteredGamepadTextLength()
{
PRINT_DEBUG("GetEnteredGamepadTextLength\n");
return 0;
}
bool GetEnteredGamepadTextInput( char *pchText, uint32 cchText )
{
PRINT_DEBUG("GetEnteredGamepadTextInput\n");
return false;
}
// returns the language the steam client is running in, you probably want ISteamApps::GetCurrentGameLanguage instead, this is for very special usage cases
const char *GetSteamUILanguage()
{
PRINT_DEBUG("GetSteamUILanguage\n");
return settings->get_language();
}
// returns true if Steam itself is running in VR mode
bool IsSteamRunningInVR()
{
PRINT_DEBUG("IsSteamRunningInVR\n");
return false;
}
// Sets the inset of the overlay notification from the corner specified by SetOverlayNotificationPosition.
void SetOverlayNotificationInset( int nHorizontalInset, int nVerticalInset )
{
PRINT_DEBUG("SetOverlayNotificationInset\n");
}
// returns true if Steam & the Steam Overlay are running in Big Picture mode
// Games much be launched through the Steam client to enable the Big Picture overlay. During development,
// a game can be added as a non-steam game to the developers library to test this feature
bool IsSteamInBigPictureMode()
{
PRINT_DEBUG("IsSteamInBigPictureMode\n");
return false;
}
// ask SteamUI to create and render its OpenVR dashboard
void StartVRDashboard()
{
PRINT_DEBUG("StartVRDashboard\n");
}
// Returns true if the HMD content will be streamed via Steam In-Home Streaming
bool IsVRHeadsetStreamingEnabled()
{
PRINT_DEBUG("IsVRHeadsetStreamingEnabled\n");
return false;
}
// Set whether the HMD content will be streamed via Steam In-Home Streaming
// If this is set to true, then the scene in the HMD headset will be streamed, and remote input will not be allowed.
// If this is set to false, then the application window will be streamed instead, and remote input will be allowed.
// The default is true unless "VRHeadsetStreaming" "0" is in the extended appinfo for a game.
// (this is useful for games that have asymmetric multiplayer gameplay)
void SetVRHeadsetStreamingEnabled( bool bEnabled )
{
PRINT_DEBUG("SetVRHeadsetStreamingEnabled\n");
}
};

46
dll/steam_video.cpp Normal file
View File

@ -0,0 +1,46 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "steam_video.h"
// Get a URL suitable for streaming the given Video app ID's video
void Steam_Video::GetVideoURL( AppId_t unVideoAppID )
{
PRINT_DEBUG("GetVideoURL\n");
}
// returns true if user is uploading a live broadcast
bool Steam_Video::IsBroadcasting( int *pnNumViewers )
{
PRINT_DEBUG("IsBroadcasting\n");
return false;
}
// Get the OPF Details for 360 Video Playback
STEAM_CALL_BACK( GetOPFSettingsResult_t )
void Steam_Video::GetOPFSettings( AppId_t unVideoAppID )
{
PRINT_DEBUG("GetOPFSettings\n");
}
bool Steam_Video::GetOPFStringForApp( AppId_t unVideoAppID, char *pchBuffer, int32 *pnBufferSize )
{
PRINT_DEBUG("GetOPFStringForApp\n");
return false;
}

34
dll/steam_video.h Normal file
View File

@ -0,0 +1,34 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Video : public ISteamVideo
{
public:
// Get a URL suitable for streaming the given Video app ID's video
void GetVideoURL( AppId_t unVideoAppID );
// returns true if user is uploading a live broadcast
bool IsBroadcasting( int *pnNumViewers );
// Get the OPF Details for 360 Video Playback
STEAM_CALL_BACK( GetOPFSettingsResult_t )
void GetOPFSettings( AppId_t unVideoAppID );
bool GetOPFStringForApp( AppId_t unVideoAppID, char *pchBuffer, int32 *pnBufferSize );
};

View File

@ -0,0 +1 @@
1337

View File

@ -0,0 +1,20 @@
SteamClient015
SteamGameServer012
SteamGameServerStats001
SteamUser017
SteamFriends014
SteamUtils007
SteamMatchMaking009
SteamMatchMakingServers002
STEAMUSERSTATS_INTERFACE_VERSION011
STEAMAPPS_INTERFACE_VERSION006
SteamNetworking005
STEAMREMOTESTORAGE_INTERFACE_VERSION012
STEAMSCREENSHOTS_INTERFACE_VERSION002
STEAMHTTP_INTERFACE_VERSION002
STEAMUNIFIEDMESSAGES_INTERFACE_VERSION001
STEAMCONTROLLER_INTERFACE_VERSION
STEAMUGC_INTERFACE_VERSION002
STEAMAPPLIST_INTERFACE_VERSION001
STEAMMUSIC_INTERFACE_VERSION001
STEAMMUSICREMOTE_INTERFACE_VERSION001

View File

@ -0,0 +1,3 @@
1234=DLCNAME
23454=This is another example DLC name
7869=It's the same format as other emus like the codex emu

View File

@ -0,0 +1 @@
This is an example mod. Delete this 12345 mod folder and put real mods in the mods directory.

110
lobby_connect.cpp Normal file
View File

@ -0,0 +1,110 @@
/*
This is the source code for the lobby_connect.exe program.
If you want to build it, just dl the steam sdk and link it to the steam api dll
Then to execute it just use my emu dll instead of the steam sdk dll.
If you make any improvements and want to share them post your code in the thread and if it improves it I'll use it.
You can also use this code to add this functionality to your launchers.
Note if you use my steam emu dll: When I build my lobby_connect.exe I static link it to a build of my emu that has reading from disk disabled.
This means that it's a good idea for you to use local_save.txt so that your launcher doesn't conflict with running
games by having the same steam user id. There must also be no steam_appid.txt or else you will only see games with that appid.
*/
#include "sdk_includes/steam_api.h"
#include "dll/defines.h"
#include <iostream>
#include <chrono>
#include <thread>
#include <string>
#include <vector>
#include <windows.h>
int main() {
if (SteamAPI_Init()) {
//Set appid to: LOBBY_CONNECT_APPID
SteamAPI_RestartAppIfNecessary(LOBBY_CONNECT_APPID);
std::cout << "This is a program to find lobbies and run the game with lobby connect parameters" << std::endl;
std::cout << "Api initialized, ";
top:
std::cout << "waiting a few seconds for connections:" << std::endl;
for (int i = 0; i < 10; ++i) {
SteamAPI_RunCallbacks();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
int friend_count = SteamFriends()->GetFriendCount(0);
std::cout << "People on the network: " << friend_count << std::endl;
for (int i = 0; i < friend_count; ++i) {
CSteamID id = SteamFriends()->GetFriendByIndex(i, 0);
const char *name = SteamFriends()->GetFriendPersonaName(id);
FriendGameInfo_t friend_info = {};
SteamFriends()->GetFriendGamePlayed(id, &friend_info);
std::cout << name << " is playing: " << friend_info.m_gameID.AppID() << std::endl;
}
std::cout << std::endl << "--------------Menu-------------" << std::endl << "\tappid\tname\tcommand line" << std::endl;
std::vector<std::string> arguments;
for (int i = 0; i < friend_count; ++i) {
CSteamID id = SteamFriends()->GetFriendByIndex(i, 0);
const char *name = SteamFriends()->GetFriendPersonaName(id);
const char *connect = SteamFriends()->GetFriendRichPresence( id, "connect");
FriendGameInfo_t friend_info = {};
SteamFriends()->GetFriendGamePlayed(id, &friend_info);
if (strlen(connect) > 0) {
std::cout << arguments.size() << "\t" << friend_info.m_gameID.AppID() << "\t" << name << "\t" << connect << std::endl;
arguments.push_back(connect);
} else {
if (friend_info.m_steamIDLobby != k_steamIDNil) {
std::string connect = "+connect_lobby " + std::to_string(friend_info.m_steamIDLobby.ConvertToUint64());
std::cout << arguments.size() << "\t" << friend_info.m_gameID.AppID() << "\t" << name << "\t" << connect << std::endl;
arguments.push_back(connect);
}
}
}
std::cout << arguments.size() << ": Retry." << std::endl;
std::cout << std::endl << "Enter the number corresponding to your choice then press Enter." << std::endl;
unsigned int choice;
std::cin >> choice;
if (choice >= arguments.size()) goto top;
std::cout << "starting the game with: " << arguments[choice] << std::endl << "Please select the game exe" << std::endl;
OPENFILENAMEA ofn;
char szFileName[MAX_PATH] = "";
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = 0;
ofn.lpstrFilter = "Exe Files (*.exe)\0*.exe\0All Files (*.*)\0*.*\0";
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrDefExt = "txt";
if(GetOpenFileNameA(&ofn))
{
std::string filename = szFileName;
filename = "\"" + filename + "\" " + arguments[choice];
std::cout << filename << std::endl;
STARTUPINFOA lpStartupInfo;
PROCESS_INFORMATION lpProcessInfo;
ZeroMemory( &lpStartupInfo, sizeof( lpStartupInfo ) );
lpStartupInfo.cb = sizeof( lpStartupInfo );
ZeroMemory( &lpProcessInfo, sizeof( lpProcessInfo ) );
CreateProcessA( NULL,
const_cast<char *>(filename.c_str()), NULL, NULL,
NULL, NULL, NULL, NULL,
&lpStartupInfo,
&lpProcessInfo
);
}
}
}

View File

@ -0,0 +1,68 @@
//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. =======
//
// Purpose: interface to app data in Steam
//
//=============================================================================
#ifndef ISTEAMAPPLIST_H
#define ISTEAMAPPLIST_H
#ifdef STEAM_WIN32
#pragma once
#endif
#include "steam_api_common.h"
#include "steamtypes.h"
//-----------------------------------------------------------------------------
// Purpose: This is a restricted interface that can only be used by previously approved apps,
// contact your Steam Account Manager if you believe you need access to this API.
// This interface lets you detect installed apps for the local Steam client, useful for debugging tools
// to offer lists of apps to debug via Steam.
//-----------------------------------------------------------------------------
class ISteamAppList
{
public:
virtual uint32 GetNumInstalledApps() = 0;
virtual uint32 GetInstalledApps( AppId_t *pvecAppID, uint32 unMaxAppIDs ) = 0;
virtual int GetAppName( AppId_t nAppID, STEAM_OUT_STRING() char *pchName, int cchNameMax ) = 0; // returns -1 if no name was found
virtual int GetAppInstallDir( AppId_t nAppID, char *pchDirectory, int cchNameMax ) = 0; // returns -1 if no dir was found
virtual int GetAppBuildId( AppId_t nAppID ) = 0; // return the buildid of this app, may change at any time based on backend updates to the game
};
#define STEAMAPPLIST_INTERFACE_VERSION "STEAMAPPLIST_INTERFACE_VERSION001"
#ifndef STEAM_API_EXPORTS
// Global interface accessor
inline ISteamAppList *SteamAppList();
STEAM_DEFINE_USER_INTERFACE_ACCESSOR( ISteamAppList *, SteamAppList, STEAMAPPLIST_INTERFACE_VERSION );
#endif
// callbacks
#if defined( VALVE_CALLBACK_PACK_SMALL )
#pragma pack( push, 4 )
#elif defined( VALVE_CALLBACK_PACK_LARGE )
#pragma pack( push, 8 )
#else
#error steam_api_common.h should define VALVE_CALLBACK_PACK_xxx
#endif
//---------------------------------------------------------------------------------
// Purpose: Sent when a new app is installed
//---------------------------------------------------------------------------------
STEAM_CALLBACK_BEGIN( SteamAppInstalled_t, k_iSteamAppListCallbacks + 1 );
STEAM_CALLBACK_MEMBER( 0, AppId_t, m_nAppID ) // ID of the app that installs
STEAM_CALLBACK_END(1)
//---------------------------------------------------------------------------------
// Purpose: Sent when an app is uninstalled
//---------------------------------------------------------------------------------
STEAM_CALLBACK_BEGIN( SteamAppUninstalled_t, k_iSteamAppListCallbacks + 2 );
STEAM_CALLBACK_MEMBER( 0, AppId_t, m_nAppID ) // ID of the app that installs
STEAM_CALLBACK_END(1)
#pragma pack( pop )
#endif // ISTEAMAPPLIST_H

204
sdk_includes/isteamapps.h Normal file
View File

@ -0,0 +1,204 @@
//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. =======
//
// Purpose: interface to app data in Steam
//
//=============================================================================
#ifndef ISTEAMAPPS_H
#define ISTEAMAPPS_H
#ifdef STEAM_WIN32
#pragma once
#endif
#include "steam_api_common.h"
const int k_cubAppProofOfPurchaseKeyMax = 240; // max supported length of a legacy cd key
//-----------------------------------------------------------------------------
// Purpose: interface to app data
//-----------------------------------------------------------------------------
class ISteamApps
{
public:
virtual bool BIsSubscribed() = 0;
virtual bool BIsLowViolence() = 0;
virtual bool BIsCybercafe() = 0;
virtual bool BIsVACBanned() = 0;
virtual const char *GetCurrentGameLanguage() = 0;
virtual const char *GetAvailableGameLanguages() = 0;
// only use this member if you need to check ownership of another game related to yours, a demo for example
virtual bool BIsSubscribedApp( AppId_t appID ) = 0;
// Takes AppID of DLC and checks if the user owns the DLC & if the DLC is installed
virtual bool BIsDlcInstalled( AppId_t appID ) = 0;
// returns the Unix time of the purchase of the app
virtual uint32 GetEarliestPurchaseUnixTime( AppId_t nAppID ) = 0;
// Checks if the user is subscribed to the current app through a free weekend
// This function will return false for users who have a retail or other type of license
// Before using, please ask your Valve technical contact how to package and secure your free weekened
virtual bool BIsSubscribedFromFreeWeekend() = 0;
// Returns the number of DLC pieces for the running app
virtual int GetDLCCount() = 0;
// Returns metadata for DLC by index, of range [0, GetDLCCount()]
virtual bool BGetDLCDataByIndex( int iDLC, AppId_t *pAppID, bool *pbAvailable, char *pchName, int cchNameBufferSize ) = 0;
// Install/Uninstall control for optional DLC
virtual void InstallDLC( AppId_t nAppID ) = 0;
virtual void UninstallDLC( AppId_t nAppID ) = 0;
// Request legacy cd-key for yourself or owned DLC. If you are interested in this
// data then make sure you provide us with a list of valid keys to be distributed
// to users when they purchase the game, before the game ships.
// You'll receive an AppProofOfPurchaseKeyResponse_t callback when
// the key is available (which may be immediately).
virtual void RequestAppProofOfPurchaseKey( AppId_t nAppID ) = 0;
virtual bool GetCurrentBetaName( char *pchName, int cchNameBufferSize ) = 0; // returns current beta branch name, 'public' is the default branch
virtual bool MarkContentCorrupt( bool bMissingFilesOnly ) = 0; // signal Steam that game files seems corrupt or missing
virtual uint32 GetInstalledDepots( AppId_t appID, DepotId_t *pvecDepots, uint32 cMaxDepots ) = 0; // return installed depots in mount order
// returns current app install folder for AppID, returns folder name length
virtual uint32 GetAppInstallDir( AppId_t appID, char *pchFolder, uint32 cchFolderBufferSize ) = 0;
virtual bool BIsAppInstalled( AppId_t appID ) = 0; // returns true if that app is installed (not necessarily owned)
// returns the SteamID of the original owner. If this CSteamID is different from ISteamUser::GetSteamID(),
// the user has a temporary license borrowed via Family Sharing
virtual CSteamID GetAppOwner() = 0;
// Returns the associated launch param if the game is run via steam://run/<appid>//?param1=value1&param2=value2&param3=value3 etc.
// Parameter names starting with the character '@' are reserved for internal use and will always return and empty string.
// Parameter names starting with an underscore '_' are reserved for steam features -- they can be queried by the game,
// but it is advised that you not param names beginning with an underscore for your own features.
// Check for new launch parameters on callback NewUrlLaunchParameters_t
virtual const char *GetLaunchQueryParam( const char *pchKey ) = 0;
// get download progress for optional DLC
virtual bool GetDlcDownloadProgress( AppId_t nAppID, uint64 *punBytesDownloaded, uint64 *punBytesTotal ) = 0;
// return the buildid of this app, may change at any time based on backend updates to the game
virtual int GetAppBuildId() = 0;
// Request all proof of purchase keys for the calling appid and asociated DLC.
// A series of AppProofOfPurchaseKeyResponse_t callbacks will be sent with
// appropriate appid values, ending with a final callback where the m_nAppId
// member is k_uAppIdInvalid (zero).
virtual void RequestAllProofOfPurchaseKeys() = 0;
STEAM_CALL_RESULT( FileDetailsResult_t )
virtual SteamAPICall_t GetFileDetails( const char* pszFileName ) = 0;
// Get command line if game was launched via Steam URL, e.g. steam://run/<appid>//<command line>/.
// This method of passing a connect string (used when joining via rich presence, accepting an
// invite, etc) is preferable to passing the connect string on the operating system command
// line, which is a security risk. In order for rich presence joins to go through this
// path and not be placed on the OS command line, you must set a value in your app's
// configuration on Steam. Ask Valve for help with this.
//
// If game was already running and launched again, the NewUrlLaunchParameters_t will be fired.
virtual int GetLaunchCommandLine( char *pszCommandLine, int cubCommandLine ) = 0;
// Check if user borrowed this game via Family Sharing, If true, call GetAppOwner() to get the lender SteamID
virtual bool BIsSubscribedFromFamilySharing() = 0;
};
#define STEAMAPPS_INTERFACE_VERSION "STEAMAPPS_INTERFACE_VERSION008"
#ifndef STEAM_API_EXPORTS
// Global interface accessor
inline ISteamApps *SteamApps();
STEAM_DEFINE_USER_INTERFACE_ACCESSOR( ISteamApps *, SteamApps, STEAMAPPS_INTERFACE_VERSION );
// Global accessor for the gameserver client
inline ISteamApps *SteamGameServerApps();
STEAM_DEFINE_GAMESERVER_INTERFACE_ACCESSOR( ISteamApps *, SteamGameServerApps, STEAMAPPS_INTERFACE_VERSION );
#endif
// callbacks
#if defined( VALVE_CALLBACK_PACK_SMALL )
#pragma pack( push, 4 )
#elif defined( VALVE_CALLBACK_PACK_LARGE )
#pragma pack( push, 8 )
#else
#error steam_api_common.h should define VALVE_CALLBACK_PACK_xxx
#endif
//-----------------------------------------------------------------------------
// Purpose: posted after the user gains ownership of DLC & that DLC is installed
//-----------------------------------------------------------------------------
struct DlcInstalled_t
{
enum { k_iCallback = k_iSteamAppsCallbacks + 5 };
AppId_t m_nAppID; // AppID of the DLC
};
//-----------------------------------------------------------------------------
// Purpose: possible results when registering an activation code
//-----------------------------------------------------------------------------
enum ERegisterActivationCodeResult
{
k_ERegisterActivationCodeResultOK = 0,
k_ERegisterActivationCodeResultFail = 1,
k_ERegisterActivationCodeResultAlreadyRegistered = 2,
k_ERegisterActivationCodeResultTimeout = 3,
k_ERegisterActivationCodeAlreadyOwned = 4,
};
//-----------------------------------------------------------------------------
// Purpose: response to RegisterActivationCode()
//-----------------------------------------------------------------------------
struct RegisterActivationCodeResponse_t
{
enum { k_iCallback = k_iSteamAppsCallbacks + 8 };
ERegisterActivationCodeResult m_eResult;
uint32 m_unPackageRegistered; // package that was registered. Only set on success
};
//---------------------------------------------------------------------------------
// Purpose: posted after the user gains executes a Steam URL with command line or query parameters
// such as steam://run/<appid>//-commandline/?param1=value1&param2=value2&param3=value3 etc
// while the game is already running. The new params can be queried
// with GetLaunchQueryParam and GetLaunchCommandLine
//---------------------------------------------------------------------------------
struct NewUrlLaunchParameters_t
{
enum { k_iCallback = k_iSteamAppsCallbacks + 14 };
};
//-----------------------------------------------------------------------------
// Purpose: response to RequestAppProofOfPurchaseKey/RequestAllProofOfPurchaseKeys
// for supporting third-party CD keys, or other proof-of-purchase systems.
//-----------------------------------------------------------------------------
struct AppProofOfPurchaseKeyResponse_t
{
enum { k_iCallback = k_iSteamAppsCallbacks + 21 };
EResult m_eResult;
uint32 m_nAppID;
uint32 m_cchKeyLength;
char m_rgchKey[k_cubAppProofOfPurchaseKeyMax];
};
//-----------------------------------------------------------------------------
// Purpose: response to GetFileDetails
//-----------------------------------------------------------------------------
struct FileDetailsResult_t
{
enum { k_iCallback = k_iSteamAppsCallbacks + 23 };
EResult m_eResult;
uint64 m_ulFileSize; // original file size in bytes
uint8 m_FileSHA[20]; // original file SHA1 hash
uint32 m_unFlags; //
};
#pragma pack( pop )
#endif // ISTEAMAPPS_H

View File

@ -0,0 +1,28 @@
//====== Copyright 1996-2008, Valve Corporation, All rights reserved. =======
//
// Purpose: a private, but well versioned, interface to get at critical bits
// of a steam3 appticket - consumed by the simple drm wrapper to let it
// ask about ownership with greater confidence.
//
//=============================================================================
#ifndef ISTEAMAPPTICKET_H
#define ISTEAMAPPTICKET_H
#pragma once
//-----------------------------------------------------------------------------
// Purpose: hand out a reasonable "future proof" view of an app ownership ticket
// the raw (signed) buffer, and indices into that buffer where the appid and
// steamid are located. the sizes of the appid and steamid are implicit in
// (each version of) the interface - currently uin32 appid and uint64 steamid
//-----------------------------------------------------------------------------
class ISteamAppTicket
{
public:
virtual uint32 GetAppOwnershipTicketData( uint32 nAppID, void *pvBuffer, uint32 cbBufferLength, uint32 *piAppId, uint32 *piSteamId, uint32 *piSignature, uint32 *pcbSignature ) = 0;
};
#define STEAMAPPTICKET_INTERFACE_VERSION "STEAMAPPTICKET_INTERFACE_VERSION001"
#endif // ISTEAMAPPTICKET_H

174
sdk_includes/isteamclient.h Normal file
View File

@ -0,0 +1,174 @@
//====== Copyright Valve Corporation, All rights reserved. ====================
//
// Internal low-level access to Steamworks interfaces.
//
// Most users of the Steamworks SDK do not need to include this file.
// You should only include this if you are doing something special.
//=============================================================================
#ifndef ISTEAMCLIENT_H
#define ISTEAMCLIENT_H
#ifdef STEAM_WIN32
#pragma once
#endif
#include "steam_api_common.h"
//-----------------------------------------------------------------------------
// Purpose: Interface to creating a new steam instance, or to
// connect to an existing steam instance, whether it's in a
// different process or is local.
//
// For most scenarios this is all handled automatically via SteamAPI_Init().
// You'll only need these APIs if you have a more complex versioning scheme,
// or if you want to implement a multiplexed gameserver where a single process
// is handling multiple games at once with independent gameserver SteamIDs.
//-----------------------------------------------------------------------------
class ISteamClient
{
public:
// Creates a communication pipe to the Steam client.
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
virtual HSteamPipe CreateSteamPipe() = 0;
// Releases a previously created communications pipe
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
virtual bool BReleaseSteamPipe( HSteamPipe hSteamPipe ) = 0;
// connects to an existing global user, failing if none exists
// used by the game to coordinate with the steamUI
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
virtual HSteamUser ConnectToGlobalUser( HSteamPipe hSteamPipe ) = 0;
// used by game servers, create a steam user that won't be shared with anyone else
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
virtual HSteamUser CreateLocalUser( HSteamPipe *phSteamPipe, EAccountType eAccountType ) = 0;
// removes an allocated user
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
virtual void ReleaseUser( HSteamPipe hSteamPipe, HSteamUser hUser ) = 0;
// retrieves the ISteamUser interface associated with the handle
virtual ISteamUser *GetISteamUser( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// retrieves the ISteamGameServer interface associated with the handle
virtual ISteamGameServer *GetISteamGameServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// set the local IP and Port to bind to
// this must be set before CreateLocalUser()
virtual void SetLocalIPBinding( uint32 unIP, uint16 usPort ) = 0;
// returns the ISteamFriends interface
virtual ISteamFriends *GetISteamFriends( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamUtils interface
virtual ISteamUtils *GetISteamUtils( HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamMatchmaking interface
virtual ISteamMatchmaking *GetISteamMatchmaking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamMatchmakingServers interface
virtual ISteamMatchmakingServers *GetISteamMatchmakingServers( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the a generic interface
virtual void *GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamUserStats interface
virtual ISteamUserStats *GetISteamUserStats( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamGameServerStats interface
virtual ISteamGameServerStats *GetISteamGameServerStats( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns apps interface
virtual ISteamApps *GetISteamApps( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// networking
virtual ISteamNetworking *GetISteamNetworking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// remote storage
virtual ISteamRemoteStorage *GetISteamRemoteStorage( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// user screenshots
virtual ISteamScreenshots *GetISteamScreenshots( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// game search
virtual ISteamGameSearch *GetISteamGameSearch( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// Deprecated. Applications should use SteamAPI_RunCallbacks() or SteamGameServer_RunCallbacks() instead.
STEAM_PRIVATE_API( virtual void RunFrame() = 0; )
// returns the number of IPC calls made since the last time this function was called
// Used for perf debugging so you can understand how many IPC calls your game makes per frame
// Every IPC call is at minimum a thread context switch if not a process one so you want to rate
// control how often you do them.
virtual uint32 GetIPCCallCount() = 0;
// API warning handling
// 'int' is the severity; 0 for msg, 1 for warning
// 'const char *' is the text of the message
// callbacks will occur directly after the API function is called that generated the warning or message.
virtual void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction ) = 0;
// Trigger global shutdown for the DLL
virtual bool BShutdownIfAllPipesClosed() = 0;
// Expose HTTP interface
virtual ISteamHTTP *GetISteamHTTP( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// Deprecated - the ISteamUnifiedMessages interface is no longer intended for public consumption.
STEAM_PRIVATE_API( virtual void *DEPRECATED_GetISteamUnifiedMessages( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0 ; )
// Exposes the ISteamController interface - deprecated in favor of Steam Input
virtual ISteamController *GetISteamController( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// Exposes the ISteamUGC interface
virtual ISteamUGC *GetISteamUGC( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns app list interface, only available on specially registered apps
virtual ISteamAppList *GetISteamAppList( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// Music Player
virtual ISteamMusic *GetISteamMusic( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// Music Player Remote
virtual ISteamMusicRemote *GetISteamMusicRemote(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion) = 0;
// html page display
virtual ISteamHTMLSurface *GetISteamHTMLSurface(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion) = 0;
// Helper functions for internal Steam usage
STEAM_PRIVATE_API( virtual void DEPRECATED_Set_SteamAPI_CPostAPIResultInProcess( void (*)() ) = 0; )
STEAM_PRIVATE_API( virtual void DEPRECATED_Remove_SteamAPI_CPostAPIResultInProcess( void (*)() ) = 0; )
STEAM_PRIVATE_API( virtual void Set_SteamAPI_CCheckCallbackRegisteredInProcess( SteamAPI_CheckCallbackRegistered_t func ) = 0; )
// inventory
virtual ISteamInventory *GetISteamInventory( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// Video
virtual ISteamVideo *GetISteamVideo( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// Parental controls
virtual ISteamParentalSettings *GetISteamParentalSettings( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// Exposes the Steam Input interface for controller support
virtual ISteamInput *GetISteamInput( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// Steam Parties interface
virtual ISteamParties *GetISteamParties( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
};
#define STEAMCLIENT_INTERFACE_VERSION "SteamClient018"
#ifndef STEAM_API_EXPORTS
// Global ISteamClient interface accessor
inline ISteamClient *SteamClient();
STEAM_DEFINE_INTERFACE_ACCESSOR( ISteamClient *, SteamClient, SteamInternal_CreateInterface( STEAMCLIENT_INTERFACE_VERSION ) );
// The internal ISteamClient used for the gameserver interface.
// (This is actually the same thing. You really shouldn't need to access any of this stuff directly.)
inline ISteamClient *SteamGameServerClient() { return SteamClient(); }
#endif
#endif // ISTEAMCLIENT_H

87
sdk_includes/isteamclient007.h Executable file
View File

@ -0,0 +1,87 @@
#ifndef ISTEAMCLIENT007_H
#define ISTEAMCLIENT007_H
#ifdef STEAM_WIN32
#pragma once
#endif
#include "isteammasterserverupdater.h"
class ISteamClient007
{
public:
// Creates a communication pipe to the Steam client
virtual HSteamPipe CreateSteamPipe() = 0;
// Releases a previously created communications pipe
virtual bool BReleaseSteamPipe( HSteamPipe hSteamPipe ) = 0;
// connects to an existing global user, failing if none exists
// used by the game to coordinate with the steamUI
virtual HSteamUser ConnectToGlobalUser( HSteamPipe hSteamPipe ) = 0;
// used by game servers, create a steam user that won't be shared with anyone else
virtual HSteamUser CreateLocalUser( HSteamPipe *phSteamPipe ) = 0;
// removes an allocated user
virtual void ReleaseUser( HSteamPipe hSteamPipe, HSteamUser hUser ) = 0;
// retrieves the ISteamUser interface associated with the handle
virtual ISteamUser *GetISteamUser( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// retrieves the ISteamGameServer interface associated with the handle
virtual ISteamGameServer *GetISteamGameServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// set the local IP and Port to bind to
// this must be set before CreateLocalUser()
virtual void SetLocalIPBinding( uint32 unIP, uint16 usPort ) = 0;
// returns the ISteamFriends interface
virtual ISteamFriends *GetISteamFriends( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamUtils interface
virtual ISteamUtils *GetISteamUtils( HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamMatchmaking interface
virtual ISteamMatchmaking *GetISteamMatchmaking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamContentServer interface
virtual ISteamContentServer *GetISteamContentServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamMasterServerUpdater interface
virtual ISteamMasterServerUpdater *GetISteamMasterServerUpdater( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamMatchmakingServers interface
virtual ISteamMatchmakingServers *GetISteamMatchmakingServers( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the a generic interface
virtual void *GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// this needs to be called every frame to process matchmaking results
// redundant if you're already calling SteamAPI_RunCallbacks()
virtual void RunFrame() = 0;
// returns the number of IPC calls made since the last time this function was called
// Used for perf debugging so you can understand how many IPC calls your game makes per frame
// Every IPC call is at minimum a thread context switch if not a process one so you want to rate
// control how often you do them.
virtual uint32 GetIPCCallCount() = 0;
// returns the ISteamUserStats interface
virtual ISteamUserStats *GetISteamUserStats( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns apps interface
virtual ISteamApps *GetISteamApps( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// networking
virtual ISteamNetworking *GetISteamNetworking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// API warning handling
// 'int' is the severity; 0 for msg, 1 for warning
// 'const char *' is the text of the message
// callbacks will occur directly after the API function is called that generated the warning or message
virtual void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction ) = 0;
};
#endif // ISTEAMCLIENT007_H

85
sdk_includes/isteamclient008.h Executable file
View File

@ -0,0 +1,85 @@
#ifndef ISTEAMCLIENT008_H
#define ISTEAMCLIENT008_H
#ifdef STEAM_WIN32
#pragma once
#endif
class ISteamClient008
{
public:
// Creates a communication pipe to the Steam client
virtual HSteamPipe CreateSteamPipe() = 0;
// Releases a previously created communications pipe
virtual bool BReleaseSteamPipe( HSteamPipe hSteamPipe ) = 0;
// connects to an existing global user, failing if none exists
// used by the game to coordinate with the steamUI
virtual HSteamUser ConnectToGlobalUser( HSteamPipe hSteamPipe ) = 0;
// used by game servers, create a steam user that won't be shared with anyone else
virtual HSteamUser CreateLocalUser( HSteamPipe *phSteamPipe, EAccountType eAccountType ) = 0;
// removes an allocated user
virtual void ReleaseUser( HSteamPipe hSteamPipe, HSteamUser hUser ) = 0;
// retrieves the ISteamUser interface associated with the handle
virtual ISteamUser *GetISteamUser( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// retrieves the ISteamGameServer interface associated with the handle
virtual ISteamGameServer *GetISteamGameServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// set the local IP and Port to bind to
// this must be set before CreateLocalUser()
virtual void SetLocalIPBinding( uint32 unIP, uint16 usPort ) = 0;
// returns the ISteamFriends interface
virtual ISteamFriends *GetISteamFriends( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamUtils interface
virtual ISteamUtils *GetISteamUtils( HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamMatchmaking interface
virtual ISteamMatchmaking *GetISteamMatchmaking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamMasterServerUpdater interface
virtual ISteamMasterServerUpdater *GetISteamMasterServerUpdater( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamMatchmakingServers interface
virtual ISteamMatchmakingServers *GetISteamMatchmakingServers( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the a generic interface
virtual void *GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamUserStats interface
virtual ISteamUserStats *GetISteamUserStats( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns apps interface
virtual ISteamApps *GetISteamApps( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// networking
virtual ISteamNetworking *GetISteamNetworking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// remote storage
virtual ISteamRemoteStorage *GetISteamRemoteStorage( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// this needs to be called every frame to process matchmaking results
// redundant if you're already calling SteamAPI_RunCallbacks()
virtual void RunFrame() = 0;
// returns the number of IPC calls made since the last time this function was called
// Used for perf debugging so you can understand how many IPC calls your game makes per frame
// Every IPC call is at minimum a thread context switch if not a process one so you want to rate
// control how often you do them.
virtual uint32 GetIPCCallCount() = 0;
// API warning handling
// 'int' is the severity; 0 for msg, 1 for warning
// 'const char *' is the text of the message
// callbacks will occur directly after the API function is called that generated the warning or message
virtual void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction ) = 0;
};
#endif // ISTEAMCLIENT008_H

88
sdk_includes/isteamclient009.h Executable file
View File

@ -0,0 +1,88 @@
#ifndef ISTEAMCLIENT009_H
#define ISTEAMCLIENT009_H
#ifdef STEAM_WIN32
#pragma once
#endif
class ISteamClient009
{
public:
// Creates a communication pipe to the Steam client
virtual HSteamPipe CreateSteamPipe() = 0;
// Releases a previously created communications pipe
virtual bool BReleaseSteamPipe( HSteamPipe hSteamPipe ) = 0;
// connects to an existing global user, failing if none exists
// used by the game to coordinate with the steamUI
virtual HSteamUser ConnectToGlobalUser( HSteamPipe hSteamPipe ) = 0;
// used by game servers, create a steam user that won't be shared with anyone else
virtual HSteamUser CreateLocalUser( HSteamPipe *phSteamPipe, EAccountType eAccountType ) = 0;
// removes an allocated user
virtual void ReleaseUser( HSteamPipe hSteamPipe, HSteamUser hUser ) = 0;
// retrieves the ISteamUser interface associated with the handle
virtual ISteamUser *GetISteamUser( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// retrieves the ISteamGameServer interface associated with the handle
virtual ISteamGameServer *GetISteamGameServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// set the local IP and Port to bind to
// this must be set before CreateLocalUser()
virtual void SetLocalIPBinding( uint32 unIP, uint16 usPort ) = 0;
// returns the ISteamFriends interface
virtual ISteamFriends *GetISteamFriends( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamUtils interface
virtual ISteamUtils *GetISteamUtils( HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamMatchmaking interface
virtual ISteamMatchmaking *GetISteamMatchmaking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamMasterServerUpdater interface
virtual ISteamMasterServerUpdater *GetISteamMasterServerUpdater( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamMatchmakingServers interface
virtual ISteamMatchmakingServers *GetISteamMatchmakingServers( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the a generic interface
virtual void *GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamUserStats interface
virtual ISteamUserStats *GetISteamUserStats( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns the ISteamGameServerStats interface
virtual ISteamGameServerStats *GetISteamGameServerStats( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// returns apps interface
virtual ISteamApps *GetISteamApps( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// networking
virtual ISteamNetworking *GetISteamNetworking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// remote storage
virtual ISteamRemoteStorage *GetISteamRemoteStorage( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0;
// this needs to be called every frame to process matchmaking results
// redundant if you're already calling SteamAPI_RunCallbacks()
virtual void RunFrame() = 0;
// returns the number of IPC calls made since the last time this function was called
// Used for perf debugging so you can understand how many IPC calls your game makes per frame
// Every IPC call is at minimum a thread context switch if not a process one so you want to rate
// control how often you do them.
virtual uint32 GetIPCCallCount() = 0;
// API warning handling
// 'int' is the severity; 0 for msg, 1 for warning
// 'const char *' is the text of the message
// callbacks will occur directly after the API function is called that generated the warning or message
virtual void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction ) = 0;
};
#endif // ISTEAMCLIENT009_H

Some files were not shown because too many files have changed in this diff Show More