360 Commits
nightly ... CBT

Author SHA1 Message Date
Eero
9d6cb5225a Refactor server event processing and entity updates 2026-05-02 00:28:12 +08:00
NotAlwaysTrue
8b6da6b033 Added a null check for STW
Changed positions of notes
2026-04-30 22:35:30 +08:00
NotAlwaysTrue
02689d0d86 Refactor single-thread worker
Re-parallel Hull Update
Use new gap shaffle algorithm
2026-04-30 20:05:23 +08:00
NotAlwaysTrue
099d664731 Fixed issues bring by update 2026-04-25 13:21:25 +08:00
NotAlwaysTrue
1f7d695bba Removed LuaSafeUserData to sync with upstream 2026-04-25 13:11:05 +08:00
NotAlwaysTrue
50327a4d83 Merge branch 'heads/upstream' into OBT/1.2.0(SpringUpdate) 2026-04-25 13:08:16 +08:00
NotAlwaysTrue
9b35f6b23f Sync with upstream
* Update bug-reports.yml

* Fix modifyChatMessage hook

* Add LuaCsSetup.Lua back for compatibility

* Fix Game.AssignOnExecute having command arguments be passed as varargs instead of a table

* Actually use the PackageId const everywhere we need to refer to our content package

* Load languages files even if the package is disabled

* Fix Hook.Remove not being implemented properly

* - Changed event aliases to be case insensitive.

* - Fixed assembly logging style.
- Fixed double logging during execution.

* Fix garbage network data being read by the game when reading LuaCs network messages

* PackageId -> PackageName

* Added caching toggle to PluginManagementService

* Fix LuaCs initializing too late for singleplayer campaigns and rework the C# prompt to only show when enabling mods/joining server

* Oops, fix NRE crash

* Fix hide username in logs config not doing anything

* Fix Cs prompt showing up more than one between rounds

* Fix server host being prompted twice with the C# popup

* Ignore our workshop packages from the game's dependency thing since it doesn't really make sense

* Load console commands after executing and possible fix for the not console command permitted

* Added fallback friendly name resolution for ModConfig assembly contents.

* Register Voronoi2 stuff

* Added configinfo null check to SettingBase.cs

* Add safety check so this stops crashing when we look at it the wrong way

* Fixed "Folder" attribute files not being found.

* Keep the LuaCsConfig class laying around for compatibility, not sure anywhere in our code base (and shouldn't be)

* Added fallback compilation for UseInternalsAwareAssembly if the publicized script compilation fails.

* Added legacy overload of AddCommand for mod compat.

* Added LoggerService to Lua env. Made ILoggerService compliant with LuaCsLogger API.

* Changed csharp script compilation algorithm to be best effort.

* Added "RunUnrestricted" mode for lua scripts that need to run outside of sandbox.

* - Fixed networking sync vars failing to sync initially.
- Fixed lua failing to differentiate overloads ISettingBase.

* Add alias for human.CPRSuccess and human.CPRFailed

* - Fixed up the settings menu.
- Made SettingEntry throw an error if "Value" attribute is not found in XML.
- Fixed saved values for settings sometimes not reloading after disabling and re-enabling a package.

* Fix LuaCs net messages received during connection initialization to be read incorrectly, happened because we would reset the BitPosition in our harmony patch which would cause the message to be read incorrectly later

* Allow reloadlua to force the state to running

* New icon for settings and make the top left text more user friendly

* Fix client.packages hook sending normal packages

* Fixed OnUpdate() not passing in deltaTime instead of totalTime.

* Missing diffs from bb21a09244

* Added networking tests for configs.

* Added missing diffs for f61f852a25.

* Some tweaks to the text

* Remove missing Value error, it should just use the default value if it's not specified

* Fix UseInternalAccessName

* Always purge cashes for plugin content on unloading.

* Fix texture not multiple of 4

* v1.12.7.0 (Spring Update 2026 Hotfix 1)

---------

Co-authored-by: Joonas Rikkonen <poe.regalis@gmail.com>
Co-authored-by: Evil Factory <36804725+evilfactory@users.noreply.github.com>
Co-authored-by: MapleWheels <njainanan@hotmail.com>
2026-04-25 12:10:24 +08:00
Evil Factory
928cfb4fde Re-add loaded Lua hook call 2026-04-09 12:09:00 -03:00
Evil Factory
a9634988e9 Fix signalReceived hook not being called 2026-04-09 11:18:18 -03:00
MapleWheels
87e0191a97 - Debug tests. 2026-04-09 08:35:17 -04:00
Evil Factory
790378d2a5 Merge remote-tracking branch 'upstream/master' into develop 2026-04-09 09:12:16 -03:00
Regalis11
a4607dffad v1.12.6.2 (Spring Update 2026) 2026-04-09 15:10:07 +03:00
MapleWheels
ef66d27ffe - Fixed network synchro of vars, needs synctype testing. 2026-04-09 05:34:50 -04:00
MapleWheels
84cb7cfeb7 server auth var. 2026-04-09 01:29:48 -04:00
MapleWheels
53be3f0073 Fixed lua attempting to invoke the base interface type. 2026-04-08 19:49:57 -04:00
MapleWheels
89aa818c0b Made Config initialization errors more forgiving to noobs. 2026-04-08 19:38:31 -04:00
Evil Factory
3a5fbfde1e Fix cs enabled for session state not being preserved between reloads 2026-04-08 20:12:20 -03:00
Evil Factory
b9b457262f Test Lua config mod 2026-04-08 19:56:32 -03:00
Evil Factory
e161d92117 Fix settings not being properly exposed to Lua 2026-04-08 19:44:16 -03:00
MapleWheels
2e656509a8 - Added null check to get string value. 2026-04-08 17:53:12 -04:00
MapleWheels
b0b4acf392 - Made readonly readonly. 2026-04-08 17:29:56 -04:00
Evil Factory
e9673bf7f5 Update moonsharp 2026-04-08 18:29:29 -03:00
Evil Factory
eeeb3d9db3 Expose all settings in Lua 2026-04-08 18:29:07 -03:00
Evil Factory
c882b0bb45 Update moonsharp 2026-04-08 18:07:12 -03:00
Evil Factory
6c32873d4e Only register ILuaConfigService 2026-04-08 18:04:25 -03:00
MapleWheels
bdd4dcfb9e Disabled caching on the ConfigService. 2026-04-08 16:27:01 -04:00
MapleWheels
ebe8ec455f LuaCs CSharp Enabled Rework
- New UI for the prompt

- Third time's the charm.

- Fixed TOCTOU for cs prompt on main menu. - Fixed SubEditor not running lua when don't run is selected for cs.

- LuaCs CSharp Enabled Rework
2026-04-08 16:31:42 -03:00
Evil Factory
d440ccbce2 Register IConfigService and add back some missing APIs 2026-04-08 15:32:31 -03:00
MapleWheels
1294ba6dec - Added API to get package by name to PackageManagementService.cs and LuaScriptManagementService.cs
- Added ILuaConfigService to Lua API.
2026-04-08 11:03:52 -04:00
MapleWheels
4c8e016dea - Added extra source code translation (for CTS).
- Added localization to SettingList.cs display dropdown.
2026-04-08 08:09:45 -04:00
MapleWheels
232f7203e2 Fixed shared Csharp src files not being included if there weren't also architecture-specific files. 2026-04-07 16:07:55 -04:00
MapleWheels
df0a4e62f5 Added logging for additional plugin loading exceptions. 2026-04-07 15:43:01 -04:00
MapleWheels
7055480015 - Fixed publicized Barotrauma.dll missing error on DedicatedServer.
- Fixed non-implemented folder search for ModConfig resources.
2026-04-05 13:09:34 -04:00
Evil Factory
0e14983e88 Allow System.Console 2026-04-05 11:20:08 -03:00
Evil Factory
9b05c51ae5 Fix calling Hook.Call directly from Lua being broken 2026-04-04 22:52:27 -03:00
Evil Factory
5e003bcf11 Fix missing Game.Commands 2026-04-04 22:30:46 -03:00
Evil Factory
121f670c8b Fix character.death hook getting called a billion times per second 2026-04-04 13:04:34 -03:00
Evil Factory
f05d4ef129 Fic CSharp enabling message printing before config got loaded 2026-04-04 12:24:12 -03:00
Evil Factory
4167448279 Fix Game.Settings being nil 2026-04-04 11:50:35 -03:00
MapleWheels
413cc3ed4c - Added setting to disable lua scripts caching in the storage service for scenarios that use extern editors. 2026-04-02 23:29:21 -04:00
MapleWheels
4f2da55a8e - Added legacy LuaCsPerformanceCounter.cs
- Added legacy redirects.
- Added IConsoleCommandsService injection to plugins.
2026-03-31 10:48:44 -04:00
Maplewheels
f1808d9231 Added verbose cs compilation. 2026-03-29 00:30:18 -04:00
Evil Factory
bcec409618 Fix deep-fried main menu text 2026-03-28 22:20:22 -03:00
Evil Factory
14c610e6c7 Re-added the install_cl_lua command 2026-03-28 21:50:13 -03:00
Evil Factory
958335a01c Replicate old Add() method signature structure 2026-03-28 14:14:11 -03:00
Evil Factory
b358882016 This should be ItemPrefab, not Item, also add it the source code compat 2026-03-28 14:03:28 -03:00
Evil Factory
b8f1642d9d Simplify command register 2026-03-28 13:04:11 -03:00
Evil Factory
64818775cb Whoops 2026-03-28 13:04:04 -03:00
Evil Factory
5bfa15564a Move partial classes to extension methods, the ones that can't, turn into Lua compatibility members 2026-03-28 12:51:53 -03:00
Evil Factory
2ea97d3d5e Add CallLuaFunction legacy redirect 2026-03-28 10:52:33 -03:00
Evil Factory
bb2490eb13 Fix wrong arguments in RemovePatch 2026-03-27 21:58:06 -03:00
Evil Factory
57daa3ccb7 Fix missing type register 2026-03-27 21:40:44 -03:00
MapleWheels
c1fdedf955 Fixed ConfigService not filtering out configs that it shouldn't be loading. 2026-03-24 10:36:46 -04:00
MapleWheels
b70b6bf326 Fixed deadlock scenario in the event dispatcher CRUD API. 2026-03-24 02:44:08 -04:00
MapleWheels
28acf02de0 Updated client-side/common files tracking list for luacsinstaller. 2026-03-23 08:47:19 -04:00
MapleWheels
1e76377b63 - Added full implementation of lua event "modifyChatMessage" in IEvents. 2026-03-23 07:51:49 -04:00
MapleWheels
3dd9cfa741 Implemented plugin assembly lookup api. 2026-03-23 07:30:15 -04:00
Maplewheels
26ac37ed46 Added fallback native assembly resolver. 2026-03-23 00:25:51 -04:00
Maplewheels
5c136aab41 Added test debug settings for settings list. 2026-03-22 23:56:27 -04:00
Maplewheels
cf6104f630 Added Dropdown menu implementation for settings list. 2026-03-22 23:56:26 -04:00
Maplewheels
7c8dd452cf SettingsList implementation.
Merge conflict resolution.
2026-03-22 23:56:25 -04:00
MapleWheels
cf3a50d6b5 - Added back modifyChatMessage.
- Added missing commit file for previous commit.
2026-03-22 18:05:21 -04:00
MapleWheels
ac329a70a4 - Reduced console spam when failing to load config file in release builds.
- Added console logging for failing to unload assembly load ctx.
- Removed unused StorageService instance.
- Removed unused luacs settings.
- Added plugin event service and event service dispatcher api.
2026-03-22 18:01:19 -04:00
Evil Factory
e62450c763 Actually subscribe to IEventAssemblyUnloading 2026-03-22 17:24:02 -03:00
Evil Factory
44a9ce2417 Guh? 2026-03-22 12:44:17 -03:00
Evil Factory
23e0ff7aa6 Remove types that have been registered when unloading 2026-03-22 12:42:23 -03:00
Evil Factory
de53b4514e Fix console command registration 2026-03-22 12:42:09 -03:00
Maplewheels
994610869d Fixed deadlock scenario caused by unsubscribing during an event. 2026-03-22 01:56:30 -04:00
Evil Factory
b0e3faa2ad Make LuaCsLogger public 2026-03-21 15:59:48 -03:00
MapleWheels
fe34dcb0bd Added null location check to assembly inclusion list. 2026-03-17 10:58:48 -04:00
Evil Factory
8717706b1c Fix check ready to run not working properly 2026-03-15 18:44:15 -03:00
Evil Factory
b402e09b82 Move MainMenu code to a separate service that just injects our stuff into the mainmenu 2026-03-15 17:52:40 -03:00
Maplewheels
80832459b9 Added api for getting content package associatged with a plugin type. 2026-03-14 23:47:44 -04:00
Evil Factory
cf2c8b8e0e Add missing HttpGet in the interface 2026-03-14 14:45:03 -03:00
Evil Factory
f802356b0a Add legacy redirect for LuaCsSetup.Timer 2026-03-14 14:31:03 -03:00
Evil Factory
ecc4d8a6c1 Update moonsharp 2026-03-14 14:00:59 -03:00
Evil Factory
d4b774642b ModUtils namespace compatibility 2026-03-14 13:41:12 -03:00
Evil Factory
103f28481d Register setting types 2026-03-14 12:07:23 -03:00
Evil Factory
dd51bdae3f Revert "performance fixes for IDE/attached debugger stuttering."
This reverts commit e5aa381e38.
2026-03-14 12:04:29 -03:00
NotAlwaysTrue
5207b381b7 OBT/1.1.1 (#51)
Reverted an outdated change which is no longer in use
Removed CL-EP install command due to partical system issues
2026-03-13 13:34:21 +08:00
MapleWheels
e5aa381e38 performance fixes for IDE/attached debugger stuttering. 2026-03-10 03:22:30 -04:00
Maplewheels
8190b3f312 Fixed cs enabled prompt for hosts. 2026-03-08 22:07:56 -04:00
MapleWheels
e26cfa2e52 [Untested][Save/Sync]
- Fix for IsCsEnabled check.
2026-03-06 18:49:55 -05:00
MapleWheels
5b52d22b1f Added references cache for some hot paths services. 2026-03-06 18:15:44 -05:00
MapleWheels
5a452709c3 Minor adjustment to setting entry height and styles. 2026-03-06 08:31:09 -05:00
MapleWheels
8470e81dc7 Added tooltips and SettingControl input listener to the settings menu. Minor cosmetic adjustments. 2026-03-06 04:57:00 -05:00
MapleWheels
947a481400 Added spacer to settings labels. 2026-03-05 19:43:04 -05:00
MapleWheels
368b18d440 - Fixed shet. 2026-03-05 17:48:27 -05:00
MapleWheels
1fe68aa861 - Added float, double settingentry types. Adjusted display formatting.
- Added menu refresh on Apply button.
- Fixed package name resolution for invalid chars.
- Added helper console command to get xml-safe name for use in localization files.
- Made error messages for bad settings xml more descriptive.
- Modified GUISlider.
- Converted HarmonyEventPatchesService to a System.
- Fixed package name resolution for incompatible names in Xml and files, in many places.
- Fixed base textbox implementation for settings.
2026-03-05 15:45:00 -05:00
MapleWheels
f38a7bd574 The gameplay settings menu kinda works (only for luacsforbarotrauma). 2026-03-04 20:39:13 -05:00
MapleWheels
ce8b984542 Fixed Csharp/Shared path resolution in ModConfigService. 2026-03-04 14:51:31 -05:00
MapleWheels
d0969cc723 - Fixed assembly references not resolving for minor dll signature differences.
- Fixed deadlock during assembly resolution.
2026-03-04 14:38:00 -05:00
MapleWheels
a66b9041ec Fixed reference name error. 2026-03-03 22:44:56 -05:00
MapleWheels
83c198bc26 [Save/Sync] Commit work before .NET 10 tests. 2026-03-03 20:45:51 -05:00
MapleWheels
26b3827210 - Made publicizer only copy the required assemblies. 2026-03-02 08:46:03 -05:00
Maplewheels
4b04131fe3 Added post-publishing publicized assemblies copy task. 2026-03-02 02:45:43 -05:00
MapleWheels
168ce83820 Fixed potential NRE that shouldn't happen so long as the LuaCsForBarotrauma exists. 2026-03-01 17:13:15 -05:00
Evil Factory
5cbb635e54 Ok this should be the last one 2026-03-01 14:39:07 -03:00
Evil Factory
0452c1fc2d oops 2026-03-01 14:18:51 -03:00
Evil Factory
800dec3b84 I guess this doesnt exist anymore 2026-03-01 14:06:45 -03:00
Evil Factory
580f26b526 New CI files 2026-03-01 13:40:08 -03:00
Evil Factory
f8ff97d2b7 Fix debug console commands 2026-03-01 13:26:36 -03:00
Evil Factory
9ee4728e2a Better logging 2026-03-01 13:09:05 -03:00
Evil Factory
22a74bf1fd Move compat hooks 2026-03-01 12:43:59 -03:00
Evil Factory
845fcefad7 Fix Start(0 not returning an empty write only message 2026-03-01 09:50:52 -03:00
Maplewheels
9b55bf4847 [Save/Sync] Work on the settings menu. 2026-03-01 06:03:31 -05:00
Maplewheels
e74f083300 LuaCs package bug fixed. 2026-02-28 22:32:01 -05:00
Maplewheels
09bc2d0891 - Weird LuaCs Settings Menu bug present: package is loaded on startup but is then unloaded if the settingsmenu is opened and the package is not in the enabled list. 2026-02-28 22:10:29 -05:00
Evil Factory
28b355911d Add missing ILuaCsNetworking APIs 2026-02-28 17:57:02 -03:00
Evil Factory
e2c4282477 Add netMessageReceived hook back 2026-02-28 16:56:14 -03:00
Evil Factory
3192cc8b00 Remove need to define custom network header in upstream 2026-02-28 16:47:18 -03:00
Evil Factory
de73a18637 Compatibility for in memory scripts that used GameMain.LuaCs 2026-02-28 16:13:07 -03:00
Evil Factory
8e8b8eb8aa GameMain.LuaCs is no more 2026-02-28 16:05:20 -03:00
MapleWheels
c676233dfd [Save/Sync] Work on SettingsMenu 2026-02-27 17:56:47 -05:00
MapleWheels
9b61cda6cf - SettingsMenu work.
- Fixed NRE on NetSync in SettingEntry<T>
2026-02-27 17:25:25 -05:00
MapleWheels
ebf2935fb4 - Some menu implementation. 2026-02-27 15:21:40 -05:00
MapleWheels
6e556a02d4 I'm cooked, will finish this later. 2026-02-26 19:16:50 -05:00
MapleWheels
94556fd6e7 - SettingsMenuService added.
- Added Settings Tab to vanilla SettingsMenu
- Fixed ISystem implementation.
2026-02-25 15:19:01 -05:00
MapleWheels
d9d980122d - Moved all console commands to their respective services and added via injector service.
- Fixed LuaCs IsCsEnabled prompt not working.
- Add settings profiles support.
2026-02-24 15:26:49 -05:00
NotAlwaysTrue
679ffd380b OBT/1.1.0 (#50)
Finished CL-EP
Restored early Parallelism setting
2026-02-24 15:16:41 +08:00
MapleWheels
394856fa04 - Final commit before migration to /dev
- Localization files added.
2026-02-23 17:40:59 -05:00
Maplewheels
a28333ff4e Implemented ConfigProfiles internally. 2026-02-23 01:01:23 -05:00
Maplewheels
f4138d2398 - Added networking permission checks to SettingEntry.
- Removed error logging for missing save data for settings.
2026-02-22 06:35:37 -05:00
Evil Factory
91992194a9 Fix return events not working in Lua 2026-02-20 20:40:06 -03:00
Evil Factory
a7e9dc8c9c oops 2026-02-20 20:39:53 -03:00
Evil Factory
da5b9389db Fix missing HookMethod APIs in the interface 2026-02-20 20:20:15 -03:00
Evil Factory
ddd9dac2fb Fix Descriptors not being populated correctly 2026-02-20 19:32:15 -03:00
Evil Factory
124b1e545d Fix missing ILuaCsNetworking API 2026-02-20 19:32:03 -03:00
Evil Factory
f52617deab The great event move 2026-02-20 18:45:41 -03:00
Joonas Rikkonen
c8657caefa Update .NET SDK version from 6 to 8 in README 2026-02-20 19:56:19 +02:00
NotAlwaysTrue
de1f1c599f OBT/1.0.17
Removed Client Kick to see if this will fix some of "Received an update for an entity that doesn't exist"
Revert some change from pervious updates due to performance issue
Sync with upstream
2026-02-20 14:54:04 +08:00
Maplewheels
c28a08a713 Revert "Oops"
This reverts commit a0287b8561.
2026-02-16 02:22:19 -05:00
Maplewheels
a0287b8561 Oops 2026-02-16 02:22:14 -05:00
Maplewheels
6ac49a10f4 - XML GUI Asset service implemented (alpha, requires testing). 2026-02-16 02:22:14 -05:00
Evil Factory
f70251fa3b Event pain 2026-02-15 17:58:25 -03:00
Evil Factory
13bfffa777 More events moved 2026-02-15 16:57:16 -03:00
Evil Factory
a50dce8fc2 Forgot to remove old Hook Call 2026-02-13 19:56:33 -03:00
Evil Factory
a36bd70505 Strip down the original logger class 2026-02-13 18:58:35 -03:00
Evil Factory
36471f2239 Move more events to be Harmony patches 2026-02-13 18:44:27 -03:00
NotAlwaysTrue
d16707a2f3 OBT/1.0.16
Fixed the GapList bug again to see if this will work

P L E A S E   W O R K
2026-02-13 15:02:17 +08:00
MapleWheels
dff38f7609 - Added XmlDoc for InternalScript instance. 2026-02-12 20:21:46 -05:00
Evil Factory
5f38b4a43a Fix bullshit Lua issues 2026-02-12 21:50:13 -03:00
MapleWheels
07d838eee0 Finished removing Impromptu Interfaces package. 2026-02-12 14:59:24 -05:00
MapleWheels
5747d896eb - Removed ImpromptuInterfaces 2026-02-12 14:53:33 -05:00
MapleWheels
ad152ee747 - pOoosh 2026-02-11 15:19:40 -05:00
MapleWheels
471e256353 Revert "-- MonoMod.Hook replacement."
This reverts commit 948b7c48c5.
2026-02-11 15:17:32 -05:00
MapleWheels
59584fefc1 - Updated dependencies versions.
- Made EventService handle errors again.
2026-02-11 15:16:04 -05:00
MapleWheels
948b7c48c5 -- MonoMod.Hook replacement. 2026-02-11 12:57:49 -05:00
MapleWheels
5e33a908d2 Removed NIException. 2026-02-10 19:15:23 -05:00
MapleWheels
9e2fc13460 - Fixed the server not loading saved convars and not having absolute authority. 2026-02-10 18:48:31 -05:00
Evil Factory
6bbe5be5e6 Fix networking being completely bamboozled 2026-02-10 20:26:46 -03:00
MapleWheels
9dde5cac7d Fixed Evil's obsession with OnKeyUpdate. 2026-02-10 15:11:46 -05:00
MapleWheels
6b9e48f96a - Added ISystem (automatically run services) service type. 2026-02-10 14:28:22 -05:00
MapleWheels
30149b504d - Added publicized assemblies to LuaCsForBarotrauma package via ModConfig.xml
- Added XmlAttribute tags for ModConfig.xml defined properties.
- GetType and GetImplementingTypes<T> rework.
2026-02-09 21:32:57 -05:00
Evil Factory
fb4648d759 Converting everything into Harmony patches part 1 2026-02-09 21:54:44 -03:00
Evil Factory
d14c353115 Remove old LuaCsNetworking 2026-02-09 21:01:07 -03:00
MapleWheels
f1bae4c1ca - Added Copy-On-Build for publicized assemblies to the luatrauma localmods folder. 2026-02-09 18:37:27 -05:00
Evil Factory
a61e705dbf Better LoggerService 2026-02-09 19:51:31 -03:00
MapleWheels
64831bd580 - Made the assembly compilation logs output in production builds.
- Removed double logging in debug builds.
2026-02-09 17:43:59 -05:00
MapleWheels
dc1093eeed Added LuaCs package lookup to the info provider. 2026-02-09 17:18:50 -05:00
MapleWheels
95376622fa - Added LuaCsForBarotrauma check to enabled packages list. 2026-02-09 16:58:53 -05:00
MapleWheels
511f98ec18 - Added pre-touch to the ContentPath.FullPath to make them thread-safe. 2026-02-09 15:52:37 -05:00
NotAlwaysTrue
740ace88f0 OBT/1.0.15
* Removed PF support again
Fixed Object reference not set to an instance of an object exception in EnemyAIController.cs UpdateLimbAttack()

* Reverted pervious fix on EnemyAIController bcuz the fix will not fix anything
Reduced MaxDegreeOfParallelism by 1 bcuz already a task there

* Re-removed all PF related stuff to fix issue
Removed all RUN_PHYSICS_IN_SEPARATE_THREAD related code because these codes are no longer functional
Removed a duplicated loop in GameScreen
Removed mulitple unnessary parallel operations

* Potentially fixed #44 and #43

* added a ToList for Gap.GapList
2026-02-09 15:34:07 +08:00
Evil Factory
70e98467e8 nuh uh 2026-02-08 23:47:02 -03:00
Evil Factory
e8bec96970 THE REQUIRE PATHS WORK!@!!!!!!11! 2026-02-08 23:38:52 -03:00
Maplewheels
a505d48a4c - Loading/Saving for Settings via console. 2026-02-08 20:23:49 -05:00
Evil Factory
bcc4357a16 Pass in cs enabled check in ExecuteLoadedScripts 2026-02-08 21:46:00 -03:00
Evil Factory
668197ad6f Fix Lua/Cs execute order 2026-02-08 21:45:17 -03:00
Evil Factory
0a91b89694 Add publicized assemblies to metadata references 2026-02-08 21:29:31 -03:00
Evil Factory
5b10661874 Legacy network stuff 2026-02-08 18:26:59 -03:00
Evil Factory
36a126774a Register INetworkIdProvider 2026-02-08 15:06:22 -03:00
Evil Factory
705137e993 Finalize networking service 2026-02-08 15:03:03 -03:00
Evil Factory
ce4cd1fefd Implement most of the net var networking functionality 2026-02-08 11:51:09 -03:00
Maplewheels
224e32ccf1 Some work on save/load for configs. 2026-02-08 06:39:44 -05:00
Evil Factory
f01ee61278 Clear file cache when resetting LuaScriptManagementService 2026-02-08 01:37:24 -03:00
Evil Factory
c637e34b48 Fix GUI enums in Lua 2026-02-08 01:30:44 -03:00
Maplewheels
67c195034d Network Sync work. Added network unique key. 2026-02-07 22:58:56 -05:00
Evil Factory
02b1f524b6 Re-enable DefaultHook.lua 2026-02-07 23:29:22 -03:00
Evil Factory
e76aaf5a34 Fix deadlock when reloading packages 2026-02-07 23:29:00 -03:00
Evil Factory
422e8a6185 Misc Lua fixes 2026-02-07 20:11:46 -05:00
Evil Factory
ba10d9d031 Working NetworkingService without net vars 2026-02-07 20:11:46 -05:00
Maplewheels
87dc9be10e -Changed NetworkSync interfaces. 2026-02-07 20:11:46 -05:00
Evil Factory
d47b75c778 Give Lua references to IEvent and finally add an alias for think 2026-02-07 20:11:46 -05:00
MapleWheels
2c29969bfb - Oops. 2026-02-07 20:11:46 -05:00
Evil Factory
c84d9660e2 Add command cfg_getvalue 2026-02-07 20:11:46 -05:00
MapleWheels
dcd7df4860 Fixed TrySetValue returning an incorrect result. 2026-02-07 20:11:46 -05:00
Evil Factory
b3d0fbeb5d Add cfg_setvalue command 2026-02-07 20:11:46 -05:00
MapleWheels
fa340e91de Fixed an issue affecting parsing the required runstate to edit a setting. 2026-02-07 20:11:46 -05:00
MapleWheels
771e73a798 - Basic Config & Settings working (read-only, changes must be made via debug halt). 2026-02-07 20:11:46 -05:00
MapleWheels
e75208507d - Config Services almost ready.
- Refactored and flattened namespaces.
2026-02-07 20:11:46 -05:00
MapleWheels
863ee23583 - Some work on config service. 2026-02-07 20:11:46 -05:00
MapleWheels
9cc20a03c0 Fixed networking references errors. 2026-02-07 20:11:46 -05:00
MapleWheels
cae3741953 Made most of the networking interfaces public. 2026-02-07 20:11:46 -05:00
Evil Factory
80555ef933 IT WORKS!!!!!!!!!!!!!!!!!!!! 2026-02-07 20:11:45 -05:00
Evil Factory
cf251451ed Fix EventService.Call not implemented correctly 2026-02-07 20:11:45 -05:00
MapleWheels
4cf4b1604b Fixed some NREs. 2026-02-07 20:11:45 -05:00
MapleWheels
fd037153ee - Fixed recursion deadlock due to the EventService.Reset() being called during event publishing. 2026-02-07 20:11:45 -05:00
MapleWheels
02a7338ab8 Removed duplicate rawrrs 2026-02-07 20:11:45 -05:00
Evil Factory
70dd602bcf Move the Lua IL patching bullshit to a separate service 2026-02-07 20:11:45 -05:00
MapleWheels
ea602f6d2f Woof 2026-02-07 20:11:45 -05:00
MapleWheels
06348d3ba5 It works. Except (HookMethod->Harmony: L189) is throwing NRE. 2026-02-07 20:11:45 -05:00
MapleWheels
2eb593f461 - Debugging LuaCsHook compat issues. 2026-02-07 20:11:45 -05:00
MapleWheels
244c0fbec3 Made ACsMod even more useless so hopefully people stop using it... 2026-02-07 20:11:45 -05:00
MapleWheels
7b529bce57 Revert "- Removed ACsMod.cs"
This reverts commit 54a3e2e5de0cff38ea4fc3750d2eb90f5ea73fe9.
2026-02-07 20:11:45 -05:00
MapleWheels
36bed09bde - Fixed stack ovewrflow from ServicesProvider (???). 2026-02-07 20:11:45 -05:00
Maplewheels
024b07d5f4 Added logging to the EventService. 2026-02-07 20:11:45 -05:00
Maplewheels
0cab7954f8 Fixed immediate errors (untested). 2026-02-07 20:11:45 -05:00
Maplewheels
b325a01eea Rewrote the EventService. 2026-02-07 20:11:45 -05:00
MapleWheels
7e541cef3d - Removed ACsMod.cs 2026-02-07 20:11:45 -05:00
MapleWheels
bb8869268e - Refactored the EventService interfaces and event system, incomplete. 2026-02-07 20:11:45 -05:00
Maplewheels
cb171d350d Alpha PluginManagementService, plugin loading functionality implemented. 2026-02-07 20:11:45 -05:00
Evil Factory
5777b64a18 Move LuaUserData and registration into a proper service 2026-02-07 20:11:41 -05:00
Maplewheels
9b9529107c Added limited multithreaded compatibility. Still requires locks. 2026-02-07 20:11:33 -05:00
MapleWheels
5421c7df4f - Made SyncPackages function always complete the unload->reload process.
- Basic assembly loading is completed (alpha), unloading/disposal not yet supported.
2026-02-07 20:11:33 -05:00
Evil Factory
4f02cb4967 Working Hook.Patch and old patch methods 2026-02-07 20:11:33 -05:00
Evil Factory
6b8a0a7dca Take in account ForcedAutorun for legacy as well 2026-02-07 20:11:32 -05:00
Evil Factory
dfb31eef16 Move Lua classes to the appropriate places 2026-02-07 20:11:16 -05:00
Evil Factory
c6c0aadb00 Rename LuaCsHook 2026-02-07 20:11:08 -05:00
Evil Factory
3b65ea9008 Remove unused LuaCsConfig 2026-02-07 20:11:08 -05:00
Evil Factory
708fe93efe Some extra logging and bring back LuaCsTimer 2026-02-07 20:11:08 -05:00
MapleWheels
13a9bc443e - Some work on PluginManagementService, IAssemblyLoaderService and IAssemblyManagementService refactors.
- Fixed mod list sync not checking for zero package diff length.
- Fixed the services provider not being able to inject itself as a dependcency.
- Added UseInternalName data spec to ModConfig.xml
- Changed Basic.Reference.Assemblies to Net80.
2026-02-07 20:11:08 -05:00
MapleWheels
37e3a195dc - Now logs results from SyncLoadedPackagesList 2026-02-07 20:11:08 -05:00
MapleWheels
a28a6f3320 - Added LuaCs ordering filter. 2026-02-07 20:11:07 -05:00
Evil Factory
67d3d5f587 Reimplementation of DoString with lua/cl_lua commands and fix Lua scripts not being loaded properly 2026-02-07 20:11:07 -05:00
Evil Factory
f0f09c20fa Plugin moment 2026-02-07 20:11:07 -05:00
Evil Factory
f28749d455 Missing IsAutorun 2026-02-07 20:11:07 -05:00
Evil Factory
ab2638b2cb Fix event service not clearing _luaOrphanSubscribers 2026-02-07 20:11:07 -05:00
MapleWheels
22f587b7b9 Changed ModConfig.xml spec: RunFile => IsAutorun. 2026-02-07 20:11:07 -05:00
MapleWheels
92232d114b Ensures that ILuaCsHook will resolve to the existing event service instance. 2026-02-07 20:11:07 -05:00
Evil Factory
6619def365 The concurrent toolbox dictionary situation is crazy 2026-02-07 20:11:07 -05:00
MapleWheels
c776a5424d Made GetType return interfaces by default. 2026-02-07 20:11:07 -05:00
MapleWheels
2a1d7760e6 - Enabled caching for LuaScriptLoader.cs 2026-02-07 20:11:07 -05:00
MapleWheels
009957e3b6 - Added alpha legacy mod support.
- StorageService: Fixed ContentPackages' directories not properly resolving.
- TODO: Rewrite StorageService LocalData functions to use ContentPath for resolution.
2026-02-07 20:11:07 -05:00
Evil Factory
297f6a38cb Basic legacy Lua converter 2026-02-07 20:11:07 -05:00
MapleWheels
440cbed76a - Fixed find files in package using the wrong ContentPackage property. 2026-02-07 20:11:07 -05:00
MapleWheels
b36224f480 Removed unused data interfaces. 2026-02-07 20:11:06 -05:00
MapleWheels
f9a467453a - Removed all MoveImmute situations.
- Added filtering and ordering functions for package resources.
2026-02-07 20:11:06 -05:00
Maplewheels
60ed549605 - Removed unused scheduler context. 2026-02-07 20:11:06 -05:00
Evil Factory
3d51abc56b Semi-working Lua scripts 2026-02-07 20:11:06 -05:00
Maplewheels
295c365a8f - The return null situation is no longer crazy. 2026-02-07 20:11:06 -05:00
Maplewheels
adf9303a7e - The GetType lag situation is less crazy. 2026-02-07 20:11:06 -05:00
Maplewheels
b626fd1e47 - The GetType lag situation is craazy. 2026-02-07 20:11:06 -05:00
Maplewheels
ed09908c3b - Added default load context type resolution. 2026-02-07 20:11:06 -05:00
Maplewheels
0a9c673753 - Removed unused settings
- Added Settings.xml
2026-02-07 20:11:06 -05:00
Maplewheels
c2aa7dd948 Added packages to running packages list on execution. 2026-02-07 20:11:05 -05:00
Evil Factory
25081466be Add the content package and move the Lua files to it 2026-02-07 20:11:02 -05:00
Evil Factory
8dedc54468 Add Any for Platform and Target enums 2026-02-07 20:10:55 -05:00
Maplewheels
d0f5cb87e7 - Fixed resource data not returning into the correct context. 2026-02-07 20:10:55 -05:00
Evil Factory
8dc58445f9 MoveToImmutable -> ToImmutable 2026-02-07 20:10:55 -05:00
Evil Factory
6faf1a0fcb Fix perfidious task moment 2026-02-07 20:10:55 -05:00
Evil Factory
ee0988588f Add ModConfig for test mod 2026-02-07 20:10:55 -05:00
MapleWheels
6e66a3114a - Made Package loading conditional on resources being available.
- Made States registration use named parameters.
- Changed IPluginManagementService interface to better suit expected return results.
2026-02-07 20:10:55 -05:00
MapleWheels
15e0a2bd10 Disabled LuaCsConfig until ModConfig.xml and ISettingEntry<T> are completed. 2026-02-07 20:10:55 -05:00
MapleWheels
09faa8403a Fixed StatemMachine State delegate assignment being backwards. 2026-02-07 20:10:55 -05:00
MapleWheels
7e0e671539 - ConfigService.cs alpha testing. 2026-02-07 20:10:54 -05:00
MapleWheels
cc07db941f - Fixed the "deadlock" (tasks not started). 2026-02-07 20:10:54 -05:00
Evil Factory
f1e1b9238d The deadlock situation is craaaaazy
Fix
2026-02-07 20:10:54 -05:00
MapleWheels
dda26df250 For sure this time....right guys? 2026-02-07 20:10:54 -05:00
MapleWheels
6e7b7c804c - Added other package locations to if statement check. 2026-02-07 20:10:54 -05:00
Maplewheels
6a21255a38 - IT BUILDS!!2:BARO BOOGALOO 2026-02-07 20:10:54 -05:00
MapleWheels
6362a9c34f - Work on LuaCs system state machine. 2026-02-07 20:10:54 -05:00
MapleWheels
3ddaceb5ac - SafeStorageService glow up.
- ILuaScriptLoader now inherits the ISafeStorageValidation interface.
 - LuaScriptLoader now uses the SafeStorageService.
2026-02-07 20:10:54 -05:00
MapleWheels
055a508901 - Deleted unused service IPackageListRetrievalService.cs
- Added caching function to LuaScriptLoader.cs
- Added sample async code to LuaScriptManagementService.cs
- Removed most of the State functions in LuaCsSetup.cs (requires rewrite).
- Fixed CsEnabled check.
- Moved IsRunningWorkshop check to client-only project.
2026-02-07 20:10:54 -05:00
MapleWheels
0bfceacaf3 - Completed most of PackageManagementService.cs
- Some areas of code need to be rewritten for the simplified loading and execution process.
2026-02-07 20:10:54 -05:00
Maplewheels
0438d3c4ba [Save/Sync]
- Work on PMS.
2026-02-07 20:10:54 -05:00
MapleWheels
3e81e27160 [Save/Sync] In-Progress ModConfigXml loading rewrite.
+ Fixed async operations lock for Dispose() pattern in working files.
+ Rewrote StorageService.cs:
--- Now uses ContentPath instead of raw strings where possible.
--- Now throws exceptions for developer errors and critical program states.
+ Rewrote ModConfigService.cs:
--- All functions are now completely async.
+ Removed ConfigProfilesResources completely as they exist in common Config xml files.
+ Somewhat simplified package data and processes.
2026-02-07 20:10:54 -05:00
MapleWheels
42acb32c69 Marked Async-compatibility issue with Log() in LoggerService.cs 2026-02-07 20:10:53 -05:00
Maplewheels
d6968f4ea9 [Save/Sync] Work on ModConfig loading. 2026-02-07 20:10:53 -05:00
Evil Factory
75da3a398d This should be full path 2026-02-07 20:10:53 -05:00
Evil Factory
1dad2babb7 Move Lua compatibility files around and start adding them to LuaScriptManagementService 2026-02-07 20:10:53 -05:00
Evil Factory
fe982b15b0 Fix LuaScriptManagementService compile error 2026-02-07 20:10:53 -05:00
Maplewheels
595470ccfb [Save/Sync] In-Progress rewrite of ConfigService and ModConfigService 2026-02-07 20:10:53 -05:00
MapleWheels
7d39c092c6 [Save/Sync] Big If tru Rewrite in progress.
- Removed IProcessors
- Removed old ModConfigService format.
- Converting to ContentPath from absolute paths where possible.
- Added: Microsoft.Toolkit.Diagnostics package.
2026-02-07 20:10:53 -05:00
MapleWheels
6a6be3caa4 - Removed all package dependency lookup code.
- Changed from absolute file paths to the upstream `ContentPath` system.
2026-02-07 20:10:53 -05:00
Evil Factory
1daf68dea1 Oops 2026-02-07 20:10:53 -05:00
Evil Factory
4f8531332e Implement LogResults 2026-02-07 20:10:53 -05:00
Evil Factory
d70885711b Fix some runstate if checks 2026-02-07 20:10:53 -05:00
Evil Factory
6499e7608c Fix package management service constructor and create [DebugOnlyTest]TestLuaMod 2026-02-07 20:10:53 -05:00
MapleWheels
569e14f50f - Removed generic exception from LuaCsSetup.cs services retrival, replaced with exception propagation.
- Fixed `PerScopeLifetime` and `Transient` lifetimes for container services.
2026-02-07 20:10:53 -05:00
Evil Factory
71c2e54afd Remove CheckUpdate 2026-02-07 20:10:53 -05:00
Maplewheels
bd5d04f5ab Work on plugin loader. 2026-02-07 20:10:53 -05:00
Maplewheels
26b657a96f [In-Progress] Plugin system rewrite.
Game starts up, runs until unimplemented functions are reached without errors.
2026-02-07 20:10:52 -05:00
Maplewheels
cce5bf26c8 Completed SafeStorageService.cs 2026-02-07 20:10:52 -05:00
Evil Factory
4d97a427f9 WIP Lua script management service 2026-02-07 20:10:52 -05:00
MapleWheels
aa7e825e70 - Deleted old assembly loader. 2026-02-07 20:10:52 -05:00
MapleWheels
2778df0fe7 - Changes to the Lua ScriptSystem spec. 2026-02-07 20:10:52 -05:00
MapleWheels
c6713f37bb IT BUILDS!!!
- Removed LocalizationServices and other sus things.
- Rewrote AssemblyLoader
[In Progress] SafeStorageService
[In Progress] LuaScriptLoader
2026-02-07 20:10:52 -05:00
MapleWheels
52d920d969 [Milestone] PackageManagementService completed.
- ContentPackageInfoLookup Service completed.
- Implemented ModConfigService.cs
- Implemented some of the resource processors.
2026-02-07 20:10:45 -05:00
EvilFactory
cb88d215fa LuaGame legacy service 2026-02-07 20:10:39 -05:00
MapleWheels
7436ea3e8c - Finished most of LuaCsSetup top-level functionality.
- Removed some unneeded interface definitions.
- Clean-slated some Services that need to be re-written.
2026-02-07 20:10:39 -05:00
MapleWheels
d2b9ca4c1b [Refactor-Minor]
- Refactored interface definition.
- Plugin Loading System Refactor (incomplete).
2026-02-07 20:10:39 -05:00
MapleWheels
4b2bac7cd8 [Milestone] StorageService completed. 2026-02-07 20:10:39 -05:00
Regalis11
1da82cdec2 v1.7.7.0 (Winter Update 2024) 2026-02-07 20:10:39 -05:00
MapleWheels
76fc52e042 - Work on storage service. Pre-squash commit. 2026-02-07 20:10:39 -05:00
MapleWheels
6880e5e9ee [Milestone] AssemblyLoader completed.
Details:
- Assembly Mgmt Service for loading now a separate interface, not intended for normal use.
- Assembly Loader work; implemented custom dictionary key and table.
- Assembly loading work.
- EventService completed.
- Moved assembly extensions to ModUtils.cs
- Work to event service.
NetworkService work
- Added ImpromptuInterfaces package.
- Networking Service work to support NetVars
- Event Service
- Added assemblies references package for script compilation. Updated Roslyn version for compatibility.
- Package Loading work.
Swap Harmony to HarmonyX
- More refactor conversion to FluentResults.
- Updated StylesService to return Results.
- Refactor of PackageService partially complete.
- Made IService.Reset() required to return a Result.
- Moved plugin/assembly related code to their own folder (same namespace).
- Updated interfaces to reflect the use of Result<T>.
- Partial refactor, incomplete.
- Added 'FluentResults' so we can stop using cursed Exception-based flow control in loading code.
- Added 'OneOf' nuget package: https://github.com/mcintyre321/OneOf
for the implementation of the Optional<T> pattern and complex discrete return types instead of cursed enums (see current AssemblyManager.cs).
- Reapplied old branch changes.
2026-02-07 20:10:26 -05:00
MapleWheels
01cc1d331b -- Squash:
- In progress implementation of services model.
2026-02-07 20:10:04 -05:00
Evil Factory
9e957a75b0 Update MoonSharp 2026-02-02 21:30:06 -03:00
Evil Factory
a546615f69 Oops accidentally broke this check 2026-02-01 09:18:11 -03:00
Evil Factory
9f1c3fa823 Move UserData checks out of Lua 2026-01-31 17:44:36 -03:00
NotAlwaysTrue
8bfe8a2c37 OBT/1.0.14
Fixed parallelism count issue
Added SingleThreadWorker to handle single-thread related works
Fixed incorrect order when updating gaps
Potentially Fixed #39
2026-01-08 12:40:30 +08:00
NotAlwaysTrue
f3c22315a1 OBT/1.0.13
Fixed another crash(Object reference not set to an instance of an object.)
2026-01-02 18:34:12 +08:00
NotAlwaysTrue
59da9211f6 OBT/1.0.2 (#35)
* Fixed? the cause of gap crashes. May require further monitor and check :(

* Fixed a bug that will cause the server stop responding

* Fixed (?) an issue causing a SetPosition() will crash the game
Removed CL release, Added manual dispatcher inputs
2026-01-01 14:14:40 +08:00
NotAlwaysTrue
82d26b5bb8 OBT/1.0.11
Fixed a bug that caused the server to stop responding
Fixed an issue in gap.Update that may cause the server to crash
2025-12-30 17:41:50 +08:00
NotAlwaysTrue
4c151a4cf1 OBT/1.0.10
Reverted a fixed bug that caused a rare crash but the fix itself will do impact on server performance
2025-12-29 22:52:29 +08:00
NotAlwaysTrue
85b71f1dd6 Revert a change made in 1.0.9 due to large-scale performence impact.
Note : This may cause server crash
2025-12-29 22:36:38 +08:00
NotAlwaysTrue
24484496d2 OBT/1.0.9
Fixed an issue causing gap.update crashes the game(engine issue)
Fixed an potential issue that on MacOS we cannot get core count and cause MaxDegreeOfParallelism will be set to 0. Now if we cant get that number we simply use a fixed 16 instead
2025-12-29 16:33:37 +08:00
NotAlwaysTrue
190c98d8f2 Fixed 2 issues
Fixed an issue causing gap.update crashes the game(engine issue)
Fixed an potential issue that on MacOS we cannot get core count and cause MaxDegreeOfParallelism will be set to 0. Now if we cant get that number we simply use a fixed 16 instead
2025-12-29 16:28:34 +08:00
Eero
046483b9da Revert "OBT1.1.0 Merge branch 'dev_pte' into dev"
This reverts commit 177cf89756, reversing
changes made to 42ba733cd4.
2025-12-29 11:18:11 +08:00
Eero
177cf89756 OBT1.1.0 Merge branch 'dev_pte' into dev 2025-12-29 11:15:41 +08:00
Eero
e167a34f32 Make entity lists thread-safe with copy-on-write wrappers
Replaced static entity lists (e.g., HullList, GapList, MapEntityList, etc.) with thread-safe copy-on-write wrappers to improve concurrency and prevent race conditions. Updated usages and related methods to support the new thread-safe collections, ensuring atomic operations and lock-free reads throughout the codebase.
2025-12-28 21:59:03 +08:00
Eero
bd1e624eb1 Remove unnecessary thread-safety code from entity spawning
Eliminated redundant locks and related comments in EntitySpawner and Entity classes, simplifying the spawn and remove queue handling. Also removed outdated comments in GameScreen regarding thread safety. These changes assume entity spawning and removal are no longer performed from multiple threads, improving code clarity and maintainability.
2025-12-28 17:45:51 +08:00
Eero
1db14631df Defer physics transforms to ensure thread safety
Refactored multiple components to defer Farseer physics transform operations using PhysicsBodyQueue, preventing unsafe calls from parallel contexts. This change addresses thread safety issues with Farseer's DynamicTree and ensures transforms are executed in a safe context. Also increased the spawn amount limit in DebugConsole from 100 to 100000.
2025-12-28 17:14:16 +08:00
Eero
59bf2749dd Improve thread safety in sound and physics systems
Refactored SoundChannel and SoundManager to use explicit locking for OpenAL operations and channel assignment, preventing race conditions during parallel sound playback. Added thread-local stacks in DynamicTree to ensure thread safety during parallel physics queries and raycasts. These changes address concurrency issues when sounds or physics queries are triggered from multiple threads.
2025-12-28 16:18:49 +08:00
Eero
ad0bcddaa4 CL_Fix Use TryAdd when adding afflictions to dictionary
Replaces afflictions.Add with afflictions.TryAdd to prevent exceptions if the affliction already exists in the dictionary.
2025-12-28 15:11:56 +08:00
Eero
f485583621 Unstable 0.2 Defer physics operations during parallel updates
Introduces a thread-safe queue for deferring physics operations (such as body creation and transforms) to the main thread, ensuring Farseer Physics is not accessed from parallel contexts. Updates Holdable, Item, MapEntity, and GameScreen to use the new PhysicsBodyQueue for safe physics operations during parallel updates, and refactors PhysicsBodyQueue to support general deferred physics actions.
2025-12-28 15:10:06 +08:00
Eero
49355fe32b Unstable Add thread-safe queue for deferred physics body creation
Introduces PhysicsBodyQueue to safely defer physics body creation to the main thread, addressing thread-safety issues with Farseer Physics during parallel updates. Updates LevelResource, TriggerComponent, BallastFloraBehavior, and MapEntity to use the queue for all physics body creation and refresh operations, ensuring they are processed outside of parallel loops. Also adds cleanup of the queue at round end.
2025-12-28 14:42:17 +08:00
NotAlwaysTrue
356ae6cf74 OBT/1.0.8
Fixed an mod conflict due to execute order
2025-12-28 14:36:09 +08:00
NotAlwaysTrue
770f76a658 Fixed a potential conflict with mods 2025-12-28 14:21:32 +08:00
NotAlwaysTrue
42ba733cd4 Fixed a potential conflict with mods 2025-12-28 14:17:01 +08:00
Eero
45312af297 WIP Make static collections thread-safe using ThreadStatic and ThreadLocal
Refactored various static and instance collections to use [ThreadStatic], ThreadLocal, or local variables to prevent concurrent modification issues during parallel updates. This affects status effect targets, affliction lists, damage modifiers, and cached data in Character, CharacterHealth, Limb, Explosion, Hull, Submarine, and ToolBox classes. Also replaced Dictionary caches with ConcurrentDictionary where appropriate for thread safety.
2025-12-28 14:14:53 +08:00
NotAlwaysTrue
baee73e132 OBT/1.0.7
Fixed #7
Fixed #22/#25
Fixed a potential cause to teleport/etc stuff
Added a Client counter in PM
Fixed typo
2025-12-28 14:01:44 +08:00
NotAlwaysTrue
279f40c82e Merge branch 'master' into master-1.0.6 2025-12-28 14:00:03 +08:00
NotAlwaysTrue
aaf0763e09 Removed a potential issue 2025-12-28 13:54:29 +08:00
Eero
c5fa49405f WIP Make networking code thread-safe and refactor update ID
Replaces direct increments of LastClientListUpdateID with a thread-safe IncrementLastClientListUpdateID method and uses Interlocked for atomic operations. Refactors EntitySpawner to lock access to the spawn/remove queue for thread safety. Updates INetSerializableStruct to use concurrent collections for cached variables and type behaviors, improving thread safety in networking code.
2025-12-28 13:10:17 +08:00
NotAlwaysTrue
3d96e4adb6 Fixed #22 2025-12-28 13:05:56 +08:00
NotAlwaysTrue
42af6f2ec0 Fixed #22 2025-12-28 13:02:31 +08:00
Eero
31812d524d Make collections thread-safe for AI and character systems
Refactored various collections (lists, dictionaries, queues, and caches) in AI, character, animation, and item component systems to use thread-safe patterns and concurrent data structures. This includes introducing copy-on-write wrappers, ConcurrentDictionary, ConcurrentQueue, and ThreadLocal where appropriate to ensure safe concurrent access and mutation, improving stability in multi-threaded scenarios.
2025-12-28 12:53:10 +08:00
NotAlwaysTrue
21a2863a1a Fixed a typo using e instead of a :( 2025-12-28 11:45:55 +08:00
NotAlwaysTrue
bd643503b3 Added ClientCount for PerformenceMonitor
Add a marker to help distinguish EP from other SV Executables
Fixed a "Failed to copy object. Source is null." introduced by last update
Uses dynamic ThreadCount instead of fixed 16
Re-Removed most PF support
Re-Parallelzed Level update and Character Update(a conflict warning will be issued
2025-12-28 11:45:21 +08:00
NotAlwaysTrue
51a1fb1235 Re-applied multiple fixs 2025-12-28 11:43:49 +08:00
NotAlwaysTrue
8f0eec7031 Fixed #7 (re-applied) 2025-12-28 11:43:15 +08:00
Evil Factory
6dd36a1575 Fix memory leak that happens when you press retry in singleplayer 2025-12-28 11:42:03 +08:00
Eero
46595b1399 WIP Make collections thread-safe and add safe iteration
Replaced static lists and dictionaries with thread-safe ConcurrentDictionary or ThreadLocal collections for various item components and systems. Updated all relevant code to use snapshots (ToArray, ToList) for safe iteration, and added helper methods for marking and clearing changed connections. These changes improve thread safety and prevent potential concurrency issues in multi-threaded scenarios.
2025-12-28 04:59:56 +08:00
Eero
90962b2328 Refactor Item collections for thread safety and performance
Replaces static Item.ItemList and related collections with thread-safe data structures using ConcurrentDictionary and ImmutableHashSet. Adds thread-safe helpers for marking items for deconstruction and managing item lists. Updates all usages of Item.ItemList and DeconstructItems to use new APIs, improving performance and safety in multi-threaded contexts. Also refactors MeleeWeapon and Projectile impact queues to use ConcurrentQueue, and updates related logic throughout the codebase.
2025-12-28 03:57:04 +08:00
NotAlwaysTrue
edd50ef181 Update publish-release.yml 2025-12-27 20:54:06 +08:00
NotAlwaysTrue
ced6dca7a3 OBT/1.0.6
Fixed a typo using "e" instead of "a" in word "performance"
2025-12-27 20:33:51 +08:00
NotAlwaysTrue
559aeb3c3f Fixed a typo using e instead of a :( 2025-12-27 18:47:50 +08:00
NotAlwaysTrue
16131e0acc Fixed a typo 2025-12-27 18:45:51 +08:00
NotAlwaysTrue
e8f377c20d Sync with Main
Sync with Main
2025-12-27 16:08:15 +08:00
NotAlwaysTrue
27896d53ef OBT/1.0.5
OBT/1.0.5
2025-12-27 16:06:23 +08:00
NotAlwaysTrue
a44d89f953 Revert "Revert back to 1.0.3" 2025-12-27 16:05:20 +08:00
NotAlwaysTrue
75465f2f21 OBT/1.0.5
Same as 1.0.4
Fixed a crash introduced by 1.0.4
2025-12-27 16:02:38 +08:00
NotAlwaysTrue
3aadff7a3c Improved thread safety for Gap.update() 2025-12-27 15:55:50 +08:00
NotAlwaysTrue
c65101537d Merge pull request #17 from NotAlwaysTrue/revert-16-master-fix
Revert back to 1.0.3
2025-12-27 15:35:07 +08:00
NotAlwaysTrue
b35eee5561 Revert "OBT/1.0.4" 2025-12-27 15:32:16 +08:00
NotAlwaysTrue
cdfe87c81b OBT/1.0.4
Add a marker to help distinguish EP from other SV Executables
Fixed a "Failed to copy object. Source is null." introduced by last update
Uses dynamic ThreadCount instead of fixed 16
Re-Removed most PF support
Re-Parallelzed Level update and Character Update(a conflict warning will be issued
2025-12-27 13:26:30 +08:00
NotAlwaysTrue
7e899d900a Added ClientCount for PerformenceMonitor
Add a marker to help distinguish EP from other SV Executables
Fixed a "Failed to copy object. Source is null." introduced by last update
Uses dynamic ThreadCount instead of fixed 16
Re-Removed most PF support
Re-Parallelzed Level update and Character Update(a conflict warning will be issued
2025-12-27 13:19:36 +08:00
NotAlwaysTrue
a037e616bf OBT/1.0.3
Restored multiple removed parallel stuff
Restored PF support(why)
Fixed #7
2025-12-27 02:12:25 +08:00
NotAlwaysTrue
f7650bd6df Re-applied multiple fixs 2025-12-27 02:06:04 +08:00
NotAlwaysTrue
716a35701c Revision 6032010
Removed a potential issue causing the server to stuck in GameScreen.cs (Internal reports)
Added an Warning message to SEEM
2025-12-27 01:59:17 +08:00
Eero
6032010847 Improve parallelization in map and game screen updates
Refactored update logic in MapEntity and GameScreen to use more granular and conditional parallelization, reducing unnecessary allocations and improving performance. Updates to hulls, structures, items, and physics bodies are now executed in parallel where safe, and item updates are only performed when necessary. Also parallelized submarine and physics body transform updates.
2025-12-26 21:04:07 +08:00
NotAlwaysTrue
5446795196 Fixed #7 (re-applied) 2025-12-26 11:07:54 +08:00
NotAlwaysTrue
31ed540939 OBT/1.0.2
Fixed #10
Fixed #12
Sync with upstream
2025-12-26 01:19:04 +08:00
NotAlwaysTrue
e715fdc835 Fixed #10
Fixed #12
2025-12-26 01:16:05 +08:00
Evil Factory
81f44969ee Fix memory leak that happens when you press retry in singleplayer 2025-12-25 23:42:55 +08:00
Evil Factory
886eebdbb2 Fix memory leak that happens when you press retry in singleplayer 2025-12-23 20:47:40 -03:00
NotAlwaysTrue
fc35d888fc Merge pull request #6 from NotAlwaysTrue/master-dev 2025-12-23 23:12:38 +08:00
NotAlwaysTrue
eb01597dd3 Try-catch Sever Entity Event Manager thread-safety issue 2025-12-23 22:38:06 +08:00
Eero
572430cc2b Add server-only usage warning to README
Added prominent warnings in both English and Chinese indicating that this release is intended for server-side use only and should not be run on the client. Users are advised to thoroughly test compatibility before deployment.
2025-12-23 13:19:10 +08:00
eero
7b263676e0 Reapply "Refactor ServerEntityEventManager event processing"
This reverts commit bdd6c52e4e.
2025-12-23 00:36:47 +08:00
427 changed files with 24574 additions and 11588 deletions

View File

@@ -73,7 +73,7 @@ body:
label: Version
description: Which version of the game did the bug happen in? You can see the current version number in the bottom left corner of your screen in the main menu.
options:
- v1.11.5.0 (Winter Update 2025 Hotfix 1)
- v1.12.6.2 (Spring Update 2026)
- Other
validations:
required: true

View File

@@ -4,6 +4,20 @@ name: Publish release
on:
workflow_dispatch:
inputs:
target:
description: "The git ref to checkout, build from and release"
required: true
type: string
tag:
description: "The tag of the release"
required: true
type: string
prerelease:
description: "Prerelease"
required: false
default: false
type: boolean
workflow_call:
inputs:
target:
@@ -23,13 +37,13 @@ on:
env:
CI_DIR: 2049ef39-42a2-46d2-b513-ee6d2e3a7b15
RELEASES: |
windows:client:Windows/Client
windows:server:Windows/Server
linux:client:Linux/Client
linux:server:Linux/Server
mac:client:Mac/Client/Barotrauma.app/Contents/MacOS
mac:server:Mac/Server
ARCHIVE_BASE_NAME: luacsforbarotrauma
windows:client:Windows/Client
linux:client:Linux/Client
mac:client:Mac/Client/Barotrauma.app/Contents/MacOS
ARCHIVE_BASE_NAME: luacsforbarotraumaEP
# XXX: these file names are subject to shell expansion.
# Be careful when using special characters.
ARCHIVE_FILES_SERVER: |
@@ -64,15 +78,19 @@ env:
Mono.Cecil.Mdb.dll
Mono.Cecil.Pdb.dll
Mono.Cecil.Rocks.dll
Microsoft.CodeAnalysis.CSharp.Scripting.dll
LightInject.dll
OneOf.dll
FluentResults.dll
Basic.Reference.Assemblies.Net80.dll
Microsoft.Extensions.Logging.Abstractions.dll
Microsoft.Toolkit.Diagnostics.dll
Microsoft.CodeAnalysis.CSharp.dll
Microsoft.CodeAnalysis.dll
Microsoft.CodeAnalysis.Scripting.dll
System.Collections.Immutable.dll
System.Reflection.Metadata.dll
System.Runtime.CompilerServices.Unsafe.dll
mscordaccore_amd64_amd64_*
Lua
LocalMods/LuaCsForBarotrauma
jobs:
build:

1
.gitignore vendored
View File

@@ -15,6 +15,7 @@ bld/
[Rr]eleaseMac/
[Dd]ebugLinux/
[Rr]eleaseLinux/
LocalMods/
*.o
*/Barotrauma*/doc/

View File

@@ -29,7 +29,7 @@ namespace Barotrauma
}
}
}
else if (SelectedAiTarget?.Entity != null)
else if (SelectedAiTarget?.Entity != null && AttackLimb != null)
{
Vector2 targetPos = SelectedAiTarget.Entity.DrawPosition;
if (State == AIState.Attack)
@@ -37,15 +37,16 @@ namespace Barotrauma
targetPos = attackWorldPos;
}
targetPos.Y = -targetPos.Y;
GUI.DrawLine(spriteBatch, pos, targetPos, GUIStyle.Red * 0.5f, 0, 4);
Vector2 attackLimbPos = AttackLimb.DrawPosition;
attackLimbPos.Y = -attackLimbPos.Y;
GUI.DrawLine(spriteBatch, attackLimbPos, targetPos, GUIStyle.Red * 0.75f, 0, 4);
if (wallTarget != null && !IsCoolDownRunning)
{
Vector2 wallTargetPos = wallTarget.Position;
if (wallTarget.Structure.Submarine != null) { wallTargetPos += wallTarget.Structure.Submarine.DrawPosition; }
wallTargetPos.Y = -wallTargetPos.Y;
GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Orange, false);
GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5);
GUI.DrawLine(spriteBatch, attackLimbPos, wallTargetPos, Color.Orange * 0.75f, 0, 5);
}
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{SelectedAiTarget.Entity}", GUIStyle.Red, Color.Black);
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 40.0f, $"{targetValue.FormatZeroDecimal()} (M: {CurrentTargetMemory?.Priority.FormatZeroDecimal()}, P: {CurrentTargetingParams?.Priority.FormatZeroDecimal()})", GUIStyle.Red, Color.Black);

View File

@@ -23,6 +23,8 @@ namespace Barotrauma
//GUI.DrawString(spriteBatch, pos + textOffset, $"AI TARGET: {SelectedAiTarget.Entity.ToString()}", Color.White, Color.Black);
}
Vector2 spacing = new Vector2(0, GUIStyle.Font.MeasureChar('T').Y);
Vector2 stringDrawPos = pos + textOffset;
GUI.DrawString(spriteBatch, stringDrawPos, Character.Name, Color.White, Color.Black);
@@ -33,14 +35,14 @@ namespace Barotrauma
currentOrders.Sort((x, y) => y.ManualPriority.CompareTo(x.ManualPriority));
for (int i = 0; i < currentOrders.Count; i++)
{
stringDrawPos += new Vector2(0, 20);
stringDrawPos += spacing;
var order = currentOrders[i];
GUI.DrawString(spriteBatch, stringDrawPos, $"ORDER {i + 1}: {order.Objective.DebugTag} ({order.Objective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
}
}
else if (ObjectiveManager.WaitTimer > 0)
{
stringDrawPos += new Vector2(0, 20);
stringDrawPos += spacing;
GUI.DrawString(spriteBatch, stringDrawPos - textOffset, $"Waiting... {ObjectiveManager.WaitTimer.FormatZeroDecimal()}", Color.White, Color.Black);
}
var currentObjective = ObjectiveManager.CurrentObjective;
@@ -49,19 +51,19 @@ namespace Barotrauma
int offset = currentOrder != null ? 20 + ((ObjectiveManager.CurrentOrders.Count - 1) * 20) : 0;
if (currentOrder == null || currentOrder.Priority <= 0)
{
stringDrawPos += new Vector2(0, 20);
stringDrawPos += spacing;
GUI.DrawString(spriteBatch, stringDrawPos, $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
}
var subObjective = currentObjective.CurrentSubObjective;
if (subObjective != null)
{
stringDrawPos += new Vector2(0, 20);
stringDrawPos += spacing;
GUI.DrawString(spriteBatch, stringDrawPos, $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
}
var activeObjective = ObjectiveManager.GetActiveObjective();
if (activeObjective != null)
{
stringDrawPos += new Vector2(0, 20);
stringDrawPos += spacing;
GUI.DrawString(spriteBatch, stringDrawPos, $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
}
if (currentObjective is AIObjectiveCombat
@@ -85,12 +87,12 @@ namespace Barotrauma
}
}
Vector2 objectiveStringDrawPos = stringDrawPos + new Vector2(120, 40);
Vector2 objectiveStringDrawPos = stringDrawPos + new Vector2(120, spacing.Y * 2);
for (int i = 0; i < ObjectiveManager.Objectives.Count; i++)
{
var objective = ObjectiveManager.Objectives[i];
GUI.DrawString(spriteBatch, objectiveStringDrawPos, $"{objective.DebugTag} ({objective.Priority.FormatZeroDecimal()})", Color.White, Color.Black * 0.5f);
objectiveStringDrawPos += new Vector2(0, 18);
objectiveStringDrawPos += spacing * 0.8f;
}
if (steeringManager is IndoorsSteeringManager pathSteering)

View File

@@ -547,7 +547,7 @@ namespace Barotrauma
}
}
public void Draw(SpriteBatch spriteBatch, Camera cam)
public void Draw(SpriteBatch spriteBatch, Camera cam, bool onlyDrawSeveredLimbs)
{
if (simplePhysicsEnabled) { return; }
@@ -573,8 +573,12 @@ namespace Barotrauma
{
foreach (Limb limb in limbs) { limb.ActiveSprite.Depth += depthOffset; }
}
for (int i = 0; i < limbs.Length; i++)
for (int i = 0; i < inversedLimbDrawOrder.Length; i++)
{
if (onlyDrawSeveredLimbs && !inversedLimbDrawOrder[i].IsSevered)
{
continue;
}
inversedLimbDrawOrder[i].Draw(spriteBatch, cam, color);
}
if (!MathUtils.NearlyEqual(depthOffset, 0.0f))

View File

@@ -938,8 +938,8 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch, Camera cam)
{
if (!Enabled || InvisibleTimer > 0.0f) { return; }
AnimController.Draw(spriteBatch, cam);
if (!Enabled) { return; }
AnimController.Draw(spriteBatch, cam, onlyDrawSeveredLimbs: InvisibleTimer > 0.0f);
}
public void DrawHUD(SpriteBatch spriteBatch, Camera cam, bool drawHealth = true)

View File

@@ -4,6 +4,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
using Barotrauma.Items.Components;
using System.Linq;
namespace Barotrauma;
@@ -106,12 +107,17 @@ public static class InteractionLabelManager
}
RectangleF textRect = GetLabelRect(interactableInRange, cam);
if (labels.None(l => l.Item == interactableInRange))
var existingLabel = labels.FirstOrDefault(l => l.Item == interactableInRange);
if (existingLabel == null)
{
var labelData = new LabelData(interactableInRange, textRect, RichString.Rich(interactableInRange.Prefab.Name), cam);
labels.Add(labelData);
}
//size of the label doesn't match - can happen when we're using a CJK font which we asynchronously render new symbols for
else if (existingLabel.TextRect.Size != textRect.Size)
{
existingLabel.TextRect = textRect;
}
}
PreventInteractionLabelOverlap(centerPos: character.Position);
@@ -127,7 +133,11 @@ public static class InteractionLabelManager
private static RectangleF GetLabelRect(Item item, Camera cam)
{
// create rectangle for overlap prevention
Vector2 itemTextSizeScreen = GUIStyle.SubHeadingFont.MeasureString(RichString.Rich(item.Prefab.Name).SanitizedValue) * LabelScale;
string nameText = RichString.Rich(item.Prefab.Name).SanitizedValue;
var font = GUIStyle.SubHeadingFont.GetFontForStr(nameText)!;
Vector2 itemTextSizeScreen = font.MeasureString(nameText) * LabelScale;
Vector2 interactablePosScreen = cam.WorldToScreen(item.Position);
RectangleF textRect = new RectangleF(interactablePosScreen.X, interactablePosScreen.Y, itemTextSizeScreen.X, itemTextSizeScreen.Y);
// center the rectangle on the item

View File

@@ -340,10 +340,6 @@ namespace Barotrauma
break;
case "randomcolor":
randomColor = subElement.GetAttributeColorArray("colors", null)?.GetRandomUnsynced();
if (randomColor.HasValue)
{
Params.GetSprite().Color = randomColor.Value;
}
break;
case "lightsource":
LightSource = new LightSource(subElement, GetConditionalTarget())
@@ -631,6 +627,8 @@ namespace Barotrauma
SoundPlayer.PlayDamageSound(damageSoundType, Math.Max(damage, bleedingDamage), WorldPosition);
}
if (character.InvisibleTimer > 0.0f) { return; }
// spawn damage particles
float damageParticleAmount = damage < 1 ? 0 : Math.Min(damage / 5, 1.0f) * damageMultiplier;
if (damageParticleAmount > 0.001f)
@@ -734,7 +732,8 @@ namespace Barotrauma
if (spriteParams == null || Alpha <= 0) { return; }
float burn = spriteParams.IgnoreTint ? 0 : burnOverLayStrength;
float brightness = Math.Max(1.0f - burn, 0.2f);
Color tintedColor = spriteParams.Color;
Color baseColor = randomColor ?? spriteParams.Color;
Color tintedColor = baseColor;
if (!spriteParams.IgnoreTint)
{
tintedColor = tintedColor.Multiply(ragdoll.RagdollParams.Color);
@@ -752,7 +751,7 @@ namespace Barotrauma
}
}
Color color = new Color(tintedColor.Multiply(brightness), tintedColor.A);
Color colorWithoutTint = new Color(spriteParams.Color.Multiply(brightness), spriteParams.Color.A);
Color colorWithoutTint = new Color(baseColor.Multiply(brightness), baseColor.A);
Color blankColor = new Color(brightness, brightness, brightness, 1);
if (deadTimer > 0)
{

View File

@@ -31,7 +31,7 @@ namespace Barotrauma
GUILayoutGroup connLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.12f), labelList.Content.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(0.4f, 1f), connLayout.RectTransform), text: conn.Connection.DisplayName, font: GUIStyle.SubHeadingFont);
GUITextBox box = GUI.CreateTextBoxWithPlaceholder(new RectTransform(new Vector2(0.6f, 1f), connLayout.RectTransform), text: found ? labelOverride : string.Empty, conn.Connection.DisplayName.Value);
GUITextBox box = GUI.CreateTextBoxWithPlaceholder(new RectTransform(new Vector2(0.6f, 1f), connLayout.RectTransform), text: found ? labelOverride : string.Empty, conn.Connection.DefaultDisplayName.Value);
box.MaxTextLength = MaxConnectionLabelLength;
textBoxes.Add(conn.Name, box);

View File

@@ -16,6 +16,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Barotrauma.LuaCs.Events;
using static Barotrauma.FabricationRecipe;
namespace Barotrauma
@@ -222,8 +223,6 @@ namespace Barotrauma
private static bool IsCommandPermitted(Identifier command, GameClient client)
{
if (GameMain.LuaCs.Game.IsCustomCommandPermitted(command)) { return true; }
switch (command.Value.ToLowerInvariant())
{
case "kick":
@@ -659,14 +658,6 @@ namespace Barotrauma
return;
}
bool luaCsEnabled = true;
if (args.Length > 3)
{
bool.TryParse(args[3], out luaCsEnabled);
}
if (luaCsEnabled) { GameMain.LuaCs.Initialize(); }
GameMain.MainMenuScreen.QuickStart(fixedSeed: false, subName, difficulty, levelGenerationParams);
}, getValidArgs: () => new[] { SubmarineInfo.SavedSubmarines.Select(s => s.Name).Distinct().OrderBy(s => s).ToArray() }));
@@ -3553,6 +3544,11 @@ namespace Barotrauma
ContentPackageManager.RegularPackages.Select(p => p.Name).ToArray()
}));
commands.Add(new Command("ShowServerPerf", "Immediately log server performance info", (string[] args) =>
{
// TODO: Not yet :)
}));
#if WINDOWS
commands.Add(new Command("startdedicatedserver", "", (string[] args) =>
{
@@ -3586,6 +3582,14 @@ namespace Barotrauma
}
}));*/
AssignOnClientExecute(
"ShowServerPerf",
(string[] args) =>
{
GameMain.Client?.SendConsoleCommand("ShowServerPerf");
}
);
AssignOnClientExecute(
"giveperm",
(string[] args) =>
@@ -4223,51 +4227,8 @@ namespace Barotrauma
NewMessage("Minimum main path width: " + (Level.Loaded.LevelData?.MinMainPathWidth?.ToString() ?? "unknown"));
}
});
commands.Add(new Command("cl_lua", $"cl_lua: Runs a string on the client.", (string[] args) =>
{
if (GameMain.Client != null && !GameMain.Client.HasPermission(ClientPermissions.ConsoleCommands))
{
ThrowError("Command not permitted.");
return;
}
if (GameMain.LuaCs.Lua == null)
{
ThrowError("LuaCs not initialized, use the console command cl_reloadluacs to force initialization.");
return;
}
try
{
GameMain.LuaCs.Lua.DoString(string.Join(" ", args));
}
catch(Exception ex)
{
LuaCsLogger.HandleException(ex, LuaCsMessageOrigin.LuaMod);
}
}));
commands.Add(new Command("cl_reloadlua|cl_reloadcs|cl_reloadluacs", "Re-initializes the LuaCs environment.", (string[] args) =>
{
GameMain.LuaCs.Initialize();
}));
commands.Add(new Command("cl_toggleluadebug", "Toggles the MoonSharp Debug Server.", (string[] args) =>
{
int port = 41912;
if (args.Length > 0)
{
int.TryParse(args[0], out port);
}
GameMain.LuaCs.ToggleDebugger(port);
}));
}
private static void ReloadWearables(Character character, int variant = 0)
{
foreach (var limb in character.AnimController.Limbs)

View File

@@ -277,6 +277,14 @@ namespace Barotrauma
int selectedOption = (userdata as int?) ?? 0;
if (actionInstance != null)
{
var option = actionInstance.Options[selectedOption];
if (GameMain.Client == null && option.ForceSay)
{
Character.Controlled.ForceSay(
option.ForceSayText.IsNullOrEmpty() ? TextManager.Get(option.Text).Fallback(option.Text) : TextManager.Get(option.ForceSayText).Fallback(option.ForceSayText),
option.ForceSayInRadio,
option.ForceSayRemoveQuotes);
}
actionInstance.selectedOption = selectedOption;
DisableButtons(optionButtons, btn);
btn.ExternalHighlight = true;
@@ -340,7 +348,8 @@ namespace Barotrauma
if (speaker?.Info != null && drawChathead)
{
// chathead
new GUICustomComponent(new RectTransform(new Vector2(0.15f, 0.8f), content.RectTransform), onDraw: (sb, customComponent) =>
int chatHeadWidth = (int)(content.RectTransform.Rect.Width * 0.15f);
new GUICustomComponent(new RectTransform(new Point(chatHeadWidth, chatHeadWidth), content.RectTransform, isFixedSize: true), onDraw: (sb, customComponent) =>
{
speaker.Info.DrawIcon(sb, customComponent.Rect.Center.ToVector2(), customComponent.Rect.Size.ToVector2());
});
@@ -382,7 +391,7 @@ namespace Barotrauma
}
textContent.RectTransform.MinSize = new Point(0, textContent.Children.Sum(c => c.Rect.Height + textContent.AbsoluteSpacing) + GUI.IntScale(16));
content.RectTransform.MinSize = new Point(0, content.Children.Sum(c => c.Rect.Height));
content.RectTransform.MinSize = textContent.RectTransform.MinSize;
// Recalculate the text size as it is scaled up and no longer matching the text height due to the textContent's minSize increasing
textBlock.CalculateHeightFromText();

View File

@@ -61,17 +61,9 @@ namespace Barotrauma
{
Item.ReadSpawnData(msg);
}
if (character.Submarine != null && character.AIController is EnemyAIController enemyAi)
if (character.AIController is EnemyAIController enemyAi && character.Submarine is Submarine ownSub)
{
enemyAi.UnattackableSubmarines.Add(character.Submarine);
if (Submarine.MainSub != null)
{
enemyAi.UnattackableSubmarines.Add(Submarine.MainSub);
foreach (Submarine sub in Submarine.MainSub.DockedTo)
{
enemyAi.UnattackableSubmarines.Add(sub);
}
}
enemyAi.SetUnattackableSubmarines(ownSub);
}
}
if (characters.Contains(null))

View File

@@ -0,0 +1,8 @@
#nullable enable
namespace Barotrauma;
internal sealed partial class CustomMission : Mission
{
public override bool DisplayAsCompleted => State == SuccessState;
public override bool DisplayAsFailed => State == FailureState;
}

View File

@@ -14,7 +14,7 @@ namespace Barotrauma
private void TryShowRetrievedMessage()
{
if (DetermineCompleted())
if (DetermineCompleted(CampaignMode.TransitionType.None))
{
HandleMessage(ref allRetrievedMessage);
}

View File

@@ -1,5 +1,6 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.LuaCs.Events;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
@@ -413,7 +414,8 @@ namespace Barotrauma
{
if (GameMain.IsSingleplayer)
{
var should = GameMain.LuaCs.Hook.Call<bool?>("chatMessage", message.Text, message.SenderClient, message.Type, message);
bool? should = null;
LuaCsSetup.Instance.EventService.PublishEvent<IEventChatMessage>(x => should = x.OnChatMessage(message.Text, message.SenderClient, message.Type, message) ?? should);
if (should != null && should.Value) { return; }
}

View File

@@ -1435,8 +1435,15 @@ namespace Barotrauma
Uri baseAddress = new Uri(url);
Uri remoteDirectory = new Uri(baseAddress, ".");
string remoteFileName = Path.GetFileName(baseAddress.LocalPath);
IRestClient client = new RestClient(remoteDirectory);
var response = client.Execute(new RestRequest(remoteFileName, Method.GET));
var client = RestFactory.CreateClient(remoteDirectory.ToString());
var request = RestFactory.CreateRequest(remoteFileName);
var response = client.Execute(request);
if (response.ErrorException != null)
{
DebugConsole.AddWarning($"Connection error: Failed to load remote sprite from {url} " +
$"({response.ErrorException.Message}).");
return null;
}
if (response.ResponseStatus != ResponseStatus.Completed) { return null; }
if (response.StatusCode != HttpStatusCode.OK) { return null; }

View File

@@ -26,7 +26,9 @@ namespace Barotrauma
public OnSelectedHandler OnDropped;
private readonly GUIButton button;
private readonly GUIButton button;
public GUIButton Button => button;
private readonly GUIImage icon;
private readonly GUIListBox listBox;

View File

@@ -710,19 +710,24 @@ namespace Barotrauma
if (listBox == pendingList || listBox == crewList)
{
nameBlock.RectTransform.Resize(new Point(nameBlock.Rect.Width - nameBlock.Rect.Height, nameBlock.Rect.Height));
nameBlock.Text = ToolBox.LimitString(characterName, nameBlock.Font, nameBlock.Rect.Width);
nameBlock.RectTransform.Resize(new Point((int)(nameBlock.Padding.X + nameBlock.TextSize.X + nameBlock.Padding.Z), nameBlock.Rect.Height));
Point size = new Point((int)(0.7f * nameBlock.Rect.Height));
new GUIImage(new RectTransform(size, nameGroup.RectTransform), "EditIcon") { CanBeFocused = false };
size = new Point(3 * mainGroup.AbsoluteSpacing + icon.Rect.Width + nameAndJobGroup.Rect.Width, mainGroup.Rect.Height);
new GUIButton(new RectTransform(size, frame.RectTransform) { RelativeOffset = new Vector2(0.025f) }, style: null)
//if the character is already in the crew, only check permissions - reputation doesn't matter for renaming an already-hired bot
bool canRename = listBox == crewList ? HasPermissionToHire : CanHire(characterInfo);
if (canRename)
{
Enabled = CanHire(characterInfo),
ToolTip = TextManager.GetWithVariable("campaigncrew.givenicknametooltip", "[mouseprimary]", PlayerInput.PrimaryMouseLabel),
UserData = characterInfo,
OnClicked = CreateRenamingComponent
};
nameBlock.RectTransform.Resize(new Point(nameBlock.Rect.Width - nameBlock.Rect.Height, nameBlock.Rect.Height));
nameBlock.Text = ToolBox.LimitString(characterName, nameBlock.Font, nameBlock.Rect.Width);
nameBlock.RectTransform.Resize(new Point((int)(nameBlock.Padding.X + nameBlock.TextSize.X + nameBlock.Padding.Z), nameBlock.Rect.Height));
Point iconSize = new Point((int)(0.7f * nameBlock.Rect.Height));
new GUIImage(new RectTransform(iconSize, nameGroup.RectTransform), "EditIcon") { CanBeFocused = false };
Point buttonSize = new Point(3 * mainGroup.AbsoluteSpacing + icon.Rect.Width + nameAndJobGroup.Rect.Width + (int)(iconSize.X * 1.5f), mainGroup.Rect.Height);
new GUIButton(new RectTransform(buttonSize, frame.RectTransform) { RelativeOffset = new Vector2(0.025f) }, style: null)
{
ClampMouseRectToParent = false,
ToolTip = TextManager.GetWithVariable("campaigncrew.givenicknametooltip", "[mouseprimary]", PlayerInput.PrimaryMouseLabel),
UserData = characterInfo,
OnClicked = CreateRenamingComponent
};
}
}
//recalculate everything and truncate texts if needed

View File

@@ -18,12 +18,12 @@ using System.Reflection;
using System.Threading;
using Barotrauma.Extensions;
using System.Collections.Immutable;
using Barotrauma.LuaCs.Events;
namespace Barotrauma
{
class GameMain : Game
{
public static LuaCsSetup LuaCs;
public static bool ShowFPS;
public static bool ShowPerf;
public static bool DebugDraw;
@@ -244,8 +244,6 @@ namespace Barotrauma
throw new Exception("Content folder not found. If you are trying to compile the game from the source code and own a legal copy of the game, you can copy the Content folder from the game's files to BarotraumaShared/Content.");
}
LuaCs = new LuaCsSetup();
GameSettings.Init();
CreatureMetrics.Init();
@@ -297,6 +295,8 @@ namespace Barotrauma
MainThread = Thread.CurrentThread;
Window.FileDropped += OnFileDropped;
LuaCsSetup.Instance.GetType();
}
public static void ExecuteAfterContentFinishedLoading(Action action)
@@ -636,9 +636,6 @@ namespace Barotrauma
HasLoaded = true;
log("LOADING COROUTINE FINISHED");
#if CLIENT
LuaCsInstaller.CheckUpdate();
#endif
contentLoaded = true;
while (postContentLoadActions.TryDequeue(out Action action))
@@ -986,8 +983,6 @@ namespace Barotrauma
Screen.Selected.AddToGUIUpdateList();
LuaCsLogger.AddToGUIUpdateList();
Client?.AddToGUIUpdateList();
SubmarinePreview.AddToGUIUpdateList();
@@ -1054,8 +1049,6 @@ namespace Barotrauma
SoundManager?.Update();
GameMain.LuaCs.Update();
Timing.Accumulator -= Timing.Step;
updateCount++;
@@ -1237,8 +1230,6 @@ namespace Barotrauma
GUIMessageBox.CloseAll();
MainMenuScreen.Select();
GameSession = null;
GameMain.LuaCs.Stop();
}
public void ShowBugReporter()
@@ -1301,6 +1292,18 @@ namespace Barotrauma
{
IsExiting = true;
CreatureMetrics.Save();
try
{
if (LuaCsSetup.Instance is not null)
{
LuaCsSetup.Instance.Dispose();
}
}
catch (Exception e)
{
DebugConsole.ThrowError($"Error while disposing of LuaCsForBarotrauma: {e.Message} | {e.StackTrace}");
}
DebugConsole.NewMessage("Exiting...");
Client?.Quit();
SteamManager.ShutDown();

View File

@@ -179,8 +179,6 @@ namespace Barotrauma.Tutorials
public void Start()
{
GameMain.LuaCs.CheckInitialize();
GameMain.Instance.ShowLoading(Loading());
ObjectiveManager.ResetObjectives();

View File

@@ -487,7 +487,21 @@ namespace Barotrauma.Items.Components
return 0.0f;
}
public virtual bool ShouldDrawHUD(Character character)
public bool ShouldDrawHUD(Character character)
{
if (Character.Controlled?.SelectedItem != null)
{
Controller controller = item.GetComponent<Controller>();
if (controller != null && controller.User == Character.Controlled && controller.HideAllItemComponentHUDs)
{
return false;
}
}
return ShouldDrawHUDComponentSpecific(character);
}
protected virtual bool ShouldDrawHUDComponentSpecific(Character character)
{
return true;
}

View File

@@ -552,9 +552,9 @@ namespace Barotrauma.Items.Components
if (flippedY) { origin.Y = contained.Item.Sprite.SourceRect.Height - origin.Y; }
float containedSpriteDepth = ContainedSpriteDepth < 0.0f ? contained.Item.Sprite.Depth : ContainedSpriteDepth;
if (i < containedSpriteDepths.Length)
if (targetSlotIndex < containedSpriteDepths.Length)
{
containedSpriteDepth = containedSpriteDepths[i];
containedSpriteDepth = containedSpriteDepths[targetSlotIndex];
}
containedSpriteDepth = itemDepth + (containedSpriteDepth - (item.Sprite?.Depth ?? item.SpriteDepth)) / 10000.0f;

View File

@@ -52,10 +52,12 @@ namespace Barotrauma.Items.Components
partial void SetLightSourceTransformProjSpecific()
{
Vector2 offset = Vector2.Zero;
if (LightOffset != Vector2.Zero)
Vector2 offset = LightOffset * item.Scale;
if (offset != Vector2.Zero)
{
offset = Vector2.Transform(LightOffset, Matrix.CreateRotationZ(item.FlippedY ? -item.RotationRad - MathHelper.Pi : -item.RotationRad)) * item.Scale;
if (item.FlippedX) { offset.X *= -1; }
if (item.FlippedY) { offset.Y *= -1; }
offset = Vector2.Transform(offset, Matrix.CreateRotationZ(-item.RotationRad));
}
if (ParentBody != null)
@@ -101,7 +103,10 @@ namespace Barotrauma.Items.Components
if (Light?.LightSprite == null) { return; }
if ((item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn && Light.Enabled)
{
Vector2 offset = Vector2.Transform(LightOffset, Matrix.CreateRotationZ(item.FlippedY ? -item.RotationRad - MathHelper.Pi : -item.RotationRad)) * item.Scale;
Vector2 offset = LightOffset * item.Scale;
if (item.FlippedX) { offset.X *= -1; }
if (item.FlippedY) { offset.Y *= -1; }
offset = Vector2.Transform(offset, Matrix.CreateRotationZ(-item.RotationRad));
Vector2 origin = Light.LightSprite.Origin;
if ((Light.LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X = Light.LightSprite.SourceRect.Width - origin.X; }
@@ -114,6 +119,7 @@ namespace Barotrauma.Items.Components
{
color = new Color(lightColor, Light.OverrideLightSpriteAlpha.Value);
}
Light.LightSprite.Draw(spriteBatch,
new Vector2(drawPos.X, -drawPos.Y),
color * lightBrightness,
@@ -128,8 +134,16 @@ namespace Barotrauma.Items.Components
{
if (Light?.LightSprite != null && item.Prefab.CanSpriteFlipX)
{
Light.LightSpriteEffect = Light.LightSpriteEffect == SpriteEffects.None ?
SpriteEffects.FlipHorizontally : SpriteEffects.None;
Light.LightSpriteEffect ^= SpriteEffects.FlipHorizontally;
}
SetLightSourceTransformProjSpecific();
}
public override void FlipY(bool relativeToSub)
{
if (Light?.LightSprite != null && item.Prefab.CanSpriteFlipY)
{
Light.LightSpriteEffect ^= SpriteEffects.FlipVertically;
}
SetLightSourceTransformProjSpecific();
}

View File

@@ -8,6 +8,30 @@ namespace Barotrauma.Items.Components
{
private bool isHUDsHidden;
public void UpdateMsg()
{
if (Character.Controlled == null) { return; }
if (!string.IsNullOrEmpty(KickOutCharacterMsg) &&
SelectingKicksCharacterOut &&
User != null && !User.Removed)
{
DisplayMsg = TextManager.ParseInputTypes(TextManager.Get(KickOutCharacterMsg));
}
else if (!string.IsNullOrEmpty(PutOtherCharacterMsg) &&
AllowPuttingInOtherCharacters &&
CanPutSelectedCharacter(Character.Controlled.SelectedCharacter))
{
DisplayMsg = TextManager.ParseInputTypes(TextManager.Get(PutOtherCharacterMsg));
}
else
{
DisplayMsg = TextManager.ParseInputTypes(TextManager.Get(Msg));
}
CharacterHUD.RecreateHudTextsIfControlling(Character.Controlled);
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
base.DrawHUD(spriteBatch, character);
@@ -69,21 +93,33 @@ namespace Barotrauma.Items.Components
ushort userID = msg.ReadUInt16();
if (userID == 0)
{
if (user != null)
if (User != null)
{
IsActive = false;
CancelUsing(user);
user = null;
CancelUsing(User);
User = null;
}
}
else
{
Character newUser = Entity.FindEntityByID(userID) as Character;
if (newUser != user)
if (newUser != User)
{
CancelUsing(user);
CancelUsing(User);
}
user = newUser;
User = newUser;
// If the server assigned a user to this controller but the character is not selecting the item
// on the client-side, force the selection to prevent desync. This is required for force attaching,
// since the character placed into the controller may be unconscious, and in that state
// the server no longer syncs the current SelectedItem to clients.
if (ForceUserToStayAttached &&
user != null &&
!user.IsAnySelectedItem(Item))
{
user.SelectedItem = Item;
}
IsActive = true;
}
}

View File

@@ -434,18 +434,13 @@ namespace Barotrauma.Items.Components
foreach (FabricationRecipe fi in fabricationRecipes.Values)
{
RichString recipeTooltip =
fi.RequiresRecipe ?
RichString.Rich(fi.TargetItem.Description + "\n\n" + $"‖color:{XMLExtensions.ToStringHex(GUIStyle.Red)}‖{TextManager.Get("fabricatorrequiresrecipe")}‖color:end‖") :
RichString.Rich(fi.TargetItem.Description);
var frame = new GUIFrame(new RectTransform(new Point(itemList.Content.Rect.Width, (int)(40 * GUI.yScale)), itemList.Content.RectTransform), style: null)
{
UserData = fi,
HoverColor = Color.Gold * 0.2f,
SelectedColor = Color.Gold * 0.5f,
ToolTip = recipeTooltip
};
SetRecipeTooltip(frame, fi);
var container = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform),
childAnchor: Anchor.CenterLeft, isHorizontal: true) { RelativeSpacing = 0.02f };
@@ -457,7 +452,7 @@ namespace Barotrauma.Items.Components
itemIcon, scaleToFit: true)
{
Color = itemIcon == fi.TargetItem.Sprite ? fi.TargetItem.SpriteColor : fi.TargetItem.InventoryIconColor,
ToolTip = recipeTooltip
CanBeFocused = false
};
}
@@ -466,7 +461,7 @@ namespace Barotrauma.Items.Components
{
Padding = Vector4.Zero,
AutoScaleVertical = true,
ToolTip = recipeTooltip
CanBeFocused = false
};
new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), frame.RectTransform, Anchor.BottomRight),
@@ -478,6 +473,20 @@ namespace Barotrauma.Items.Components
}
}
private void SetRecipeTooltip(GUIComponent component, FabricationRecipe recipe)
{
if (!recipe.RequiresRecipe)
{
component.ToolTip = RichString.Rich(recipe.TargetItem.Description);
}
else
{
component.ToolTip = AnyOneHasRecipeForItem(Character.Controlled, recipe.TargetItem) ?
RichString.Rich(recipe.TargetItem.Description + "\n\n" + $"‖color:{XMLExtensions.ToStringHex(GUIStyle.Green)}‖{TextManager.Get("unlockedrecipe.true")}‖color:end‖") :
RichString.Rich(recipe.TargetItem.Description + "\n\n" + $"‖color:{XMLExtensions.ToStringHex(GUIStyle.Red)}‖{TextManager.Get("fabricatorrequiresrecipe")}‖color:end‖");
}
}
private void InitInventoryUIs()
{
if (inputInventoryHolder != null)
@@ -927,16 +936,24 @@ namespace Barotrauma.Items.Components
}
}
if (recipe.RequiresRecipe && recipe.HideIfNoRecipe)
if (recipe.RequiresRecipe)
{
if (Character.Controlled != null)
if (recipe.HideIfNoRecipe)
{
if (!AnyOneHasRecipeForItem(Character.Controlled, recipe.TargetItem))
bool anyOneHasRecipe = AnyOneHasRecipeForItem(Character.Controlled, recipe.TargetItem);
if (Character.Controlled != null)
{
child.Visible = false;
continue;
if (!anyOneHasRecipe)
{
child.Visible = false;
continue;
}
}
}
else
{
SetRecipeTooltip(child, recipe);
}
}
child.Visible =
@@ -1147,7 +1164,16 @@ namespace Barotrauma.Items.Components
var lines = description.WrappedText.Split('\n');
if (lines.Count <= 1) { break; }
string newString = string.Join('\n', lines.Take(lines.Count - 1));
description.Text = newString.Substring(0, newString.Length - 4) + "...";
if (newString.Length > 4)
{
description.Text = newString.Substring(0, newString.Length - 4) + "...";
}
else
{
description.Text = newString + "...";
}
description.CalculateHeightFromText();
description.ToolTip = richDescription;
}

View File

@@ -443,6 +443,7 @@ namespace Barotrauma.Items.Components
var wire = targetItem.GetComponent<Wire>();
if (wire != null && wire.Connections.Any(c => c != null)) { return false; }
if (targetItem.Container is { NonInteractable: true }) { return false; }
if (targetItem.Container?.GetComponent<ItemContainer>() is { DrawInventory: false } or { AllowAccess: false }) { return false; }
if (targetItem.HasTag(Tags.TraitorMissionItem)) { return false; }

View File

@@ -575,6 +575,22 @@ namespace Barotrauma.Items.Components
pos /= c.Resources.Count;
MineralClusters.Add((center: pos, resources: c.Resources));
}
if (GameMain.GameSession != null)
{
foreach (var mission in GameMain.GameSession.Missions)
{
if (mission is MineralMission mineralMission)
{
foreach (var minerals in mineralMission.SpawnedResources)
{
MineralClusters.Add((
center: new Vector2(minerals.Average(m => m.WorldPosition.X), minerals.Average(m => m.WorldPosition.Y)),
resources: minerals));
}
}
}
}
}
else
{
@@ -823,18 +839,20 @@ namespace Barotrauma.Items.Components
if (t.Entity is Character c && !c.IsUnconscious && c.Params.HideInSonar) { continue; }
if (t.SoundRange <= 0.0f || float.IsNaN(t.SoundRange) || float.IsInfinity(t.SoundRange)) { continue; }
float sonarSoundRange = t.SoundRange * t.SoundRangeOnSonarMultiplier;
float distSqr = Vector2.DistanceSquared(t.WorldPosition, transducerCenter);
if (distSqr > t.SoundRange * t.SoundRange * 2) { continue; }
if (distSqr > sonarSoundRange * sonarSoundRange * 2) { continue; }
float dist = (float)Math.Sqrt(distSqr);
if (dist > prevPassivePingRadius * Range && dist <= passivePingRadius * Range && Rand.Int(sonarBlips.Count) < 500)
{
Ping(t.WorldPosition, transducerCenter,
t.SoundRange * DisplayScale, 0, DisplayScale, range,
sonarSoundRange * DisplayScale, 0, DisplayScale, range,
passive: true, pingStrength: 0.5f, needsToBeInSector: t);
if (t.IsWithinSector(transducerCenter))
{
sonarBlips.Add(new SonarBlip(t.WorldPosition, fadeTimer: 1.0f, scale: MathHelper.Clamp(t.SoundRange / 2000, 1.0f, 5.0f)));
sonarBlips.Add(new SonarBlip(t.WorldPosition, fadeTimer: 1.0f, scale: MathHelper.Clamp(sonarSoundRange / 2000, 1.0f, 5.0f)));
}
}
}
@@ -977,7 +995,9 @@ namespace Barotrauma.Items.Components
if (aiTarget.InDetectable) { continue; }
if (aiTarget.SonarLabel.IsNullOrEmpty() || aiTarget.SoundRange <= 0.0f) { continue; }
if (Vector2.DistanceSquared(aiTarget.WorldPosition, transducerCenter) < aiTarget.SoundRange * aiTarget.SoundRange)
float sonarSoundRange = aiTarget.SoundRange * aiTarget.SoundRangeOnSonarMultiplier;
if (Vector2.DistanceSquared(aiTarget.WorldPosition, transducerCenter) < sonarSoundRange * sonarSoundRange)
{
DrawMarker(spriteBatch,
aiTarget.SonarLabel.Value,

View File

@@ -58,7 +58,7 @@ namespace Barotrauma.Items.Components
get { return Vector2.Zero; }
}
public override bool ShouldDrawHUD(Character character)
protected override bool ShouldDrawHUDComponentSpecific(Character character)
{
if (item.IsHidden) { return false; }
if (!HasRequiredItems(character, false) || character.SelectedItem != item) { return false; }

View File

@@ -78,7 +78,7 @@ namespace Barotrauma.Items.Components
}
}
public override bool ShouldDrawHUD(Character character)
protected override bool ShouldDrawHUDComponentSpecific(Character character)
=> character == Character.Controlled && (character.SelectedItem == item || character.SelectedSecondaryItem == item);
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)

View File

@@ -97,7 +97,7 @@ namespace Barotrauma.Items.Components
MoveConnectedWires(amount);
}
public override bool ShouldDrawHUD(Character character)
protected override bool ShouldDrawHUDComponentSpecific(Character character)
{
return character == Character.Controlled && character == user && (character.SelectedItem == item || character.SelectedSecondaryItem == item);
}

View File

@@ -287,20 +287,24 @@ namespace Barotrauma.Items.Components
texts.Add(target.CustomInteractHUDText);
textColors.Add(GUIStyle.Green);
}
if (!target.IsIncapacitated && target.IsPet)
if (equipper?.FocusedCharacter == target)
{
texts.Add(CharacterHUD.GetCachedHudText("PlayHint", InputType.Use));
textColors.Add(GUIStyle.Green);
}
if (equipper?.FocusedCharacter == target && target.CanBeHealedBy(equipper, checkFriendlyTeam: false))
{
texts.Add(CharacterHUD.GetCachedHudText("HealHint", InputType.Health));
textColors.Add(GUIStyle.Green);
}
if (target.CanBeDraggedBy(Character.Controlled))
{
texts.Add(CharacterHUD.GetCachedHudText("GrabHint", InputType.Grab));
textColors.Add(GUIStyle.Green);
if (!target.IsIncapacitated && target.IsPet &&
target.AIController is EnemyAIController enemyAI && enemyAI.PetBehavior.CanPlayWith(Character.Controlled))
{
texts.Add(CharacterHUD.GetCachedHudText("PlayHint", InputType.Use));
textColors.Add(GUIStyle.Green);
}
if (target.CanBeHealedBy(equipper, checkFriendlyTeam: false))
{
texts.Add(CharacterHUD.GetCachedHudText("HealHint", InputType.Health));
textColors.Add(GUIStyle.Green);
}
if (target.CanBeDraggedBy(Character.Controlled))
{
texts.Add(CharacterHUD.GetCachedHudText("GrabHint", InputType.Grab));
textColors.Add(GUIStyle.Green);
}
}
if (target.IsUnconscious)

View File

@@ -1597,7 +1597,8 @@ namespace Barotrauma
{
if (DraggingSlot == null || (!DraggingSlot.MouseOn()))
{
Sprite sprite = DraggingItems.First().Prefab.InventoryIcon ?? DraggingItems.First().Sprite;
Item firstDraggingItem = DraggingItems.First();
Sprite sprite = firstDraggingItem.OverrideInventorySprite ?? firstDraggingItem.Prefab.InventoryIcon ?? firstDraggingItem.Sprite;
int iconSize = (int)(64 * GUI.Scale);
float scale = Math.Min(Math.Min(iconSize / sprite.size.X, iconSize / sprite.size.Y), 1.5f);
@@ -1854,7 +1855,7 @@ namespace Barotrauma
if (item != null && drawItem)
{
Sprite sprite = item.Prefab.InventoryIcon ?? item.Sprite;
Sprite sprite = item.OverrideInventorySprite ?? item.Prefab.InventoryIcon ?? item.Sprite;
float scale = Math.Min(Math.Min((rect.Width - 10) / sprite.size.X, (rect.Height - 10) / sprite.size.Y), 2.0f);
Vector2 itemPos = rect.Center.ToVector2();
if (itemPos.Y > GameMain.GraphicsHeight)

View File

@@ -419,7 +419,7 @@ namespace Barotrauma
if (fadeInBrokenSprite != null)
{
float d = Math.Min(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.999f);
float d = MathHelper.Clamp(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.0f, 0.999f);
fadeInBrokenSprite.Sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + fadeInBrokenSprite.Offset.ToVector2() * Scale, size, color: color * fadeInBrokenSpriteAlpha,
textureScale: Vector2.One * Scale,
depth: d);
@@ -435,7 +435,7 @@ namespace Barotrauma
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, RotationRad, Scale, activeSprite.effects, depth);
if (fadeInBrokenSprite != null)
{
float d = Math.Min(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.999f);
float d = MathHelper.Clamp(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.0f, 0.999f);
fadeInBrokenSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, RotationRad, Scale, activeSprite.effects, d);
}
}
@@ -885,7 +885,12 @@ namespace Barotrauma
Spacing = (int)(25 * GUI.Scale)
};
var itemEditor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUIStyle.LargeFont) { UserData = this };
var itemEditor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true,
titleFont: GUIStyle.LargeFont,
dimOutDefaultValues: false)
{
UserData = this
};
activeEditors.Add(itemEditor);
itemEditor.Children.First().Color = Color.Black * 0.7f;
if (!inGame)
@@ -1045,7 +1050,12 @@ namespace Barotrauma
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), listBox.Content.RectTransform), style: "HorizontalLine");
var componentEditor = new SerializableEntityEditor(listBox.Content.RectTransform, ic, inGame, showName: !inGame, titleFont: GUIStyle.SubHeadingFont) { UserData = ic };
var componentEditor = new SerializableEntityEditor(listBox.Content.RectTransform, ic, inGame, showName: !inGame,
titleFont: GUIStyle.SubHeadingFont,
dimOutDefaultValues: false)
{
UserData = ic
};
componentEditor.Children.First().Color = Color.Black * 0.7f;
activeEditors.Add(componentEditor);
@@ -1064,7 +1074,12 @@ namespace Barotrauma
requiredItems.Add(relatedItem);
}
}
requiredItems.AddRange(ic.DisabledRequiredItems);
//if we have some actual requirements, no need to keep the empty requirement
//as a "placeholder" for the user to add requirements in the sub editor
if (ic.RequiredItems.None())
{
requiredItems.AddRange(ic.DisabledRequiredItems);
}
foreach (RelatedItem relatedItem in requiredItems)
{
@@ -1626,12 +1641,16 @@ namespace Barotrauma
activeComponents.Clear();
activeComponents.AddRange(components);
foreach (MapEntity entity in linkedTo)
Controller controller = GetComponent<Controller>();
if (controller == null || controller.User != Character.Controlled || !controller.HideAllItemComponentHUDs)
{
if (Prefab.IsLinkAllowed(entity.Prefab) && entity is Item i)
foreach (MapEntity entity in linkedTo)
{
if (!i.DisplaySideBySideWhenLinked) { continue; }
activeComponents.AddRange(i.components);
if (Prefab.IsLinkAllowed(entity.Prefab) && entity is Item i)
{
if (!i.DisplaySideBySideWhenLinked) { continue; }
activeComponents.AddRange(i.components);
}
}
}
@@ -1701,7 +1720,9 @@ namespace Barotrauma
foreach (Character otherCharacter in Character.CharacterList)
{
if (otherCharacter != character &&
otherCharacter.SelectedItem == this)
otherCharacter.SelectedItem == this &&
// Prevent the in use message from being shown if a character is, for example, inside the deconstructor
!otherCharacter.IsAttachedToController())
{
ItemInUseWarning.Visible = true;
if (mergedHUDRect.Width > GameMain.GraphicsWidth / 2) { mergedHUDRect.Inflate(-GameMain.GraphicsWidth / 4, 0); }
@@ -1751,6 +1772,11 @@ namespace Barotrauma
}
}
public void ClearActiveHUDs()
{
activeHUDs.Clear();
}
readonly List<ColoredText> texts = new();
public List<ColoredText> GetHUDTexts(Character character, bool recreateHudTexts = true)
{

View File

@@ -166,6 +166,14 @@ namespace Barotrauma
subElement.GetAttributeBool("fadein", false),
subElement.GetAttributePoint("offset", Point.Zero));
if (brokenSprite.FadeIn && brokenSprite.MaxConditionPercentage <= 0.0f)
{
DebugConsole.AddWarning(
$"Potential error in item {Identifier}: a broken sprite that's set to fade in despite the max condition being 0."+
" The sprite cannot fade in if it's set to only appear when the item is fully broken.",
ContentPackage);
}
int spriteIndex = 0;
for (int i = 0; i < brokenSprites.Count && brokenSprites[i].MaxConditionPercentage < brokenSprite.MaxConditionPercentage; i++)
{

View File

@@ -0,0 +1,33 @@
using Barotrauma.LuaCs.Data;
namespace Barotrauma.LuaCs.Data;
public partial interface IConfigInfo : IConfigDisplayInfo { }
public interface IConfigDisplayInfo
{
/// <summary>
/// Localization Token for display name.
/// </summary>
string DisplayName { get; }
/// <summary>
/// Localization Token for description.
/// </summary>
string Description { get; }
/// <summary>
/// The menu category to display under. Used for filtering.
/// </summary>
string DisplayCategory { get; }
/// <summary>
/// Should this config be displayed in end-user menus.
/// </summary>
bool ShowInMenus { get; }
/// <summary>
/// User-friendly on-hover tooltip text or Localization Token.
/// </summary>
string Tooltip { get; }
/// <summary>
/// Icon for display in menus, if available.
/// </summary>
ContentPath ImageIconPath { get; }
}

View File

@@ -0,0 +1,9 @@
using System;
using Microsoft.Xna.Framework;
namespace Barotrauma.LuaCs.Data;
public interface IDisplayable
{
public void AddDisplayComponent(GUILayoutGroup layoutGroup, Vector2 relativeSize, Action<string> onSerializedValue);
}

View File

@@ -0,0 +1,6 @@
namespace Barotrauma.LuaCs.Data;
public partial interface ISettingBase : IDisplayable
{
}

View File

@@ -0,0 +1,11 @@
using System;
namespace Barotrauma.LuaCs.Data;
public interface ISettingControl : ISettingBase
{
KeyOrMouse Value { get; }
bool TrySetValue(KeyOrMouse value);
bool IsDown();
bool IsHit();
}

View File

@@ -0,0 +1,252 @@
using System;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.LuaCs.Data;
using Microsoft.Toolkit.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using OneOf;
namespace Barotrauma.LuaCs.Data;
public sealed class SettingControl : SettingBase, ISettingControl
{
public class Factory : ISettingBase.IFactory<ISettingBase>
{
public ISettingBase CreateInstance(IConfigInfo configInfo, Func<OneOf<string, XElement, object>, bool> valueChangePredicate)
{
Guard.IsNotNull(configInfo, nameof(configInfo));
return new SettingControl(configInfo, valueChangePredicate);
}
}
public SettingControl(IConfigInfo configInfo, Func<OneOf<string, XElement, object>, bool> valueChangePredicate) : base(configInfo)
{
_valueChangePredicate = valueChangePredicate;
TrySetSerializedValue(configInfo.Element);
}
protected override void OnDispose()
{
OnValueChanged = null;
}
private Func<OneOf<string, XElement, object>, bool> _valueChangePredicate;
public override Type GetValueType() => typeof(KeyOrMouse);
public override string GetStringValue() => Value.ToString();
public override string GetDefaultStringValue() => new KeyOrMouse(Keys.NumLock).ToString();
public override bool TrySetSerializedValue(OneOf<string, XElement> value)
{
var newVal = value.Match<KeyOrMouse>(
(string v) => GetKeyOrMouse(v),
(XElement e) => e.GetAttributeKeyOrMouse("Value", null));
if (newVal is null)
{
return false;
}
if (_valueChangePredicate is not null && !_valueChangePredicate.Invoke(newVal))
{
return false;
}
Value = newVal;
OnValueChanged?.Invoke(this);
return true;
KeyOrMouse GetKeyOrMouse(string strValue)
{
strValue ??= string.Empty;
if (Enum.TryParse(strValue, true, out Microsoft.Xna.Framework.Input.Keys key))
{
return key;
}
else if (Enum.TryParse(strValue, out MouseButton mouseButton))
{
return mouseButton;
}
else if (int.TryParse(strValue, NumberStyles.Any, CultureInfo.InvariantCulture, out int mouseButtonInt) &&
Enum.GetValues<MouseButton>().Contains((MouseButton)mouseButtonInt))
{
return (MouseButton)mouseButtonInt;
}
else if (string.Equals(strValue, "LeftMouse", StringComparison.OrdinalIgnoreCase))
{
return !PlayerInput.MouseButtonsSwapped() ? MouseButton.PrimaryMouse : MouseButton.SecondaryMouse;
}
else if (string.Equals(strValue, "RightMouse", StringComparison.OrdinalIgnoreCase))
{
return !PlayerInput.MouseButtonsSwapped() ? MouseButton.SecondaryMouse : MouseButton.PrimaryMouse;
}
return null;
}
}
public override event Action<ISettingBase> OnValueChanged;
public override OneOf<string, XElement> GetSerializableValue() => Value.ToString();
public KeyOrMouse Value { get; private set; } = new KeyOrMouse(Keys.NumLock);
public bool TrySetValue(KeyOrMouse value)
{
Value = value;
OnValueChanged?.Invoke(this);
return true;
}
public bool IsDown()
{
if (this.Value is null)
return false;
switch (this.Value.MouseButton)
{
case MouseButton.None:
return Barotrauma.PlayerInput.KeyDown(this.Value.Key);
case MouseButton.PrimaryMouse:
return Barotrauma.PlayerInput.PrimaryMouseButtonHeld();
case MouseButton.SecondaryMouse:
return Barotrauma.PlayerInput.SecondaryMouseButtonHeld();
case MouseButton.MiddleMouse:
return Barotrauma.PlayerInput.MidButtonHeld();
case MouseButton.MouseButton4:
return Barotrauma.PlayerInput.Mouse4ButtonHeld();
case MouseButton.MouseButton5:
return Barotrauma.PlayerInput.Mouse5ButtonHeld();
case MouseButton.MouseWheelUp:
return Barotrauma.PlayerInput.MouseWheelUpClicked();
case MouseButton.MouseWheelDown:
return Barotrauma.PlayerInput.MouseWheelDownClicked();
}
return false;
}
public bool IsHit()
{
if (this.Value is null)
return false;
switch (this.Value.MouseButton)
{
case MouseButton.None:
return Barotrauma.PlayerInput.KeyHit(this.Value.Key);
case MouseButton.PrimaryMouse:
return Barotrauma.PlayerInput.PrimaryMouseButtonClicked();
case MouseButton.SecondaryMouse:
return Barotrauma.PlayerInput.SecondaryMouseButtonClicked();
case MouseButton.MiddleMouse:
return Barotrauma.PlayerInput.MidButtonClicked();
case MouseButton.MouseButton4:
return Barotrauma.PlayerInput.Mouse4ButtonClicked();
case MouseButton.MouseButton5:
return Barotrauma.PlayerInput.Mouse5ButtonClicked();
case MouseButton.MouseWheelUp:
return Barotrauma.PlayerInput.MouseWheelUpClicked();
case MouseButton.MouseWheelDown:
return Barotrauma.PlayerInput.MouseWheelDownClicked();
}
return false;
}
#if CLIENT
private static GUICustomComponent InputListener;
public override void AddDisplayComponent(GUILayoutGroup layoutGroup, Vector2 relativeSize, Action<string> onSerializedValue)
{
var inputButton = new GUIButton(new RectTransform(relativeSize, layoutGroup.RectTransform), Alignment.Center,
style: "GUITextBoxNoIcon")
{
Text = this.Value.ToString(),
OnClicked = (btn, obj) =>
{
if (InputListener is not null)
{
// Another button is active
return true;
}
CoroutineManager.Invoke(() =>
{
CreateListener(btn);
}, 0f); // delay one frame for button inputs
return true;
}
};
inputButton.OutlineColor = Color.PeachPuff;
inputButton.TextColor = Color.White;
void ClearListener()
{
InputListener?.Parent.RemoveChild(InputListener);
InputListener = null;
}
void CreateListener(GUIButton button)
{
ClearListener();
InputListener = new GUICustomComponent(new RectTransform(Vector2.Zero, layoutGroup.RectTransform),
onUpdate: (deltaTime, component) =>
{
var pressedKeys = PlayerInput.GetKeyboardState.GetPressedKeys();
if (pressedKeys?.Any() ?? false)
{
if (pressedKeys.Contains(Keys.Escape))
{
ClearListener();
return;
}
ApplyValue(pressedKeys.First(), button);
return;
}
if (PlayerInput.PrimaryMouseButtonClicked() &&
(GUI.MouseOn == null || !(GUI.MouseOn is GUIButton) || GUI.MouseOn.IsChildOf(layoutGroup)))
{
ApplyValue(MouseButton.PrimaryMouse, button);
return;
}
else if (PlayerInput.SecondaryMouseButtonClicked())
{
ApplyValue(MouseButton.SecondaryMouse, button);
return;
}
else if (PlayerInput.MidButtonClicked())
{
ApplyValue(MouseButton.MiddleMouse, button);
return;
}
else if (PlayerInput.Mouse4ButtonClicked())
{
ApplyValue(MouseButton.MouseButton4, button);
return;
}
else if (PlayerInput.Mouse5ButtonClicked())
{
ApplyValue(MouseButton.MouseButton5, button);
return;
}
else if (PlayerInput.MouseWheelUpClicked())
{
ApplyValue(MouseButton.MouseWheelUp, button);
return;
}
else if (PlayerInput.MouseWheelDownClicked())
{
ApplyValue(MouseButton.MouseWheelDown, button);
return;
}
});
}
void ApplyValue(KeyOrMouse input, GUIButton button)
{
button.Text = input.ToString();
onSerializedValue?.Invoke(input.ToString());
ClearListener();
}
}
#endif
}

View File

@@ -0,0 +1,17 @@
using System.Collections.Immutable;
namespace Barotrauma.LuaCs.Data;
public interface IStylesResourceInfo : IBaseResourceInfo { }
public record StylesResourceInfo : BaseResourceInfo, IStylesResourceInfo { }
public partial interface IModConfigInfo
{
public ImmutableArray<IStylesResourceInfo> Styles { get; }
}
public partial record ModConfigInfo
{
public ImmutableArray<IStylesResourceInfo> Styles { get; init; }
}

View File

@@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
#nullable enable
namespace Barotrauma.LuaCs;
/// <summary>
/// A collection of helper GUI functions. Mostly ripped from "Barotrauma/ClientSource/Settings/SettingsMenu.cs"
/// </summary>
public static class GUIUtil
{
public static (GUILayoutGroup Left, GUILayoutGroup Right) CreateSidebars(GUIFrame parent, bool split = false)
{
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform), isHorizontal: true);
GUILayoutGroup left = new GUILayoutGroup(new RectTransform((0.4875f, 1.0f), layout.RectTransform), isHorizontal: false);
var centerFrame = new GUIFrame(new RectTransform((0.025f, 1.0f), layout.RectTransform), style: null);
if (split)
{
new GUICustomComponent(new RectTransform(Vector2.One, centerFrame.RectTransform),
onDraw: (sb, c) =>
{
sb.DrawLine((c.Rect.Center.X, c.Rect.Top),
(c.Rect.Center.X, c.Rect.Bottom),
GUIStyle.TextColorDim,
2f);
});
}
GUILayoutGroup right = new GUILayoutGroup(new RectTransform((0.4875f, 1.0f), layout.RectTransform), isHorizontal: false);
return (left, right);
}
public static (GUILayoutGroup Left, GUILayoutGroup Right) CreateSidebars(GUILayoutGroup parent, bool split = false)
{
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform), isHorizontal: true);
GUILayoutGroup left = new GUILayoutGroup(new RectTransform((0.4875f, 1.0f), layout.RectTransform), isHorizontal: false);
var centerFrame = new GUIFrame(new RectTransform((0.025f, 1.0f), layout.RectTransform), style: null);
if (split)
{
new GUICustomComponent(new RectTransform(Vector2.One, centerFrame.RectTransform),
onDraw: (sb, c) =>
{
sb.DrawLine((c.Rect.Center.X, c.Rect.Top),
(c.Rect.Center.X, c.Rect.Bottom),
GUIStyle.TextColorDim,
2f);
});
}
GUILayoutGroup right = new GUILayoutGroup(new RectTransform((0.4875f, 1.0f), layout.RectTransform), isHorizontal: false);
return (left, right);
}
public static GUILayoutGroup CreateCenterLayout(GUIFrame parent)
=> new GUILayoutGroup(new RectTransform((0.5f, 1.0f), parent.RectTransform, Anchor.TopCenter, Pivot.TopCenter)) { ChildAnchor = Anchor.TopCenter };
public static RectTransform NewItemRectT(GUILayoutGroup parent, Vector2 adjustRatio)
=> new RectTransform((1.0f * adjustRatio.X, 0.06f * adjustRatio.Y), parent.RectTransform, Anchor.CenterLeft);
public static void Spacer(GUILayoutGroup parent, Vector2 adjustRatio)
=> new GUIFrame(new RectTransform((1.0f * adjustRatio.X, 0.03f * adjustRatio.Y), parent.RectTransform, Anchor.CenterLeft), style: null);
public static void ClearChildElements(GUIComponent component, bool clearSelfFromParent = false)
{
component.GetAllChildren().ForEachMod(c =>
{
c.Visible = false;
component.RemoveChild(c);
});
if (clearSelfFromParent && component.Parent is not null)
component.Parent.RemoveChild(component);
}
public static GUITextBlock Label(GUILayoutGroup parent, LocalizedString str, GUIFont font, Vector2 adjustRatio)
=> new GUITextBlock(NewItemRectT(parent, adjustRatio), str, font: font);
public static GUIDropDown DropdownEnum<T>(GUILayoutGroup parent, Func<T, LocalizedString> textFunc, Func<T, LocalizedString>? tooltipFunc, T currentValue,
Action<T> setter, Vector2 adjustRatio) where T : Enum
=> Dropdown(parent, textFunc, tooltipFunc, (T[])Enum.GetValues(typeof(T)), currentValue, setter, adjustRatio);
public static GUIDropDown Dropdown<T>(GUILayoutGroup parent, Func<T, LocalizedString> textFunc, Func<T,
LocalizedString>? tooltipFunc, IReadOnlyList<T> values, T currentValue, Action<T> setter, Vector2 adjustRatio, float listBoxScale = 1)
{
var dropdown = new GUIDropDown(NewItemRectT(parent, adjustRatio), listBoxScale: listBoxScale);
values.ForEach(v => dropdown.AddItem(text: textFunc(v), userData: v, toolTip: tooltipFunc?.Invoke(v) ?? null));
int childIndex = values.IndexOf(currentValue);
dropdown.Select(childIndex);
dropdown.ListBox.ForceLayoutRecalculation();
dropdown.ListBox.ScrollToElement(dropdown.ListBox.Content.GetChild(childIndex));
dropdown.OnSelected = (dd, obj) =>
{
setter((T)obj);
return true;
};
return dropdown;
}
public static (GUIScrollBar, GUITextBlock) Slider(GUILayoutGroup parent, Vector2 range, int steps, Func<float,
string> labelFunc, float currentValue, Action<float> setter, LocalizedString? tooltip, Vector2 adjustRatio)
{
var layout = new GUILayoutGroup(new RectTransform(adjustRatio, parent.RectTransform), isHorizontal: true);
var slider = new GUIScrollBar(new RectTransform((0.72f, 1.0f), layout.RectTransform), style: "GUISlider")
{
Range = range,
BarScrollValue = currentValue,
Step = 1.0f / (float)(steps - 1),
BarSize = 1.0f / steps
};
if (tooltip != null)
{
slider.ToolTip = tooltip;
}
var label = new GUITextBlock(new RectTransform((0.28f, 1.0f), layout.RectTransform),
labelFunc(currentValue), wrap: false, textAlignment: Alignment.Center);
slider.OnMoved = (sb, val) =>
{
label.Text = labelFunc(sb.BarScrollValue);
setter(sb.BarScrollValue);
return true;
};
return (slider, label);
}
public static GUITickBox Tickbox(GUILayoutGroup parent, LocalizedString label, LocalizedString tooltip,
bool currentValue, Action<bool> setter, Vector2 adjustRatio)
{
var tickbox = new GUITickBox(NewItemRectT(parent, adjustRatio), label)
{
Selected = currentValue,
ToolTip = tooltip,
OnSelected = (tb) =>
{
setter(tb.Selected);
return true;
}
};
return tickbox;
}
public static string Percentage(float v) => ToolBox.GetFormattedPercentage(v);
public static int Round(float v) => (int)MathF.Round(v);
}

View File

@@ -8,115 +8,7 @@ namespace Barotrauma
{
public static void Uninstall()
{
if (!File.Exists("Temp/Original/Barotrauma.dll"))
{
new GUIMessageBox("Error", "Error: Temp/Original/Barotrauma.dll not found, Github version? Use Steam validate files instead.");
return;
}
var msg = new GUIMessageBox("Confirm", "Are you sure you want to remove Client-Side LuaCs?", new LocalizedString[2] { TextManager.Get("Yes"), TextManager.Get("Cancel") });
msg.Buttons[0].OnClicked = (GUIButton button, object obj) =>
{
msg.Close();
string[] filesToRemove = new string[]
{
"Barotrauma.dll", "Barotrauma.deps.json", "Barotrauma.pdb", "BarotraumaCore.dll", "BarotraumaCore.pdb",
"System.Reflection.Metadata.dll", "System.Collections.Immutable.dll",
"System.Runtime.CompilerServices.Unsafe.dll"
};
try
{
CreateMissingDirectory();
foreach (string file in filesToRemove)
{
File.Move(file, "Temp/ToDelete/" + file, true);
File.Move("Temp/Original/" + file, file, true);
}
}
catch (Exception e)
{
new GUIMessageBox("Error", $"{e} {e.InnerException} \nTry verifying files instead.");
return false;
}
new GUIMessageBox("Restart", "Restart your game to apply the changes. If the mod continues to stay active after the restart, try verifying games instead.");
return true;
};
msg.Buttons[1].OnClicked = (GUIButton button, object obj) =>
{
msg.Close();
return true;
};
}
public static void CheckUpdate()
{
if (!File.Exists(LuaCsSetup.VersionFile)) { return; }
ContentPackage luaPackage = LuaCsSetup.GetPackage(LuaCsSetup.LuaForBarotraumaId);
if (luaPackage == null) { return; }
string luaCsPath = Path.GetDirectoryName(luaPackage.Path);
string clientVersion = File.ReadAllText(LuaCsSetup.VersionFile);
string workshopVersion = luaPackage.ModVersion;
if (clientVersion == workshopVersion || File.Exists("debugsomething")) { return; }
var msg = new GUIMessageBox($"LuaCs Update", $"Your LuaCs client version is different from the version found in the LuaCsForBarotrauma workshop files. Do you want to update?\n\n Client Version: {clientVersion}\n Workshop Version: {workshopVersion}",
new LocalizedString[2] { TextManager.Get("Yes"), TextManager.Get("Cancel") });
msg.Buttons[0].OnClicked = (GUIButton button, object obj) =>
{
string[] filesToUpdate = trackingFiles.Concat(Directory.EnumerateFiles(luaCsPath, "*.dll", SearchOption.AllDirectories)
.Where(s => s.Contains("mscordaccore_amd64_amd64")).Select(s => Path.GetFileName(s))).ToArray();
try
{
CreateMissingDirectory();
foreach (string file in filesToUpdate)
{
try
{
File.Move(file, "Temp/Old/" + file, true);
File.Copy(Path.Combine(luaCsPath, "Binary", file), file, true);
}
catch (Exception e)
{
DebugConsole.ThrowError($"Failed to update file {e}");
}
}
File.WriteAllText(LuaCsSetup.VersionFile, workshopVersion);
}
catch (Exception e)
{
new GUIMessageBox("Failed", $"Failed to update, error: {e}");
msg.Close();
return true;
}
new GUIMessageBox("Restart", $"LuaCs updated! Restart your game to apply the changes.");
msg.Close();
return true;
};
msg.Buttons[1].OnClicked = (GUIButton button, object obj) =>
{
msg.Close();
return true;
};
}
}
}

View File

@@ -1,143 +0,0 @@
using Barotrauma.Networking;
using System.Collections.Generic;
namespace Barotrauma
{
partial class LuaCsNetworking
{
private Dictionary<ushort, Queue<IReadMessage>> receiveQueue = new Dictionary<ushort, Queue<IReadMessage>>();
public void SendSyncMessage()
{
if (GameMain.Client == null) { return; }
WriteOnlyMessage message = new WriteOnlyMessage();
message.WriteByte((byte)ClientPacketHeader.LUA_NET_MESSAGE);
message.WriteByte((byte)LuaCsClientToServer.RequestAllIds);
GameMain.Client.ClientPeer.Send(message, DeliveryMethod.Reliable);
}
public void NetMessageReceived(IReadMessage netMessage, ServerPacketHeader header, Client client = null)
{
if (header != ServerPacketHeader.LUA_NET_MESSAGE)
{
GameMain.LuaCs.Hook.Call("netMessageReceived", netMessage, header, client);
return;
}
LuaCsServerToClient luaCsHeader = (LuaCsServerToClient)netMessage.ReadByte();
switch (luaCsHeader)
{
case LuaCsServerToClient.NetMessageString:
HandleNetMessageString(netMessage);
break;
case LuaCsServerToClient.NetMessageId:
HandleNetMessageId(netMessage);
break;
case LuaCsServerToClient.ReceiveIds:
ReadIds(netMessage);
break;
}
}
public IWriteMessage Start(string netMessageName)
{
var message = new WriteOnlyMessage();
message.WriteByte((byte)ClientPacketHeader.LUA_NET_MESSAGE);
if (stringToId.ContainsKey(netMessageName))
{
message.WriteByte((byte)LuaCsClientToServer.NetMessageId);
message.WriteUInt16(stringToId[netMessageName]);
}
else
{
message.WriteByte((byte)LuaCsClientToServer.NetMessageString);
message.WriteString(netMessageName);
}
return message;
}
public void Receive(string netMessageName, LuaCsAction callback)
{
RequestId(netMessageName);
netReceives[netMessageName] = callback;
}
public void RequestId(string netMessageName)
{
if (stringToId.ContainsKey(netMessageName)) { return; }
if (GameMain.Client == null) { return; }
WriteOnlyMessage message = new WriteOnlyMessage();
message.WriteByte((byte)ClientPacketHeader.LUA_NET_MESSAGE);
message.WriteByte((byte)LuaCsClientToServer.RequestSingleId);
message.WriteString(netMessageName);
Send(message, DeliveryMethod.Reliable);
}
public void Send(IWriteMessage netMessage, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable)
{
GameMain.Client.ClientPeer.Send(netMessage, deliveryMethod);
}
private void HandleNetMessageId(IReadMessage netMessage, Client client = null)
{
ushort id = netMessage.ReadUInt16();
if (idToString.ContainsKey(id))
{
string name = idToString[id];
HandleNetMessage(netMessage, name, client);
}
else
{
if (!receiveQueue.ContainsKey(id)) { receiveQueue[id] = new Queue<IReadMessage>(); }
receiveQueue[id].Enqueue(netMessage);
if (GameSettings.CurrentConfig.VerboseLogging)
{
LuaCsLogger.LogMessage($"Received NetMessage with unknown id {id} from server, storing in queue in case we receive the id later.");
}
}
}
private void ReadIds(IReadMessage netMessage)
{
ushort size = netMessage.ReadUInt16();
for (int i = 0; i < size; i++)
{
ushort id = netMessage.ReadUInt16();
string name = netMessage.ReadString();
idToString[id] = name;
stringToId[name] = id;
if (!receiveQueue.ContainsKey(id))
{
continue;
}
while (receiveQueue[id].TryDequeue(out var queueMessage))
{
if (netReceives.ContainsKey(name))
{
netReceives[name](queueMessage, null);
}
}
}
}
}
}

View File

@@ -1,115 +0,0 @@
using Microsoft.Xna.Framework;
namespace Barotrauma
{
static class LuaCsSettingsMenu
{
private static GUIFrame frame;
public static void Open(RectTransform rectTransform)
{
Close();
frame = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.6f), rectTransform, Anchor.Center));
GUIListBox list = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.95f), frame.RectTransform, Anchor.Center), false);
new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), list.Content.RectTransform), "LuaCs Settings", textAlignment: Alignment.Center);
new GUITickBox(new RectTransform(new Vector2(0.8f, 0.1f), list.Content.RectTransform), "Enable CSharp Scripting")
{
Selected = GameMain.LuaCs.Config.EnableCsScripting,
ToolTip = "This enables CSharp Scripting for mods to use, WARNING: CSharp is NOT sandboxed, be careful with what mods you download.",
OnSelected = (GUITickBox tick) =>
{
GameMain.LuaCs.Config.EnableCsScripting = tick.Selected;
GameMain.LuaCs.WriteSettings();
return true;
}
};
new GUITickBox(new RectTransform(new Vector2(0.8f, 0.1f), list.Content.RectTransform), "Treat Forced Mods As Normal")
{
Selected = GameMain.LuaCs.Config.TreatForcedModsAsNormal,
ToolTip = "This makes mods that were setup to run even when disabled to only run when enabled.",
OnSelected = (GUITickBox tick) =>
{
GameMain.LuaCs.Config.TreatForcedModsAsNormal = tick.Selected;
GameMain.LuaCs.WriteSettings();
return true;
}
};
new GUITickBox(new RectTransform(new Vector2(0.8f, 0.1f), list.Content.RectTransform), "Prefer To Use Workshop Lua Setup")
{
Selected = GameMain.LuaCs.Config.PreferToUseWorkshopLuaSetup,
ToolTip = "This makes Lua look first for the Lua/LuaSetup.lua located in the Workshop package instead of the one located locally.",
OnSelected = (GUITickBox tick) =>
{
GameMain.LuaCs.Config.PreferToUseWorkshopLuaSetup = tick.Selected;
GameMain.LuaCs.WriteSettings();
return true;
}
};
new GUITickBox(new RectTransform(new Vector2(0.8f, 0.1f), list.Content.RectTransform), "Disable Error GUI Overlay")
{
Selected = GameMain.LuaCs.Config.DisableErrorGUIOverlay,
ToolTip = "",
OnSelected = (GUITickBox tick) =>
{
GameMain.LuaCs.Config.DisableErrorGUIOverlay = tick.Selected;
GameMain.LuaCs.WriteSettings();
return true;
}
};
new GUITickBox(new RectTransform(new Vector2(0.8f, 0.1f), list.Content.RectTransform), "Hide usernames In Error Logs")
{
Selected = GameMain.LuaCs.Config.HideUserNames,
ToolTip = "Hides the operating system username when displaying error logs (eg your username on windows).",
OnSelected = (GUITickBox tick) =>
{
GameMain.LuaCs.Config.HideUserNames = tick.Selected;
GameMain.LuaCs.WriteSettings();
return true;
}
};
new GUIButton(new RectTransform(new Vector2(1f, 0.1f), list.Content.RectTransform), $"Remove Client-Side LuaCs", style: "GUIButtonSmall")
{
ToolTip = "Remove Client-Side LuaCs.",
OnClicked = (tb, userdata) =>
{
LuaCsInstaller.Uninstall();
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.8f, 0.01f), frame.RectTransform, Anchor.BottomCenter)
{
RelativeOffset = new Vector2(0f, 0.05f)
}, "Close")
{
OnClicked = (GUIButton button, object obj) =>
{
Close();
return true;
}
};
}
public static void Close()
{
frame?.Parent.RemoveChild(frame);
frame = null;
}
}
}

View File

@@ -1,75 +1,184 @@
using System.Collections.Generic;
using Barotrauma.CharacterEditor;
using Barotrauma.Extensions;
using Barotrauma.LuaCs;
using Barotrauma.LuaCs.Data;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using static System.Collections.Specialized.BitVector32;
// ReSharper disable ObjectCreationAsStatement
namespace Barotrauma
{
partial class LuaCsSetup
{
public void AddToGUIUpdateList()
{
public void PromptCSharpMods(Action<bool> onSelection, bool joiningServer)
{
if (!GameMain.LuaCs.Config.DisableErrorGUIOverlay)
ImmutableArray<ContentPackage> contentPackages = PackageManagementService.GetLoadedUnrestrictedPackages()
.Where(p => p.Name != PackageName)
.ToImmutableArray();
if (_csRunPolicy?.Value is "Enabled")
{
LuaCsLogger.AddToGUIUpdateList();
IsCsEnabledForSession = true;
onSelection(true);
return;
}
else if (_csRunPolicy?.Value is "Disabled")
{
IsCsEnabledForSession = false;
onSelection(false);
return;
}
if (contentPackages.None())
{
onSelection(true);
return;
}
GUIMessageBox messageBox = new GUIMessageBox(
TextManager.Get("warning"),
relativeSize: new Vector2(0.3f, 0.55f),
minSize: new Point(400, 500),
text: string.Empty,
buttons: []);
GUILayoutGroup msgBoxLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.75f), messageBox.Content.RectTransform), isHorizontal: false, childAnchor: Anchor.TopCenter)
{
RelativeSpacing = 0.01f,
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), msgBoxLayout.RectTransform), "The following mods contain CSharp code OR Unsandboxed Lua Code",
font: GUIStyle.SubHeadingFont, wrap: true, textAlignment: Alignment.Center);
GUIListBox packageListBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.4f), msgBoxLayout.RectTransform))
{
CurrentSelectMode = GUIListBox.SelectMode.None
};
foreach (ContentPackage package in contentPackages)
{
GUIFrame packageFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), packageListBox.Content.RectTransform), style: "ListBoxElement");
GUILayoutGroup packageLayout = new GUILayoutGroup(new RectTransform(Vector2.One, packageFrame.RectTransform), true, Anchor.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(0.7f, 1f), packageLayout.RectTransform), package.Name);
new GUIButton(new RectTransform(new Vector2(0.3f, 1f), packageLayout.RectTransform, Anchor.CenterRight), "Open Folder", style: "GUIButtonSmall")
{
OnClicked = (GUIButton button, object obj) =>
{
string directory = package.Dir;
if (string.IsNullOrEmpty(directory)) { return false; }
ToolBox.OpenFileWithShell(directory);
return true;
}
};
}
string bodyText =
joiningServer ?
"You are joining a server that includes mods with C# code OR unrestricted Lua code. These mods are not sandboxed and may access your computer without restrictions. If you trust these mods, select 'Enable C# for this session'. Otherwise, select 'Cancel' to run only Lua mods."
: "You have enabled mods that include C# code. These mods are not sandboxed and may access your computer without restrictions. If you trust these mods, select 'Enable C# for this session'. Otherwise, select 'Cancel' to run only Sandboxed Lua mods.";
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0f), msgBoxLayout.RectTransform), bodyText, wrap: true)
{
Wrap = true
};
GUILayoutGroup buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), messageBox.Content.RectTransform, Anchor.BottomCenter), isHorizontal: false, childAnchor: Anchor.TopCenter);
new GUIButton(new RectTransform(new Vector2(0.8f, 0.0f), buttonLayout.RectTransform), "Enable C# for this session")
{
TextBlock = { AutoScaleHorizontal = true },
OnClicked = (btn, userdata) =>
{
IsCsEnabledForSession = true;
onSelection(true);
messageBox.Close();
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.8f, 0.0f), buttonLayout.RectTransform), "Cancel")
{
OnClicked = (btn, userdata) =>
{
IsCsEnabledForSession = false;
onSelection(false);
messageBox.Close();
return true;
}
};
}
public void CheckInitialize()
private void SetupServicesProviderClient(IServicesProvider serviceProvider)
{
List<ContentPackage> csharpMods = new List<ContentPackage>();
foreach (ContentPackage cp in ContentPackageManager.EnabledPackages.All)
serviceProvider.RegisterServiceType<IUIStylesService, UIStylesService>(ServiceLifetime.Singleton);
// supplied via factory
//serviceProvider.RegisterServiceType<IUIStylesCollection, UIStylesCollection>(ServiceLifetime.Transient);
serviceProvider.RegisterServiceType<IParserServiceAsync<ResourceParserInfo, IStylesResourceInfo>, ModConfigFileParserService>(ServiceLifetime.Transient);
serviceProvider.RegisterServiceType<IUIStylesCollection.IFactory, UIStylesCollection.Factory>(ServiceLifetime.Transient);
serviceProvider.RegisterServiceType<ISettingsMenuSystem, SettingsMenuSystem>(ServiceLifetime.Singleton);
}
/// <summary>
/// Handles changes in game states tracked by screen changes.
/// </summary>
/// <param name="screen">The new game screen.</param>
public partial void OnScreenSelected(Screen screen)
{
/*Note: This logic needs to be run after the triggering event so that recursion scenarios (ie. resetting the EventService)
do not occur, so we delay it by one game tick.*/
CoroutineManager.Invoke(() =>
{
if (Directory.Exists(cp.Dir + "/CSharp") || Directory.Exists(cp.Dir + "/bin"))
switch (screen)
{
csharpMods.Add(cp);
// menus and navigation states
case MainMenuScreen:
case ModDownloadScreen:
case ServerListScreen:
SetRunState(RunState.Unloaded);
SetRunState(RunState.LoadedNoExec);
break;
// running lobby or editor states
case CampaignEndScreen:
case CharacterEditorScreen:
case EventEditorScreen:
case GameScreen:
case LevelEditorScreen:
case NetLobbyScreen:
case ParticleEditorScreen:
case RoundSummaryScreen:
case SpriteEditorScreen:
case SubEditorScreen:
case TestScreen: // notes: TestScreen is a Linux edge case editor screen and is deprecated.
if (screen is NetLobbyScreen && CurrentRunState != RunState.Running && GameMain.Client?.ClientPeer is not P2POwnerPeer)
{
PromptCSharpMods(selection =>
{
SetRunState(RunState.Running);
}, joiningServer: true);
}
else
{
SetRunState(RunState.Running);
}
break;
default:
Logger.LogError(
$"{nameof(LuaCsSetup)}: Received an unknown screen {screen?.GetType().Name ?? "'null screen'"}. Retarding load state to 'unloaded'.");
SetRunState(RunState.Unloaded);
break;
}
}
if (csharpMods.Count == 0 || ShouldRunCs)
{
Initialize();
return;
}
StringBuilder sb = new StringBuilder();
foreach (ContentPackage cp in csharpMods)
{
if (cp.UgcId.TryUnwrap(out ContentPackageId id))
{
sb.AppendLine($"- {cp.Name} ({id})");
}
else
{
sb.AppendLine($"- {cp.Name} (Not On Workshop)");
}
}
if (GameMain.Client == null || GameMain.Client.IsServerOwner)
{
new GUIMessageBox("", $"You have CSharp mods enabled but don't have the CSharp Scripting enabled, those mods might not work, go to the Main Menu, click on LuaCs Settings and check Enable CSharp Scripting.\n\n{sb}");
Initialize();
return;
}
GUIMessageBox msg = new GUIMessageBox(
"Confirm",
$"This server has the following CSharp mods installed: \n{sb}\nDo you wish to run them? Cs mods are not sandboxed so make sure you trust these mods.",
new LocalizedString[2] { "Run", "Don't Run" });
msg.Buttons[0].OnClicked = (GUIButton button, object obj) =>
{
Initialize(true);
msg.Close();
return true;
};
msg.Buttons[1].OnClicked = (GUIButton button, object obj) =>
{
Initialize();
msg.Close();
return true;
};
}, delay: 0f); // min is one tick delay.
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.LuaCs.Data;
namespace Barotrauma.LuaCs;
public sealed partial class ConfigService
{
public ImmutableArray<ISettingBase> GetDisplayableConfigs()
{
using var _ = _operationLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
return _settingsInstances.Values
.Where(s => !s.IsDisposed)
.Where(s => s.GetDisplayInfo().ShowInMenus)
.Where(s => !GameMain.IsMultiplayer || s.GetConfigInfo().NetSync != NetSync.ServerAuthority)
.Where(s => s.GetConfigInfo().EditableStates >= _infoProvider.CurrentRunState)
.ToImmutableArray();
}
}

View File

@@ -0,0 +1,50 @@
using Microsoft.Xna.Framework;
namespace Barotrauma.LuaCs;
public partial class LoggerService : ILoggerService, IClientLoggerService
{
private GUIFrame _overlayFrame;
private GUITextBlock _textBlock;
private double _showTimer = 0;
private void CreateOverlay(string message)
{
_overlayFrame = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.03f), null), null, new Color(50, 50, 50, 100))
{
CanBeFocused = false
};
GUILayoutGroup layout =
new GUILayoutGroup(
new RectTransform(new Vector2(0.8f, 0.8f), _overlayFrame.RectTransform, Anchor.CenterLeft), false,
Anchor.Center);
_textBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0f), layout.RectTransform), message);
_overlayFrame.RectTransform.MinSize = new Point((int)(_textBlock.TextSize.X * 1.2), 0);
layout.Recalculate();
}
public void AddToGUIUpdateList()
{
if (_overlayFrame != null && Timing.TotalTime <= _showTimer)
{
_overlayFrame.AddToGUIUpdateList();
}
}
public void ShowErrorOverlay(string message, float time = 5f, float duration = 1.5f)
{
if (Timing.TotalTime <= _showTimer)
{
return;
}
CreateOverlay(message);
_overlayFrame.Flash(Color.Red, duration, true);
_showTimer = Timing.TotalTime + time;
}
}

View File

@@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Barotrauma.LuaCs.Data;
using FluentResults;
namespace Barotrauma.LuaCs;
public sealed partial class ModConfigFileParserService :
IParserServiceAsync<ResourceParserInfo, IStylesResourceInfo>
{
async Task<Result<IStylesResourceInfo>> IParserServiceAsync<ResourceParserInfo, IStylesResourceInfo>.TryParseResourceAsync(ResourceParserInfo src)
{
using var lck = await _operationsLock.AcquireReaderLock();
IService.CheckDisposed(this);
if (CheckThrowNullRefs(src, "Style") is { IsFailed: true } fail)
return fail;
var runtimeEnv = GetRuntimeEnvironment(src.Element);
var fileResults = await UnsafeGetCheckedFiles(src.Element, src.Owner, ".xml");
if (fileResults.IsFailed)
return FluentResults.Result.Fail(fileResults.Errors);
return new StylesResourceInfo()
{
SupportedPlatforms = runtimeEnv.Platform,
SupportedTargets = Target.Client, // clientside only
LoadPriority = src.Element.GetAttributeInt("LoadPriority", 0),
FilePaths = fileResults.Value,
Optional = src.Element.GetAttributeBool("Optional", false),
InternalName = src.Element.GetAttributeString("Name", string.Empty),
OwnerPackage = src.Owner,
RequiredPackages = src.Required,
IncompatiblePackages = src.Incompatible
};
}
public async Task<ImmutableArray<Result<IStylesResourceInfo>>> TryParseResourcesAsync(IEnumerable<ResourceParserInfo> sources)
{
return await this.TryParseGenericResourcesAsync<IStylesResourceInfo>(sources);
}
}

View File

@@ -0,0 +1,163 @@
using Barotrauma.LuaCs;
using Barotrauma.LuaCs.Events;
using Barotrauma.Networking;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace Barotrauma.LuaCs;
partial class NetworkingService : INetworkingService, IEventServerConnected, IEventServerRawNetMessageReceived
{
private ConcurrentDictionary<ushort, ConcurrentQueue<IReadMessage>> receiveQueue = new();
public void OnServerConnected()
{
ActivateNetVars();
SendSyncMessage();
}
private void ActivateNetVars()
{
if (GameMain.Client == null)
{
return;
}
// re-activate net vars
// todo: unregister net vars on client disconnect, currently handled by unloading the state machine.
foreach (var networkSyncVar in netVars.Keys)
{
networkSyncVar.SetNetworkOwner(this);
}
}
public bool? OnReceivedServerNetMessage(IReadMessage netMessage, ServerPacketHeader serverPacketHeader)
{
if (serverPacketHeader != ServerHeader)
{
return null;
}
ServerToClient luaCsHeader = (ServerToClient)netMessage.ReadByte();
switch (luaCsHeader)
{
case ServerToClient.NetMessageNetId:
HandleNetMessageString(netMessage);
break;
case ServerToClient.NetMessageInternalId:
HandleNetMessageId(netMessage);
break;
case ServerToClient.ReceiveNetIds:
ReadIds(netMessage);
break;
}
return true;
}
private void SendSyncMessage()
{
if (GameMain.Client == null) { return; }
WriteOnlyMessage message = new WriteOnlyMessage();
message.WriteByte((byte)ClientHeader);
message.WriteByte((byte)ClientToServer.RequestSync);
GameMain.Client.ClientPeer.Send(message, DeliveryMethod.Reliable);
}
public IWriteMessage Start(NetId netId)
{
var message = new WriteOnlyMessage();
message.WriteByte((byte)ClientHeader);
if (idToPacket.ContainsKey(netId))
{
message.WriteByte((byte)ClientToServer.NetMessageInternalId);
message.WriteUInt16(idToPacket[netId]);
}
else
{
message.WriteByte((byte)ClientToServer.NetMessageNetId);
NetId.Write(message, netId);
}
return message;
}
public void SendToServer(IWriteMessage netMessage, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable)
{
GameMain.Client.ClientPeer.Send(netMessage, deliveryMethod);
}
public void Send(IWriteMessage netMessage, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable)
=> SendToServer(netMessage, deliveryMethod);
private void RequestId(NetId netId)
{
if (idToPacket.ContainsKey(netId)) { return; }
if (GameMain.Client == null) { return; }
WriteOnlyMessage message = new WriteOnlyMessage();
message.WriteByte((byte)ClientHeader);
message.WriteByte((byte)ClientToServer.RequestSingleNetId);
NetId.Write(message, netId);
SendToServer(message, DeliveryMethod.Reliable);
}
private void HandleNetMessageId(IReadMessage netMessage, Client client = null)
{
ushort id = netMessage.ReadUInt16();
if (packetToId.ContainsKey(id))
{
HandleNetMessage(netMessage, packetToId[id], client);
}
else
{
if (!receiveQueue.ContainsKey(id)) { receiveQueue[id] = new ConcurrentQueue<IReadMessage>(); }
receiveQueue[id].Enqueue(netMessage);
if (GameSettings.CurrentConfig.VerboseLogging)
{
_loggerService.LogMessage($"Received NetMessage with unknown id {id} from server, storing in queue in case we receive the id later.");
}
}
}
private void ReadIds(IReadMessage netMessage)
{
ushort size = netMessage.ReadUInt16();
for (int i = 0; i < size; i++)
{
ushort packetId = netMessage.ReadUInt16();
NetId netId = NetId.Read(netMessage);
packetToId[packetId] = netId;
idToPacket[netId] = packetId;
if (!receiveQueue.ContainsKey(packetId))
{
continue;
}
// We could have received messages before receiving the sync message, so we need to process them now
while (receiveQueue[packetId].TryDequeue(out var queueMessage))
{
if (netReceives.ContainsKey(netId))
{
netReceives[netId](queueMessage);
}
}
}
}
}

View File

@@ -0,0 +1,239 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using Barotrauma.Extensions;
using Barotrauma.LuaCs.Data;
using FluentResults;
using Microsoft.Toolkit.Diagnostics;
namespace Barotrauma.LuaCs;
public class UIStylesCollection : HashlessFile, IUIStylesCollection
{
public class Factory : IUIStylesCollection.IFactory
{
public IEnumerable<IUIStylesCollection> CreateInstance(IStylesResourceInfo info, IStorageService storageService)
{
Guard.IsNotNull(info, nameof(info));
Guard.IsNotNull(info.OwnerPackage, nameof(info.OwnerPackage));
if (info.FilePaths.IsDefaultOrEmpty)
{
return ImmutableArray<IUIStylesCollection>.Empty;
}
var builder = ImmutableArray.CreateBuilder<IUIStylesCollection>();
foreach (var contentPath in info.FilePaths)
{
builder.Add(new UIStylesCollection(contentPath, storageService));
}
return builder.ToImmutable();
}
public void Dispose()
{
//ignore, stateless service
}
public bool IsDisposed => false;
}
private readonly ConcurrentDictionary<string, GUIFont> _fonts = new();
private readonly ConcurrentDictionary<string, GUISprite> _sprites = new();
private readonly ConcurrentDictionary<string, GUISpriteSheet> _spriteSheets = new();
private readonly ConcurrentDictionary<string, GUICursor> _cursors = new();
private readonly ConcurrentDictionary<string, GUIColor> _colors = new();
/// <summary>
/// Only for internal reference.
/// </summary>
private UIStyleFile _fakeFile;
private IStorageService _storageService;
public UIStylesCollection(ContentPath path, IStorageService storageService) : base(path.ContentPackage, path)
{
Guard.IsNotNull(path, nameof(path));
Guard.IsNotNull(path.ContentPackage, nameof(path.ContentPackage));
_storageService = storageService;
_fakeFile = new UIStyleFile(path.ContentPackage, path);
}
public new ContentPath Path => base.Path;
public Result<GUIFont> GetFont(string name)
{
using var lck = _lock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
if (_fonts.TryGetValue(name, out var asset))
{
return asset;
}
return FluentResults.Result.Fail($"{nameof(GetFont)}: Failed to find the font with the name '{name}'");
}
public Result<GUISprite> GetSprite(string name)
{
using var lck = _lock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
if (_sprites.TryGetValue(name, out var asset))
{
return asset;
}
return FluentResults.Result.Fail($"{nameof(GetSprite)}: Failed to find the sprite with the name '{name}'");
}
public Result<GUISpriteSheet> GetSpriteSheet(string name)
{
using var lck = _lock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
if (_spriteSheets.TryGetValue(name, out var asset))
{
return asset;
}
return FluentResults.Result.Fail($"{nameof(GetSpriteSheet)}: Failed to find the spritesheet with the name '{name}'");
}
public Result<GUICursor> GetCursor(string name)
{
using var lck = _lock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
if (_cursors.TryGetValue(name, out var asset))
{
return asset;
}
return FluentResults.Result.Fail($"{nameof(GetCursor)}: Failed to find the cursor with the name '{name}'");
}
public Result<GUIColor> GetColor(string name)
{
using var lck = _lock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
if (_colors.TryGetValue(name, out var asset))
{
return asset;
}
return FluentResults.Result.Fail($"{nameof(GetColor)}: Failed to find the color with the name '{name}'");
}
public override void LoadFile()
{
using var lck = _lock.AcquireWriterLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
if (_storageService.LoadPackageXml(Path) is not { IsSuccess: true } result)
{
DebugConsole.LogError($"Failed to load xml from {Path.FullPath}.");
ThrowHelper.ThrowArgumentException($"Failed to load xml from {Path.FullPath}.");
return;
}
var root = result.Value.Root?.FromPackage(Path.ContentPackage);
if (root is null)
{
return;
}
var styleElement = root.Name.LocalName.ToLowerInvariant() == "style" ? root : root.GetChildElement("style");
if (styleElement is null)
return;
var childElements = styleElement.GetChildElements("Font");
if (childElements is not null)
AddToList<GUIFont, GUIFontPrefab>(_fonts, childElements, _fakeFile);
childElements = styleElement.GetChildElements("Sprite");
if (childElements is not null)
AddToList<GUISprite, GUISpritePrefab>(_sprites, childElements, _fakeFile);
childElements = styleElement.GetChildElements("Spritesheet");
if (childElements is not null)
AddToList<GUISpriteSheet, GUISpriteSheetPrefab>(_spriteSheets, childElements, _fakeFile);
childElements = styleElement.GetChildElements("Cursor");
if (childElements is not null)
AddToList<GUICursor, GUICursorPrefab>(_cursors, childElements, _fakeFile);
childElements = styleElement.GetChildElements("Color");
if (childElements is not null)
AddToList<GUIColor, GUIColorPrefab>(_colors, childElements, _fakeFile);
void AddToList<T1, T2>(ConcurrentDictionary<string, T1> dict, IEnumerable<ContentXElement> elem, UIStyleFile file) where T1 : GUISelector<T2> where T2 : GUIPrefab
{
foreach (ContentXElement prefabElement in elem)
{
string name = prefabElement.GetAttributeString("name", string.Empty);
if (name != string.Empty)
{
var prefab = (T2)Activator.CreateInstance(typeof(T2), new object[]{ prefabElement, file })!;
if (!dict.ContainsKey(name))
dict[name] = (T1)Activator.CreateInstance(typeof(T1), new object[] { name })!;
dict[name].Prefabs.Add(prefab, false);
}
}
}
}
public override void UnloadFile()
{
using var lck = _lock.AcquireWriterLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
_fonts.Values.ForEach(p => p.Prefabs.RemoveByFile(_fakeFile));
_sprites.Values.ForEach(p => p.Prefabs.RemoveByFile(_fakeFile));
_spriteSheets.Values.ForEach(p => p.Prefabs.RemoveByFile(_fakeFile));
_cursors.Values.ForEach(p => p.Prefabs.RemoveByFile(_fakeFile));
_colors.Values.ForEach(p => p.Prefabs.RemoveByFile(_fakeFile));
}
public override void Sort()
{
using var lck = _lock.AcquireWriterLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
_fonts.Values.ForEach(p => p.Prefabs.Sort());
_sprites.Values.ForEach(p => p.Prefabs.Sort());
_spriteSheets.Values.ForEach(p => p.Prefabs.Sort());
_cursors.Values.ForEach(p => p.Prefabs.Sort());
_colors.Values.ForEach(p => p.Prefabs.Sort());
}
#region INTERNAL_DISPOSE
private readonly AsyncReaderWriterLock _lock = new();
public void Dispose()
{
using var lck = _lock.AcquireWriterLock().ConfigureAwait(false).GetAwaiter().GetResult();
if (!ModUtils.Threading.CheckIfClearAndSetBool(ref _isDisposed))
{
return;
}
_fonts.Values.ForEach(p => p.Prefabs.RemoveByFile(_fakeFile));
_sprites.Values.ForEach(p => p.Prefabs.RemoveByFile(_fakeFile));
_spriteSheets.Values.ForEach(p => p.Prefabs.RemoveByFile(_fakeFile));
_cursors.Values.ForEach(p => p.Prefabs.RemoveByFile(_fakeFile));
_colors.Values.ForEach(p => p.Prefabs.RemoveByFile(_fakeFile));
_fonts.Clear();
_sprites.Clear();
_spriteSheets.Clear();
_cursors.Clear();
_colors.Clear();
}
private int _isDisposed;
public bool IsDisposed
{
get => ModUtils.Threading.GetBool(ref _isDisposed);
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
}
#endregion
}

View File

@@ -0,0 +1,350 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.LuaCs.Data;
using FluentResults;
using Microsoft.Toolkit.Diagnostics;
namespace Barotrauma.LuaCs;
public class UIStylesService : IUIStylesService
{
#region DISPOSAL
public void Dispose()
{
using var lck = _lock.AcquireWriterLock().ConfigureAwait(false).GetAwaiter().GetResult();
if (!ModUtils.Threading.CheckIfClearAndSetBool(ref _isDisposed))
{
return;
}
foreach (var collection in _stylesCollections.Values.SelectMany(c => c))
{
try
{
collection.Dispose();
}
catch
{
//ignored
}
}
_stylesCollections.Clear();
_storageService.Dispose();
_stylesCollectionFactory.Dispose();
_storageService = null;
_stylesCollectionFactory = null;
}
private int _isDisposed = 0;
public bool IsDisposed
{
get => ModUtils.Threading.GetBool(ref _isDisposed);
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
}
public FluentResults.Result Reset()
{
using var lck = _lock.AcquireWriterLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
var result = FluentResults.Result.Ok();
foreach (var collection in _stylesCollections.Values.SelectMany(c => c))
{
try
{
collection.Dispose();
}
catch (Exception e)
{
result.WithError(new ExceptionalError(e));
}
}
_stylesCollections.Clear();
return result;
}
private readonly AsyncReaderWriterLock _lock = new();
#endregion
private IStorageService _storageService;
private IUIStylesCollection.IFactory _stylesCollectionFactory;
private ConcurrentDictionary<(ContentPackage Package, string InternalName), ImmutableArray<IUIStylesCollection>>
_stylesCollections = new();
public UIStylesService(IUIStylesCollection.IFactory stylesCollectionFactory, IStorageService storageService)
{
_stylesCollectionFactory = stylesCollectionFactory;
_storageService = storageService;
}
public Result<GUIColor> GetColor(ContentPackage package, string internalName, string assetName)
{
using var lck = _lock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
Guard.IsNotNull(package, nameof(package));
Guard.IsNotNullOrWhiteSpace(internalName, nameof(internalName));
Guard.IsNotNullOrWhiteSpace(assetName, nameof(assetName));
if (!_stylesCollections.TryGetValue((package, internalName), out var collection)
|| collection.IsDefaultOrEmpty)
{
return FluentResults.Result.Fail(
$"{nameof(UIStylesService)}: No styles loaded for [ContentPackage].[InternalName] of: [{package.Name}].[{internalName}]");
}
var failedResult = new FluentResults.Result();
foreach (var stylesCollection in collection)
{
var res = stylesCollection.GetColor(assetName);
if (res.IsSuccess)
{
return res;
}
failedResult.WithErrors(res.Errors);
}
return failedResult;
}
public Result<GUICursor> GetCursor(ContentPackage package, string internalName, string assetName)
{
using var lck = _lock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
Guard.IsNotNull(package, nameof(package));
Guard.IsNotNullOrWhiteSpace(internalName, nameof(internalName));
Guard.IsNotNullOrWhiteSpace(assetName, nameof(assetName));
if (!_stylesCollections.TryGetValue((package, internalName), out var collection)
|| collection.IsDefaultOrEmpty)
{
return FluentResults.Result.Fail(
$"{nameof(UIStylesService)}: No styles loaded for [ContentPackage].[InternalName] of: [{package.Name}].[{internalName}]");
}
var failedResult = new FluentResults.Result();
foreach (var stylesCollection in collection)
{
var res = stylesCollection.GetCursor(assetName);
if (res.IsSuccess)
{
return res;
}
failedResult.WithErrors(res.Errors);
}
return failedResult;
}
public Result<GUIFont> GetFont(ContentPackage package, string internalName, string assetName)
{
using var lck = _lock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
Guard.IsNotNull(package, nameof(package));
Guard.IsNotNullOrWhiteSpace(internalName, nameof(internalName));
Guard.IsNotNullOrWhiteSpace(assetName, nameof(assetName));
if (!_stylesCollections.TryGetValue((package, internalName), out var collection)
|| collection.IsDefaultOrEmpty)
{
return FluentResults.Result.Fail(
$"{nameof(UIStylesService)}: No styles loaded for [ContentPackage].[InternalName] of: [{package.Name}].[{internalName}]");
}
var failedResult = new FluentResults.Result();
foreach (var stylesCollection in collection)
{
var res = stylesCollection.GetFont(assetName);
if (res.IsSuccess)
{
return res;
}
failedResult.WithErrors(res.Errors);
}
return failedResult;
}
public Result<GUISprite> GetSprite(ContentPackage package, string internalName, string assetName)
{
using var lck = _lock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
Guard.IsNotNull(package, nameof(package));
Guard.IsNotNullOrWhiteSpace(internalName, nameof(internalName));
Guard.IsNotNullOrWhiteSpace(assetName, nameof(assetName));
if (!_stylesCollections.TryGetValue((package, internalName), out var collection)
|| collection.IsDefaultOrEmpty)
{
return FluentResults.Result.Fail(
$"{nameof(UIStylesService)}: No styles loaded for [ContentPackage].[InternalName] of: [{package.Name}].[{internalName}]");
}
var failedResult = new FluentResults.Result();
foreach (var stylesCollection in collection)
{
var res = stylesCollection.GetSprite(assetName);
if (res.IsSuccess)
{
return res;
}
failedResult.WithErrors(res.Errors);
}
return failedResult;
}
public Result<GUISpriteSheet> GetSpriteSheet(ContentPackage package, string internalName, string assetName)
{
using var lck = _lock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
Guard.IsNotNull(package, nameof(package));
Guard.IsNotNullOrWhiteSpace(internalName, nameof(internalName));
Guard.IsNotNullOrWhiteSpace(assetName, nameof(assetName));
if (!_stylesCollections.TryGetValue((package, internalName), out var collection)
|| collection.IsDefaultOrEmpty)
{
return FluentResults.Result.Fail(
$"{nameof(UIStylesService)}: No styles loaded for [ContentPackage].[InternalName] of: [{package.Name}].[{internalName}]");
}
var failedResult = new FluentResults.Result();
foreach (var stylesCollection in collection)
{
var res = stylesCollection.GetSpriteSheet(assetName);
if (res.IsSuccess)
{
return res;
}
failedResult.WithErrors(res.Errors);
}
return failedResult;
}
public FluentResults.Result LoadAssets(ImmutableArray<IStylesResourceInfo> resources)
{
using var lck = _lock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
if (resources.IsDefaultOrEmpty)
{
ThrowHelper.ThrowArgumentNullException(nameof(resources));
}
var operationSuccess = FluentResults.Result.Ok();
foreach (var resource in resources)
{
var builder = ImmutableArray.CreateBuilder<IUIStylesCollection>();
if (_stylesCollections.TryGetValue((resource.OwnerPackage, resource.InternalName), out var collection))
{
builder.AddRange(collection);
}
try
{
var newCollections = _stylesCollectionFactory.CreateInstance(resource, _storageService).ToImmutableArray();
foreach (var stylesCollection in newCollections)
{
stylesCollection.LoadFile();
}
builder.AddRange(newCollections);
}
catch (Exception e)
{
operationSuccess.WithError(new ExceptionalError(e));
continue;
}
_stylesCollections[(resource.OwnerPackage, resource.InternalName)] = builder.ToImmutable();
}
return operationSuccess;
}
public FluentResults.Result UnloadPackages(ImmutableArray<ContentPackage> packages)
{
using var lck = _lock.AcquireWriterLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
var toRemove = _stylesCollections
.Select(c => c.Key)
.Where(c => packages.Contains(c.Package))
.ToImmutableArray();
var result = FluentResults.Result.Ok();
foreach (var key in toRemove)
{
if (_stylesCollections.TryRemove(key, out var collection) && !collection.IsDefaultOrEmpty)
{
foreach (var stylesCollection in collection)
{
try
{
stylesCollection.UnloadFile();
}
catch (Exception e)
{
result.WithError(new ExceptionalError(e));
}
}
}
}
return result;
}
public FluentResults.Result UnloadPackage(ContentPackage package)
{
// Yes, this is very cursed/inefficient. We don't care.
return UnloadPackages(new [] { package }.ToImmutableArray());
}
public FluentResults.Result UnloadAllPackages()
{
using var lck = _lock.AcquireWriterLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
var result = FluentResults.Result.Ok();
foreach (var key in _stylesCollections.Keys.ToImmutableArray())
{
if (_stylesCollections.TryRemove(key, out var collection) && !collection.IsDefaultOrEmpty)
{
foreach (var stylesCollection in collection)
{
try
{
stylesCollection.UnloadFile();
}
catch (Exception e)
{
result.WithError(new ExceptionalError(e));
}
}
}
}
return result;
}
}

View File

@@ -0,0 +1,7 @@
namespace Barotrauma.LuaCs;
public interface IClientLoggerService : IReusableService
{
void AddToGUIUpdateList();
void ShowErrorOverlay(string message, float time = 5f, float duration = 1.5f);
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs;
using Barotrauma.Networking;
namespace Barotrauma.LuaCs;
public partial interface IConfigService
{
ImmutableArray<ISettingBase> GetDisplayableConfigs();
}

View File

@@ -0,0 +1,6 @@
namespace Barotrauma.LuaCs;
public interface ISettingsMenuSystem : ISystem
{
}

View File

@@ -0,0 +1,75 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using Barotrauma.LuaCs.Data;
using FluentResults;
namespace Barotrauma.LuaCs;
public interface IUIStylesCollection : IService
{
public interface IFactory : IService
{
/// <summary>
/// Returns a new <see cref="IUIStylesCollection"/> for-each <see cref="ContentPath"/> in the given
/// <see cref="IStylesResourceInfo.FilePaths"/> or empty is none.
/// </summary>
/// <param name="info"></param>
/// <param name="storageService"></param>
/// <returns></returns>
IEnumerable<IUIStylesCollection> CreateInstance(IStylesResourceInfo info, IStorageService storageService);
}
/// <summary>
/// The assigned/target <see cref="ContentPath"/> for this collection.
/// </summary>
public ContentPath Path { get; }
/// <summary>
/// Gets the <see cref="GUIFont"/> with the given name.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public Result<GUIFont> GetFont(string name);
/// <summary>
/// Gets the <see cref="GUISprite"/> with the given name.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public Result<GUISprite> GetSprite(string name);
/// <summary>
/// Gets the <see cref="GUISpriteSheet"/> with the given name.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public Result<GUISpriteSheet> GetSpriteSheet(string name);
/// <summary>
/// Gets the <see cref="GUICursor"/> with the given name.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public Result<GUICursor> GetCursor(string name);
/// <summary>
/// Gets the <see cref="GUIColor"/> with the given name.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public Result<GUIColor> GetColor(string name);
#region BAROTRAUMA.UISTYLEFILE
/// <summary>
/// Definition of <see cref="HashlessFile.LoadFile"/>
/// </summary>
internal void LoadFile();
/// <summary>
/// Definition of <see cref="HashlessFile.UnloadFile"/>
/// </summary>
internal void UnloadFile();
/// <summary>
/// Definition of <see cref="HashlessFile.Sort"/>
/// </summary>
internal void Sort();
#endregion
}

View File

@@ -0,0 +1,57 @@
using System.Collections.Immutable;
using Barotrauma.LuaCs.Data;
using FluentResults;
namespace Barotrauma.LuaCs;
public interface IUIStylesService : IReusableService
{
/// <summary>
/// Gets the first loaded <see cref="GUIColor"/>.
/// </summary>
/// <param name="package">The target <see cref="ContentPackage"/></param>
/// <param name="internalName">The targets <see cref="IDataInfo.InternalName"/> as specified in the ModConfig.xml.</param>
/// <param name="assetName">The asset's name as specified in the styles XML file.</param>
/// <returns>A <see cref="FluentResults.Result"/> indicating success, and the target if succeeded.</returns>
public Result<GUIColor> GetColor(ContentPackage package, string internalName, string assetName);
/// <summary>
/// Gets the loaded <see cref="GUICursor"/>.
/// </summary>
/// <param name="package">The target <see cref="ContentPackage"/></param>
/// <param name="internalName">The targets <see cref="IDataInfo.InternalName"/> as specified in the ModConfig.xml.</param>
/// <param name="assetName">The asset's name as specified in the styles XML file.</param>
/// <returns>A <see cref="FluentResults.Result"/> indicating success, and the target if succeeded.</returns>
public Result<GUICursor> GetCursor(ContentPackage package, string internalName, string assetName);
/// <summary>
/// Gets the loaded <see cref="GUIFont"/>.
/// </summary>
/// <param name="package">The target <see cref="ContentPackage"/></param>
/// <param name="internalName">The targets <see cref="IDataInfo.InternalName"/> as specified in the ModConfig.xml.</param>
/// <param name="assetName">The asset's name as specified in the styles XML file.</param>
/// <returns>A <see cref="FluentResults.Result"/> indicating success, and the target if succeeded.</returns>
public Result<GUIFont> GetFont(ContentPackage package, string internalName, string assetName);
/// <summary>
/// Gets the loaded <see cref="GUISprite"/>.
/// </summary>
/// <param name="package">The target <see cref="ContentPackage"/></param>
/// <param name="internalName">The targets <see cref="IDataInfo.InternalName"/> as specified in the ModConfig.xml.</param>
/// <param name="assetName">The asset's name as specified in the styles XML file.</param>
/// <returns>A <see cref="FluentResults.Result"/> indicating success, and the target if succeeded.</returns>
public Result<GUISprite> GetSprite(ContentPackage package, string internalName, string assetName);
/// <summary>
/// Gets the loaded <see cref="GUISpriteSheet"/>.
/// </summary>
/// <param name="package">The target <see cref="ContentPackage"/></param>
/// <param name="internalName">The targets <see cref="IDataInfo.InternalName"/> as specified in the ModConfig.xml.</param>
/// <param name="assetName">The asset's name as specified in the styles XML file.</param>
/// <returns>A <see cref="FluentResults.Result"/> indicating success, and the target if succeeded.</returns>
public Result<GUISpriteSheet> GetSpriteSheet(ContentPackage package, string internalName, string assetName);
public FluentResults.Result LoadAssets(ImmutableArray<IStylesResourceInfo> resources);
public FluentResults.Result UnloadPackages(ImmutableArray<ContentPackage> packages);
public FluentResults.Result UnloadPackage(ContentPackage package);
public FluentResults.Result UnloadAllPackages();
}

View File

@@ -0,0 +1,22 @@
namespace Barotrauma.LuaCs;
internal sealed class ModsControlsSettingsMenu : ModsSettingsMenuBase
{
public ModsControlsSettingsMenu(GUIFrame contentFrame,
IPackageManagementService packageManagementService,
IConfigService configService,
SettingsMenu settingsMenuInstance) : base(contentFrame, packageManagementService, configService, settingsMenuInstance)
{
}
protected override void DisposeInternal()
{
// TODO: Finish this later.
}
public override void ApplyInstalledModChanges()
{
// TODO: Finish this later.
}
}

View File

@@ -0,0 +1,458 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.Xna.Framework;
using System.Linq;
using System.Numerics;
using Barotrauma.LuaCs.Data;
using Vector2 = Microsoft.Xna.Framework.Vector2;
using Vector4 = Microsoft.Xna.Framework.Vector4;
// ReSharper disable ObjectCreationAsStatement
namespace Barotrauma.LuaCs;
internal sealed class ModsGameplaySettingsMenu : ModsSettingsMenuBase
{
private ImmutableArray<ISettingBase> _settingsInstancesGameplay;
// menu vars
private GUILayoutGroup _modCategoryDisplayGroup, _settingsDisplayGroup;
private string _selectedSearchQuery = string.Empty;
private ContentPackage _selectedContentPackage;
private string _selectedCategory = string.Empty;
private ImmutableArray<ISettingBase> _currentlyDisplayedSettings;
private ILoggerService _loggerService;
private bool _promptOpen = false;
// Note: "static" instead of "const" for Hot Reload and to allow changing at runtime.
// ReSharper disable FieldCanBeMadeReadOnly.Local
// --- UI controls ---
private static float MenuTitleHeight = 0.06f; // (ContentDisplayAreaHeightContainer + MenuTitleHeight) < 1f
private static float ContentDisplayAreaHeightContainer = 0.93f;
private static float ContentDisplayAreaHeightInnerCategories = 0.99f;
private static float ContentDisplayAreaHeightInnerSettings = 0.97f;
private static float ContentLeftRightSplitPosition = 0.3f;
// Search Bar
private static float SearchBarLayoutHeight = 0.06f;
private static float SearchBarLabelWidth = 0.1f;
private static float SearchBarLabelBoxSpacing = 0.05f;
private static float SearchBarTextBoxWidth = 1f - SearchBarLabelWidth - SearchBarLabelBoxSpacing;
// Categories, Packages Display Area
private static float CategoriesDisplayListHeight = 0.945f;
private static float CategoryButtonHeightRelative = 0.122f;
private static float PackageSelectionButtonHeight = 0.07f;
private static Color CategoryButtonHoverSelectColor = new Color(50, 50, 50, 255);
private static Color CategoryButtonTextColor = Color.PeachPuff;
private static Color CategoryButtonTextColorSelected = Color.White;
private static Color CategoryButtonColorPressed = Color.TransparentBlack;
// Settings Display Area
private static float SettingLabelWidth = 0.6f;
private static float SettingControlWidth = 0.4f;
private static float SettingHeight = 0.05625f/ContentDisplayAreaHeightContainer/ContentDisplayAreaHeightInnerSettings;
private static Color SettingEntryLabelTextColor = Color.PeachPuff;
private static string SettingGUIFrameStyle = "";
private static Color? SettingGUIFrameColor = null;
// settings reset
private static Vector2 SettingsResetButtonTopSpacer = new Vector2(0f, 0.02f);
private static Vector2 SettingsResetButtonDimensions = new Vector2(0.3f, 0.05f);
private static string SettingsResetButtonStyle = "GUIButtonSmall";
private static Color SettingsResetButtonColor = Color.DarkOliveGreen;
private static Color SettingsResetButtonHoverColor = Color.Olive;
private static Color SettingsResetButtonTextColor = Color.PeachPuff;
private static Color SettingsResetButtonTextColorSelected = Color.White;
private static Vector2 ResetConfirmationPromptDimensions = new Vector2(0.15f, 0.2f);
// ReSharper restore FieldCanBeMadeReadOnly.Local
private const string SettingsResetButtonText = "LuaCsForBarotrauma.SettingsMenu.ResetVisibleSettings";
private const string SettingsResetPromptTitle = "LuaCsForBarotrauma.SettingsMenu.ResetPrompt.Title";
private const string SettingsResetPromptContents = "LuaCsForBarotrauma.SettingsMenu.ResetPrompt.Message";
private const string SettingsResetPromptYesText = "LuaCsForBarotrauma.SettingsMenu.ResetPrompt.Yes";
private const string SettingsResetPromptNoText = "LuaCsForBarotrauma.SettingsMenu.ResetPrompt.No";
private event Action OnApplyInstalledModsChanges;
public ModsGameplaySettingsMenu(GUIFrame contentFrame,
IPackageManagementService packageManagementService,
IConfigService configService,
ILoggerService loggerService,
SettingsMenu settingsMenuInstance) : base(contentFrame, packageManagementService, configService, settingsMenuInstance)
{
_settingsInstancesGameplay = configService.GetDisplayableConfigs()
.ToImmutableArray();
_loggerService = loggerService;
var mainLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), contentFrame.RectTransform, Anchor.Center), false, Anchor.TopLeft);
// page title
var menuTitleLayoutGroup = new GUILayoutGroup(
new RectTransform(new Vector2(1f, MenuTitleHeight), mainLayoutGroup.RectTransform, Anchor.TopLeft), true, Anchor.TopLeft);
GUIUtil.Label(menuTitleLayoutGroup,
GetLocalizedString("LuaCsForBarotrauma.SettingsMenu.ModGameplayButton", "Mod Gameplay Settings"),
GUIStyle.LargeFont, new Vector2(1f, 1f));
// page contents
var contentAreaLayoutGroup = new GUILayoutGroup(
new RectTransform(new Vector2(1f, 0.94f), mainLayoutGroup.RectTransform, Anchor.BottomLeft), false,
Anchor.TopLeft);
var searchBarLayoutGroup = new GUILayoutGroup(
new RectTransform(new Vector2(1f, SearchBarLayoutHeight), contentAreaLayoutGroup.RectTransform, Anchor.TopCenter), true, Anchor.CenterLeft);
GUIUtil.Label(searchBarLayoutGroup, "Search: ", GUIStyle.SubHeadingFont, new Vector2(SearchBarLabelWidth, 1f));
var searchBar = new GUITextBox(
new RectTransform(new Vector2(SearchBarTextBoxWidth, 0.1f), searchBarLayoutGroup.RectTransform, Anchor.TopLeft),
createClearButton: true)
{
OnTextChangedDelegate = (btn, txt) =>
{
GenerateDisplayFromFilter(txt);
return true;
}
};
// main display area
var settingsContentAreaGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, ContentDisplayAreaHeightContainer), contentAreaLayoutGroup.RectTransform, Anchor.BottomCenter));
GUIUtil.Spacer(settingsContentAreaGroup, Vector2.One);
(_modCategoryDisplayGroup, _settingsDisplayGroup) = GUIUtil.CreateSidebars(settingsContentAreaGroup, true);
_modCategoryDisplayGroup.RectTransform.RelativeSize = new Vector2(ContentLeftRightSplitPosition, ContentDisplayAreaHeightInnerCategories);
_settingsDisplayGroup.RectTransform.RelativeSize = new Vector2(1f-ContentLeftRightSplitPosition, ContentDisplayAreaHeightInnerSettings);
// default category
_selectedCategory = "All";
OnApplyInstalledModsChanges = () =>
{
_settingsInstancesGameplay = configService.GetDisplayableConfigs()
.ToImmutableArray();
if (_selectedContentPackage is not null && !GetTargetPackagesList().Contains(_selectedContentPackage))
{
_selectedContentPackage = null;
_selectedCategory = string.Empty;
}
GenerateCategoryListDisplay(_modCategoryDisplayGroup, GetTargetPackagesList(), GetDisplayCategoriesList());
GenerateSettingsListDisplay(_settingsDisplayGroup, GetDisplaySettingsList());
};
GenerateCategoryListDisplay(_modCategoryDisplayGroup, GetTargetPackagesList(), GetDisplayCategoriesList());
GenerateSettingsListDisplay(_settingsDisplayGroup, GetDisplaySettingsList());
void GenerateDisplayFromFilter(string text)
{
_selectedSearchQuery = text;
GenerateCategoryListDisplay(_modCategoryDisplayGroup, GetTargetPackagesList(), GetDisplayCategoriesList());
GenerateSettingsListDisplay(_settingsDisplayGroup, GetDisplaySettingsList());
}
string GetLocalizedString(string identifier, string defaultValue)
{
var lstr = TextManager.Get(identifier);
return lstr.IsNullOrWhiteSpace() ? defaultValue : lstr.Value;
}
// Filters by selected package and query text
ImmutableArray<string> GetDisplayCategoriesList()
{
return GetFilteredSettingsList()
.Select(s => GetLocalizedString(s.GetDisplayInfo().DisplayCategory, "General"))
.Concat(new []{ "All" })
.Distinct()
.OrderBy(s => s)
.ToImmutableArray();
}
// Filters by query text
ImmutableArray<ContentPackage> GetTargetPackagesList()
{
return _settingsInstancesGameplay
.Where(s => SettingMatchesQuery(s, _selectedSearchQuery))
.Select(s => s.OwnerPackage)
.Concat(new[] { ContentPackageManager.VanillaCorePackage })
.Distinct()
.OrderByDescending(p => p == ContentPackageManager.VanillaCorePackage ? 0 : 1)
.ThenBy(p => p.Name)
.ToImmutableArray();
}
// Filters by selected package, query text, and selected category.
ImmutableArray<ISettingBase> GetDisplaySettingsList()
{
return GetFilteredSettingsList()
.Where(s => _selectedCategory.IsNullOrWhiteSpace()
|| _selectedCategory == "All"
|| GetLocalizedString(s.GetDisplayInfo().DisplayCategory, "General") == _selectedCategory)
.OrderBy(s => GetLocalizedString(s.GetDisplayInfo().DisplayName, s.InternalName))
.ToImmutableArray();
}
// Filters by selected package and by query text.
ImmutableArray<ISettingBase> GetFilteredSettingsList()
{
return _settingsInstancesGameplay
.Where(s => SettingMatchesQuery(s, _selectedSearchQuery))
.Where(s => _selectedContentPackage is null
|| _selectedContentPackage == ContentPackageManager.VanillaCorePackage // vanilla is treated as all packages
|| s.OwnerPackage == _selectedContentPackage)
.OrderBy(s => GetLocalizedString(s.GetDisplayInfo().DisplayName, s.InternalName))
.ToImmutableArray();
}
bool SettingMatchesQuery(ISettingBase setting, string queryText)
{
if (queryText.IsNullOrWhiteSpace())
{
return true;
}
queryText = queryText.ToLowerInvariant().Trim();
if (setting.InternalName.ToLowerInvariant().Trim().Contains(queryText) || setting.OwnerPackage.Name.ToLowerInvariant().Trim().Contains(queryText))
{
return true;
}
var displayInfo = setting.GetDisplayInfo();
return TextManager.Get(displayInfo.DisplayName).Value.ToLowerInvariant().Trim().Contains(queryText)
|| TextManager.Get(displayInfo.DisplayCategory).Value.ToLowerInvariant().Trim().Contains(queryText)
|| TextManager.Get(displayInfo.Description).Value.ToLowerInvariant().Trim().Contains(queryText)
|| TextManager.Get(displayInfo.Tooltip).Value.ToLowerInvariant().Trim().Contains(queryText);
}
string GetPackageName(ContentPackage package)
{
return package is null || package == ContentPackageManager.VanillaCorePackage ? "All" : package.Name;
}
ContentPackage GetCurrentSelectedPackage(ImmutableArray<ContentPackage> packages)
{
if (_selectedContentPackage is null)
{
return ContentPackageManager.VanillaCorePackage;
}
if (packages.Contains(_selectedContentPackage))
{
return _selectedContentPackage;
}
if (packages.Length > 0)
{
_selectedContentPackage = packages[0];
return packages[0];
}
return null;
}
void GenerateCategoryListDisplay(GUILayoutGroup layoutGroup, ImmutableArray<ContentPackage> packagesList,
ImmutableArray<string> categories)
{
layoutGroup.ClearChildren();
var packageSelectionList = GUIUtil.Dropdown<ContentPackage>(layoutGroup, cp => GetPackageName(cp), null,
packagesList, GetCurrentSelectedPackage(packagesList), cp =>
{
_selectedContentPackage = cp;
_selectedCategory = string.Empty;
GenerateCategoryListDisplay(_modCategoryDisplayGroup, GetTargetPackagesList(), GetDisplayCategoriesList());
GenerateSettingsListDisplay(_settingsDisplayGroup, GetDisplaySettingsList());
}, new Vector2(1f, PackageSelectionButtonHeight));
var containerBox = new GUIListBox(new RectTransform(new Vector2(1f, CategoriesDisplayListHeight), layoutGroup.RectTransform));
float sizeY = MathF.Max(categories.Length * CategoryButtonHeightRelative, 1f);
var displayedCategoriesFrame = new GUIFrame(new RectTransform(new Vector2(1f, sizeY), containerBox.Content.RectTransform), style: null, color: Color.Black)
{
CanBeFocused = false
};
var displayCategoriesLayout = new GUILayoutGroup(new RectTransform(Vector2.One, displayedCategoriesFrame.RectTransform));
foreach (var category in categories)
{
var btn = new GUIButton(new RectTransform(new Vector2(1f, CategoryButtonHeightRelative), displayCategoriesLayout.RectTransform),
text: category, color: Color.TransparentBlack)
{
CanBeFocused = true,
CanBeSelected = true,
TextColor = CategoryButtonTextColor,
HoverColor = CategoryButtonHoverSelectColor,
HoverTextColor = CategoryButtonTextColorSelected,
PressedColor = CategoryButtonColorPressed,
SelectedColor = CategoryButtonHoverSelectColor,
SelectedTextColor = CategoryButtonHoverSelectColor,
OnClicked = (btn, obj) =>
{
_selectedCategory = category;
GenerateSettingsListDisplay(_settingsDisplayGroup, GetDisplaySettingsList());
return true;
}
};
}
}
void GenerateSettingsListDisplay(GUILayoutGroup layoutGroup, ImmutableArray<ISettingBase> settings)
{
layoutGroup.ClearChildren();
_currentlyDisplayedSettings = settings;
var containerBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f-SettingsResetButtonDimensions.Y), layoutGroup.RectTransform));
foreach (var setting in settings)
{
var entry = AddSettingToDisplay(
setting,
containerBox.Content.RectTransform,
settingHeight: SettingHeight,
labelSize: new Vector2(SettingLabelWidth, 1f),
controlSize: new Vector2(SettingControlWidth, 1f));
}
var spacer = new GUIFrame(new RectTransform(SettingsResetButtonTopSpacer, layoutGroup.RectTransform),
style: null, color: Color.TransparentBlack);
var resetSettingsButton = new GUIButton(
new RectTransform(SettingsResetButtonDimensions, layoutGroup.RectTransform),
GetLocalizedString(SettingsResetButtonText, "Reset Visible Settings"),
style: SettingsResetButtonStyle)
{
CanBeSelected = true,
CanBeFocused = true,
Color = SettingsResetButtonColor,
HoverColor = SettingsResetButtonHoverColor,
SelectedColor = SettingsResetButtonHoverColor,
SelectedTextColor = SettingsResetButtonTextColorSelected,
TextColor = SettingsResetButtonTextColor,
OnClicked = (btn, obj) =>
{
DisplayResetConfirmationPrompt(settings);
return true;
}
};
}
(GUIFrame entryFrame, GUILayoutGroup entryLayoutGroup)
AddSettingToDisplay(ISettingBase setting, RectTransform parent, float settingHeight, Vector2 labelSize, Vector2 controlSize)
{
GUIFrame entryFrame = new GUIFrame(new RectTransform(new Vector2(1f, settingHeight), parent),
style: SettingGUIFrameStyle, color: SettingGUIFrameColor)
{
Color = Color.DarkGray
};
GUILayoutGroup entryLayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, entryFrame.RectTransform), isHorizontal: true);
// padding
new GUIFrame(new RectTransform(new Vector2(0.02f, 1f), entryLayoutGroup.RectTransform),
color: Color.TransparentBlack);
// setting label
new GUITextBlock(new RectTransform(labelSize - new Vector2(0.05f, 0f), entryLayoutGroup.RectTransform),
GetLocalizedString(setting.GetDisplayInfo().DisplayName, setting.GetDisplayInfo().DisplayName),
textColor: SettingEntryLabelTextColor,
font: GUIStyle.SmallFont,
textAlignment: Alignment.Left)
{
ToolTip = GetLocalizedString(setting.GetDisplayInfo().Tooltip, string.Empty)
};
setting.AddDisplayComponent(entryLayoutGroup, controlSize, newValue =>
{
NewValuesCache[setting] = newValue;
});
return (entryFrame, entryLayoutGroup);
}
void DisplayResetConfirmationPrompt(ImmutableArray<ISettingBase> settings)
{
if (_promptOpen)
{
return;
}
_promptOpen = true;
var msgBox = new GUIMessageBox(GetLocalizedString(SettingsResetPromptTitle, "Reset Visible Settings"),
GetLocalizedString(SettingsResetPromptContents,
"Are you sure you want to reset the values for currently displayed settings?"),
new LocalizedString[]
{
GetLocalizedString(SettingsResetPromptYesText, "Yes"),
GetLocalizedString(SettingsResetPromptNoText, "No")
}, ResetConfirmationPromptDimensions);
msgBox.Buttons[0].OnClicked = (btn, obj) =>
{
ResetValuesForDisplayedSettings(settings);
btn.Visible = false;
_promptOpen = false;
msgBox.Close();
return true;
};
msgBox.Buttons[1].OnClicked = (btn, obj) =>
{
btn.Visible = false;
_promptOpen = false;
msgBox.Close();
return true;
};
}
void ResetValuesForDisplayedSettings(ImmutableArray<ISettingBase> settings)
{
if (settings.IsDefaultOrEmpty)
{
return;
}
NewValuesCache.Clear();
foreach (var setting in settings)
{
var str = setting.GetDefaultStringValue();
NewValuesCache[setting] = str;
loggerService.LogDebug($"Resetting value for {setting.InternalName} to '{str}'");
}
ApplyInstalledModChanges();
}
}
protected override void DisposeInternal()
{
NewValuesCache.Clear();
_modCategoryDisplayGroup?.Parent.RemoveChild(_modCategoryDisplayGroup);
_settingsDisplayGroup?.Parent.RemoveChild(_settingsDisplayGroup);
_modCategoryDisplayGroup = null;
_settingsDisplayGroup = null;
}
public override void ApplyInstalledModChanges()
{
foreach (var kvp in NewValuesCache)
{
if (kvp.Key.IsDisposed)
{
continue;
}
var success = kvp.Key.TrySetSerializedValue(kvp.Value);
if (success)
{
ConfigService.SaveConfigValue(kvp.Key);
_loggerService.LogDebug($"Applied save value for {kvp.Key.InternalName} of {kvp.Value.ToString()}");
}
}
NewValuesCache.Clear();
OnApplyInstalledModsChanges?.Invoke();
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Concurrent;
using System.Xml.Linq;
using Barotrauma.Extensions;
using Barotrauma.LuaCs.Data;
using Microsoft.Xna.Framework;
using OneOf;
namespace Barotrauma.LuaCs;
internal abstract class ModsSettingsMenuBase : IDisposable
{
public GUIFrame ContentFrame { get; private set; }
protected IPackageManagementService PackageManagementService { get; private set; }
protected IConfigService ConfigService { get; private set; }
protected SettingsMenu SettingsMenuInstance { get; private set; }
protected readonly ConcurrentDictionary<ISettingBase, OneOf<string, XElement>> NewValuesCache = new();
protected ModsSettingsMenuBase(GUIFrame contentFrame,
IPackageManagementService packageManagementService,
IConfigService configService, SettingsMenu settingsMenuInstance)
{
ContentFrame = contentFrame;
PackageManagementService = packageManagementService;
ConfigService = configService;
SettingsMenuInstance = settingsMenuInstance;
}
protected abstract void DisposeInternal();
public abstract void ApplyInstalledModChanges();
public void Dispose()
{
DisposeInternal();
ContentFrame?.Parent.RemoveChild(ContentFrame);
SettingsMenuInstance = null;
ContentFrame = null;
PackageManagementService = null;
ConfigService = null;
NewValuesCache.Clear();
}
}

View File

@@ -0,0 +1,125 @@
using System;
using System.Linq;
using Barotrauma.Extensions;
using HarmonyLib;
using Microsoft.Xna.Framework;
namespace Barotrauma.LuaCs;
public class SettingsMenuSystem : ISettingsMenuSystem
{
private ModsControlsSettingsMenu _controlsMenuInstance;
private ModsGameplaySettingsMenu _gameplayMenuInstance;
private GUIFrame _gameplayContentFrame;
private GUIFrame _controlsContentFrame;
private SettingsMenu _settingsMenuInstance;
private readonly Harmony _harmony;
private readonly IPackageManagementService _packageManagementService;
private readonly IConfigService _configService;
private readonly ILoggerService _loggerService;
private static SettingsMenuSystem SystemInstance;
public SettingsMenuSystem(IPackageManagementService packageManagementService, IConfigService configService, ILoggerService loggerService)
{
_packageManagementService = packageManagementService;
_configService = configService;
_loggerService = loggerService;
SystemInstance = this;
_harmony = Harmony.CreateAndPatchAll(typeof(SettingsMenuSystem));
}
[HarmonyPatch(typeof(SettingsMenu), "CreateModsTab"), HarmonyPostfix]
private static void SettingsMenu_CreateModsTab_Post(SettingsMenu __instance)
{
SystemInstance._settingsMenuInstance = __instance;
SystemInstance.CreateSettingsMenu(__instance);
}
private void CreateSettingsMenu(SettingsMenu __instance)
{
DisposeMenuFrames();
var tabCount = Enum.GetValues<SettingsMenu.Tab>().Length;
var tabGameplayIndex = (SettingsMenu.Tab)tabCount;
var tabControlsIndex = (SettingsMenu.Tab)tabCount+1;
_gameplayContentFrame = CreateNewContentTab(tabGameplayIndex, __instance,
GUIStyle.ComponentStyles.ContainsKey("SettingsMenuTab.LuaCsSettings") ? "SettingsMenuTab.LuaCsSettings" : "SettingsMenuTab.Mods",
"LuaCsForBarotrauma.SettingsMenu.ModGameplayButton");
/*_controlsContentFrame = CreateNewContentTab(tabControlsIndex, __instance,
"SettingsMenuTab.Controls", "LuaCsForBarotrauma.SettingsMenu.ModControlsButton");
*/
_gameplayMenuInstance = new ModsGameplaySettingsMenu(_gameplayContentFrame, _packageManagementService, _configService, _loggerService, __instance);
//_controlsMenuInstance = new ModsControlsSettingsMenu(_controlsContentFrame, _packageManagementService, _configService, __instance);
}
private GUIFrame CreateNewContentTab(SettingsMenu.Tab tab, SettingsMenu settingsMenuInstance, string settingsMenuTabName, string settingMenuHoverTextIdent)
{
if (settingsMenuInstance.tabContents.TryGetValue(tab, out (GUIButton Button, GUIFrame Content) tabContent))
{
return tabContent.Content;
}
var contentFr = new GUIFrame(new RectTransform(Vector2.One * 0.95f, settingsMenuInstance.contentFrame.RectTransform, Anchor.Center, Pivot.Center), style: null);
var button = new GUIButton(new RectTransform(Vector2.One, settingsMenuInstance.tabber.RectTransform,
Anchor.TopLeft, Pivot.TopLeft, scaleBasis: ScaleBasis.Smallest), "", style: settingsMenuTabName)
{
ToolTip = TextManager.Get(settingMenuHoverTextIdent),
OnClicked = (b, _) =>
{
settingsMenuInstance.SelectTab(tab);
return false;
}
};
button.RectTransform.MaxSize = RectTransform.MaxPoint;
button.Children.ForEach(c => c.RectTransform.MaxSize = RectTransform.MaxPoint);
settingsMenuInstance.tabContents.Add(tab, (button, contentFr));
return contentFr;
}
[HarmonyPatch(typeof(SettingsMenu), nameof(SettingsMenu.ApplyInstalledModChanges)), HarmonyPostfix]
private static void SettingsMenu_ApplyInstalledModChanges_Post()
{
SystemInstance._gameplayMenuInstance?.ApplyInstalledModChanges();
SystemInstance._controlsMenuInstance?.ApplyInstalledModChanges();
}
private void DisposeMenuFrames()
{
_controlsMenuInstance?.Dispose();
_gameplayMenuInstance?.Dispose();
_controlsMenuInstance = null;
_gameplayMenuInstance = null;
}
#region DISPOSAL
public void Dispose()
{
if (!ModUtils.Threading.CheckIfClearAndSetBool(ref _isDisposed))
{
return;
}
DisposeMenuFrames();
GC.SuppressFinalize(this);
}
private int _isDisposed = 0;
public bool IsDisposed
{
get => ModUtils.Threading.GetBool(ref _isDisposed);
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
}
public FluentResults.Result Reset()
{
throw new NotImplementedException();
}
#endregion
}

View File

@@ -16,15 +16,17 @@ namespace Barotrauma
{
public readonly UInt32 DecalId;
public readonly int SpriteIndex;
public Vector2 NormalizedPos;
public readonly Vector2 NormalizedPos;
public readonly float Scale;
public readonly float DecalAlpha;
public RemoteDecal(UInt32 decalId, int spriteIndex, Vector2 normalizedPos, float scale)
public RemoteDecal(UInt32 decalId, int spriteIndex, Vector2 normalizedPos, float scale, float decalAlpha)
{
DecalId = decalId;
SpriteIndex = spriteIndex;
NormalizedPos = normalizedPos;
Scale = scale;
DecalAlpha = decalAlpha;
}
}
@@ -696,7 +698,7 @@ namespace Barotrauma
var decal = decalEventData.Decal;
int decalIndex = decals.IndexOf(decal);
msg.WriteByte((byte)(decalIndex < 0 ? 255 : decalIndex));
msg.WriteRangedSingle(decal.BaseAlpha, 0.0f, 1.0f, 8);
msg.WriteRangedSingle(decal.BaseAlpha, 0f, 1f, 8);
break;
default:
throw new Exception($"Malformed hull event: did not expect {eventData.GetType().Name}");
@@ -752,7 +754,9 @@ namespace Barotrauma
float normalizedXPos = msg.ReadRangedSingle(0.0f, 1.0f, 8);
float normalizedYPos = msg.ReadRangedSingle(0.0f, 1.0f, 8);
float decalScale = msg.ReadRangedSingle(0.0f, 2.0f, 12);
remoteDecals.Add(new RemoteDecal(decalId, spriteIndex, new Vector2(normalizedXPos, normalizedYPos), decalScale));
float decalAlpha = msg.ReadRangedSingle(0f, 1f, 8);
remoteDecals.Add(new RemoteDecal(decalId, spriteIndex, new Vector2(normalizedXPos, normalizedYPos), decalScale, decalAlpha));
}
break;
case EventType.BallastFlora:
@@ -804,7 +808,8 @@ namespace Barotrauma
decalPosX += Submarine.Position.X;
decalPosY += Submarine.Position.Y;
}
AddDecal(remoteDecal.DecalId, new Vector2(decalPosX, decalPosY), remoteDecal.Scale, isNetworkEvent: true, spriteIndex: remoteDecal.SpriteIndex);
Decal decal = AddDecal(remoteDecal.DecalId, new Vector2(decalPosX, decalPosY), remoteDecal.Scale, isNetworkEvent: true, spriteIndex: remoteDecal.SpriteIndex);
decal.BaseAlpha = remoteDecal.DecalAlpha;
}
remoteDecals.Clear();
}

View File

@@ -296,13 +296,9 @@ namespace Barotrauma.Lights
light.Priority = lightPriority(range, light);
int i = 0;
while (i < activeLights.Count && light.Priority < activeLights[i].Priority)
{
i++;
}
activeLights.Insert(i, light);
activeLights.Add(light);
}
activeLights.Sort(static (a, b) => b.Priority.CompareTo(a.Priority));
ActiveLightCount = activeLights.Count;
float lightPriority(float range, LightSource light)
@@ -332,7 +328,7 @@ namespace Barotrauma.Lights
activeLights.Remove(activeShadowCastingLights[i]);
}
}
activeLights.Sort((l1, l2) => l1.LastRecalculationTime.CompareTo(l2.LastRecalculationTime));
activeLights.Sort(static (l1, l2) => l1.LastRecalculationTime.CompareTo(l2.LastRecalculationTime));
//draw light sprites attached to characters
//render into a separate rendertarget using alpha blending (instead of on top of everything else with alpha blending)

View File

@@ -50,8 +50,14 @@ namespace Barotrauma.Lights
[Serialize("0, 0", IsPropertySaveable.Yes), Editable(ValueStep = 1, DecimalCount = 1, MinValueFloat = -1000f, MaxValueFloat = 1000f)]
public Vector2 Offset { get; set; }
public float RotationRad { get; private set; }
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = -360, MaxValueFloat = 360, ValueStep = 1, DecimalCount = 0)]
public float Rotation { get; set; }
public float Rotation
{
get => MathHelper.ToDegrees(RotationRad);
set => RotationRad = MathHelper.ToRadians(value);
}
[Serialize(false, IsPropertySaveable.Yes, "Directional lights only shine in \"one direction\", meaning no shadows are cast behind them."+
" Note that this does not affect how the light texture is drawn: if you want something like a conical spotlight, you should use an appropriate texture for that.")]
@@ -314,6 +320,10 @@ namespace Barotrauma.Lights
private float prevCalculatedRotation;
private float rotation;
/// <summary>
/// Current rotation in radians. Note that LightSourceParams.RotationRad also affects the final rotation of the light.
/// </summary>
public float Rotation
{
get { return rotation; }
@@ -322,7 +332,7 @@ namespace Barotrauma.Lights
if (Math.Abs(value - rotation) < 0.001f) { return; }
rotation = value;
dir = new Vector2(MathF.Cos(rotation), -MathF.Sin(rotation));
RefreshDirection();
if (Math.Abs(rotation - prevCalculatedRotation) < RotationRecalculationThreshold && vertices != null)
{
@@ -486,6 +496,9 @@ namespace Barotrauma.Lights
break;
}
}
//make sure the rotation defined in the parameters is taken into account
RefreshDirection();
NeedsRecalculation = true;
}
public LightSource(LightSourceParams lightSourceParams)
@@ -497,6 +510,9 @@ namespace Barotrauma.Lights
{
DeformableLightSprite = new DeformableSprite(lightSourceParams.DeformableLightSpriteElement, invert: true);
}
//make sure the rotation defined in the parameters is taken into account
RefreshDirection();
NeedsRecalculation = true;
}
public LightSource(Vector2 position, float range, Color color, Submarine submarine, bool addLight=true)
@@ -511,6 +527,14 @@ namespace Barotrauma.Lights
if (addLight) { GameMain.LightManager.AddLight(this); }
}
/// <summary>
/// Refresh the direction vector of the light (which is used for calculating shadows) based on the rotation and <see cref="LightSourceParams.RotationRad"/>
/// </summary>
private void RefreshDirection()
{
dir = new Vector2(MathF.Cos(rotation - LightSourceParams.RotationRad), -MathF.Sin(rotation - LightSourceParams.RotationRad));
}
public void Update(float time)
{
float brightness = 1.0f;
@@ -773,9 +797,6 @@ namespace Barotrauma.Lights
float boundsExtended = TextureRange;
if (OverrideLightTexture != null)
{
float cosAngle = (float)Math.Cos(rotation);
float sinAngle = -(float)Math.Sin(rotation);
var overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
Vector2 origin = OverrideLightTextureOrigin;
@@ -790,8 +811,11 @@ namespace Barotrauma.Lights
origin *= TextureRange;
drawOffset.X = -origin.X * cosAngle - origin.Y * sinAngle;
drawOffset.Y = origin.X * sinAngle + origin.Y * cosAngle;
//rotate the origin based on the direction
float cos = dir.X;
float sin = dir.Y;
drawOffset.X = -origin.X * cos - origin.Y * sin;
drawOffset.Y = origin.X * sin + origin.Y * cos;
}
//add a square-shaped boundary to make sure we've got something to construct the triangles from
@@ -1536,7 +1560,6 @@ namespace Barotrauma.Lights
Vector2 offset = ParentSub == null ? Vector2.Zero : ParentSub.DrawPosition;
lightEffect.World =
Matrix.CreateTranslation(-new Vector3(position, 0.0f)) *
Matrix.CreateRotationZ(MathHelper.ToRadians(LightSourceParams.Rotation)) *
Matrix.CreateTranslation(new Vector3(position + offset + translateVertices, 0.0f)) *
transform;

View File

@@ -193,7 +193,12 @@ namespace Barotrauma
{
CanTakeKeyBoardFocus = false
};
var editor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUIStyle.LargeFont) { UserData = this };
var editor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true,
titleFont: GUIStyle.LargeFont,
dimOutDefaultValues: false)
{
UserData = this
};
if (editor.Fields.TryGetValue(nameof(Scale).ToIdentifier(), out GUIComponent[] scaleFields) &&
scaleFields.FirstOrDefault() is GUINumberInput scaleInput)

View File

@@ -1,6 +1,7 @@
using Barotrauma.Extensions;
using Barotrauma.IO;
using Barotrauma.Items.Components;
using Barotrauma.LuaCs.Events;
using Barotrauma.Steam;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
@@ -603,8 +604,6 @@ namespace Barotrauma.Networking
{
ServerPacketHeader header = (ServerPacketHeader)inc.ReadByte();
GameMain.LuaCs.Networking.NetMessageReceived(inc, header);
if (roundInitStatus == RoundInitStatus.WaitingForStartGameFinalize
&& header is not (
ServerPacketHeader.STARTGAMEFINALIZE
@@ -2905,8 +2904,6 @@ namespace Barotrauma.Networking
public void Quit()
{
GameMain.LuaCs.Stop();
ClientPeer?.Close(PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
GUIMessageBox.MessageBoxes.RemoveAll(c => c?.UserData is RoundSummary);
@@ -3006,7 +3003,8 @@ namespace Barotrauma.Networking
public override void AddChatMessage(ChatMessage message)
{
var should = GameMain.LuaCs.Hook.Call<bool?>("chatMessage", message.Text, message.SenderClient, message.Type, message);
bool? should = null;
LuaCsSetup.Instance.EventService.PublishEvent<IEventChatMessage>(x => should = x.OnChatMessage(message.Text, message.SenderClient, message.Type, message) ?? should);
if (should != null && should.Value) { return; }
if (string.IsNullOrEmpty(message.Text)) { return; }

View File

@@ -30,6 +30,13 @@ namespace Barotrauma.Networking
{
DualStack = GameSettings.CurrentConfig.UseDualModeSockets
};
if (NetConfig.UseLenientHandshake)
{
// More lenient timeouts for local testing, so the server would start even without perfect conditions
netPeerConfiguration.ConnectionTimeout = 60.0f;
netPeerConfiguration.ResendHandshakeInterval = 5.0f;
netPeerConfiguration.MaximumHandshakeAttempts = 20;
}
if (endpoint.NetEndpoint.Address.AddressFamily == AddressFamily.InterNetworkV6)
{
netPeerConfiguration.LocalAddress = System.Net.IPAddress.IPv6Any;

View File

@@ -495,8 +495,6 @@ namespace Barotrauma
allowInput = true;
}
GameMain.LuaCs.Hook.Call("keyUpdate", deltaTime);
oldMouseState = mouseState;
mouseState = latestMouseState;
UpdateVariable();

View File

@@ -352,7 +352,7 @@ namespace Barotrauma
{
Level.Loaded.DrawBack(graphics, spriteBatch, cam);
}
else if (GameMain.GameSession.GameMode is TestGameMode testMode)
else if (GameMain.GameSession?.GameMode is TestGameMode testMode)
{
graphics.Clear(testMode.BackgroundParams.BackgroundColor);

View File

@@ -49,6 +49,9 @@ namespace Barotrauma
private GUITextBox serverNameBox, passwordBox, maxPlayersBox;
private GUITickBox isPublicBox, wrongPasswordBanBox, karmaBox;
private GUIDropDown languageDropdown, serverExecutableDropdown;
#if DEBUG
private GUITickBox lenientHandshakeBox;
#endif
private readonly GUIButton joinServerButton, hostServerButton;
private readonly GUIFrame modsButtonContainer;
@@ -531,23 +534,6 @@ namespace Barotrauma
}
};
#endif
new GUIButton(new RectTransform(new Point(300, 30), Frame.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(40, 50) },
$"Open LuaCs Settings", style: "MainMenuGUIButton", color: GUIStyle.Red)
{
IgnoreLayoutGroups = true,
OnClicked = (tb, userdata) =>
{
LuaCsSettingsMenu.Open(Frame.RectTransform);
return true;
}
};
string version = File.Exists(LuaCsSetup.VersionFile) ? File.ReadAllText(LuaCsSetup.VersionFile) : "Github";
new GUITextBlock(new RectTransform(new Point(300, 30), Frame.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(10, 10) }, $"Using LuaCsForBarotrauma revision {AssemblyInfo.GitRevision} version {version}", Color.Red)
{
IgnoreLayoutGroups = false
};
var minButtonSize = new Point(120, 20);
var maxButtonSize = new Point(480, 80);
@@ -703,8 +689,6 @@ namespace Barotrauma
#region Selection
public override void Select()
{
GameMain.LuaCs.Stop();
ResetModUpdateButton();
if (WorkshopItemsToUpdate.Any())
@@ -1044,7 +1028,7 @@ namespace Barotrauma
else
{
StartServer();
}
}
}
private IEnumerable<CoroutineStatus> WaitForSubmarineHashCalculations(GUIMessageBox messageBox)
@@ -1095,7 +1079,7 @@ namespace Barotrauma
"-public", isPublicBox.Selected.ToString(),
"-playstyle", ((PlayStyle)playstyleBanner.UserData).ToString(),
"-banafterwrongpassword", wrongPasswordBanBox.Selected.ToString(),
"-karmaenabled", (!karmaBox.Selected).ToString(),
"-karmaenabled", (karmaBox.Selected).ToString(),
"-maxplayers", maxPlayersBox.Text,
"-language", languageDropdown.SelectedData.ToString()
};
@@ -1134,6 +1118,13 @@ namespace Barotrauma
int ownerKey = Math.Max(CryptoRandom.Instance.Next(), 1);
arguments.Add("-ownerkey");
arguments.Add(ownerKey.ToString());
#if DEBUG
if (lenientHandshakeBox.Selected)
{
arguments.Add("-lenienthandshake");
NetConfig.UseLenientHandshake = true;
}
#endif
var processInfo = new ProcessStartInfo
{
@@ -1314,8 +1305,6 @@ namespace Barotrauma
return;
}
GameMain.LuaCs.CheckInitialize();
selectedSub = new SubmarineInfo(Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"));
GameMain.GameSession = new GameSession(selectedSub, Option.None, CampaignDataPath.CreateRegular(savePath), GameModePreset.SinglePlayerCampaign, settings, mapSeed);
@@ -1331,8 +1320,6 @@ namespace Barotrauma
{
if (string.IsNullOrWhiteSpace(path)) return;
GameMain.LuaCs.CheckInitialize();
try
{
CampaignDataPath dataPath =
@@ -1392,7 +1379,7 @@ namespace Barotrauma
}
int maxPlayers = Math.Clamp(maxPlayersElement, min: 1, max: NetConfig.MaxPlayers);
var karmaEnabled = serverSettings.GetAttributeBool("karmaenabled", true);
var karmaEnabled = serverSettings.GetAttributeBool("karmaenabled", false);
var selectedPlayStyle = serverSettings.GetAttributeEnum("playstyle", PlayStyle.Casual);
Vector2 textLabelSize = new Vector2(1.0f, 0.05f);
@@ -1603,10 +1590,18 @@ namespace Barotrauma
karmaBox = new GUITickBox(new RectTransform(new Vector2(0.5f, 1.0f), tickboxAreaLower.RectTransform), TextManager.Get("HostServerKarmaSetting"))
{
Selected = !karmaEnabled,
Selected = karmaEnabled,
ToolTip = TextManager.Get("hostserverkarmasettingtooltip")
};
#if DEBUG
lenientHandshakeBox = new GUITickBox(new RectTransform(new Vector2(0.5f, 1.0f), tickboxAreaLower.RectTransform), "DEBUG: Lenient server startup timeouts")
{
Selected = true,
ToolTip = "Start with more lenient Lidgren handshake timeouts. The server is more likely to start even when running multiple instances on the same machine under heavy load."
};
#endif
tickboxAreaLower.RectTransform.IsFixedSize = true;
//spacing
@@ -1695,8 +1690,8 @@ namespace Barotrauma
if (string.IsNullOrEmpty(remoteContentUrl)) { return; }
try
{
var client = new RestClient(remoteContentUrl);
var request = new RestRequest("MenuContent.xml", Method.GET);
var client = RestFactory.CreateClient(remoteContentUrl);
var request = RestFactory.CreateRequest("MenuContent.xml");
TaskPool.Add("RequestMainMenuRemoteContent", client.ExecuteAsync(request),
RemoteContentReceived);
}
@@ -1717,12 +1712,17 @@ namespace Barotrauma
try
{
if (!t.TryGetResult(out IRestResponse remoteContentResponse)) { throw new Exception("Task did not return a valid result"); }
if (remoteContentResponse.ErrorException != null)
{
DebugConsole.AddWarning($"Connection error: Failed to fetch remote main menu content " +
$"({remoteContentResponse.ErrorException.Message}).");
return;
}
if (remoteContentResponse.StatusCode != HttpStatusCode.OK)
{
DebugConsole.AddWarning(
"Failed to receive remote main menu content. " +
"There may be an issue with your internet connection, or the master server might be temporarily unavailable " +
$"(error code: {remoteContentResponse.StatusCode})");
$"The master server might be temporarily unavailable (HTTP error: {remoteContentResponse.StatusCode})");
return;
}
string xml = remoteContentResponse.Content;

View File

@@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.IO;
using Barotrauma.LuaCs.Events;
using Barotrauma.Networking;
using Barotrauma.Steam;
using Microsoft.Xna.Framework;
@@ -118,7 +119,6 @@ namespace Barotrauma
ContentPackageManager.EnabledPackages.SetRegular(regularPackages);
}
GameMain.NetLobbyScreen.Select();
GameMain.LuaCs.CheckInitialize();
return;
}
@@ -366,7 +366,7 @@ namespace Barotrauma
ContentPackageManager.EnabledPackages.BackUp();
ContentPackageManager.EnabledPackages.SetCore(corePackage);
ContentPackageManager.EnabledPackages.SetRegular(regularPackages);
//see if any of the packages we enabled contain subs that we were missing previously, and update their paths
foreach (var serverSub in GameMain.Client.ServerSubmarines)
{
@@ -379,7 +379,6 @@ namespace Barotrauma
}
GameMain.NetLobbyScreen.UpdateSubList(GameMain.NetLobbyScreen.SubList, GameMain.Client.ServerSubmarines);
GameMain.NetLobbyScreen.Select();
GameMain.LuaCs.CheckInitialize();
}
}
else if (GameMain.Client.FileReceiver.ActiveTransfers.None())
@@ -400,7 +399,7 @@ namespace Barotrauma
string dir = path.RemoveFromEnd(ModReceiver.Extension, StringComparison.OrdinalIgnoreCase);
SaveUtil.DecompressToDirectory(path, dir);
var result = ContentPackage.TryLoad(Path.Combine(dir, ContentPackage.FileListFileName));
var result = ContentPackage.TryLoad(Path.Combine(dir, ContentPackage.FileListFileName).CleanUpPathCrossPlatform());
if (!result.TryUnwrapSuccess(out var newPackage))
{

View File

@@ -12,6 +12,7 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using Barotrauma.LuaCs.Events;
using Barotrauma.Sounds;
namespace Barotrauma
@@ -733,6 +734,8 @@ namespace Barotrauma
AutoHideScrollBar = false,
OnSelected = (component, userdata) =>
{
//if we're clicking on a checkbox (toggle visibility) on the list, don't select the entry on the list
if (GUI.MouseOn is GUITickBox) { return false; }
//toggling selection is not how listboxes normally work, need to do that manually here
SoundPlayer.PlayUISound(GUISoundType.Select);
if (layerList.SelectedData == userdata)
@@ -1531,8 +1534,6 @@ namespace Barotrauma
public override void Select()
{
Select(enableAutoSave: true);
GameMain.LuaCs.CheckInitialize();
}
public void Select(bool enableAutoSave = true)
@@ -3255,6 +3256,20 @@ namespace Barotrauma
= new GUITextBox(new RectTransform((1.0f, 0.15f), saveInPackageLayout.RectTransform),
createClearButton: true);
packToSaveInFilter.OnTextChanged += (GUITextBox textBox, string text) =>
{
foreach (GUIComponent child in packageToSaveInList.Content.Children)
{
child.Visible =
// Get the pkgText from below
!(child.GetChild<GUILayoutGroup>()?.GetChild<GUITextBlock>() is GUITextBlock textBlock &&
!textBlock.Text.Contains(packToSaveInFilter.Text, StringComparison.OrdinalIgnoreCase));
}
return true;
};
GUILayoutGroup addItemToPackageToSaveList(LocalizedString itemText, ContentPackage p)
{
var listItem = new GUIFrame(new RectTransform((1.0f, 0.15f), packageToSaveInList.Content.RectTransform),
@@ -3275,28 +3290,26 @@ namespace Barotrauma
return retVal;
}
ContentPackage ownerPkg = null;
#if DEBUG
//this is a debug-only option so I won't bother submitting it for localization
var modifyVanillaListItem = addItemToPackageToSaveList("Modify Vanilla content package", ContentPackageManager.VanillaCorePackage);
var modifyVanillaListIcon = modifyVanillaListItem.GetChild<GUIFrame>();
GUIStyle.Apply(modifyVanillaListIcon, "WorkshopMenu.EditButton");
if (MainSub?.Info != null && IsVanillaSub(MainSub.Info))
{
ownerPkg = ContentPackageManager.VanillaCorePackage;
}
#endif
var newPackageListItem = addItemToPackageToSaveList(TextManager.Get("CreateNewLocalPackage"), null);
var newPackageListIcon = newPackageListItem.GetChild<GUIFrame>();
var newPackageListText = newPackageListItem.GetChild<GUITextBlock>();
GUIStyle.Apply(newPackageListIcon, "NewContentPackageIcon");
new GUICustomComponent(new RectTransform(Vector2.Zero, saveInPackageLayout.RectTransform),
onUpdate: (f, component) =>
{
foreach (GUIComponent contentChild in packageToSaveInList.Content.Children)
{
contentChild.Visible &= !(contentChild.GetChild<GUILayoutGroup>()?.GetChild<GUITextBlock>() is GUITextBlock tb &&
!tb.Text.Contains(packToSaveInFilter.Text, StringComparison.OrdinalIgnoreCase));
}
});
ContentPackage ownerPkg = null;
if (MainSub?.Info != null) { ownerPkg = GetLocalPackageThatOwnsSub(MainSub.Info); }
if (ownerPkg == null && MainSub?.Info != null) { ownerPkg = GetLocalPackageThatOwnsSub(MainSub.Info); }
foreach (var p in ContentPackageManager.LocalPackages)
{
var packageListItem = addItemToPackageToSaveList(p.Name, p);
@@ -3851,6 +3864,10 @@ namespace Barotrauma
return true;
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), deleteButtonHolder.RectTransform), TextManager.Get("DragAndDropSubmarineTip").Fallback(LocalizedString.EmptyString), textAlignment: Alignment.Center, font: GUIStyle.Font)
{
Wrap = true
};
if (AutoSaveInfo?.Root != null)
{
@@ -4488,6 +4505,7 @@ namespace Barotrauma
public void ReconstructLayers()
{
Dictionary<string, LayerData> previousLayers = Layers.ToDictionary();
ClearLayers();
foreach (MapEntity entity in MapEntity.MapEntityList)
{
@@ -4496,6 +4514,13 @@ namespace Barotrauma
Layers.TryAdd(entity.Layer, new LayerData(!entity.IsLayerHidden));
}
}
foreach ((string layerName, LayerData data) in previousLayers)
{
if (Layers.ContainsKey(layerName))
{
Layers[layerName] = data;
}
}
UpdateLayerPanel();
}

View File

@@ -26,6 +26,8 @@ namespace Barotrauma
public static DateTime NextCommandPush;
public static Tuple<SerializableProperty, PropertyCommand> CommandBuffer;
private bool dimOutDefaultValues;
private bool isReadonly;
public bool Readonly
{
@@ -316,16 +318,17 @@ namespace Barotrauma
}
}
public SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, bool inGame, bool showName, string style = "", int elementHeight = 24, GUIFont titleFont = null)
public SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, bool inGame, bool showName, string style = "", int elementHeight = 24, GUIFont titleFont = null, bool dimOutDefaultValues = true)
: this(parent, entity, inGame ?
SerializableProperty.GetProperties<InGameEditable>(entity).Union(SerializableProperty.GetProperties<ConditionallyEditable>(entity).Where(p => p.GetAttribute<ConditionallyEditable>()?.IsEditable(entity) ?? false))
: SerializableProperty.GetProperties<Editable>(entity).Where(p => p.GetAttribute<ConditionallyEditable>()?.IsEditable(entity) ?? true), showName, style, elementHeight, titleFont)
: SerializableProperty.GetProperties<Editable>(entity).Where(p => p.GetAttribute<ConditionallyEditable>()?.IsEditable(entity) ?? true), showName, style, elementHeight, titleFont, dimOutDefaultValues)
{
}
public SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, IEnumerable<SerializableProperty> properties, bool showName, string style = "", int elementHeight = 24, GUIFont titleFont = null)
public SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, IEnumerable<SerializableProperty> properties, bool showName, string style = "", int elementHeight = 24, GUIFont titleFont = null, bool dimOutDefaultValues = true)
: base(style, new RectTransform(Vector2.One, parent))
{
this.dimOutDefaultValues = dimOutDefaultValues;
elementHeight = (int)(elementHeight * GUI.Scale);
var tickBoxStyle = GUIStyle.GetComponentStyle("GUITickBox");
var textBoxStyle = GUIStyle.GetComponentStyle("GUITextBox");
@@ -523,9 +526,67 @@ namespace Barotrauma
{
propertyField = CreateStringField(entity, property, value.ToString(), displayName, toolTip);
}
if (propertyField != null && dimOutDefaultValues)
{
UpdateTextColors(property, entity, propertyField);
}
return propertyField;
}
private void UpdateTextColors(SerializableProperty property, object parentObject, GUIComponent parentElement)
{
if (!dimOutDefaultValues) { return; }
bool isSetToDefaultValue = false;
object currentValue = property.GetValue(parentObject);
foreach (var attribute in property.Attributes.OfType<Serialize>())
{
if (XMLExtensions.DefaultValueEquals(attribute.DefaultValue, currentValue) ||
//treat null and empty strings as identical, because there's no way to differentiate between those in the editor
(currentValue == null && attribute.DefaultValue is string defaultValueStr && defaultValueStr.IsNullOrEmpty()))
{
isSetToDefaultValue = true;
break;
}
}
foreach (var component in parentElement.GetAllChildren())
{
UpdateTextColors(component, isSetToDefaultValue);
}
}
private void UpdateTextColors(GUIComponent component, bool isSetToDefaultValue)
{
if (!dimOutDefaultValues) { return; }
if (component is GUINumberInput numberInput)
{
SetTextColor(numberInput.TextBox.TextBlock);
}
else if (component is GUIDropDown dropDown)
{
SetTextColor(dropDown.Button.TextBlock);
}
else if (component is GUITextBox textBox)
{
SetTextColor(textBox.TextBlock);
}
else if (component is GUITextBlock textBlock)
{
SetTextColor(textBlock);
}
else if (component is GUITickBox tickBox)
{
SetTextColor(tickBox.TextBlock);
}
void SetTextColor(GUITextBlock textBlock)
{
textBlock.TextColor = new Color(textBlock.TextColor, alpha: isSetToDefaultValue ? 0.5f : 1.0f);
}
}
public GUIComponent CreateBoolField(ISerializableEntity entity, SerializableProperty property, bool value, LocalizedString displayName, LocalizedString toolTip)
{
var editableAttribute = property.GetAttribute<Editable>();
@@ -564,6 +625,7 @@ namespace Barotrauma
tickBox.Selected = propertyValue;
tickBox.Flash(Color.Red);
}
UpdateTextColors(property, entity, tickBox);
return true;
}
};
@@ -611,6 +673,7 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
refresh += () =>
{
@@ -654,6 +717,7 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
HandleSetterValueTampering(numberInput, () => property.GetFloatValue(entity));
@@ -711,6 +775,7 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
return true;
};
refresh += () =>
@@ -829,6 +894,7 @@ namespace Barotrauma
TrySendNetworkUpdate(entity, property);
textBox.Text = StripPrefabTags(property.GetValue(entity).ToString());
textBox.Flash(GUIStyle.Green, flashDuration: 1f);
UpdateTextColors(property, entity, frame);
}
//restore the entities that were selected before applying
MapEntity.SelectedList.Clear();
@@ -973,6 +1039,7 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
fields[i] = numberInput;
}
@@ -1046,6 +1113,7 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
HandleSetterValueTampering(numberInput, () =>
{
@@ -1126,6 +1194,7 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
fields[i] = numberInput;
}
@@ -1206,6 +1275,7 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
fields[i] = numberInput;
}
@@ -1299,6 +1369,7 @@ namespace Barotrauma
TrySendNetworkUpdate(entity, property);
colorBox.Color = colorBox.HoverColor = colorBox.PressedColor = colorBox.SelectedTextColor = newVal;
}
UpdateTextColors(property, entity, frame);
};
colorBox.Color = colorBox.HoverColor = colorBox.PressedColor = colorBox.SelectedTextColor = (Color)property.GetValue(entity);
fields[i] = numberInput;
@@ -1373,6 +1444,7 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
fields[i] = numberInput;
}
@@ -1437,6 +1509,7 @@ namespace Barotrauma
TrySendNetworkUpdate(entity, property);
textBox.Flash(color: GUIStyle.Green, flashDuration: 1f);
}
UpdateTextColors(property, entity, frame);
}
else
{

View File

@@ -33,10 +33,10 @@ namespace Barotrauma
private GameSettings.Config unsavedConfig;
private readonly GUIFrame mainFrame;
public readonly GUIFrame mainFrame;
private readonly GUILayoutGroup tabber;
private readonly GUIFrame contentFrame;
public readonly GUILayoutGroup tabber;
public readonly GUIFrame contentFrame;
private readonly GUILayoutGroup bottom;
public readonly WorkshopMenu WorkshopMenu;
@@ -103,7 +103,7 @@ namespace Barotrauma
newContent.Visible = true;
}
private readonly Dictionary<Tab, (GUIButton Button, GUIFrame Content)> tabContents;
public readonly Dictionary<Tab, (GUIButton Button, GUIFrame Content)> tabContents;
public void SelectTab(Tab tab)
{
@@ -149,7 +149,7 @@ namespace Barotrauma
return content;
}
private static (GUILayoutGroup Left, GUILayoutGroup Right) CreateSidebars(GUIFrame parent, bool split = false)
public static (GUILayoutGroup Left, GUILayoutGroup Right) CreateSidebars(GUIFrame parent, bool split = false)
{
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform), isHorizontal: true);
GUILayoutGroup left = new GUILayoutGroup(new RectTransform((0.4875f, 1.0f), layout.RectTransform), isHorizontal: false);
@@ -166,29 +166,29 @@ namespace Barotrauma
return (left, right);
}
private static GUILayoutGroup CreateCenterLayout(GUIFrame parent)
public static GUILayoutGroup CreateCenterLayout(GUIFrame parent)
{
return new GUILayoutGroup(new RectTransform((0.5f, 1.0f), parent.RectTransform, Anchor.TopCenter, Pivot.TopCenter)) { ChildAnchor = Anchor.TopCenter };
}
private static RectTransform NewItemRectT(GUILayoutGroup parent)
public static RectTransform NewItemRectT(GUILayoutGroup parent)
=> new RectTransform((1.0f, 0.06f), parent.RectTransform, Anchor.CenterLeft);
private static void Spacer(GUILayoutGroup parent)
public static void Spacer(GUILayoutGroup parent, float height = 0.03f)
{
new GUIFrame(new RectTransform((1.0f, 0.03f), parent.RectTransform, Anchor.CenterLeft), style: null);
new GUIFrame(new RectTransform((1.0f, height), parent.RectTransform, Anchor.CenterLeft), style: null);
}
private static GUITextBlock Label(GUILayoutGroup parent, LocalizedString str, GUIFont font)
public static GUITextBlock Label(GUILayoutGroup parent, LocalizedString str, GUIFont font)
{
return new GUITextBlock(NewItemRectT(parent), str, font: font);
}
private static void DropdownEnum<T>(GUILayoutGroup parent, Func<T, LocalizedString> textFunc, Func<T, LocalizedString>? tooltipFunc, T currentValue,
public static void DropdownEnum<T>(GUILayoutGroup parent, Func<T, LocalizedString> textFunc, Func<T, LocalizedString>? tooltipFunc, T currentValue,
Action<T> setter) where T : Enum
=> Dropdown(parent, textFunc, tooltipFunc, (T[])Enum.GetValues(typeof(T)), currentValue, setter);
private static GUIDropDown Dropdown<T>(GUILayoutGroup parent, Func<T, LocalizedString> textFunc, Func<T, LocalizedString>? tooltipFunc, IReadOnlyList<T> values, T currentValue, Action<T> setter)
public static GUIDropDown Dropdown<T>(GUILayoutGroup parent, Func<T, LocalizedString> textFunc, Func<T, LocalizedString>? tooltipFunc, IReadOnlyList<T> values, T currentValue, Action<T> setter)
{
var dropdown = new GUIDropDown(NewItemRectT(parent), elementCount: values.Count);
values.ForEach(v => dropdown.AddItem(text: textFunc(v), userData: v, toolTip: tooltipFunc?.Invoke(v) ?? null));
@@ -204,7 +204,7 @@ namespace Barotrauma
return dropdown;
}
private static (GUIScrollBar slider, GUITextBlock label) Slider(GUILayoutGroup parent, Vector2 range, int steps, Func<float, string> labelFunc, float currentValue, Action<float> setter, LocalizedString? tooltip = null)
public static (GUIScrollBar slider, GUITextBlock label) Slider(GUILayoutGroup parent, Vector2 range, int steps, Func<float, string> labelFunc, float currentValue, Action<float> setter, LocalizedString? tooltip = null)
{
var layout = new GUILayoutGroup(NewItemRectT(parent), isHorizontal: true);
var slider = new GUIScrollBar(new RectTransform((0.72f, 1.0f), layout.RectTransform), style: "GUISlider")
@@ -229,7 +229,7 @@ namespace Barotrauma
return (slider, label);
}
private static GUITickBox Tickbox(GUILayoutGroup parent, LocalizedString label, LocalizedString tooltip, bool currentValue, Action<bool> setter)
public static GUITickBox Tickbox(GUILayoutGroup parent, LocalizedString label, LocalizedString tooltip, bool currentValue, Action<bool> setter)
{
return new GUITickBox(NewItemRectT(parent), label)
{
@@ -243,9 +243,9 @@ namespace Barotrauma
};
}
private string Percentage(float v) => ToolBox.GetFormattedPercentage(v);
public string Percentage(float v) => ToolBox.GetFormattedPercentage(v);
private static int Round(float v) => MathUtils.RoundToInt(v);
public static int Round(float v) => MathUtils.RoundToInt(v);
private void CreateGraphicsTab()
{
@@ -507,6 +507,47 @@ namespace Barotrauma
return true;
}
};
#if OSX
Spacer(voiceChat, 0.003f);
// On macOS, microphone permission can apparently sometimes end up in a broken state when the app binary changes (eg. after a Steam update).
// The device seems to be there, but won't receive anything, even if the mic permission is fine.
// This button lets the user reset it and reboot the game, so the mic permission check will be retriggered on next run.
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), voiceChat.RectTransform),
text: TextManager.Get("MacResetMicPermissions"),
style: "GUIButtonSmall")
{
ToolTip = TextManager.Get("MacResetMicPermissionsToolTip"),
OnClicked = (btn, obj) =>
{
var confirmBox = new GUIMessageBox(
TextManager.Get("MacResetMicPermissions"),
TextManager.Get("MacResetMicPermissionsConfirm"),
[TextManager.Get("OK"), TextManager.Get("Cancel")]);
confirmBox.Buttons[0].OnClicked = (_, _) =>
{
try
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
{
FileName = "tccutil",
Arguments = "reset Microphone com.FakeFish.Barotrauma",
UseShellExecute = false
});
}
catch (Exception e)
{
DebugConsole.NewMessage($"Failed to reset microphone permission: {e.Message}", Color.Orange);
}
GameMain.Instance.Exit();
confirmBox.Close();
return true;
};
confirmBox.Buttons[1].OnClicked = confirmBox.Close;
return true;
}
};
#endif
Spacer(voiceChat);
Label(voiceChat, TextManager.Get("VCInputMode"), GUIStyle.SubHeadingFont);
@@ -965,4 +1006,4 @@ namespace Barotrauma
GUI.SettingsMenuOpen = false;
}
}
}
}

View File

@@ -387,13 +387,11 @@ These will hide all servers that have a discord.gg link in their name or descrip
try
{
var client = new RestClient($"{remoteContentUrl}spamfilter")
{
CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore)
};
var client = RestFactory.CreateClient($"{remoteContentUrl}spamfilter");
client.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
client.AddDefaultHeader("Cache-Control", "no-cache");
client.AddDefaultHeader("Pragma", "no-cache");
var request = new RestRequest("serve_spamlist.php", Method.GET);
var request = RestFactory.CreateRequest("serve_spamlist.php");
TaskPool.Add("RequestGlobalSpamFilter", client.ExecuteAsync(request), RemoteContentReceived);
}
catch (Exception e)
@@ -410,12 +408,18 @@ These will hide all servers that have a discord.gg link in their name or descrip
try
{
if (!t.TryGetResult(out IRestResponse? remoteContentResponse)) { throw new Exception("Task did not return a valid result"); }
if (remoteContentResponse.ErrorException != null)
{
DebugConsole.AddWarning(
"Connection error: Failed to receive global spam filter " +
$"({remoteContentResponse.ErrorException.Message}).");
return;
}
if (remoteContentResponse.StatusCode != HttpStatusCode.OK)
{
DebugConsole.AddWarning(
"Failed to receive global spam filter." +
"There may be an issue with your internet connection, or the master server might be temporarily unavailable " +
$"(error code: {remoteContentResponse.StatusCode})");
"Failed to receive global spam filter. " +
$"The master server might be temporarily unavailable, HTTP status: {remoteContentResponse.StatusCode}");
return;
}
string data = remoteContentResponse.Content;

View File

@@ -18,18 +18,36 @@ namespace Barotrauma.Steam
{
public const int MaxThumbnailSize = 1024 * 1024;
/// <summary>
/// Tags the players can choose for their workshop items. These must match the ones defined in the Steamworks backend. They're case insensitive, but must otherwise match exactly for the tag filtering to work correctly.
/// The localized names for these are fetched from the loca files with the identifier "workshop.contenttag.{tag.RemoveWhitespace()}".
/// </summary>
public static readonly ImmutableArray<Identifier> Tags = new []
{
"submarine",
"item",
"monster",
"art",
"mission",
"outpost",
"beacon station",
"wreck",
"ruin",
"weapons",
"medical",
"equipment",
"art",
"event set",
"total conversion",
"game mode",
"gameplay mechanics",
"environment",
"item assembly",
"language",
"qol",
"client-side",
"server-side",
"outdated",
"library"
}.ToIdentifiers().ToImmutableArray();
public class ItemThumbnail : IDisposable
@@ -113,10 +131,14 @@ namespace Barotrauma.Steam
string? thumbnailUrl = item.PreviewImageUrl;
if (thumbnailUrl.IsNullOrWhiteSpace()) { return null; }
var client = new RestClient(thumbnailUrl);
var request = new RestRequest(".", Method.GET);
var client = RestFactory.CreateClient(thumbnailUrl);
var request = RestFactory.CreateRequest(".");
IRestResponse response = await client.ExecuteAsync(request, cancellationToken);
if (response is { StatusCode: System.Net.HttpStatusCode.OK, ResponseStatus: ResponseStatus.Completed })
if (response.ErrorException != null)
{
DebugConsole.NewMessage($"Connection error: Failed to load workshop item thumbnail for {item.Id} ({response.ErrorException.Message}).");
}
else if (response is { StatusCode: System.Net.HttpStatusCode.OK, ResponseStatus: ResponseStatus.Completed })
{
using var dataStream = new System.IO.MemoryStream();
await dataStream.WriteAsync(response.RawBytes, cancellationToken);

View File

@@ -535,9 +535,9 @@ namespace Barotrauma.Steam
= new GUIListBox(rectT, style: null, isHorizontal: false)
{
UseGridLayout = true,
ScrollBarEnabled = false,
ScrollBarEnabled = true,
ScrollBarVisible = false,
HideChildrenOutsideFrame = false,
HideChildrenOutsideFrame = true,
Spacing = GUI.IntScale(4)
};
tagsList.Content.ClampMouseRectToParent = false;

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>1.11.5.0</Version>
<Version>1.12.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2024</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>
@@ -14,6 +14,7 @@
<Configurations>Debug;Release;Unstable</Configurations>
<InvariantGlobalization>true</InvariantGlobalization>
<WarningsAsErrors>;NU1605;CS0114;CS0108;CS8597;CS8600;CS8601;CS8602;CS8603;CS8604;CS8605;CS8606;CS8607;CS8608;CS8609;CS8610;CS8611;CS8612;CS8613;CS8614;CS8615;CS8616;CS8617;CS8618;CS8619;CS8620;CS8621;CS8622;CS8624;CS8625;CS8626;CS8629;CS8631;CS8632;CS8633;CS8634;CS8638;CS8643;CS8644;CS8645;CS8653;CS8654;CS8655;CS8667;CS8669;CS8670;CS8714;CS8717;CS8765</WarningsAsErrors>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -215,5 +216,5 @@
<ItemGroup>
<None Include="../BarotraumaShared/Luatrauma.props" />
</ItemGroup>
<Import Project="../BarotraumaShared/LuatraumaBuild.props" />
</Project>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>1.11.5.0</Version>
<Version>1.12.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2024</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>
@@ -15,6 +15,7 @@
<InvariantGlobalization>true</InvariantGlobalization>
<WarningsAsErrors>;NU1605;CS0114;CS0108;CS8597;CS8600;CS8601;CS8602;CS8603;CS8604;CS8605;CS8606;CS8607;CS8608;CS8609;CS8610;CS8611;CS8612;CS8613;CS8614;CS8615;CS8616;CS8617;CS8618;CS8619;CS8620;CS8621;CS8622;CS8624;CS8625;CS8626;CS8629;CS8631;CS8632;CS8633;CS8634;CS8638;CS8643;CS8644;CS8645;CS8653;CS8654;CS8655;CS8667;CS8669;CS8670;CS8714;CS8717;CS8765</WarningsAsErrors>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -220,5 +221,6 @@
<ItemGroup>
<None Include="../BarotraumaShared/Luatrauma.props" />
</ItemGroup>
<Import Project="../BarotraumaShared/LuatraumaBuild.props" />
</Project>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>1.11.5.0</Version>
<Version>1.12.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2024</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>
@@ -15,6 +15,7 @@
<InvariantGlobalization>true</InvariantGlobalization>
<ApplicationManifest>app.manifest</ApplicationManifest>
<WarningsAsErrors>;NU1605;CS0114;CS0108;CS8597;CS8600;CS8601;CS8602;CS8603;CS8604;CS8605;CS8606;CS8607;CS8608;CS8609;CS8610;CS8611;CS8612;CS8613;CS8614;CS8615;CS8616;CS8617;CS8618;CS8619;CS8620;CS8621;CS8622;CS8624;CS8625;CS8626;CS8629;CS8631;CS8632;CS8633;CS8634;CS8638;CS8643;CS8644;CS8645;CS8653;CS8654;CS8655;CS8667;CS8669;CS8670;CS8714;CS8717;CS8765</WarningsAsErrors>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -247,5 +248,5 @@
<ItemGroup>
<None Include="../BarotraumaShared/Luatrauma.props" />
</ItemGroup>
<Import Project="../BarotraumaShared/LuatraumaBuild.props" />
</Project>

View File

@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=clientsource/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -6,14 +6,16 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>1.11.5.0</Version>
<Version>1.12.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
<Configurations>Debug;Release;Unstable</Configurations>
<InvariantGlobalization>true</InvariantGlobalization>
<LangVersion>latest</LangVersion>
<WarningsAsErrors>;NU1605;CS0114;CS0108;CS8597;CS8600;CS8601;CS8602;CS8603;CS8604;CS8605;CS8606;CS8607;CS8608;CS8609;CS8610;CS8611;CS8612;CS8613;CS8614;CS8615;CS8616;CS8617;CS8618;CS8619;CS8620;CS8621;CS8622;CS8624;CS8625;CS8626;CS8629;CS8631;CS8632;CS8633;CS8634;CS8638;CS8643;CS8644;CS8645;CS8653;CS8654;CS8655;CS8667;CS8669;CS8670;CS8714;CS8717;CS8765</WarningsAsErrors>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -53,8 +55,9 @@
<OutputPath>..\bin\$(Configuration)Linux\</OutputPath>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml;..\BarotraumaShared\LocalMods\[DebugOnlyTest]*\**" />
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml" />
<Content Remove="..\BarotraumaShared\**\*.cs" />
<Content Remove="..\BarotraumaShared\**\*.props" />
@@ -161,4 +164,6 @@
<ItemGroup>
<None Include="../BarotraumaShared/Luatrauma.props" />
</ItemGroup>
<Import Project="../BarotraumaShared/LuatraumaBuild.props" />
</Project>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>1.11.5.0</Version>
<Version>1.12.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>
@@ -14,6 +14,7 @@
<Configurations>Debug;Release;Unstable</Configurations>
<InvariantGlobalization>true</InvariantGlobalization>
<WarningsAsErrors>;NU1605;CS0114;CS0108;CS8597;CS8600;CS8601;CS8602;CS8603;CS8604;CS8605;CS8606;CS8607;CS8608;CS8609;CS8610;CS8611;CS8612;CS8613;CS8614;CS8615;CS8616;CS8617;CS8618;CS8619;CS8620;CS8621;CS8622;CS8624;CS8625;CS8626;CS8629;CS8631;CS8632;CS8633;CS8634;CS8638;CS8643;CS8644;CS8645;CS8653;CS8654;CS8655;CS8667;CS8669;CS8670;CS8714;CS8717;CS8765</WarningsAsErrors>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -166,4 +167,6 @@
<ItemGroup>
<None Include="../BarotraumaShared/Luatrauma.props" />
</ItemGroup>
<Import Project="../BarotraumaShared/LuatraumaBuild.props" />
</Project>

View File

@@ -65,7 +65,7 @@ namespace Barotrauma
var owner = GameMain.Server.ConnectedClients.Find(c => c.Character == this);
if (owner != null)
{
if (!GameMain.LuaCs.Game.overrideTraitors)
if (!LuaCsSetup.Instance.Game.overrideTraitors)
{
GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("KilledByTraitorNotification"), owner, ChatMessageType.ServerMessageBoxInGame);
}

View File

@@ -320,11 +320,7 @@ namespace Barotrauma
if (TalentTree.IsViableTalentForCharacter(this, prefab.Identifier, talentSelection))
{
bool? should = GameMain.LuaCs.Hook.Call<bool?>("character.updateTalent", this, prefab, c);
if (should == null)
{
GiveTalent(prefab.Identifier);
}
GiveTalent(prefab.Identifier);
talentSelection.Add(prefab.Identifier);
}
}
@@ -815,7 +811,7 @@ namespace Barotrauma
var tempBuffer = new ReadWriteMessage();
WriteStatus(tempBuffer, forceAfflictionData: true);
if (msgLengthBeforeStatus + tempBuffer.LengthBytes >= 255 && restrictMessageSize && GameMain.LuaCs.Networking.RestrictMessageSize)
if (msgLengthBeforeStatus + tempBuffer.LengthBytes >= 255 && restrictMessageSize)
{
msg.WriteBoolean(false);
if (msgLengthBeforeStatus < 255)

View File

@@ -10,6 +10,7 @@ using System.Linq;
using System.Text;
using Barotrauma.Steam;
using Barotrauma.Extensions;
using Barotrauma.LuaCs.Events;
namespace Barotrauma
{
@@ -1287,41 +1288,6 @@ namespace Barotrauma
GameMain.NetLobbyScreen.LevelSeed = string.Join(" ", args);
}));
commands.Add(new Command("lua", "lua: Runs a string.", (string[] args) =>
{
try
{
GameMain.LuaCs.Lua.DoString(string.Join(" ", args));
}
catch (Exception ex)
{
LuaCsLogger.HandleException(ex, LuaCsMessageOrigin.LuaMod);
}
}));
commands.Add(new Command("reloadlua|reloadcs|reloadluacs", "Re-initializes the LuaCs environment.", (string[] args) =>
{
GameMain.LuaCs.Initialize();
}));
commands.Add(new Command("toggleluadebug", "Toggles the MoonSharp Debug Server.", (string[] args) =>
{
int port = 41912;
if (args.Length > 0)
{
int.TryParse(args[0], out port);
}
GameMain.LuaCs.ToggleDebugger(port);
}));
commands.Add(new Command("install_cl_lua|install_cl|install_cl_cs|install_cl_luacs", "Installs Client-Side LuaCs into your client.", (string[] args) =>
{
LuaCsInstaller.Install();
}));
commands.Add(new Command("randomizeseed", "randomizeseed: Toggles level seed randomization on/off.", (string[] args) =>
{
GameMain.Server.ServerSettings.RandomizeSeed = !GameMain.Server.ServerSettings.RandomizeSeed;
@@ -2771,10 +2737,17 @@ namespace Barotrauma
commands.Add(new Command("ShowServerPerf", "Immediately log server performance info in ServerMessage", (string[] args) =>
{
GameServer.Log(PerformenceMonitor.PM.ToString(), ServerLog.MessageType.ServerMessage);
NewMessage(PerformenceMonitor.PM.ToString(), Color.Green);
GameServer.Log(PerformanceMonitor.PM.ToString(), ServerLog.MessageType.ServerMessage);
}));
AssignOnClientRequestExecute(
"ShowServerPerf",
(senderClient, cursorWorldPos, args) =>
{
GameMain.Server.SendConsoleMessage(PerformanceMonitor.PM.ToString(), senderClient);
}
);
#if DEBUG
commands.Add(new Command("spamevents", "A debug command that creates a ton of entity events.", (string[] args) =>
{

View File

@@ -79,6 +79,15 @@ namespace Barotrauma
convAction.SelectedOption = selectedOption;
if (convAction.Options.Any() && !convAction.GetEndingOptions().Contains(selectedOption))
{
var option = convAction.Options[selectedOption];
if (option.ForceSay && sender.Character != null)
{
sender.Character.ForceSay(
option.ForceSayText.IsNullOrEmpty() ? TextManager.Get(option.Text).Fallback(option.Text) : TextManager.Get(option.ForceSayText).Fallback(option.ForceSayText),
option.ForceSayInRadio,
option.ForceSayRemoveQuotes);
}
foreach (Client c in convAction.TargetClients)
{
if (c == sender) { continue; }

View File

@@ -133,7 +133,7 @@ namespace Barotrauma
switch (winCondition)
{
case WinCondition.LastManStanding:
if (crews[0].Count == 0 || crews[1].Count == 0)
if (crews[0].Count == 0 && crews[1].Count == 0)
{
//if there are no characters in either crew, end the round
teamDead[0] = teamDead[1] = true;

View File

@@ -14,6 +14,7 @@ using MoonSharp.Interpreter;
using System.Net;
using Barotrauma.Extensions;
using System.Threading.Tasks;
using Barotrauma.LuaCs.Events;
namespace Barotrauma
{
@@ -35,8 +36,6 @@ namespace Barotrauma
set { world = value; }
}
public static LuaCsSetup LuaCs;
public static GameServer Server;
public static NetworkMember NetworkMember
{
@@ -113,6 +112,8 @@ namespace Barotrauma
GameScreen = new GameScreen();
MainThread = Thread.CurrentThread;
LuaCsSetup.Instance.GetType();
}
public void Init()
@@ -131,8 +132,6 @@ namespace Barotrauma
NetLobbyScreen = new NetLobbyScreen();
CheckContentPackage();
LuaCs = new LuaCsSetup();
}
@@ -244,6 +243,9 @@ namespace Barotrauma
//handled in TryStartChildServerRelay
i += 2;
break;
case "-lenienthandshake":
NetConfig.UseLenientHandshake = true;
break;
}
}
@@ -330,7 +332,7 @@ namespace Barotrauma
Stopwatch performanceCounterTimer = Stopwatch.StartNew();
stopwatch = Stopwatch.StartNew();
PerformenceMonitor PM = new PerformenceMonitor();
PerformanceMonitor PM = new PerformanceMonitor();
long prevTicks = stopwatch.ElapsedTicks;
while (ShouldRun)
@@ -367,12 +369,16 @@ namespace Barotrauma
TaskPool.Update();
CoroutineManager.Update(paused: false, (float)Timing.Step);
GameMain.LuaCs.Update();
performanceCounterTimer.Stop();
if (GameMain.LuaCs.PerformanceCounter.EnablePerformanceCounter)
if (LuaCsSetup.Instance.PerformanceCounterService.EnablePerformanceCounter)
{
GameMain.LuaCs.PerformanceCounter.UpdateElapsedTime = (double)performanceCounterTimer.ElapsedTicks / Stopwatch.Frequency;
LuaCsSetup.Instance.PerformanceCounterService.AddElapsedTicks(new SimplePerformanceData("Update", performanceCounterTimer.ElapsedTicks));
}
if (LuaCsSetup.Instance.PerformanceCounter.EnablePerformanceCounter)
{
LuaCsSetup.Instance.PerformanceCounter.UpdateElapsedTime = (double)performanceCounterTimer.ElapsedTicks / Stopwatch.Frequency;
}
performanceCounterTimer.Reset();
Timing.Accumulator -= Timing.Step;
@@ -426,7 +432,7 @@ namespace Barotrauma
}
}
PerformenceMonitor.PM.Dispose();
PerformanceMonitor.PM.Dispose();
stopwatch.Stop();
@@ -459,7 +465,17 @@ namespace Barotrauma
public void Exit()
{
ShouldRun = false;
GameMain.LuaCs.Stop();
try
{
if (LuaCsSetup.Instance is not null)
{
LuaCsSetup.Instance.Dispose();
}
}
catch (Exception e)
{
DebugConsole.ThrowError($"Error while disposing of LuaCsForBarotrauma: {e.Message} | {e.StackTrace}");
}
}
}
}

View File

@@ -7,7 +7,7 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.WriteBoolean(State);
msg.WriteUInt16(user == null ? (ushort)0 : user.ID);
msg.WriteUInt16(User == null || User.Removed ? (ushort)0 : User.ID);
}
}
}

View File

@@ -16,8 +16,11 @@ namespace Barotrauma.Items.Components
public void ServerEventRead(IReadMessage msg, Client c)
{
if (c.Character == null) { return; }
var requestedFixAction = (FixActions)msg.ReadRangedInteger(0, 2);
var QTESuccess = msg.ReadBoolean();
FixActions requestedFixAction = (FixActions)msg.ReadRangedInteger(0, 2);
bool QTESuccess = msg.ReadBoolean();
if (!item.CanClientAccess(c) || !HasRequiredItems(c.Character, addMessage: false)) { return; }
if (requestedFixAction != FixActions.None)
{
if (!c.Character.IsTraitor && requestedFixAction == FixActions.Sabotage)

View File

@@ -121,9 +121,9 @@ namespace Barotrauma
if (shouldBeRemoved)
{
bool itemAccessDenied = prevItems.Contains(item) && // if the item was in the inventory before
!itemAccessibility[item] && // and the sender is not allowed to access it
(item.PreviousParentInventory == null || // and either the item has no previous inventory
!sender.Character.CanAccessInventory(item.PreviousParentInventory)); // or the sender can't access the previous inventory
!itemAccessibility[item] && // and the sender is not allowed to access it
(item.PreviousParentInventory == null || // and either the item has no previous inventory
!sender.Character.CanAccessInventory(item.PreviousParentInventory)); // or the sender can't access the previous inventory
if (itemAccessDenied)
{
@@ -136,7 +136,7 @@ namespace Barotrauma
Item droppedItem = item;
Entity prevOwner = Owner;
Inventory previousInventory = droppedItem.ParentInventory;
droppedItem.Drop(null);
droppedItem.Drop(sender.Character);
droppedItem.PreviousParentInventory = previousInventory;
var previousCharacterInventory = prevOwner switch
@@ -188,9 +188,18 @@ namespace Barotrauma
if (holdable != null && !holdable.CanBeDeattached()) { continue; }
bool itemAccessDenied = !prevItems.Contains(item) && !itemAccessibility[item] &&
(sender.Character == null || item.PreviousParentInventory == null || !sender.Character.CanAccessInventory(item.PreviousParentInventory));
bool itemAccessDenied = !prevItems.Contains(item) &&
!itemAccessibility[item] &&
(item.PreviousParentInventory == null ||
!sender.Character.CanAccessInventory(item.PreviousParentInventory));
// Prevent modified clients from being able to steal items from characters by item swapping with an existing item
// due to drag and drop being enabled
if (!sender.Character.CanAccessInventory(this, CharacterInventory.AccessLevel.AllowBotsAndPets) && GetItemAt(slotIndex) != null)
{
itemAccessDenied = true;
}
//more restricted "adding" of handcuffs: we can't allow putting handcuffs on a player just because dragging and dropping is allowed
if (item.HasTag(Tags.HandLockerItem) && !itemAccessDenied)
{

View File

@@ -12,8 +12,6 @@ namespace Barotrauma
{
private CoroutineHandle logPropertyChangeCoroutine;
public Inventory PreviousParentInventory;
public override Sprite Sprite
{
get { return base.Prefab?.Sprite; }

View File

@@ -1,107 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Barotrauma.Networking
{
partial class Client
{
public void SetClientCharacter(Character character)
{
GameMain.Server.SetClientCharacter(this, character);
}
public void Kick(string reason = "")
{
GameMain.Server.KickClient(this.Connection, reason);
}
public void Ban(string reason = "", float seconds = -1)
{
if (seconds == -1)
{
GameMain.Server.BanClient(this, reason, null);
}
else
{
GameMain.Server.BanClient(this, reason, TimeSpan.FromSeconds(seconds));
}
}
public static void UnbanPlayer(string playerName)
{
GameMain.Server.UnbanPlayer(playerName);
}
public static void BanPlayer(string player, string reason, bool range = false, float seconds = -1)
{
if (seconds == -1)
{
GameMain.Server.BanPlayer(player, reason, null);
}
else
{
GameMain.Server.BanPlayer(player, reason, TimeSpan.FromSeconds(seconds));
}
}
public bool CheckPermission(ClientPermissions permissions)
{
return this.Permissions.HasFlag(permissions);
}
}
}
namespace Barotrauma
{
using Microsoft.Xna.Framework;
using System.Reflection;
partial class Item
{
public object CreateServerEventString(string component)
{
var comp = GetComponentString(component);
if (comp == null)
return null;
MethodInfo method = typeof(Item).GetMethod(nameof(Item.CreateServerEvent), new Type[]{ Type.MakeGenericMethodParameter(0) });
MethodInfo generic = method.MakeGenericMethod(comp.GetType());
return generic.Invoke(this, new object[]{ comp });
}
public object CreateServerEventString(string component, object[] extraData)
{
var comp = GetComponentString(component);
if (comp == null)
return null;
MethodInfo method = typeof(Item).GetMethod(nameof(Item.CreateServerEvent), new Type[]{ Type.MakeGenericMethodParameter(0), typeof(object[]) });
MethodInfo generic = method.MakeGenericMethod(comp.GetType());
return generic.Invoke(this, new object[]{comp, extraData });
}
}
}
namespace Barotrauma.Items.Components
{
using Barotrauma.Networking;
partial struct Signal
{
public static Signal Create(string value, int stepsTaken = 0, Character sender = null, Item source = null, float power = 0.0f, float strength = 1.0f)
{
return new Signal(value, stepsTaken, sender, source, power, strength);
}
}
partial class Quality
{
public void SetValue(StatType statType, float value)
{
statValues[statType] = value;
}
}
}

View File

@@ -2,6 +2,7 @@
using System;
using System.IO;
using System.Linq;
using Barotrauma.LuaCs;
namespace Barotrauma
{
@@ -9,11 +10,11 @@ namespace Barotrauma
{
public static void Install()
{
ContentPackage luaPackage = LuaCsSetup.GetPackage(LuaCsSetup.LuaForBarotraumaId);
ContentPackage luaPackage = LuaCsSetup.GetLuaCsPackage();
if (luaPackage == null)
{
GameMain.Server.SendChatMessage("Couldn't find the LuaCs For Barotrauma package.", ChatMessageType.ServerMessageBox);
GameMain.Server.SendChatMessage("Couldn't find the ProjectEP package.", ChatMessageType.ServerMessageBox);
return;
}
@@ -45,15 +46,13 @@ namespace Barotrauma
File.Copy(Path.Combine(path, "Binary", file), file, true);
}
File.WriteAllText(LuaCsSetup.VersionFile, luaPackage.ModVersion);
#if WINDOWS
File.WriteAllText("LuaCsDedicatedServer.bat", "\"%LocalAppData%/Daedalic Entertainment GmbH/Barotrauma/WorkshopMods/Installed/2559634234/Binary/DedicatedServer.exe\"");
#endif
}
catch (UnauthorizedAccessException e)
{
LuaCsLogger.LogError($"Unauthorized file access exception. This usually means you already have LuaCs installed. ${e}", LuaCsMessageOrigin.LuaCs);
LuaCsLogger.LogError($"Unauthorized file access exception. This usually means you already have ProjectEP installed. ${e}", LuaCsMessageOrigin.LuaCs);
return;
}
@@ -64,7 +63,7 @@ namespace Barotrauma
return;
}
GameMain.Server.SendChatMessage("Client-Side LuaCs installed, restart your game to apply changes.", ChatMessageType.ServerMessageBox);
GameMain.Server.SendChatMessage("Client-Side ProjectEP installed, restart your game to apply changes.", ChatMessageType.ServerMessageBox);
}
}
}

View File

@@ -1,194 +0,0 @@
using Barotrauma.Networking;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
partial class LuaCsNetworking
{
private const int MaxRegisterPerClient = 1000;
private Dictionary<string, int> clientRegisterCount = new Dictionary<string, int>();
private ushort currentId = 0;
public void NetMessageReceived(IReadMessage netMessage, ClientPacketHeader header, Client client = null)
{
if (header != ClientPacketHeader.LUA_NET_MESSAGE)
{
GameMain.LuaCs.Hook.Call("netMessageReceived", netMessage, header, client);
return;
}
LuaCsClientToServer luaCsHeader = (LuaCsClientToServer)netMessage.ReadByte();
switch (luaCsHeader)
{
case LuaCsClientToServer.NetMessageString:
HandleNetMessageString(netMessage, client);
break;
case LuaCsClientToServer.NetMessageId:
HandleNetMessageId(netMessage, client);
break;
case LuaCsClientToServer.RequestAllIds:
WriteAllIds(client);
break;
case LuaCsClientToServer.RequestSingleId:
RequestIdSingle(netMessage, client);
break;
}
}
private void HandleNetMessageId(IReadMessage netMessage, Client client = null)
{
ushort id = netMessage.ReadUInt16();
if (idToString.ContainsKey(id))
{
string name = idToString[id];
HandleNetMessage(netMessage, name, client);
}
else
{
if (GameSettings.CurrentConfig.VerboseLogging)
{
LuaCsLogger.LogError($"Received NetMessage for unknown id {id} from {GameServer.ClientLogName(client)}.");
}
}
}
public IWriteMessage Start(string netMessageName)
{
var message = new WriteOnlyMessage();
message.WriteByte((byte)ServerPacketHeader.LUA_NET_MESSAGE);
if (stringToId.ContainsKey(netMessageName))
{
message.WriteByte((byte)LuaCsServerToClient.NetMessageId);
message.WriteUInt16(stringToId[netMessageName]);
}
else
{
message.WriteByte((byte)LuaCsServerToClient.NetMessageString);
message.WriteString(netMessageName);
}
return message;
}
public void Receive(string netMessageName, LuaCsAction callback)
{
RegisterId(netMessageName);
netReceives[netMessageName] = callback;
}
public ushort RegisterId(string name)
{
if (stringToId.ContainsKey(name))
{
return stringToId[name];
}
if (currentId >= ushort.MaxValue)
{
LuaCsLogger.LogError($"Tried to register more than {ushort.MaxValue} network ids!");
return 0;
}
currentId++;
idToString[currentId] = name;
stringToId[name] = currentId;
WriteIdToAll(currentId, name);
return currentId;
}
private void RequestIdSingle(IReadMessage netMessage, Client client)
{
string name = netMessage.ReadString();
if (!stringToId.ContainsKey(name) && client.AccountId.TryUnwrap(out AccountId id))
{
if (!clientRegisterCount.ContainsKey(id.StringRepresentation))
{
clientRegisterCount[id.StringRepresentation] = 0;
}
clientRegisterCount[id.StringRepresentation]++;
if (clientRegisterCount[id.StringRepresentation] > MaxRegisterPerClient)
{
LuaCsLogger.Log($"{GameServer.ClientLogName(client)} Tried to register more than {MaxRegisterPerClient} Ids!");
return;
}
}
RegisterId(name);
}
private void WriteIdToAll(ushort id, string name)
{
WriteOnlyMessage message = new WriteOnlyMessage();
message.WriteByte((byte)ServerPacketHeader.LUA_NET_MESSAGE);
message.WriteByte((byte)LuaCsServerToClient.ReceiveIds);
message.WriteUInt16(1);
message.WriteUInt16(id);
message.WriteString(name);
Send(message, null, DeliveryMethod.Reliable);
}
private void WriteAllIds(Client client)
{
WriteOnlyMessage message = new WriteOnlyMessage();
message.WriteByte((byte)ServerPacketHeader.LUA_NET_MESSAGE);
message.WriteByte((byte)LuaCsServerToClient.ReceiveIds);
message.WriteUInt16((ushort)idToString.Count());
foreach ((ushort id, string name) in idToString)
{
message.WriteUInt16(id);
message.WriteString(name);
}
Send(message, client.Connection, DeliveryMethod.Reliable);
}
public void ClientWriteLobby(Client client) => GameMain.Server.ClientWriteLobby(client);
public void Send(IWriteMessage netMessage, NetworkConnection connection = null, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable)
{
if (connection == null)
{
foreach (NetworkConnection conn in Client.ClientList.Select(c => c.Connection))
{
GameMain.Server.ServerPeer.Send(netMessage, conn, deliveryMethod);
}
}
else
{
GameMain.Server.ServerPeer.Send(netMessage, connection, deliveryMethod);
}
}
public void UpdateClientPermissions(Client client)
{
GameMain.Server.UpdateClientPermissions(client);
}
public int FileSenderMaxPacketsPerUpdate
{
get { return FileSender.FileTransferOut.MaxPacketsPerUpdate; }
set { FileSender.FileTransferOut.MaxPacketsPerUpdate = value; }
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.IO;
using Barotrauma.Networking;
namespace Barotrauma;
partial class LuaCsSetup
{
partial void CheckReadyToRun(Action onReadyToRun)
{
onReadyToRun?.Invoke();
}
/// <summary>
/// Handles changes in game states tracked by screen changes.
/// </summary>
/// <param name="screen">The new game screen.</param>
public partial void OnScreenSelected(Screen screen)
{
// the server is always in the running state unless explicitly stopped.
if (screen == UnimplementedScreen.Instance)
SetRunState(RunState.Unloaded);
SetRunState(RunState.Running);
}
}

View File

@@ -0,0 +1,188 @@
using Barotrauma.LuaCs.Events;
using Barotrauma.Networking;
using System;
using System.Collections.Generic;
using System.Linq;
// ReSharper disable once CheckNamespace
namespace Barotrauma.LuaCs;
partial class NetworkingService : INetworkingService, IEventClientRawNetMessageReceived
{
private const int MaxRegisterPerClient = 1000;
private Dictionary<string, int> clientRegisterCount = new Dictionary<string, int>();
private ushort currentId = 0;
public IWriteMessage Start(NetId netId)
{
var message = new WriteOnlyMessage();
message.WriteByte((byte)ServerHeader);
if (idToPacket.ContainsKey(netId))
{
message.WriteByte((byte)ServerToClient.NetMessageInternalId);
message.WriteUInt16(idToPacket[netId]);
}
else
{
message.WriteByte((byte)ServerToClient.NetMessageNetId);
NetId.Write(message, netId);
}
return message;
}
public bool? OnReceivedClientNetMessage(IReadMessage netMessage, ClientPacketHeader clientPacketHeader, NetworkConnection sender)
{
if (clientPacketHeader != ClientHeader)
{
return null;
}
Client client = GameMain.Server.ConnectedClients.First(c => c.Connection == sender);
ClientToServer luaCsHeader = (ClientToServer)netMessage.ReadByte();
switch (luaCsHeader)
{
case ClientToServer.NetMessageNetId:
HandleNetMessageString(netMessage, client);
break;
case ClientToServer.NetMessageInternalId:
HandleNetMessageId(netMessage, client);
break;
case ClientToServer.RequestSync:
WriteSync(client);
break;
case ClientToServer.RequestSingleNetId:
RequestIdSingle(netMessage, client);
break;
}
return true;
}
private void HandleNetMessageId(IReadMessage netMessage, Client client = null)
{
ushort id = netMessage.ReadUInt16();
if (packetToId.ContainsKey(id))
{
NetId netId = packetToId[id];
HandleNetMessage(netMessage, netId, client);
}
else
{
if (GameSettings.CurrentConfig.VerboseLogging)
{
_loggerService.LogError($"Received NetMessage for unknown id {id} from {GameServer.ClientLogName(client)}.");
}
}
}
private ushort RegisterId(NetId netId)
{
if (idToPacket.ContainsKey(netId))
{
return idToPacket[netId];
}
if (currentId >= ushort.MaxValue)
{
_loggerService.LogError($"Tried to register more than {ushort.MaxValue} network ids!");
return 0;
}
currentId++;
packetToId[currentId] = netId;
idToPacket[netId] = currentId;
WriteIdToAll(currentId, netId);
return currentId;
}
private void RequestIdSingle(IReadMessage netMessage, Client client)
{
NetId netId = NetId.Read(netMessage);
if (!idToPacket.ContainsKey(netId) && client.AccountId.TryUnwrap(out AccountId id))
{
if (!clientRegisterCount.ContainsKey(id.StringRepresentation))
{
clientRegisterCount[id.StringRepresentation] = 0;
}
clientRegisterCount[id.StringRepresentation]++;
if (clientRegisterCount[id.StringRepresentation] > MaxRegisterPerClient)
{
_loggerService.Log($"{GameServer.ClientLogName(client)} Tried to register more than {MaxRegisterPerClient} Ids!");
return;
}
}
RegisterId(netId);
}
private void WriteIdToAll(ushort packet, NetId netId)
{
WriteOnlyMessage message = new WriteOnlyMessage();
message.WriteByte((byte)ServerHeader);
message.WriteByte((byte)ServerToClient.ReceiveNetIds);
message.WriteUInt16(1);
message.WriteUInt16(packet);
NetId.Write(message, netId);
SendToClient(message, null, DeliveryMethod.Reliable);
}
private void WriteSync(Client client)
{
WriteOnlyMessage message = new WriteOnlyMessage();
message.WriteByte((byte)ServerHeader);
message.WriteByte((byte)ServerToClient.ReceiveNetIds);
message.WriteUInt16((ushort)packetToId.Count());
foreach ((ushort packet, NetId netId) in packetToId)
{
message.WriteUInt16(packet);
NetId.Write(message, netId);
}
SendToClient(message, client.Connection, DeliveryMethod.Reliable);
// TODO: when we move to using GUIDs for everything, this should combined into a single message
foreach (INetworkSyncVar netVar in netVars.Keys)
{
SendNetVar(netVar, client.Connection);
}
}
public void SendToClient(IWriteMessage netMessage, NetworkConnection connection = null, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable)
{
if (connection == null)
{
foreach (NetworkConnection conn in ModUtils.Client.ClientList.Select(c => c.Connection))
{
GameMain.Server.ServerPeer.Send(netMessage, conn, deliveryMethod);
}
}
else
{
GameMain.Server.ServerPeer.Send(netMessage, connection, deliveryMethod);
}
}
public void Send(IWriteMessage netMessage, NetworkConnection connection = null, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable)
=> SendToClient(netMessage, connection, deliveryMethod);
}

View File

@@ -112,6 +112,7 @@ namespace Barotrauma
msg.WriteRangedSingle(normalizedXPos, 0.0f, 1.0f, 8);
msg.WriteRangedSingle(normalizedYPos, 0.0f, 1.0f, 8);
msg.WriteRangedSingle(decal.Scale, 0f, 2f, 12);
msg.WriteRangedSingle(decal.BaseAlpha, 0f, 1f, 8);
}
break;
case BallastFloraEventData ballastFloraEventData:
@@ -251,7 +252,7 @@ namespace Barotrauma
break;
case EventType.Decal:
byte decalIndex = msg.ReadByte();
float decalAlpha = msg.ReadRangedSingle(0.0f, 1.0f, 255);
float decalAlpha = msg.ReadRangedSingle(0f, 1f, 8);
if (decalIndex < 0 || decalIndex >= decals.Count) { return; }
if (c.Character != null && c.Character.AllowInput && c.Character.HeldItems.Any(it => it.GetComponent<Sprayer>() != null))
{

View File

@@ -1,6 +1,8 @@
using System;
using System.Text;
using Barotrauma.LuaCs.Events;
using MoonSharp.Interpreter;
using MoonSharp.VsCodeDebugger.SDK;
using System;
using System.Text;
namespace Barotrauma.Networking
{
@@ -67,6 +69,9 @@ namespace Barotrauma.Networking
txt = msg.ReadString() ?? "";
}
// Sanitize incoming text message from client so they can't use RichString features
txt = txt.Replace('‖', ' ');
if (!NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID)) { return; }
c.LastSentChatMsgID = ID;
@@ -86,12 +91,9 @@ namespace Barotrauma.Networking
HandleSpamFilter(c, txt, out bool flaggedAsSpam, similarityMultiplier);
if (flaggedAsSpam) { return; }
var should = GameMain.LuaCs.Hook.Call<bool?>("chatMessage", txt, c, type);
if (should != null && should.Value)
{
return;
}
bool? should = null;
LuaCsSetup.Instance.EventService.PublishEvent<IEventChatMessage>(x => should = x.OnChatMessage(txt, c, type, ChatMessage.Create(c.Name, txt, type, null, c)) ?? should);
if (should != null && should.Value) { return; }
if (type == ChatMessageType.Order)
{

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