├── .gitignore
├── DuckGame.Mod.mm.sln
├── DuckGame.Mod.mm
├── DuckGame.Mod.mm.csproj
├── Properties
│ └── AssemblyInfo.cs
└── src
│ ├── patch_DuckFile.cs
│ ├── patch_ModLoader.cs
│ └── patch_Program.cs
├── LICENSE
├── README.md
├── Steam
├── Properties
│ └── AssemblyInfo.cs
├── README.md
├── Steam.csproj
└── src
│ ├── FriendRelationship.cs
│ ├── ItemUpdateStatus.cs
│ ├── Lobby.cs
│ ├── Lock.cs
│ ├── OfflineSelfUser.cs
│ ├── P2PDataSendType.cs
│ ├── RemoteStoragePublishedFileVisibility.cs
│ ├── SharpSteam.cs
│ ├── Steam.cs
│ ├── SteamLobbyComparison.cs
│ ├── SteamLobbyJoinResult.cs
│ ├── SteamLobbyType.cs
│ ├── SteamLobbyUserStatusFlags.cs
│ ├── SteamPacket.cs
│ ├── SteamResult.cs
│ ├── SteamUserState.cs
│ ├── TransferProgress.cs
│ ├── User.cs
│ ├── UserInfo.cs
│ ├── WorkshopItem.cs
│ ├── WorkshopItemData.cs
│ ├── WorkshopItemState.cs
│ ├── WorkshopList.cs
│ ├── WorkshopQueryAll.cs
│ ├── WorkshopQueryBase.cs
│ ├── WorkshopQueryData.cs
│ ├── WorkshopQueryFileDetails.cs
│ ├── WorkshopQueryFilterOrder.cs
│ ├── WorkshopQueryResult.cs
│ ├── WorkshopQueryResultAdditionalPreview.cs
│ ├── WorkshopQueryResultDetails.cs
│ ├── WorkshopQueryUGC.cs
│ ├── WorkshopQueryUser.cs
│ ├── WorkshopResultStatistic.cs
│ ├── WorkshopSortOrder.cs
│ ├── WorkshopType.cs
│ └── _.cs
├── Steamworks.NET
├── CSteamworks.bundle
│ └── Contents
│ │ ├── Info.plist
│ │ └── MacOS
│ │ ├── CSteamworks
│ │ └── libsteam_api.dylib
├── CSteamworks.dll
├── Steamworks.NET-linux.dll
├── Steamworks.NET-stubbed.dll
├── Steamworks.NET.dll
├── libCSteamworks.so
├── libsteam_api.so
└── steam_api64.dll
└── libs
├── DuckGame.exe
├── FNA.dll
├── README.md
└── XnaToFna.exe
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | # [Xx]64/
19 | # [Xx]86/
20 | [Bb]uild/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 |
85 | # Visual Studio profiler
86 | *.psess
87 | *.vsp
88 | *.vspx
89 | *.sap
90 |
91 | # TFS 2012 Local Workspace
92 | $tf/
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 | *.DotSettings.user
101 |
102 | # JustCode is a .NET coding add-in
103 | .JustCode
104 |
105 | # TeamCity is a build add-in
106 | _TeamCity*
107 |
108 | # DotCover is a Code Coverage Tool
109 | *.dotCover
110 |
111 | # NCrunch
112 | _NCrunch_*
113 | .*crunch*.local.xml
114 | nCrunchTemp_*
115 |
116 | # MightyMoose
117 | #*.mm.*
118 | AutoTest.Net/
119 |
120 | # Web workbench (sass)
121 | .sass-cache/
122 |
123 | # Installshield output folder
124 | [Ee]xpress/
125 |
126 | # DocProject is a documentation generator add-in
127 | DocProject/buildhelp/
128 | DocProject/Help/*.HxT
129 | DocProject/Help/*.HxC
130 | DocProject/Help/*.hhc
131 | DocProject/Help/*.hhk
132 | DocProject/Help/*.hhp
133 | DocProject/Help/Html2
134 | DocProject/Help/html
135 |
136 | # Click-Once directory
137 | publish/
138 |
139 | # Publish Web Output
140 | *.[Pp]ublish.xml
141 | *.azurePubxml
142 |
143 | # TODO: Un-comment the next line if you do not want to checkin
144 | # your web deploy settings because they may include unencrypted
145 | # passwords
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # NuGet Packages
150 | *.nupkg
151 | # The packages folder can be ignored because of Package Restore
152 | **/packages/*
153 | # except build/, which is used as an MSBuild target.
154 | !**/packages/build/
155 | # Uncomment if necessary however generally it will be regenerated when needed
156 | #!**/packages/repositories.config
157 | # NuGet v3's project.json files produces more ignoreable files
158 | *.nuget.props
159 | *.nuget.targets
160 |
161 | # Microsoft Azure Build Output
162 | csx/
163 | *.build.csdef
164 |
165 | # Microsoft Azure Emulator
166 | ecf/
167 | rcf/
168 |
169 | # Windows Store app package directory
170 | AppPackages/
171 | BundleArtifacts/
172 |
173 | # Visual Studio cache files
174 | # files ending in .cache can be ignored
175 | *.[Cc]ache
176 | # but keep track of directories ending in .cache
177 | !*.[Cc]ache/
178 |
179 | # Others
180 | ClientBin/
181 | [Ss]tyle[Cc]op.*
182 | ~$*
183 | *~
184 | *.dbmdl
185 | *.dbproj.schemaview
186 | *.pfx
187 | *.publishsettings
188 | node_modules/
189 | orleans.codegen.cs
190 |
191 | # RIA/Silverlight projects
192 | Generated_Code/
193 |
194 | # Backup & report files from converting an old project file
195 | # to a newer Visual Studio version. Backup files are not needed,
196 | # because we have git ;-)
197 | _UpgradeReport_Files/
198 | Backup*/
199 | UpgradeLog*.XML
200 | UpgradeLog*.htm
201 |
202 | # SQL Server files
203 | *.mdf
204 | *.ldf
205 |
206 | # Business Intelligence projects
207 | *.rdl.data
208 | *.bim.layout
209 | *.bim_*.settings
210 |
211 | # Microsoft Fakes
212 | FakesAssemblies/
213 |
214 | # GhostDoc plugin setting file
215 | *.GhostDoc.xml
216 |
217 | # Node.js Tools for Visual Studio
218 | .ntvs_analysis.dat
219 |
220 | # Visual Studio 6 build log
221 | *.plg
222 |
223 | # Visual Studio 6 workspace options file
224 | *.opt
225 |
226 | # Visual Studio LightSwitch build output
227 | **/*.HTMLClient/GeneratedArtifacts
228 | **/*.DesktopClient/GeneratedArtifacts
229 | **/*.DesktopClient/ModelManifest.xml
230 | **/*.Server/GeneratedArtifacts
231 | **/*.Server/ModelManifest.xml
232 | _Pvt_Extensions
233 |
234 | # LightSwitch generated files
235 | GeneratedArtifacts/
236 | ModelManifest.xml
237 |
238 | # Paket dependency manager
239 | .paket/paket.exe
240 |
241 | # FAKE - F# Make
242 | .fake/
243 |
--------------------------------------------------------------------------------
/DuckGame.Mod.mm.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DuckGame.Mod.mm", "DuckGame.Mod.mm\DuckGame.Mod.mm.csproj", "{73E9C6D2-D61F-4956-81BC-A8CA9A292AB8}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Steam", "Steam\Steam.csproj", "{D63AD3FC-DCF0-4DB6-B944-5D828A10CCB8}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {73E9C6D2-D61F-4956-81BC-A8CA9A292AB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {73E9C6D2-D61F-4956-81BC-A8CA9A292AB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {73E9C6D2-D61F-4956-81BC-A8CA9A292AB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {73E9C6D2-D61F-4956-81BC-A8CA9A292AB8}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {D63AD3FC-DCF0-4DB6-B944-5D828A10CCB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {D63AD3FC-DCF0-4DB6-B944-5D828A10CCB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {D63AD3FC-DCF0-4DB6-B944-5D828A10CCB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {D63AD3FC-DCF0-4DB6-B944-5D828A10CCB8}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/DuckGame.Mod.mm/DuckGame.Mod.mm.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {73E9C6D2-D61F-4956-81BC-A8CA9A292AB8}
8 | Library
9 | Properties
10 | DuckGame
11 | DuckGame.Mod.mm
12 | v4.5
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 | true
25 | false
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 | true
35 | false
36 |
37 |
38 |
39 | ..\libs\DuckGame.exe
40 |
41 |
42 | ..\libs\FNA.dll
43 |
44 |
45 |
46 | ..\libs\XnaToFna.exe
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
63 |
--------------------------------------------------------------------------------
/DuckGame.Mod.mm/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("DuckGame.Mod.mm")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("DuckGame.Mod.mm")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("73e9c6d2-d61f-4956-81bc-a8ca9a292ab8")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/DuckGame.Mod.mm/src/patch_DuckFile.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable CS0626 // Method, operator, or accessor is marked external and has no attributes on it
2 | using MonoMod;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace DuckGame {
11 | class patch_DuckFile {
12 |
13 | [MonoModIgnore]
14 | private static string _saveRoot;
15 | [MonoModIgnore]
16 | private static string _saveDirectory;
17 | [MonoModIgnore]
18 | private static List _allPaths;
19 |
20 | public static extern void orig_Initialize();
21 | public static void Initialize() {
22 | orig_Initialize();
23 | string prefix = _saveRoot + _saveDirectory;
24 | foreach (string path in _allPaths) {
25 | if (path.StartsWith(prefix))
26 | Directory.CreateDirectory(path);
27 | else
28 | Directory.CreateDirectory(prefix + path);
29 | }
30 | }
31 |
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/DuckGame.Mod.mm/src/patch_ModLoader.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable CS0626 // Method, operator, or accessor is marked external and has no attributes on it
2 | using MonoMod;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace DuckGame {
11 | class patch_ModLoader {
12 |
13 | [MonoModIgnore]
14 | internal static string modHash { get; private set; }
15 |
16 | internal static void DefaultModHash() {
17 | if (modHash == null)
18 | modHash = "nomods";
19 | }
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/DuckGame.Mod.mm/src/patch_Program.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable CS0626 // Method, operator, or accessor is marked external and has no attributes on it
2 |
3 | using MonoMod;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Diagnostics;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Reflection;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace DuckGame {
14 | internal static class patch_Program {
15 |
16 | public static void LogLine(string line) {
17 | Console.WriteLine(line);
18 | }
19 |
20 | public static void WriteToLog(string s) {
21 | Console.Error.WriteLine(s);
22 | }
23 |
24 | private static extern void orig_UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e);
25 | private static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e) {
26 | LogDetailed((Exception) e.ExceptionObject);
27 | orig_UnhandledExceptionTrapper(sender, e);
28 | }
29 |
30 | public static void LogDetailed(Exception e, string tag = null) {
31 | for (Exception e_ = e; e_ != null; e_ = e_.InnerException) {
32 | Console.WriteLine(e_.GetType().FullName + ": " + e_.Message + "\n" + e_.StackTrace);
33 | if (e_ is ReflectionTypeLoadException) {
34 | ReflectionTypeLoadException rtle = (ReflectionTypeLoadException) e_;
35 | for (int i = 0; i < rtle.Types.Length; i++) {
36 | Console.WriteLine("ReflectionTypeLoadException.Types[" + i + "]: " + rtle.Types[i]);
37 | }
38 | for (int i = 0; i < rtle.LoaderExceptions.Length; i++) {
39 | LogDetailed(rtle.LoaderExceptions[i], tag + (tag == null ? "" : ", ") + "rtle:" + i);
40 | }
41 | }
42 | if (e_ is TypeLoadException) {
43 | Console.WriteLine("TypeLoadException.TypeName: " + ((TypeLoadException) e_).TypeName);
44 | }
45 | if (e_ is BadImageFormatException) {
46 | Console.WriteLine("BadImageFormatException.FileName: " + ((BadImageFormatException) e_).FileName);
47 | }
48 | }
49 | }
50 |
51 | public static void MakeNetLog() {
52 | }
53 |
54 | [MonoModLinkTo("DuckGame.ModLoader", "System.Void set_modHash(System.String)")]
55 | private static void _ModLoader_set_modHash(string value) {
56 | }
57 |
58 | public static extern void orig_Main(string[] args);
59 | [STAThread]
60 | public static void Main(string[] args) {
61 | bool nothreading = true;
62 | bool nomods = true;
63 |
64 | Queue argq = new Queue(args);
65 | while (argq.Count > 0) {
66 | string arg = argq.Dequeue();
67 | if (arg == "-debug")
68 | Debugger.Launch();
69 | else if (arg == "-yesthreading")
70 | nothreading = false;
71 | else if (arg == "-yesmods")
72 | nomods = false;
73 | }
74 |
75 | List argl = new List(args);
76 | argl.Add("<");
77 |
78 | if (nothreading)
79 | argl.Add("-nothreading");
80 |
81 | if (nomods)
82 | argl.Add("-nomods");
83 |
84 | argl.Add(">");
85 |
86 | args = argl.ToArray();
87 |
88 | // Parse arguments again...
89 | argq = new Queue(args);
90 | while (argq.Count > 0) {
91 | string arg = argq.Dequeue();
92 | if (arg == "-nomods")
93 | // MonoMain.moddingEnabled = false skips ManagedContent.InitializeMods.
94 | // InitializeMods calls ModLoader.LoadMods.
95 | // LoadMods sets ModLoader.modHash.
96 | // -nomods thus leaves ModLoader.modHash == null, which causes issues.
97 | patch_ModLoader.DefaultModHash();
98 | }
99 |
100 | orig_Main(args);
101 | }
102 |
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Maik Macho
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Duck Game Linux FNA patches
2 | ### Because Duck Game forgets to mkdir and wine is slow
3 | ##### MIT-licensed
4 | ----
5 |
6 | [**Feel free to discuss this on Reddit!**](https://www.reddit.com/r/linux_gaming/comments/6zqrrx/duckgamelinux_fna_custom_steamworks_bindings_some/)
7 | [Screenshots](https://twitter.com/0x0ade/status/907745108010946560), [GamingOnLinux.com article](https://www.gamingonlinux.com/articles/want-to-play-duck-game-on-linux-well-its-possible-thanks-to-xnatofna.10339)
8 | 0x0ade on [**Twitter**](https://www.twitter.com/0x0ade), [**Patreon**](https://www.patreon.com/0x0ade)
9 |
10 |
11 | [**You can support me on Patreon!**](https://www.patreon.com/0x0ade)
12 | This project wouldn't be possible without the support from:
13 | * Ethan Lee: Thank you for creating FNA!
14 | * Artus Elias Meyer-Toms, Renaud Bedard and Ryan Kistner: I wouldn't be able to get my hands on the game without your support!
15 |
16 | ### Usage instructions:
17 | **Preparations:**
18 | * Get yourself a fresh copy of Duck Game, f.e. via Steam... through Wine... or from a friend with Windows.
19 | * Install `mono-complete` and `libcurl3:i386` and `ffmpeg` (or matching) via your package manager.
20 | * `ffmpeg`, not "`libav`" / `avconv`.
21 |
22 | **Installing / updating:**
23 | * Download [**the latest released DuckGame-Linux-Complete.zip**](https://github.com/0x0ade/DuckGame-Linux/releases)
24 | * Extract the .zip into the Duck Game directory. Overwrite the original files when extracting. `XnaToFna.exe`, and `DuckGame.exe` should be next to each other; The old `Steam.dll` should be replaced by the one in the .zip.
25 | * Open terminal in Duck Game directory, run `chmod a+x ./mod.sh; ./mod.sh`
26 | * `mod.sh` creates a backup of important files in an `orig` subdirectory, which XnaToFna needs. It also gives all files in the directory read-write permissions for all users, otherwise both XnaToFna and MonoMod will fail.
27 | * Run `mono DuckGame.exe` OR Launch the game via Steam (add `DuckGame.sh` to your library as "non-Steam game").
28 | * Be a duck with a gun!
29 |
30 | The game stores its save data in `~/DuckGame`, which is technically the same as on Windows.
31 |
32 | ### Current collection of patches:
33 | * [XnaToFna](https://github.com/0x0ade/XnaToFna) gets the game running using [FNA](https://fna-xna.github.io/) instead of XNA. Thanks to [flibitijibibo](https://www.patreon.com/flibitijibibo) for FNA. Without him this wouldn't be possible!
34 | * [Non-mixed-mode Steam.dll "proxy" to Steamworks.NET](https://github.com/0x0ade/DuckGame-Linux/tree/master/Steam) - this allows you to use Steam functionality natively... although it still contains a few holes. Working on it!
35 | * More verbose fatal error logging that help you when patching the game.
36 | * Create missing directories automatically. Does Windows just implicitly create the directories?!
37 | * Automatically pass -nothreading because it's faster.
38 | * Automatically pass -nomods because the mods would need to be relinked to FNA. This doesn't happen automagically yet.
39 | * Fix `ModLoader.modHash == null`, not `"nomods"` when `-nomods` is passed. This also affects vanilla Duck Game and can kill Steam.
40 |
41 | If for whatever reason something doesn't work, please create an issue on GitHub. I want this to work for everyone!
42 |
--------------------------------------------------------------------------------
/Steam/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.InteropServices;
4 | using System.Runtime.Versioning;
5 | using System.Security;
6 | using System.Security.Permissions;
7 |
8 | [assembly: AssemblyTitle("")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("")]
13 | [assembly: AssemblyCopyright("")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | [assembly: Guid("d63ad3fc-dcf0-4db6-b944-5d828a10ccb8")]
18 |
19 | [assembly: AssemblyVersion("0.0.0.0")]
20 |
21 | [assembly: SecurityRules(SecurityRuleSet.Level1)]
22 | #pragma warning disable CS0618 // RequestMinimum is obsolete
23 | [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
24 | #pragma warning restore CS0618
25 |
--------------------------------------------------------------------------------
/Steam/README.md:
--------------------------------------------------------------------------------
1 | # Duck Game `Steam.dll` for Linux
2 | ### Proxy `Steam.dll` into [Steamworks.NET](https://github.com/rlabrecque/Steamworks.NET)
3 | ##### MIT-licensed
4 | ----
5 |
6 | ### But... why?
7 | When trying to run Duck Game on Linux natively with [XnaToFna](https://github.com/0x0ade/XnaToFna), Duck Game will try to access the Steam API (Steamworks) through its `Steam.dll`.
8 | The only issue with that is... that it's a mixed-mode assembly, thus restricted to Windows.
9 | Furthermore, it's restricted to x86, which isn't that horrible for XNA games, but FNA supports making use of all 64 bits in your CPU(tm).
10 |
11 | This `Steam.dll` replacement just tries to "proxy" any `Steam.dll` calls to Steamworks.NET, which supports all Steam platforms: x86 and x64 Windows, Linux and macOS.
12 |
13 | ### Notes:
14 | * When using XnaToFna, you'll need to pass `-nothreading -nomods`.
15 | * On Linux, Duck Game will complain about missing paths as it forgets to create the save profile directories. Currently working on a separate fix.
16 | * _It's not a complete replacement yet_, it'll be outdated with future Duck Game updates and I'm stuck on a 1G connection right now, which makes testing this a pain in the a~
17 | * You need to copy the proper files from the Steamworks.NET directory to the Duck Game directory. For Linux, you need to rename `Steamworks.NET-linux.dll` to just `Steamworks.NET.dll`. The x64 variants of steam_api and CSteamworks are supplied.
18 | * You should be able to use a stubbed Steamworks.NET to get Duck Game running "DRM-free" on non-Windows platforms. I've uploaded one for FNADroid, it should just work(tm): https://github.com/0x0ade/FNADroid/blob/master/FNADroid/libs/managed/Steamworks.NET.dll
19 |
--------------------------------------------------------------------------------
/Steam/Steam.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | x86
7 | {D63AD3FC-DCF0-4DB6-B944-5D828A10CCB8}
8 | Library
9 | Properties
10 | Steam
11 | Steam
12 | v4.0
13 | 512
14 |
15 |
16 | AnyCPU
17 | bin\Debug\
18 | true
19 |
20 |
21 | AnyCPU
22 | bin\Release\
23 | true
24 |
25 |
26 |
27 | ..\Steamworks.NET\Steamworks.NET.dll
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/Steam/src/FriendRelationship.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum FriendRelationship {
4 | Max = 8,
5 | Suggested = 7,
6 | IgnoredFriend = 6,
7 | Ignored = 5,
8 | RequestInitiator = 4,
9 | Friend = 3,
10 | RequestRecipient = 2,
11 | Blocked = 1,
12 | None = 0
13 | }
14 |
--------------------------------------------------------------------------------
/Steam/src/ItemUpdateStatus.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum ItemUpdateStatus {
4 | CommittingChanges = 5,
5 | UploadingPreviewFile = 4,
6 | UploadingContent = 3,
7 | PreparingContent = 2,
8 | PreparingConfig = 1,
9 | Invalid = 0
10 | }
11 |
--------------------------------------------------------------------------------
/Steam/src/Lobby.cs:
--------------------------------------------------------------------------------
1 | using Steamworks;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Runtime.ExceptionServices;
5 |
6 | public class Lobby : IDisposable {
7 |
8 | public int randomID; // Duck Game uses this... BUT IT ISN'T USED ANYWHERE IN Steam.dll?!
9 |
10 | public delegate void UserStatusChangeDelegate(User user, SteamLobbyUserStatusFlags flags, User responsibleUser);
11 | public event UserStatusChangeDelegate UserStatusChange;
12 |
13 | private CSteamID _id;
14 | public ulong id => _id.m_SteamID;
15 |
16 | public SteamLobbyJoinResult joinResult { get; private set; }
17 |
18 | public unsafe User owner {
19 | get {
20 | if (id != 0) {
21 | return User.GetUser(SteamMatchmaking.GetLobbyOwner(_id));
22 | }
23 | return null;
24 | }
25 | }
26 |
27 | public unsafe string name {
28 | get {
29 | if (id == 0) {
30 | return "";
31 | }
32 | return SteamMatchmaking.GetLobbyData(_id, "name");
33 | }
34 | set {
35 | if (id != 0) {
36 | SteamMatchmaking.SetLobbyData(_id, "name", value);
37 | }
38 | }
39 | }
40 |
41 | private bool _joinable;
42 | public unsafe bool joinable {
43 | get {
44 | return id != 0 && _joinable;
45 | }
46 | set {
47 | if (id != 0) {
48 | SteamMatchmaking.SetLobbyJoinable(_id, value);
49 | _joinable = value;
50 | }
51 | }
52 | }
53 |
54 | private SteamLobbyType _type;
55 | public unsafe SteamLobbyType type {
56 | get {
57 | return _type;
58 | }
59 | set {
60 | if (id != 0) {
61 | SteamMatchmaking.SetLobbyType(_id, (ELobbyType) value);
62 | // This isn't set by the original Steam.dll...
63 | _type = value;
64 | }
65 | }
66 | }
67 |
68 | private int _maxMembers;
69 | public unsafe int maxMembers {
70 | get {
71 | if (id == 0) {
72 | return 0;
73 | }
74 | return _maxMembers = SteamMatchmaking.GetLobbyMemberLimit(_id);
75 | }
76 | set {
77 | if (id != 0) {
78 | SteamMatchmaking.SetLobbyMemberLimit(_id, _maxMembers = value);
79 | }
80 | }
81 | }
82 |
83 | public unsafe List users {
84 | get {
85 | if (id == 0) {
86 | return new List();
87 | }
88 |
89 | return _.GetList(SteamMatchmaking.GetNumLobbyMembers(_id), i => User.GetUser(SteamMatchmaking.GetLobbyMemberByIndex(_id, i)));
90 | }
91 | }
92 |
93 | public bool processing { get; private set; }
94 |
95 | public Lobby(ulong lobbyID)
96 | : this(new CSteamID(lobbyID)) {
97 | }
98 |
99 | internal Lobby(CSteamID lobbyID) {
100 | _type = SteamLobbyType.FriendsOnly;
101 | processing = true;
102 | _id = lobbyID;
103 | _joinable = true;
104 | }
105 |
106 | public Lobby(SteamLobbyType lobbyTypeVal, int maxMembersVal) {
107 | _type = lobbyTypeVal;
108 | // This is set to this.maxMembers by the original Steam.dll...
109 | _maxMembers = maxMembersVal;
110 | processing = true;
111 | _id = new CSteamID();
112 | _joinable = true;
113 | }
114 |
115 | public void OnProcessingComplete(ulong idVal, SteamLobbyJoinResult result) {
116 | _id = new CSteamID(idVal);
117 | joinResult = result;
118 | processing = false;
119 | }
120 |
121 | public void OnUserStatusChange(User user, SteamLobbyUserStatusFlags flags, User responsibleUser) {
122 | UserStatusChange?.Invoke(user, flags, responsibleUser);
123 | }
124 |
125 | public unsafe void SetLobbyData(string name, string value) {
126 | if (id != 0)
127 | SteamMatchmaking.SetLobbyData(_id, name, value);
128 | }
129 |
130 | public unsafe string GetLobbyData(string name) {
131 | if (id == 0)
132 | return "";
133 | return SteamMatchmaking.GetLobbyData(_id, name);
134 | }
135 |
136 | [HandleProcessCorruptedStateExceptions]
137 | protected virtual void Dispose(bool flag) {
138 | }
139 |
140 | public void Dispose() {
141 | Dispose(true);
142 | }
143 |
144 | }
145 |
--------------------------------------------------------------------------------
/Steam/src/Lock.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 |
4 | internal class Lock : IDisposable {
5 |
6 | private object m_pObject;
7 |
8 | public Lock(object pObject) {
9 | m_pObject = pObject;
10 | Monitor.Enter(m_pObject);
11 | }
12 |
13 | ~Lock() {
14 | Dispose(true);
15 | }
16 |
17 | protected virtual void Dispose(bool disposing) {
18 | if (disposing)
19 | Monitor.Exit(m_pObject);
20 | }
21 |
22 | public void Dispose() {
23 | Dispose(true);
24 | GC.SuppressFinalize(this);
25 | }
26 |
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/Steam/src/OfflineSelfUser.cs:
--------------------------------------------------------------------------------
1 | using Steamworks;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Runtime.ExceptionServices;
5 |
6 | // THIS TYPE DOESN'T EXIST IN THE ORIGINAL Steam.dll!
7 | public class OfflineSelfUser : User {
8 |
9 | public override ulong id => 0;
10 |
11 | public override unsafe string name => "UNKNOWN";
12 |
13 | public override unsafe byte[] avatarSmall => null;
14 |
15 | public override unsafe byte[] avatarMedium => null;
16 |
17 | public override unsafe bool inGame => true;
18 |
19 | public override unsafe bool inCurrentGame => true;
20 |
21 | protected override unsafe bool inLobby => false;
22 |
23 | public override unsafe bool inCurrentLobby => false;
24 |
25 | public override unsafe SteamUserState state => SteamUserState.Offline;
26 |
27 | public override unsafe FriendRelationship relationship => FriendRelationship.None;
28 |
29 | internal OfflineSelfUser()
30 | : base(new CSteamID()) {
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/Steam/src/P2PDataSendType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum P2PDataSendType {
4 | ReliableWithBuffering = 3,
5 | Reliable = 2,
6 | UnreliableNoDelay = 1,
7 | Unreliable = 0
8 | }
9 |
--------------------------------------------------------------------------------
/Steam/src/RemoteStoragePublishedFileVisibility.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum RemoteStoragePublishedFileVisibility {
4 | FriendsOnly = 1,
5 | Public = 0,
6 | Private = 2
7 | }
8 |
--------------------------------------------------------------------------------
/Steam/src/SharpSteam.cs:
--------------------------------------------------------------------------------
1 | using Steamworks;
2 | using System;
3 | using System.Text;
4 |
5 | public class SharpSteam : IDisposable {
6 |
7 | public unsafe SharpSteam() {
8 | }
9 |
10 | public bool Init() {
11 | return SteamAPI.Init();
12 | }
13 |
14 | public void Shutdown() {
15 | SteamAPI.Shutdown();
16 | }
17 |
18 | public void SetTempDirectory(string dir) {
19 | // _tempDirectory was only used in FormatName..?!
20 | throw new NotImplementedException();
21 | }
22 |
23 | public unsafe bool SetAchievement(string name) {
24 | return SteamUserStats.SetAchievement(name);
25 | }
26 |
27 | public unsafe bool StoreStats() {
28 | return SteamUserStats.StoreStats();
29 | }
30 |
31 | public unsafe bool SetStat(string name, int value) {
32 | return SteamUserStats.SetStat(name, value);
33 | }
34 |
35 | public unsafe bool IndicateAchievementProgress(string name, int cur, int maxval) {
36 | return SteamUserStats.IndicateAchievementProgress(name, (uint) cur, (uint) maxval);
37 | }
38 |
39 | public unsafe string FormatName(string name) {
40 | // Invalid signature anyway...
41 | throw new NotImplementedException();
42 | }
43 |
44 | public unsafe bool FileExists(string name) {
45 | return SteamRemoteStorage.FileExists(name);
46 | }
47 |
48 | public unsafe bool FileWrite(string managedname, string manageddata) {
49 | byte[] data = Encoding.ASCII.GetBytes(manageddata);
50 | return SteamRemoteStorage.FileWrite(managedname, data, data.Length);
51 | }
52 |
53 | public unsafe string FileRead(string managedname) {
54 | int size = SteamRemoteStorage.GetFileSize(managedname);
55 | byte[] data = new byte[size];
56 | int rv = SteamRemoteStorage.FileRead(managedname, data, size);
57 | return Encoding.ASCII.GetString(data, 0, size);
58 | }
59 |
60 | public unsafe string GetUsername() {
61 | return SteamFriends.GetPersonaName();
62 | }
63 |
64 | public unsafe bool LoadLeaderboard(string board, string type, int minRange, int maxRange) {
65 | throw new NotImplementedException();
66 | }
67 |
68 | public unsafe bool DownloadedLeaderboard(string board) {
69 | throw new NotImplementedException();
70 | }
71 |
72 | public unsafe bool UploadScoreFailed() {
73 | throw new NotImplementedException();
74 | }
75 |
76 | public unsafe bool UploadScoreComplete() {
77 | throw new NotImplementedException();
78 | }
79 |
80 | public unsafe bool DownloadScoresFailed() {
81 | throw new NotImplementedException();
82 | }
83 |
84 | public unsafe string GetLeaderboardName(string board, int index) {
85 | throw new NotImplementedException();
86 | // SteamUserStats.GetLeaderboardName(/*... this requires a SteamLeaderboard_t!*/);
87 | }
88 |
89 | public unsafe int GetLeaderboardScore(string board, int index) {
90 | throw new NotImplementedException();
91 | }
92 |
93 | public unsafe int GetLeaderboardPosition(string board, int index) {
94 | throw new NotImplementedException();
95 | }
96 |
97 | public unsafe int GetLeaderboardDetails(string board, int index) {
98 | throw new NotImplementedException();
99 | }
100 |
101 | public unsafe bool UploadScore(string board, int score, int details) {
102 | throw new NotImplementedException();
103 | // SteamUserStats.UploadLeaderboardScore(/*... this requires a SteamLeaderboard_t!*/, ELeaderboardUploadScoreMethod.k_ELeaderboardUploadScoreMethodKeepBest, score, /*???*/, /*???*/);
104 | }
105 |
106 | public void RunCallbacks() {
107 | SteamAPI.RunCallbacks();
108 | }
109 |
110 | public unsafe void ThrowException(string message, string error) {
111 | throw new NotSupportedException();
112 | // NativeMethods isn't exposed, but those would need to be called:
113 | // Steamworks.NativeMethods.SteamAPI_SetMiniDumpComment
114 | // Steamworks.NativeMethods.SteamAPI_WriteMiniDump
115 | }
116 |
117 | public unsafe void OpenInviteDialogue() {
118 | throw new NotImplementedException();
119 | // SteamFriends.ActivateGameOverlayInviteDialog(/*requires current lobby*/);
120 | }
121 |
122 | public void CreateLobby(int lobbyType, int maxMembers) {
123 | SteamMatchmaking.CreateLobby((ELobbyType) lobbyType, maxMembers);
124 | }
125 |
126 | public unsafe bool CreateLobbyComplete() {
127 | throw new NotImplementedException();
128 | }
129 |
130 | public unsafe ulong GetCreatedLobbyID() {
131 | throw new NotImplementedException();
132 | }
133 |
134 | protected unsafe virtual void Dispose(bool flag) {
135 | }
136 |
137 | public void Dispose() {
138 | Dispose(true);
139 | }
140 |
141 | }
142 |
--------------------------------------------------------------------------------
/Steam/src/Steam.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Steamworks;
4 |
5 | public class Steam : IDisposable {
6 |
7 | private static Dictionary _callResults = new Dictionary();
8 |
9 | private static CallResult GetCallResult() => (CallResult) _callResults[typeof(T)];
10 | private static void SetCallResult(CallResult.APIDispatchDelegate func) => _callResults.Add(typeof(T), new CallResult(func));
11 | private static void SetCallResult(SteamAPICall_t call) => ((CallResult) _callResults[typeof(T)]).Set(call);
12 |
13 | private static bool _initialized;
14 | private static bool _offline;
15 |
16 | private static WorkshopItem _pendingItem;
17 |
18 | private static WorkshopItem _pendingItemDownload;
19 |
20 | // private static bool _waitingForCurrentStats; // unused
21 |
22 | private readonly static int kPacketBufferSize = 2048; // originally not readonly; could be const because it's inlined
23 |
24 | private unsafe static byte[] _packetData;
25 |
26 | public delegate void ConnectionRequestedDelegate(User remote);
27 | public static event ConnectionRequestedDelegate ConnectionRequested;
28 |
29 | public delegate void ConnectionFailedDelegate(User remote);
30 | public static event ConnectionFailedDelegate ConnectionFailed;
31 |
32 | public delegate void InviteReceivedDelegate(User friend, Lobby lobby);
33 | public static event InviteReceivedDelegate InviteReceived;
34 |
35 | public delegate void LobbySearchCompleteDelegate(Lobby lobby);
36 | public static event LobbySearchCompleteDelegate LobbySearchComplete;
37 |
38 | public delegate void RequestCurrentStatsDelegate();
39 | public static event RequestCurrentStatsDelegate RequestCurrentStatsComplete;
40 |
41 | public static Lobby lobbySearchResult { get; private set; }
42 |
43 | public static bool lobbySearchComplete { get; private set; }
44 |
45 | public static bool waitingForGlobalStats { get; private set; }
46 |
47 | public static int lobbiesFound { get; private set; }
48 |
49 | public static Lobby lobby { get; private set; }
50 |
51 | public static User user { get; private set; }
52 |
53 | // private static List _friends; // unused
54 | public unsafe static List friends => _.GetList(SteamFriends.GetFriendCount(EFriendFlags.k_EFriendFlagAll), i => User.GetUser(SteamFriends.GetFriendByIndex(i, EFriendFlags.k_EFriendFlagAll)));
55 |
56 | public Steam() {
57 | _initialized = false;
58 | }
59 |
60 | public static bool NeedsRestartForSteam() {
61 | return SteamAPI.RestartAppIfNecessary(SteamUtils.GetAppID());
62 | }
63 |
64 | public unsafe static bool Authorize() {
65 | if (!_initialized)
66 | return false;
67 | // TODO: SteamApps.RequestAppProofOfPurchaseKey? SteamApps.BIsAppInstalled? SteamApps.BIsSubscribedApp?
68 | return SteamApps.BIsSubscribedApp(SteamUtils.GetAppID());
69 | }
70 |
71 | public static bool InitializeCore() {
72 | return _initialized = SteamAPI.Init();
73 | }
74 |
75 | public unsafe static void Initialize() {
76 | Callback.Create(OnLobbyMemberStatus);
77 | Callback.Create(OnConnectionRequest);
78 | Callback.Create(OnConnectionFail);
79 | Callback.Create(OnInviteReceive);
80 | Callback.Create(OnDownloadItemResult);
81 | Callback.Create(OnRequestStats);
82 |
83 | // Maintain CallResults which otherwise would be added, then removed behind the scenes:
84 | SetCallResult(OnRequestGlobalStats);
85 | SetCallResult(OnCreateItem);
86 | SetCallResult(OnSubmitItemUpdate);
87 | SetCallResult(OnSendQueryUGCRequest);
88 | SetCallResult(OnCreateLobby);
89 | SetCallResult(OnJoinLobby);
90 | SetCallResult(OnSearchForLobby);
91 |
92 | _packetData = new byte[kPacketBufferSize];
93 |
94 | // THIS IS A HORRIBLE HACK to get this to comply when using a stubbed Steamworks.NET.dll.
95 | if (_initialized)
96 | _initialized = SteamUser.GetSteamID().m_SteamID != 0;
97 |
98 | if (_initialized) {
99 | user = User.GetUser(SteamUser.GetSteamID());
100 | // TODO: The original Steam.dll would call something now, but I can't identify what.
101 | } else {
102 | // THIS IS A HORRIBLE HACK TO GET DUCK GAME TO SHUT UP WHEN "OFFLINE".
103 | _offline = true;
104 | user = new OfflineSelfUser();
105 | }
106 |
107 | lobbySearchComplete = true;
108 | lobbySearchResult = null;
109 | }
110 |
111 | public static bool IsInitialized() {
112 | return _initialized || _offline;
113 | }
114 |
115 | public unsafe static void Terminate() {
116 | if (!_initialized)
117 | return;
118 | SteamAPI.Shutdown();
119 | }
120 |
121 | public static void Update() {
122 | if (!_initialized)
123 | return;
124 | SteamAPI.RunCallbacks();
125 | }
126 |
127 | public unsafe static void OverlayOpenURL(string url) {
128 | if (!_initialized)
129 | return;
130 | SteamFriends.ActivateGameOverlayToWebPage(url);
131 | }
132 |
133 | public unsafe static void SetAchievement(string id) {
134 | if (!_initialized)
135 | return;
136 | SteamUserStats.SetAchievement(id);
137 | }
138 |
139 | public unsafe static float GetStat(string id) {
140 | if (!_initialized)
141 | return 0f;
142 | float val;
143 | SteamUserStats.GetStat(id, out val);
144 | return val;
145 | }
146 |
147 | public unsafe static void SetStat(string id, float val) {
148 | if (!_initialized)
149 | return;
150 | SteamUserStats.SetStat(id, val);
151 | }
152 |
153 | public unsafe static void SetStat(string id, int val) {
154 | if (!_initialized)
155 | return;
156 | SteamUserStats.SetStat(id, val);
157 | }
158 |
159 | public unsafe static void StoreStats() {
160 | if (!_initialized)
161 | return;
162 | SteamUserStats.StoreStats();
163 | }
164 |
165 | public static void RequestGlobalStats() {
166 | if (!_initialized)
167 | return;
168 | waitingForGlobalStats = true;
169 | SetCallResult(SteamUserStats.RequestGlobalStats(1)); // TODO: How many days for RequestGlobalStats?
170 | }
171 |
172 | public unsafe static double GetGlobalStat(string id) {
173 | if (!_initialized)
174 | return 0D;
175 | double val;
176 | SteamUserStats.GetGlobalStat(id, out val);
177 | return val;
178 | }
179 |
180 | public unsafe static double GetDailyGlobalStat(string id) {
181 | if (!_initialized)
182 | return 0D;
183 | // FIXME: This feels wrong for GetDailyGlobalStat.
184 | double val;
185 | SteamUserStats.GetGlobalStat(id, out val);
186 | return val;
187 | }
188 |
189 | public static WorkshopItem CreateItem() {
190 | if (!_initialized)
191 | return null;
192 | _pendingItem = new WorkshopItem();
193 | SetCallResult(SteamUGC.CreateItem(SteamUtils.GetAppID(), EWorkshopFileType.k_EWorkshopFileTypeFirst));
194 | return _pendingItem;
195 | }
196 |
197 | public unsafe static void ShowWorkshopLegalAgreement(string id) {
198 | if (_initialized)
199 | SteamFriends.ActivateGameOverlayToWebPage("steam://url/CommunityFilePage/" + id);
200 | }
201 |
202 | public unsafe static void StartUpload(WorkshopItem item) {
203 | if (!_initialized)
204 | return;
205 | _pendingItem = item;
206 | SetCallResult(SteamUGC.SubmitItemUpdate(new UGCUpdateHandle_t(item.updateHandle), item.data.changeNotes));
207 | }
208 |
209 | public unsafe static List GetAllWorkshopItems() {
210 | if (!_initialized)
211 | return new List();
212 | // FIXME: This seems wrong, but the original basically just does this.
213 | PublishedFileId_t[] tmp = new PublishedFileId_t[GetNumWorkshopItems()];
214 | SteamUGC.GetSubscribedItems(tmp, (uint) tmp.Length);
215 | List list = _.GetList(tmp.Length, i => WorkshopItem.GetItem(tmp[i].m_PublishedFileId));
216 | RequestWorkshopInfo(list);
217 | return list;
218 | }
219 |
220 | public unsafe static int GetNumWorkshopItems() {
221 | if (!_initialized)
222 | return 0;
223 | // FIXME: This seems wrong, but the original just calls a single function... this is the closest match.
224 | return (int) SteamUGC.GetNumSubscribedItems();
225 | }
226 |
227 | public unsafe static void RequestWorkshopInfo(List items) {
228 | if (!_initialized)
229 | return;
230 | UGCQueryHandle_t query = SteamUGC.CreateQueryUGCDetailsRequest(_.GetArray(items, item => new PublishedFileId_t(item.id)), (uint) items.Count);
231 | SetCallResult(SteamUGC.SendQueryUGCRequest(query));
232 | }
233 |
234 | public unsafe static void WorkshopUnsubscribe(ulong id) {
235 | if (!_initialized)
236 | return;
237 | SteamUGC.UnsubscribeItem(new PublishedFileId_t(id));
238 | }
239 |
240 | public unsafe static void WorkshopSubscribe(ulong id) {
241 | if (!_initialized)
242 | return;
243 | SteamUGC.SubscribeItem(new PublishedFileId_t(id));
244 | }
245 |
246 | public unsafe static bool DownloadWorkshopItem(WorkshopItem item) {
247 | bool result = SteamUGC.DownloadItem(new PublishedFileId_t(item.id), true);
248 | item.ResetProcessing();
249 | _pendingItemDownload = item;
250 | return result;
251 | }
252 |
253 | public static WorkshopQueryAll CreateQueryAll(WorkshopQueryFilterOrder queryType, WorkshopType type) {
254 | return new WorkshopQueryAll((EUGCQuery) queryType, (EUGCMatchingUGCType) type);
255 | }
256 |
257 | public static WorkshopQueryUser CreateQueryUser(ulong accountID, WorkshopList listType, WorkshopType type, WorkshopSortOrder order) {
258 | return new WorkshopQueryUser((uint) accountID, (EUserUGCList) listType, (EUGCMatchingUGCType) type, (EUserUGCListSortOrder) order);
259 | }
260 |
261 | public static WorkshopQueryFileDetails CreateQueryFileDetails() {
262 | return new WorkshopQueryFileDetails();
263 | }
264 |
265 | public unsafe static byte[] FileRead(string name) {
266 | if (!_initialized)
267 | return null;
268 | int size = SteamRemoteStorage.GetFileSize(name);
269 | byte[] data = new byte[size];
270 | int rv = SteamRemoteStorage.FileRead(name, data, size);
271 | return data;
272 | }
273 |
274 | public unsafe static bool FileExists(string name) {
275 | if (!_initialized)
276 | return false;
277 | return SteamRemoteStorage.FileExists(name);
278 | }
279 |
280 | public unsafe static bool FileWrite(string name, byte[] data, int length) {
281 | if (!_initialized)
282 | return false;
283 | return data != null && data.Length != 0 && SteamRemoteStorage.FileWrite(name, data, length);
284 | }
285 |
286 | public unsafe static bool FileDelete(string name) {
287 | if (!_initialized)
288 | return false;
289 | return SteamRemoteStorage.FileDelete(name);
290 | }
291 |
292 | public unsafe static int FileGetCount() {
293 | if (!_initialized)
294 | return 0;
295 | return SteamRemoteStorage.GetFileCount();
296 | }
297 |
298 | public unsafe static string FileGetName(int file) {
299 | if (!_initialized)
300 | return null;
301 | int size;
302 | return SteamRemoteStorage.GetFileNameAndSize(file, out size);
303 | }
304 |
305 | public unsafe static int FileGetSize(int file) {
306 | if (!_initialized)
307 | return 0;
308 | int size;
309 | SteamRemoteStorage.GetFileNameAndSize(file, out size);
310 | return size;
311 | }
312 |
313 | public static Lobby CreateLobby(SteamLobbyType lobbyType, int maxMembers) {
314 | if (!_initialized)
315 | return null;
316 | if (lobby != null)
317 | LeaveLobby(lobby);
318 | lobby = new Lobby(lobbyType, maxMembers);
319 | SetCallResult(SteamMatchmaking.CreateLobby((ELobbyType) lobbyType, maxMembers));
320 | return lobby;
321 | }
322 |
323 | public static Lobby JoinLobby(ulong lobbyID) {
324 | if (!_initialized)
325 | return null;
326 | if (lobby == null || lobbyID != lobby.id) {
327 | if (lobby != null)
328 | LeaveLobby(lobby);
329 | lobby = new Lobby(lobbyID);
330 | }
331 | SetCallResult(SteamMatchmaking.JoinLobby(new CSteamID(lobbyID)));
332 | return lobby;
333 | }
334 |
335 | public unsafe static void LeaveLobby(Lobby which) {
336 | if (!_initialized)
337 | return;
338 | if (which != null)
339 | SteamMatchmaking.LeaveLobby(new CSteamID(which.id));
340 | if (lobby == which)
341 | lobby = null;
342 | }
343 |
344 | public unsafe static void SearchForLobby(User who) {
345 | if (!_initialized)
346 | return;
347 | lobbySearchResult = null;
348 | lobbySearchComplete = false;
349 | lobbiesFound = 0;
350 | if (who != null) {
351 | // TODO: What does the original Steam.dll do? Filter by user?
352 | // This isn't critical, but only used when Duck Game's Program.testServer == true and superjoebob is your steam friend.
353 | }
354 | SetCallResult(SteamMatchmaking.RequestLobbyList());
355 | }
356 |
357 | public unsafe static void SearchForLobbyWorldwide() {
358 | if (!_initialized)
359 | return;
360 | lobbySearchResult = null;
361 | lobbySearchComplete = false;
362 | lobbiesFound = 0;
363 | SteamMatchmaking.AddRequestLobbyListDistanceFilter(ELobbyDistanceFilter.k_ELobbyDistanceFilterWorldwide);
364 | SetCallResult(SteamMatchmaking.RequestLobbyList());
365 | }
366 |
367 | public unsafe static int GetNumLobbyMembers(Lobby which) {
368 | if (!_initialized)
369 | return 0;
370 | return which != null ? 0 :
371 | SteamMatchmaking.GetNumLobbyMembers(new CSteamID(which.id));
372 | }
373 |
374 | public unsafe static User GetLobbyMemberAtIndex(Lobby which, int member) {
375 | if (!_initialized)
376 | return null;
377 | return which != null && member < GetNumLobbyMembers(which) ? null :
378 | User.GetUser(SteamMatchmaking.GetLobbyMemberByIndex(new CSteamID(which.id), member));
379 | }
380 |
381 | public unsafe static void AddLobbyStringFilter(string key, string value, SteamLobbyComparison compareType) {
382 | if (!_initialized)
383 | return;
384 | SteamMatchmaking.AddRequestLobbyListStringFilter(key, value, (ELobbyComparison) compareType);
385 | }
386 |
387 | public unsafe static void AddLobbyNumericalFilter(string key, int value, SteamLobbyComparison compareType) {
388 | if (!_initialized)
389 | return;
390 | SteamMatchmaking.AddRequestLobbyListNumericalFilter(key, value, (ELobbyComparison) compareType);
391 | }
392 |
393 | public unsafe static void AddLobbySlotsAvailableFilter(int slots) {
394 | if (!_initialized)
395 | return;
396 | SteamMatchmaking.AddRequestLobbyListFilterSlotsAvailable(slots);
397 | }
398 |
399 | public unsafe static void AddLobbyMaxResultsFilter(int max) {
400 | if (!_initialized)
401 | return;
402 | SteamMatchmaking.AddRequestLobbyListResultCountFilter(max);
403 | }
404 |
405 | public unsafe static void AddLobbyNearFilter(string key, int filt) {
406 | if (!_initialized)
407 | return;
408 | SteamMatchmaking.AddRequestLobbyListNearValueFilter(key, filt);
409 | }
410 |
411 | public unsafe static Lobby GetSearchLobbyAtIndex(int index) {
412 | if (!_initialized)
413 | return null;
414 | return new Lobby(SteamMatchmaking.GetLobbyByIndex(index));
415 | }
416 |
417 | public unsafe static bool AcceptConnection(User who) {
418 | if (!_initialized)
419 | return false;
420 | return SteamNetworking.AcceptP2PSessionWithUser(new CSteamID(who.id));
421 | }
422 |
423 | public unsafe static void SendPacket(User who, byte[] data, uint size, P2PDataSendType type) {
424 | if (!_initialized)
425 | return;
426 | SteamNetworking.SendP2PPacket(new CSteamID(who.id), data, size, (EP2PSend) type);
427 | }
428 |
429 | public unsafe static void CloseConnection(User who) {
430 | if (!_initialized)
431 | return;
432 | SteamNetworking.CloseP2PSessionWithUser(new CSteamID(who.id));
433 | }
434 |
435 | public unsafe static SteamPacket ReadPacket() {
436 | if (!_initialized)
437 | return null;
438 | uint size;
439 | if (SteamNetworking.IsP2PPacketAvailable(out size)) {
440 | byte[] data;
441 | if (size > kPacketBufferSize)
442 | data = new byte[size];
443 | else
444 | data = _packetData;
445 | CSteamID user;
446 | if (SteamNetworking.ReadP2PPacket(data, size, out size, out user)) {
447 | if (data == _packetData) {
448 | data = new byte[size];
449 | Array.Copy(_packetData, data, size);
450 | }
451 | return new SteamPacket {
452 | data = data,
453 | connection = User.GetUser(user)
454 | };
455 | }
456 | }
457 | return null;
458 | }
459 |
460 | public unsafe static bool InviteUser(User userVal, Lobby lobbyVal) {
461 | if (!_initialized)
462 | return false;
463 | if (lobbyVal == null) {
464 | lobbyVal = lobby;
465 | if (lobbyVal == null)
466 | return false;
467 | }
468 | return userVal != null && SteamMatchmaking.InviteUserToLobby(new CSteamID(lobbyVal.id), new CSteamID(userVal.id));
469 | }
470 |
471 | public unsafe static void OpenInviteDialogue() {
472 | if (!_initialized)
473 | return;
474 | if (lobby == null)
475 | return;
476 | SteamFriends.ActivateGameOverlayInviteDialog(new CSteamID(lobby.id));
477 | }
478 |
479 | public static void LogException(string message, string error) {
480 | }
481 |
482 | private unsafe static void OnCreateLobby(LobbyCreated_t result, bool ioFailure) {
483 | if (!_initialized)
484 | return;
485 | if (lobby == null)
486 | return;
487 | if (result.m_eResult == EResult.k_EResultOK)
488 | lobby.OnProcessingComplete(result.m_ulSteamIDLobby, SteamLobbyJoinResult.Success);
489 | else
490 | lobby.OnProcessingComplete(0, SteamLobbyJoinResult.Error);
491 | }
492 |
493 | private unsafe static void OnJoinLobby(LobbyEnter_t result, bool ioFailure) {
494 | if (!_initialized)
495 | return;
496 | lobby?.OnProcessingComplete(result.m_ulSteamIDLobby, (SteamLobbyJoinResult) result.m_EChatRoomEnterResponse);
497 | }
498 |
499 | private unsafe static void OnSearchForLobby(LobbyMatchList_t result, bool ioFailure) {
500 | if (!_initialized)
501 | return;
502 | if (result.m_nLobbiesMatching != 0) {
503 | lobbySearchResult = new Lobby(SteamMatchmaking.GetLobbyByIndex(0));
504 | lobbiesFound = (int) result.m_nLobbiesMatching;
505 | } else {
506 | lobbySearchResult = null;
507 | lobbiesFound = 0;
508 | }
509 | lobbySearchComplete = true;
510 | LobbySearchComplete?.Invoke(lobbySearchResult);
511 | }
512 |
513 | private unsafe static void OnRequestGlobalStats(GlobalStatsReceived_t result, bool ioFailure) {
514 | if (!_initialized)
515 | return;
516 | waitingForGlobalStats = false;
517 | }
518 |
519 | private unsafe static void OnCreateItem(CreateItemResult_t result, bool ioFailure) {
520 | if (!_initialized)
521 | return;
522 | _pendingItem?.ApplyResult((SteamResult) result.m_eResult, result.m_bUserNeedsToAcceptWorkshopLegalAgreement, result.m_nPublishedFileId.m_PublishedFileId);
523 | }
524 |
525 | private unsafe static void OnSubmitItemUpdate(SubmitItemUpdateResult_t result, bool ioFailure) {
526 | if (!_initialized)
527 | return;
528 | _pendingItem?.ApplyResult((SteamResult) result.m_eResult, result.m_bUserNeedsToAcceptWorkshopLegalAgreement, _pendingItem.id);
529 | }
530 |
531 | private unsafe static void OnSendQueryUGCRequest(SteamUGCQueryCompleted_t result, bool ioFailure) {
532 | if (!_initialized)
533 | return;
534 | for (uint i = 0; i < result.m_unNumResultsReturned; i++) {
535 | SteamUGCDetails_t details;
536 | if (SteamUGC.GetQueryUGCResult(result.m_handle, i, out details)) {
537 | WorkshopItem item = WorkshopItem.GetItem(details.m_nPublishedFileId.m_PublishedFileId);
538 | if (item != null) {
539 | WorkshopItemData workshopItemData = new WorkshopItemData();
540 | SteamUGC.GetQueryUGCPreviewURL(result.m_handle, i, out workshopItemData.previewPath, 256);
541 | workshopItemData.description = details.m_rgchDescription;
542 | workshopItemData.votesUp = (int) details.m_unVotesUp;
543 | item.SetDetails(details.m_pchFileName, workshopItemData);
544 | }
545 | }
546 | }
547 | SteamUGC.ReleaseQueryUGCRequest(result.m_handle);
548 | }
549 |
550 | private unsafe static void OnLobbyMemberStatus(LobbyChatUpdate_t result) {
551 | if (lobby == null)
552 | return;
553 | lobby.OnUserStatusChange(
554 | User.GetUser(result.m_ulSteamIDUserChanged),
555 | (SteamLobbyUserStatusFlags) result.m_rgfChatMemberStateChange,
556 | User.GetUser(result.m_ulSteamIDMakingChange)
557 | );
558 | }
559 |
560 | private unsafe static void OnConnectionRequest(P2PSessionRequest_t result) {
561 | ConnectionRequested?.Invoke(User.GetUser(result.m_steamIDRemote));
562 | }
563 |
564 | private unsafe static void OnConnectionFail(P2PSessionConnectFail_t result) {
565 | ConnectionFailed?.Invoke(User.GetUser(result.m_steamIDRemote));
566 | }
567 |
568 | private unsafe static void OnInviteReceive(GameLobbyJoinRequested_t result) {
569 | InviteReceived?.Invoke(User.GetUser(result.m_steamIDFriend), new Lobby(result.m_steamIDLobby));
570 | }
571 |
572 | private unsafe static void OnDownloadItemResult(DownloadItemResult_t result) {
573 | _pendingItemDownload?.ApplyDownloadResult((SteamResult) result.m_eResult);
574 | }
575 |
576 | private unsafe static void OnRequestStats(UserStatsReceived_t result) {
577 | RequestCurrentStatsComplete?.Invoke();
578 | }
579 |
580 | protected virtual void Dispose(bool flag) {
581 | }
582 |
583 | public void Dispose() {
584 | Dispose(true);
585 | }
586 |
587 | }
588 |
--------------------------------------------------------------------------------
/Steam/src/SteamLobbyComparison.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum SteamLobbyComparison {
4 | NotEqual = 3,
5 | EqualToOrGreaterThan = 2,
6 | GreaterThan = 1,
7 | Equal = 0,
8 | LessThan = -1,
9 | EqualOrLessThan = -2
10 | }
11 |
--------------------------------------------------------------------------------
/Steam/src/SteamLobbyJoinResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum SteamLobbyJoinResult {
4 | YouBlockedMember = 11,
5 | MemberBlockedYou = 10,
6 | CommunityBan = 9,
7 | ClanDisabled = 8,
8 | Limited = 7,
9 | Banned = 6,
10 | Full = 4,
11 | NotAllowed = 3,
12 | DoesntExist = 2,
13 | Success = 1,
14 | Error = 5
15 | }
16 |
--------------------------------------------------------------------------------
/Steam/src/SteamLobbyType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum SteamLobbyType {
4 | Invisible = 3,
5 | Public = 2,
6 | FriendsOnly = 1,
7 | Private = 0
8 | }
9 |
--------------------------------------------------------------------------------
/Steam/src/SteamLobbyUserStatusFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | [Flags]
4 | public enum SteamLobbyUserStatusFlags {
5 | Banned = 16,
6 | Kicked = 8,
7 | Disconnected = 4,
8 | Entered = 1,
9 | Left = 2
10 | }
11 |
--------------------------------------------------------------------------------
/Steam/src/SteamPacket.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public class SteamPacket {
4 | public SteamPacket() {
5 | }
6 |
7 | public User connection;
8 |
9 | public byte[] data;
10 | }
11 |
--------------------------------------------------------------------------------
/Steam/src/SteamResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum SteamResult {
4 | Ignored = 41,
5 | Blocked = 40,
6 | Banned = 17,
7 | Busy = 10,
8 | Disabled = 80,
9 | PhoneActivityLimitExceeded = 97,
10 | AccountActivityLimitExceeded = 96,
11 | AccountLimitExceeded = 95,
12 | SmsCodeFailed = 94,
13 | TimeNotSynced = 93,
14 | NoMobileDevice = 92,
15 | NotModified = 91,
16 | AccountAssociatedToMultiplePartners = 90,
17 | TwoFactorActivationCodeMismatch = 89,
18 | TwoFactorCodeMismatch = 88,
19 | AccountLoginDeniedThrottle = 87,
20 | ItemDeleted = 86,
21 | AccountLoginDeniedNeedTwoFactor = 85,
22 | RateLimitExceeded = 84,
23 | RegionLocked = 83,
24 | RestrictedDevice = 82,
25 | InvalidCEGSubmission = 81,
26 | UnexpectedError = 79,
27 | ValueOutOfRange = 78,
28 | RequirePasswordReEntry = 77,
29 | BadResponse = 76,
30 | NoMatchingURL = 75,
31 | AccountLogonDeniedVerifiedEmailRequired = 74,
32 | AccountLockedDown = 73,
33 | IPLoginRestrictionFailed = 72,
34 | ExpiredLoginAuthCode = 71,
35 | FacebookQueryError = 70,
36 | ParentalControlRestricted = 69,
37 | IPTInitError = 68,
38 | HardwareNotCapableOfIPT = 67,
39 | AccountLogonDeniedNoMail = 66,
40 | InvalidLoginAuthCode = 65,
41 | CannotUseOldPassword = 64,
42 | AccountLogonDenied = 63,
43 | SameAsPreviousValue = 62,
44 | IllegalPassword = 61,
45 | RemoteFileConflict = 60,
46 | ExternalAccountAlreadyLinked = 59,
47 | PSNTicketInvalid = 58,
48 | ExternalAccountUnlinked = 57,
49 | PasswordUnset = 56,
50 | RemoteCallFailed = 55,
51 | DiskFull = 54,
52 | DataCorruption = 53,
53 | Cancelled = 52,
54 | Suspended = 51,
55 | AlreadyLoggedInElsewhere = 50,
56 | PasswordRequiredToKickSession = 49,
57 | TryAnotherCM = 48,
58 | ContentVersion = 47,
59 | AdministratorOK = 46,
60 | AccountNotFeatured = 45,
61 | ServiceReadOnly = 44,
62 | AccountDisabled = 43,
63 | NoMatch = 42,
64 | ShoppingCartNotFound = 39,
65 | RemoteDisconnect = 38,
66 | IOFailure = 37,
67 | HandshakeFailed = 36,
68 | ConnectFailed = 35,
69 | LogonSessionReplaced = 34,
70 | LockingFailed = 33,
71 | PersistFailed = 32,
72 | IPNotFound = 31,
73 | AlreadyOwned = 30,
74 | DuplicateRequest = 29,
75 | AlreadyRedeemed = 28,
76 | Expired = 27,
77 | Revoked = 26,
78 | LimitExceeded = 25,
79 | InsufficientPrivilege = 24,
80 | EncryptionFailure = 23,
81 | Pending = 22,
82 | NotLoggedOn = 21,
83 | ServiceUnavailable = 20,
84 | InvalidSteamID = 19,
85 | AccountNotFound = 18,
86 | Timeout = 16,
87 | AccessDenied = 15,
88 | DuplicateName = 14,
89 | InvalidEmail = 13,
90 | InvalidName = 12,
91 | InvalidState = 11,
92 | FileNotFound = 9,
93 | InvalidParam = 8,
94 | InvalidProtocolVer = 7,
95 | LoggedInElsewhere = 6,
96 | InvalidPassword = 5,
97 | NoConnection = 3,
98 | Fail = 2,
99 | OK = 1
100 | }
101 |
--------------------------------------------------------------------------------
/Steam/src/SteamUserState.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum SteamUserState {
4 | Max = 7,
5 | LookingToPlay = 6,
6 | LookingToTrade = 5,
7 | Snooze = 4,
8 | Away = 3,
9 | Busy = 2,
10 | Online = 1,
11 | Offline = 0
12 | }
13 |
--------------------------------------------------------------------------------
/Steam/src/TransferProgress.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public class TransferProgress {
4 | public TransferProgress() {
5 | }
6 |
7 | public ItemUpdateStatus status;
8 |
9 | public ulong bytesDownloaded;
10 |
11 | public ulong bytesTotal;
12 | }
13 |
--------------------------------------------------------------------------------
/Steam/src/User.cs:
--------------------------------------------------------------------------------
1 | using Steamworks;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Runtime.ExceptionServices;
5 |
6 | public class User : IDisposable {
7 |
8 | private static Dictionary _users;
9 |
10 | internal static User GetUser(CSteamID id) {
11 | return GetUser(id.m_SteamID);
12 | }
13 |
14 | public static User GetUser(ulong id) {
15 | if (id == 0) {
16 | return null;
17 | }
18 | if (_users == null) {
19 | _users = new Dictionary();
20 | }
21 | using (Lock _lock = new Lock(_users)) {
22 | User user;
23 | if (!_users.TryGetValue(id, out user)) {
24 | user = new User(id);
25 | _users[id] = user;
26 | }
27 | return user;
28 | }
29 | }
30 |
31 | private CSteamID _id;
32 | public virtual ulong id => _id.m_SteamID;
33 |
34 | public virtual unsafe string name {
35 | get {
36 | if (id == 0)
37 | return "";
38 | return SteamFriends.GetFriendPersonaName(_id);
39 | }
40 | }
41 |
42 | private byte[] _avatarDataSmall;
43 | public virtual unsafe byte[] avatarSmall {
44 | get {
45 | if (id == 0)
46 | return null;
47 | if (_avatarDataSmall != null)
48 | return _avatarDataSmall;
49 | return _avatarDataSmall = _.GetImageRGBA(SteamFriends.GetSmallFriendAvatar(_id));
50 | }
51 | }
52 |
53 | private byte[] _avatarDataMedium;
54 | public virtual unsafe byte[] avatarMedium {
55 | get {
56 | if (id == 0)
57 | return null;
58 | if (_avatarDataMedium != null)
59 | return _avatarDataMedium;
60 | return _avatarDataMedium = _.GetImageRGBA(SteamFriends.GetMediumFriendAvatar(_id));
61 | }
62 | }
63 |
64 | public virtual unsafe bool inGame {
65 | get {
66 | if (id == 0)
67 | return false;
68 | FriendGameInfo_t game;
69 | return SteamFriends.GetFriendGamePlayed(_id, out game);
70 | }
71 | }
72 |
73 | public virtual unsafe bool inCurrentGame {
74 | get {
75 | if (id == 0)
76 | return false;
77 | FriendGameInfo_t game;
78 | return SteamFriends.GetFriendGamePlayed(_id, out game) && game.m_gameID.AppID() == SteamUtils.GetAppID();
79 | }
80 | }
81 |
82 | protected virtual unsafe bool inLobby {
83 | get {
84 | if (id == 0)
85 | return false;
86 | FriendGameInfo_t game;
87 | return SteamFriends.GetFriendGamePlayed(_id, out game) && game.m_steamIDLobby.m_SteamID != 0;
88 | }
89 | }
90 |
91 | public virtual unsafe bool inCurrentLobby {
92 | get {
93 | if (id == 0 || Steam.lobby == null)
94 | return false;
95 | FriendGameInfo_t game;
96 | return SteamFriends.GetFriendGamePlayed(_id, out game) && game.m_steamIDLobby.m_SteamID != Steam.lobby.id;
97 | }
98 | }
99 |
100 | public virtual unsafe UserInfo info {
101 | get {
102 | return new UserInfo() {
103 | inGame = inGame,
104 | inCurrentGame = inCurrentGame,
105 | inLobby = inLobby,
106 | inMyLobby = inCurrentLobby,
107 | state = state,
108 | relationship = relationship
109 | };
110 | }
111 | }
112 |
113 | public virtual unsafe SteamUserState state {
114 | get {
115 | if (id == 0)
116 | return SteamUserState.Offline;
117 | return (SteamUserState) SteamFriends.GetFriendPersonaState(_id);
118 | }
119 | }
120 |
121 | public virtual unsafe FriendRelationship relationship {
122 | get {
123 | if (id == 0)
124 | return FriendRelationship.None;
125 | return (FriendRelationship) SteamFriends.GetFriendRelationship(_id);
126 | }
127 | }
128 |
129 | private User(ulong id) {
130 | _id = new CSteamID(id);
131 | }
132 |
133 | internal User(CSteamID id) {
134 | _id = id;
135 | }
136 |
137 | [HandleProcessCorruptedStateExceptions]
138 | protected virtual void Dispose(bool flag) {
139 | }
140 |
141 | public void Dispose() {
142 | Dispose(true);
143 | }
144 |
145 | }
146 |
--------------------------------------------------------------------------------
/Steam/src/UserInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public class UserInfo {
4 | public UserInfo() {
5 | }
6 |
7 | public bool inGame;
8 |
9 | public bool inCurrentGame;
10 |
11 | public bool inLobby;
12 |
13 | public bool inMyLobby;
14 |
15 | public SteamUserState state;
16 |
17 | public FriendRelationship relationship;
18 | }
19 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.ExceptionServices;
4 | using Steamworks;
5 |
6 | public class WorkshopItem : IDisposable {
7 |
8 | private static Dictionary _items;
9 |
10 | internal static WorkshopItem GetItem(PublishedFileId_t id) {
11 | return GetItem(id.m_PublishedFileId);
12 | }
13 |
14 | public static WorkshopItem GetItem(ulong id) {
15 | if (id == 0) {
16 | return null;
17 | }
18 |
19 | if (_items == null) {
20 | _items = new Dictionary();
21 | }
22 | using (Lock _lock = new Lock(_items)) {
23 | WorkshopItem item;
24 | if (!_items.TryGetValue(id, out item)) {
25 | item = new WorkshopItem(id);
26 | _items[id] = item;
27 | }
28 | return item;
29 | }
30 |
31 | }
32 |
33 | private PublishedFileId_t _id;
34 | public ulong id => _id.m_PublishedFileId;
35 |
36 | private UGCUpdateHandle_t _currentUpdateHandle;
37 | public ulong updateHandle => _currentUpdateHandle.m_UGCUpdateHandle;
38 |
39 | public string name { get; private set; }
40 |
41 | public WorkshopItemData data { get; private set; }
42 |
43 | public bool finishedProcessing { get; set; }
44 | public SteamResult result { get; private set; }
45 | public SteamResult downloadResult { get; private set; }
46 |
47 | public unsafe WorkshopItemState stateFlags => (WorkshopItemState) SteamUGC.GetItemState(_id);
48 |
49 | public bool needsLegal { get; private set; }
50 |
51 | public unsafe string path {
52 | get {
53 | // FIXME: What even is the WorkshopItem.path meant to point to?
54 | /*
55 | sbyte* ptr = < Module>.new[](256u);
56 | int num = *(.SteamInternal_ContextInit((void*)(&.?s_CallbackCounterAndContext@?1??SteamInternal_ModuleContext@@YAAAVCSteamAPIContext@@XZ@4PAPAXA)) + 56);
57 | ulong num2;
58 | uint num3;
59 | if (calli(System.Byte modopt(System.Runtime.CompilerServices.CompilerMarshalOverride) modopt(System.Runtime.CompilerServices.CallConvThiscall)(System.IntPtr,System.UInt64,System.UInt64*,System.SByte modopt(System.Runtime.CompilerServices.IsSignUnspecifiedByte)*,System.UInt32,System.UInt32*), num, _id, ref num2, ptr, 256, ref num3, *(*num + 240)))
60 | {
61 | return new string((sbyte*)ptr);
62 | }
63 | */
64 |
65 | return "";
66 | }
67 | }
68 |
69 | // private object _tags; // unused
70 |
71 | public WorkshopItem(ulong id)
72 | : this(new PublishedFileId_t(id)) {
73 | }
74 |
75 | internal WorkshopItem(PublishedFileId_t id) {
76 | _id = id;
77 | finishedProcessing = true;
78 | result = SteamResult.OK;
79 | }
80 |
81 | public WorkshopItem() {
82 | }
83 |
84 | public void ApplyResult(SteamResult r, bool legal, ulong id) {
85 | result = r;
86 | needsLegal = legal;
87 | _id = new PublishedFileId_t(id);
88 | finishedProcessing = true;
89 | }
90 |
91 | public void ApplyDownloadResult(SteamResult r) {
92 | downloadResult = r;
93 | finishedProcessing = true;
94 | }
95 |
96 | public unsafe bool ApplyWorkshopData(WorkshopItemData data) {
97 | UGCUpdateHandle_t handle = SteamUGC.StartItemUpdate(SteamUtils.GetAppID(), _id);
98 | if (handle.m_UGCUpdateHandle == 0) {
99 | return false;
100 | }
101 | this.data = data;
102 | if (data.name != null) {
103 | SteamUGC.SetItemTitle(handle, data.name);
104 | SteamUGC.SetItemVisibility(handle, (ERemoteStoragePublishedFileVisibility) data.visibility);
105 | }
106 | if (data.description != null) {
107 | SteamUGC.SetItemDescription(handle, data.description);
108 | }
109 | List tags = data.tags;
110 | if (tags != null && tags.Count != 0) {
111 | SteamUGC.SetItemTags(handle, data.tags);
112 | }
113 | SteamUGC.SetItemPreview(handle, data.previewPath);
114 | SteamUGC.SetItemContent(handle, data.contentFolder);
115 | _currentUpdateHandle = handle;
116 | Steam.StartUpload(this);
117 | return true;
118 | }
119 |
120 | public unsafe TransferProgress GetUploadProgress() {
121 | ulong bytesDownloaded, bytesTotal;
122 | EItemUpdateStatus status = SteamUGC.GetItemUpdateProgress(_currentUpdateHandle, out bytesDownloaded, out bytesTotal);
123 | return new TransferProgress {
124 | status = (ItemUpdateStatus) (int) status,
125 | bytesDownloaded = bytesDownloaded,
126 | bytesTotal = bytesTotal
127 | };
128 | }
129 |
130 | public unsafe TransferProgress GetDownloadProgress() {
131 | ulong bytesDownloaded, bytesTotal;
132 | EItemUpdateStatus status = SteamUGC.GetItemUpdateProgress(_currentUpdateHandle, out bytesDownloaded, out bytesTotal);
133 | return new TransferProgress {
134 | status = ItemUpdateStatus.Invalid,
135 | bytesDownloaded = bytesDownloaded,
136 | bytesTotal = bytesTotal
137 | };
138 | }
139 |
140 | public void ResetProcessing() {
141 | finishedProcessing = false;
142 | needsLegal = false;
143 | }
144 |
145 | public void SkipProcessing() {
146 | finishedProcessing = true;
147 | needsLegal = false;
148 | result = SteamResult.OK;
149 | }
150 |
151 | public void SetDetails(string name, WorkshopItemData data) {
152 | this.name = name;
153 | this.data = data;
154 | }
155 |
156 | public unsafe void Subscribe() {
157 | SteamUGC.SubscribeItem(_id);
158 | }
159 |
160 | [HandleProcessCorruptedStateExceptions]
161 | protected virtual void Dispose(bool flag) {
162 | }
163 |
164 | public void Dispose() {
165 | Dispose(true);
166 | }
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopItemData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | public class WorkshopItemData {
5 | public WorkshopItemData() {
6 | }
7 |
8 | public string name;
9 |
10 | public string description;
11 |
12 | public string contentFolder;
13 |
14 | public string previewPath;
15 |
16 | public RemoteStoragePublishedFileVisibility visibility;
17 |
18 | public List tags;
19 |
20 | public string changeNotes;
21 |
22 | public int votesUp;
23 | }
24 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopItemState.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | [Flags]
4 | public enum WorkshopItemState : uint {
5 | None = 0,
6 | DownloadPending = 32,
7 | Downloading = 16,
8 | NeedsUpdate = 8,
9 | Installed = 4,
10 | LegacyItem = 2,
11 | Subscribed = 1
12 | }
13 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum WorkshopList {
4 | Followed = 8,
5 | UsedOrPlayed = 7,
6 | Subscribed = 6,
7 | Favorited = 5,
8 | WillVoteLater = 4,
9 | VotedDown = 3,
10 | VotedUp = 2,
11 | VotedOn = 1,
12 | Published = 0
13 | }
14 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopQueryAll.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Steamworks;
3 |
4 | public class WorkshopQueryAll : WorkshopQueryUGC {
5 |
6 | internal EUGCQuery _queryType;
7 |
8 | internal EUGCMatchingUGCType _fileType;
9 |
10 | public bool matchAnyTag { get; set; }
11 |
12 | public string searchText { get; set; }
13 |
14 | public uint trendRankDays { get; set; }
15 |
16 | internal WorkshopQueryAll(EUGCQuery eQueryType, EUGCMatchingUGCType eMatchingUGCTypeFileType) {
17 | matchAnyTag = false;
18 | searchText = null;
19 | trendRankDays = 0;
20 |
21 | _queryType = eQueryType;
22 | _fileType = eMatchingUGCTypeFileType;
23 | }
24 |
25 | internal unsafe override void Create() {
26 | _handle = SteamUGC.CreateQueryAllUGCRequest(_queryType, _fileType, SteamUtils.GetAppID(), SteamUtils.GetAppID(), page);
27 | }
28 |
29 | internal unsafe override void SetQueryData() {
30 | base.SetQueryData();
31 |
32 | SteamUGC.SetMatchAnyTag(_handle, matchAnyTag);
33 | SteamUGC.SetSearchText(_handle, searchText);
34 |
35 | if (trendRankDays != 0)
36 | SteamUGC.SetRankedByTrendDays(_handle, trendRankDays);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopQueryBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Steamworks;
4 | using System.Runtime.CompilerServices;
5 |
6 | public delegate void WorkshopQueryFinished(object sender);
7 | public delegate void WorkshopQueryResultFetched(object sender, WorkshopQueryResult result);
8 |
9 | public abstract class WorkshopQueryBase : IDisposable {
10 |
11 | internal readonly static bool _hasSetReturnOnlyIDs = typeof(SteamUGC).GetMethod("SetReturnOnlyIDs") != null;
12 |
13 | internal CallResult _completedCallResult;
14 |
15 | public event WorkshopQueryFinished QueryFinished;
16 | public event WorkshopQueryResultFetched ResultFetched;
17 |
18 | internal UGCQueryHandle_t _handle;
19 | internal ulong handle => _handle.m_UGCQueryHandle;
20 |
21 | public uint numResultsFetched { get; internal set; }
22 |
23 | public uint numResultsTotal { get; internal set; }
24 |
25 | public uint maxCachedTime { get; set; }
26 |
27 | public bool justOnePage { get; set; }
28 |
29 | public WorkshopQueryData fetchedData { get; set; }
30 |
31 | public uint page { get; set; }
32 |
33 | public bool onlyQueryIDs { get; set; }
34 |
35 | internal unsafe WorkshopQueryBase() {
36 | numResultsTotal = 0;
37 | numResultsFetched = 0;
38 | fetchedData = WorkshopQueryData.Details;
39 | maxCachedTime = 0;
40 | _completedCallResult = CallResult.Create(OnSteamUGCQueryCompleted);
41 | page = 1;
42 | _handle = new UGCQueryHandle_t();
43 | }
44 |
45 | unsafe ~WorkshopQueryBase() {
46 | Dispose(true);
47 | }
48 |
49 | internal abstract void Create();
50 |
51 | internal unsafe virtual void Destroy() {
52 | if (_handle.m_UGCQueryHandle != 0) {
53 | SteamUGC.ReleaseQueryUGCRequest(_handle);
54 | _handle = new UGCQueryHandle_t();
55 | }
56 | }
57 |
58 | internal unsafe virtual void SetQueryData() {
59 | WorkshopQueryData dataToFetch = fetchedData;
60 | if (dataToFetch == WorkshopQueryData.TotalOnly) {
61 | SteamUGC.SetReturnTotalOnly(_handle, true);
62 | } else {
63 | SteamUGC.SetReturnLongDescription(_handle, (dataToFetch & WorkshopQueryData.LongDescription) != 0);
64 | SteamUGC.SetReturnMetadata(_handle, (dataToFetch & WorkshopQueryData.Metadata) != 0);
65 | SteamUGC.SetReturnChildren(_handle, (dataToFetch & WorkshopQueryData.Children) != 0);
66 | SteamUGC.SetReturnAdditionalPreviews(_handle, (dataToFetch & WorkshopQueryData.AdditionalPreviews) != 0);
67 | }
68 | if (maxCachedTime != 0)
69 | SteamUGC.SetAllowCachedResponse(_handle, maxCachedTime);
70 | if (onlyQueryIDs && _hasSetReturnOnlyIDs)
71 | try {
72 | SetQueryData_SetReturnOnlyIDs(_handle, true);
73 | } catch (MissingMethodException) {
74 | // We're definitely using the stubbed Steamworks.NET now... unless someone dropped in an outdated version? But why?
75 | }
76 | }
77 |
78 | internal unsafe void SetQueryData_SetReturnOnlyIDs(UGCQueryHandle_t handle, bool bReturnOnlyIDs) {
79 | SteamUGC.SetReturnOnlyIDs(_handle, true);
80 | }
81 |
82 | public unsafe void Request() {
83 | try {
84 | _Request();
85 | } catch (TypeLoadException) {
86 | // We're definitely using the stubbed Steamworks.NET now... unless someone dropped in an outdated version? But why?
87 | QueryFinished?.Invoke(this);
88 | }
89 | }
90 |
91 | protected unsafe virtual void _Request() {
92 | if (handle == 0)
93 | Create();
94 | SetQueryData();
95 | SteamAPICall_t call = SteamUGC.SendQueryUGCRequest(_handle);
96 | if (call.m_SteamAPICall == 0)
97 | OnSteamUGCQueryCompleted(new SteamUGCQueryCompleted_t(), false);
98 | else
99 | _completedCallResult.Set(call);
100 | }
101 |
102 | private unsafe void OnSteamUGCQueryCompleted(SteamUGCQueryCompleted_t queryCompleted, bool ioFailure) {
103 | UGCQueryHandle_t handle = queryCompleted.m_handle;
104 | if (_handle.m_UGCQueryHandle == 0 || ioFailure)
105 | goto Finish;
106 |
107 | numResultsTotal = queryCompleted.m_unTotalMatchingResults;
108 | uint results = queryCompleted.m_unNumResultsReturned;
109 | if (results == 0 || fetchedData == WorkshopQueryData.TotalOnly)
110 | goto Finish;
111 |
112 | numResultsFetched += results;
113 | for (uint resulti = 0; resulti < results; resulti++) {
114 | WorkshopQueryResult result = new WorkshopQueryResult();
115 | SteamUGC.GetQueryUGCPreviewURL(handle, resulti, out result.previewURL, 260);
116 |
117 | SteamUGCDetails_t ugcDetails;
118 | SteamUGC.GetQueryUGCResult(queryCompleted.m_handle, resulti, out ugcDetails);
119 | WorkshopQueryResultDetails resultDetails = result.details = new WorkshopQueryResultDetails();
120 |
121 | resultDetails.acceptedForUse = ugcDetails.m_bAcceptedForUse;
122 | resultDetails.banned = ugcDetails.m_bBanned;
123 |
124 | resultDetails.title = ugcDetails.m_rgchTitle;
125 | resultDetails.description = ugcDetails.m_rgchDescription;
126 | resultDetails.steamIDOwner = ugcDetails.m_ulSteamIDOwner;
127 | resultDetails.score = ugcDetails.m_flScore;
128 | resultDetails.visibility = ugcDetails.m_eVisibility;
129 | resultDetails.votesDown = ugcDetails.m_unVotesDown;
130 | resultDetails.votesUp = ugcDetails.m_unVotesUp;
131 |
132 | resultDetails.tags = ugcDetails.m_rgchTags.Split(',');
133 | resultDetails.tagsTruncated = ugcDetails.m_bTagsTruncated;
134 |
135 | resultDetails.timeAddedToUserList = ugcDetails.m_rtimeAddedToUserList;
136 | resultDetails.timeCreated = ugcDetails.m_rtimeCreated;
137 | resultDetails.timeUpdated = ugcDetails.m_rtimeUpdated;
138 |
139 | resultDetails.file = ugcDetails.m_hFile.m_UGCHandle;
140 | resultDetails.fileName = ugcDetails.m_pchFileName;
141 | resultDetails.fileSize = ugcDetails.m_nFileSize;
142 | resultDetails.fileType = ugcDetails.m_eFileType;
143 |
144 | resultDetails.numChildren = ugcDetails.m_unNumChildren;
145 |
146 | resultDetails.previewFile = ugcDetails.m_hPreviewFile.m_UGCHandle;
147 | resultDetails.previewFileSize = ugcDetails.m_nPreviewFileSize;
148 |
149 | resultDetails.URL = ugcDetails.m_rgchURL;
150 |
151 | resultDetails.publishedFile = WorkshopItem.GetItem(ugcDetails.m_nPublishedFileId);
152 |
153 | resultDetails.result = ugcDetails.m_eResult;
154 |
155 | if ((fetchedData & WorkshopQueryData.Children) != 0) {
156 | PublishedFileId_t[] children = new PublishedFileId_t[resultDetails.numChildren];
157 | if (SteamUGC.GetQueryUGCChildren(handle, resulti, children, (uint) children.Length))
158 | result.fileList = _.GetArray(children, id => WorkshopItem.GetItem(id));
159 | }
160 |
161 | if ((fetchedData & WorkshopQueryData.Metadata) != 0) {
162 | SteamUGC.GetQueryUGCMetadata(handle, resulti, out result.metadata, 260);
163 | }
164 |
165 | if ((fetchedData & WorkshopQueryData.AdditionalPreviews) != 0) {
166 | WorkshopQueryResultAdditionalPreview[] previews = result.additionalPreviews = new WorkshopQueryResultAdditionalPreview[SteamUGC.GetQueryUGCNumAdditionalPreviews(handle, resulti)];
167 | for (uint previewi = 0; previewi < previews.Length; previewi++) {
168 | string url;
169 | string name;
170 | EItemPreviewType type;
171 | if (SteamUGC.GetQueryUGCAdditionalPreview(handle, resulti, previewi, out url, 260, out name, 260, out type))
172 | previews[previewi] = new WorkshopQueryResultAdditionalPreview(type == EItemPreviewType.k_EItemPreviewType_Image, url);
173 | }
174 | }
175 |
176 | if ((fetchedData & WorkshopQueryData.Statistics) != 0) {
177 | uint[] stats = result.statistics = new uint[8];
178 | for (WorkshopResultStatistic stat = WorkshopResultStatistic.NumSubscriptions; (int) stat < stats.Length; stat++) {
179 | ulong val;
180 | if (SteamUGC.GetQueryUGCStatistic(handle, resulti, (EItemStatistic) stat, out val))
181 | stats[(int) stat] = (uint) val;
182 | }
183 | }
184 |
185 | ResultFetched?.Invoke(this, result);
186 | }
187 |
188 | if (numResultsFetched != numResultsTotal && !justOnePage) {
189 | Destroy();
190 | page++;
191 | Create();
192 | Request();
193 | return;
194 | }
195 |
196 | Finish:
197 | QueryFinished?.Invoke(this);
198 | }
199 |
200 | protected virtual void Dispose(bool flag) {
201 | Destroy();
202 |
203 | _completedCallResult?.Cancel();
204 | (_completedCallResult as IDisposable)?.Dispose();
205 | _completedCallResult = null;
206 | }
207 |
208 | public void Dispose() {
209 | Dispose(true);
210 | GC.SuppressFinalize(this);
211 | }
212 |
213 | }
214 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopQueryData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | [Flags]
4 | public enum WorkshopQueryData {
5 | Statistics = 32,
6 | LongDescription = 16,
7 | AdditionalPreviews = 8,
8 | Children = 4,
9 | Metadata = 2,
10 | PreviewURL = 1,
11 | Details = 0,
12 | TotalOnly = -1
13 | }
14 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopQueryFileDetails.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Steamworks;
4 |
5 | public class WorkshopQueryFileDetails : WorkshopQueryBase {
6 |
7 | public IList files { get; internal set; }
8 |
9 | public WorkshopQueryFileDetails() {
10 | files = new List();
11 | }
12 |
13 | internal unsafe override void Create() {
14 | _handle = SteamUGC.CreateQueryUGCDetailsRequest(_.GetArray(files, id => new PublishedFileId_t(id)), (uint) files.Count);
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopQueryFilterOrder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum WorkshopQueryFilterOrder {
4 | RankedByTotalUniqueSubscriptions = 12,
5 | RankedByTextSearch = 11,
6 | RankedByVotesUp = 10,
7 | RankedByTotalVotesAsc = 9,
8 | NotYetRated = 8,
9 | CreatedByFollowedUsersRankedByPublicationDate = 7,
10 | RankedByNumTimesReported = 6,
11 | CreatedByFriendsRankedByPublicationDate = 5,
12 | FavoritedByFriendsRankedByPublicationDate = 4,
13 | RankedByTrend = 3,
14 | AcceptedForGameRankedByAcceptanceDate = 2,
15 | RankedByPublicationDate = 1,
16 | RankedByVote = 0
17 | }
18 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopQueryResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public class WorkshopQueryResult {
4 | public WorkshopQueryResult() {
5 | }
6 |
7 | public WorkshopQueryResultDetails details;
8 |
9 | public string previewURL;
10 |
11 | public string metadata;
12 |
13 | public WorkshopItem[] fileList;
14 |
15 | public WorkshopQueryResultAdditionalPreview[] additionalPreviews;
16 |
17 | public uint[] statistics;
18 | }
19 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopQueryResultAdditionalPreview.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public class WorkshopQueryResultAdditionalPreview {
4 | public WorkshopQueryResultAdditionalPreview(bool isImage, string urlOrVideoID) {
5 | this.isImage = isImage;
6 | this.urlOrVideoID = urlOrVideoID;
7 | }
8 |
9 | public bool isImage;
10 |
11 | public string urlOrVideoID;
12 | }
13 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopQueryResultDetails.cs:
--------------------------------------------------------------------------------
1 | using Steamworks;
2 | using System;
3 |
4 | public class WorkshopQueryResultDetails {
5 | public WorkshopQueryResultDetails() {
6 | }
7 |
8 | public WorkshopItem publishedFile;
9 |
10 | public EResult result;
11 |
12 | public EWorkshopFileType fileType;
13 |
14 | public string title;
15 |
16 | public string description;
17 |
18 | public ulong steamIDOwner;
19 |
20 | public uint timeCreated;
21 |
22 | public uint timeUpdated;
23 |
24 | public uint timeAddedToUserList;
25 |
26 | public ERemoteStoragePublishedFileVisibility visibility;
27 |
28 | public bool banned;
29 |
30 | public bool acceptedForUse;
31 |
32 | public bool tagsTruncated;
33 |
34 | public string[] tags;
35 |
36 | public ulong file;
37 |
38 | public ulong previewFile;
39 |
40 | public string fileName;
41 |
42 | public int fileSize;
43 |
44 | public int previewFileSize;
45 |
46 | public string URL;
47 |
48 | public uint votesUp;
49 |
50 | public uint votesDown;
51 |
52 | public float score;
53 |
54 | public uint numChildren;
55 | }
56 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopQueryUGC.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Steamworks;
4 |
5 | public abstract class WorkshopQueryUGC : WorkshopQueryBase {
6 |
7 | public IList requiredTags { get; internal set; }
8 |
9 | public IList excludedTags { get; internal set; }
10 |
11 | internal WorkshopQueryUGC() {
12 | requiredTags = new List();
13 | excludedTags = new List();
14 | }
15 |
16 | internal unsafe override void SetQueryData() {
17 | base.SetQueryData();
18 |
19 | foreach (string tag in requiredTags)
20 | SteamUGC.AddRequiredTag(_handle, tag);
21 |
22 | foreach (string tag in excludedTags)
23 | SteamUGC.AddExcludedTag(_handle, tag);
24 |
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopQueryUser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Steamworks;
3 |
4 | public class WorkshopQueryUser : WorkshopQueryUGC {
5 |
6 | internal AccountID_t _accountID;
7 |
8 | internal EUserUGCList _listType;
9 |
10 | internal EUGCMatchingUGCType _type;
11 |
12 | internal EUserUGCListSortOrder _sortOrder;
13 |
14 | public string cloudNameFileFilter { get; set; }
15 |
16 | internal WorkshopQueryUser(uint unAccountID, EUserUGCList eListType, EUGCMatchingUGCType eMatchingUGCType, EUserUGCListSortOrder eSortOrder) {
17 | _accountID = new AccountID_t(unAccountID);
18 | _listType = eListType;
19 | _type = eMatchingUGCType;
20 | _sortOrder = eSortOrder;
21 | }
22 |
23 | internal unsafe override void Create() {
24 | _handle = SteamUGC.CreateQueryUserUGCRequest(_accountID, _listType, _type, _sortOrder, SteamUtils.GetAppID(), SteamUtils.GetAppID(), page);
25 | }
26 |
27 | internal unsafe override void SetQueryData() {
28 | base.SetQueryData();
29 |
30 | SteamUGC.SetCloudFileNameFilter(_handle, cloudNameFileFilter);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopResultStatistic.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum WorkshopResultStatistic {
4 | Total = 8,
5 | ReportScore = 7,
6 | NumUniqueWebsiteViews = 6,
7 | NumUniqueFollowers = 5,
8 | NumUniqueFavorites = 4,
9 | NumUniqueSubscriptions = 3,
10 | NumFollowers = 2,
11 | NumFavorites = 1,
12 | NumSubscriptions = 0
13 | }
14 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopSortOrder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum WorkshopSortOrder {
4 | ForModeration = 6,
5 | VoteScoreDesc = 5,
6 | SubscriptionDateDesc = 4,
7 | LastUpdatedDesc = 3,
8 | TitleAsc = 2,
9 | CreationOrderAsc = 1,
10 | CreationOrderDesc = 0
11 | }
12 |
--------------------------------------------------------------------------------
/Steam/src/WorkshopType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum WorkshopType {
4 | Collections = 3,
5 | ControllerBindings = 11,
6 | UsableInGame = 10,
7 | IntegratedGuides = 9,
8 | WebGuides = 8,
9 | AllGuides = 7,
10 | Screenshots = 6,
11 | Videos = 5,
12 | Artwork = 4,
13 | Items_ReadyToUse = 2,
14 | Items_Mtx = 1,
15 | Items = 0
16 | }
17 |
--------------------------------------------------------------------------------
/Steam/src/_.cs:
--------------------------------------------------------------------------------
1 | using Steamworks;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | // Common helper class that turns out to be used ~ 2 times per function...
8 | internal static class _ {
9 |
10 | public static List GetList(int count, Func get) {
11 | if (count <= 0)
12 | return new List();
13 | List list = new List(count);
14 | for (int i = 0; i < count; i++)
15 | list.Add(get(i));
16 | return list;
17 | }
18 |
19 | public static TOut[] GetArray(IList list, Func get) {
20 | if (list.Count <= 0)
21 | return new TOut[0];
22 | TOut[] array = new TOut[list.Count];
23 | for (int i = 0; i < array.Length; i++)
24 | array[i] = get(list[i]);
25 | return array;
26 | }
27 |
28 | public static byte[] GetImageRGBA(int id) {
29 | uint w;
30 | uint h;
31 | if (!SteamUtils.GetImageSize(id, out w, out h))
32 | return null;
33 | byte[] data = new byte[w * h * 4];
34 | if (!SteamUtils.GetImageRGBA(id, data, data.Length))
35 | return null;
36 | return data;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Steamworks.NET/CSteamworks.bundle/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 11G63
7 | CFBundleDevelopmentRegion
8 | English
9 | CFBundleExecutable
10 | CSteamworks
11 | CFBundleIdentifier
12 | com.rileylabrecque.CSteamworks
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1.30
21 | CSResourcesFileMapped
22 | yes
23 | DTCompiler
24 |
25 | DTPlatformBuild
26 | 4H1503
27 | DTPlatformVersion
28 | GM
29 | DTSDKBuild
30 | 11E52
31 | DTSDKName
32 | macosx10.7
33 | DTXcode
34 | 0463
35 | DTXcodeBuild
36 | 4H1503
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Steamworks.NET/CSteamworks.bundle/Contents/MacOS/CSteamworks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/DuckGame-Linux/a2b51193c36d440bdab402f43b7485bb940d1fbd/Steamworks.NET/CSteamworks.bundle/Contents/MacOS/CSteamworks
--------------------------------------------------------------------------------
/Steamworks.NET/CSteamworks.bundle/Contents/MacOS/libsteam_api.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/DuckGame-Linux/a2b51193c36d440bdab402f43b7485bb940d1fbd/Steamworks.NET/CSteamworks.bundle/Contents/MacOS/libsteam_api.dylib
--------------------------------------------------------------------------------
/Steamworks.NET/CSteamworks.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/DuckGame-Linux/a2b51193c36d440bdab402f43b7485bb940d1fbd/Steamworks.NET/CSteamworks.dll
--------------------------------------------------------------------------------
/Steamworks.NET/Steamworks.NET-linux.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/DuckGame-Linux/a2b51193c36d440bdab402f43b7485bb940d1fbd/Steamworks.NET/Steamworks.NET-linux.dll
--------------------------------------------------------------------------------
/Steamworks.NET/Steamworks.NET-stubbed.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/DuckGame-Linux/a2b51193c36d440bdab402f43b7485bb940d1fbd/Steamworks.NET/Steamworks.NET-stubbed.dll
--------------------------------------------------------------------------------
/Steamworks.NET/Steamworks.NET.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/DuckGame-Linux/a2b51193c36d440bdab402f43b7485bb940d1fbd/Steamworks.NET/Steamworks.NET.dll
--------------------------------------------------------------------------------
/Steamworks.NET/libCSteamworks.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/DuckGame-Linux/a2b51193c36d440bdab402f43b7485bb940d1fbd/Steamworks.NET/libCSteamworks.so
--------------------------------------------------------------------------------
/Steamworks.NET/libsteam_api.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/DuckGame-Linux/a2b51193c36d440bdab402f43b7485bb940d1fbd/Steamworks.NET/libsteam_api.so
--------------------------------------------------------------------------------
/Steamworks.NET/steam_api64.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/DuckGame-Linux/a2b51193c36d440bdab402f43b7485bb940d1fbd/Steamworks.NET/steam_api64.dll
--------------------------------------------------------------------------------
/libs/DuckGame.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/DuckGame-Linux/a2b51193c36d440bdab402f43b7485bb940d1fbd/libs/DuckGame.exe
--------------------------------------------------------------------------------
/libs/FNA.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/DuckGame-Linux/a2b51193c36d440bdab402f43b7485bb940d1fbd/libs/FNA.dll
--------------------------------------------------------------------------------
/libs/README.md:
--------------------------------------------------------------------------------
1 | # The DuckGame.exe is mono-cil-stripped!
2 | It doesn't contain any code and is only provided to ensure that the "mod" compiles properly out of the box.
3 | If any questions arise, don't hesitate to contact me.
4 |
--------------------------------------------------------------------------------
/libs/XnaToFna.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/DuckGame-Linux/a2b51193c36d440bdab402f43b7485bb940d1fbd/libs/XnaToFna.exe
--------------------------------------------------------------------------------