3 Commits
CBT ... nightly

Author SHA1 Message Date
NotAlwaysTrue
d4af393199 Modified create pre-release workflow 2025-12-23 09:02:48 +08:00
NotAlwaysTrue
bbe4e95c17 Modified create pre-release workflow 2025-12-23 08:58:47 +08:00
NotAlwaysTrue
7fc21189c5 OBT-ReleaseVer 2025-12-23 08:36:40 +08:00
428 changed files with 11542 additions and 24528 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.12.6.2 (Spring Update 2026)
- v1.11.5.0 (Winter Update 2025 Hotfix 1)
- Other
validations:
required: true

View File

@@ -19,9 +19,9 @@ jobs:
run: |
echo "result=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> "$GITHUB_OUTPUT"
- name: Sanity checks
if: ${{ github.event_name == 'workflow_dispatch' && steps.extract-branch-name.outputs.result != 'develop' }}
if: ${{ github.event_name == 'workflow_dispatch' && steps.extract-branch-name.outputs.result != 'OBT-Release' }}
run: |
echo "::error::this workflow can only be run on the \"develop\" branch"
echo "::error::this workflow can only be run on the \"OBT-Release\" branch"
exit 1
- name: Get latest nightly-tagged commit
@@ -53,7 +53,7 @@ jobs:
const ref = await github.rest.git.getRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: "heads/develop",
ref: "heads/OBT-Release",
});
return ref.data.object.sha;
@@ -81,7 +81,7 @@ jobs:
publish-release:
needs: [check-if-release-needed]
if: ${{ needs.check-if-release-needed.outputs.has-new-commits == 'true' }}
if: true
uses: ./.github/workflows/publish-release.yml
with:
target: ${{ needs.check-if-release-needed.outputs.latest-commit-sha }}

View File

@@ -4,20 +4,6 @@ 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:
@@ -37,13 +23,13 @@ on:
env:
CI_DIR: 2049ef39-42a2-46d2-b513-ee6d2e3a7b15
RELEASES: |
windows:server:Windows/Server
linux:server:Linux/Server
mac:server:Mac/Server
windows:client:Windows/Client
windows:server:Windows/Server
linux:client:Linux/Client
linux:server:Linux/Server
mac:client:Mac/Client/Barotrauma.app/Contents/MacOS
ARCHIVE_BASE_NAME: luacsforbarotraumaEP
mac:server:Mac/Server
ARCHIVE_BASE_NAME: luacsforbarotrauma
# XXX: these file names are subject to shell expansion.
# Be careful when using special characters.
ARCHIVE_FILES_SERVER: |
@@ -78,19 +64,15 @@ env:
Mono.Cecil.Mdb.dll
Mono.Cecil.Pdb.dll
Mono.Cecil.Rocks.dll
LightInject.dll
OneOf.dll
FluentResults.dll
Basic.Reference.Assemblies.Net80.dll
Microsoft.Extensions.Logging.Abstractions.dll
Microsoft.Toolkit.Diagnostics.dll
Microsoft.CodeAnalysis.CSharp.Scripting.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_*
LocalMods/LuaCsForBarotrauma
Lua
jobs:
build:

1
.gitignore vendored
View File

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

View File

@@ -29,7 +29,7 @@ namespace Barotrauma
}
}
}
else if (SelectedAiTarget?.Entity != null && AttackLimb != null)
else if (SelectedAiTarget?.Entity != null)
{
Vector2 targetPos = SelectedAiTarget.Entity.DrawPosition;
if (State == AIState.Attack)
@@ -37,16 +37,15 @@ namespace Barotrauma
targetPos = attackWorldPos;
}
targetPos.Y = -targetPos.Y;
Vector2 attackLimbPos = AttackLimb.DrawPosition;
attackLimbPos.Y = -attackLimbPos.Y;
GUI.DrawLine(spriteBatch, attackLimbPos, targetPos, GUIStyle.Red * 0.75f, 0, 4);
GUI.DrawLine(spriteBatch, pos, targetPos, GUIStyle.Red * 0.5f, 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, attackLimbPos, wallTargetPos, Color.Orange * 0.75f, 0, 5);
GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 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,8 +23,6 @@ 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);
@@ -35,14 +33,14 @@ namespace Barotrauma
currentOrders.Sort((x, y) => y.ManualPriority.CompareTo(x.ManualPriority));
for (int i = 0; i < currentOrders.Count; i++)
{
stringDrawPos += spacing;
stringDrawPos += new Vector2(0, 20);
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 += spacing;
stringDrawPos += new Vector2(0, 20);
GUI.DrawString(spriteBatch, stringDrawPos - textOffset, $"Waiting... {ObjectiveManager.WaitTimer.FormatZeroDecimal()}", Color.White, Color.Black);
}
var currentObjective = ObjectiveManager.CurrentObjective;
@@ -51,19 +49,19 @@ namespace Barotrauma
int offset = currentOrder != null ? 20 + ((ObjectiveManager.CurrentOrders.Count - 1) * 20) : 0;
if (currentOrder == null || currentOrder.Priority <= 0)
{
stringDrawPos += spacing;
stringDrawPos += new Vector2(0, 20);
GUI.DrawString(spriteBatch, stringDrawPos, $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
}
var subObjective = currentObjective.CurrentSubObjective;
if (subObjective != null)
{
stringDrawPos += spacing;
stringDrawPos += new Vector2(0, 20);
GUI.DrawString(spriteBatch, stringDrawPos, $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
}
var activeObjective = ObjectiveManager.GetActiveObjective();
if (activeObjective != null)
{
stringDrawPos += spacing;
stringDrawPos += new Vector2(0, 20);
GUI.DrawString(spriteBatch, stringDrawPos, $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
}
if (currentObjective is AIObjectiveCombat
@@ -87,12 +85,12 @@ namespace Barotrauma
}
}
Vector2 objectiveStringDrawPos = stringDrawPos + new Vector2(120, spacing.Y * 2);
Vector2 objectiveStringDrawPos = stringDrawPos + new Vector2(120, 40);
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 += spacing * 0.8f;
objectiveStringDrawPos += new Vector2(0, 18);
}
if (steeringManager is IndoorsSteeringManager pathSteering)

View File

@@ -547,7 +547,7 @@ namespace Barotrauma
}
}
public void Draw(SpriteBatch spriteBatch, Camera cam, bool onlyDrawSeveredLimbs)
public void Draw(SpriteBatch spriteBatch, Camera cam)
{
if (simplePhysicsEnabled) { return; }
@@ -573,12 +573,8 @@ namespace Barotrauma
{
foreach (Limb limb in limbs) { limb.ActiveSprite.Depth += depthOffset; }
}
for (int i = 0; i < inversedLimbDrawOrder.Length; i++)
for (int i = 0; i < limbs.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) { return; }
AnimController.Draw(spriteBatch, cam, onlyDrawSeveredLimbs: InvisibleTimer > 0.0f);
if (!Enabled || InvisibleTimer > 0.0f) { return; }
AnimController.Draw(spriteBatch, cam);
}
public void DrawHUD(SpriteBatch spriteBatch, Camera cam, bool drawHealth = true)

View File

@@ -4,7 +4,6 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
using Barotrauma.Items.Components;
using System.Linq;
namespace Barotrauma;
@@ -107,17 +106,12 @@ public static class InteractionLabelManager
}
RectangleF textRect = GetLabelRect(interactableInRange, cam);
var existingLabel = labels.FirstOrDefault(l => l.Item == interactableInRange);
if (existingLabel == null)
if (labels.None(l => l.Item == interactableInRange))
{
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);
@@ -133,11 +127,7 @@ public static class InteractionLabelManager
private static RectangleF GetLabelRect(Item item, Camera cam)
{
// create rectangle for overlap prevention
string nameText = RichString.Rich(item.Prefab.Name).SanitizedValue;
var font = GUIStyle.SubHeadingFont.GetFontForStr(nameText)!;
Vector2 itemTextSizeScreen = font.MeasureString(nameText) * LabelScale;
Vector2 itemTextSizeScreen = GUIStyle.SubHeadingFont.MeasureString(RichString.Rich(item.Prefab.Name).SanitizedValue) * 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,6 +340,10 @@ 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())
@@ -627,8 +631,6 @@ 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)
@@ -732,8 +734,7 @@ namespace Barotrauma
if (spriteParams == null || Alpha <= 0) { return; }
float burn = spriteParams.IgnoreTint ? 0 : burnOverLayStrength;
float brightness = Math.Max(1.0f - burn, 0.2f);
Color baseColor = randomColor ?? spriteParams.Color;
Color tintedColor = baseColor;
Color tintedColor = spriteParams.Color;
if (!spriteParams.IgnoreTint)
{
tintedColor = tintedColor.Multiply(ragdoll.RagdollParams.Color);
@@ -751,7 +752,7 @@ namespace Barotrauma
}
}
Color color = new Color(tintedColor.Multiply(brightness), tintedColor.A);
Color colorWithoutTint = new Color(baseColor.Multiply(brightness), baseColor.A);
Color colorWithoutTint = new Color(spriteParams.Color.Multiply(brightness), spriteParams.Color.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.DefaultDisplayName.Value);
GUITextBox box = GUI.CreateTextBoxWithPlaceholder(new RectTransform(new Vector2(0.6f, 1f), connLayout.RectTransform), text: found ? labelOverride : string.Empty, conn.Connection.DisplayName.Value);
box.MaxTextLength = MaxConnectionLabelLength;
textBoxes.Add(conn.Name, box);

View File

@@ -16,7 +16,6 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Barotrauma.LuaCs.Events;
using static Barotrauma.FabricationRecipe;
namespace Barotrauma
@@ -223,6 +222,8 @@ namespace Barotrauma
private static bool IsCommandPermitted(Identifier command, GameClient client)
{
if (GameMain.LuaCs.Game.IsCustomCommandPermitted(command)) { return true; }
switch (command.Value.ToLowerInvariant())
{
case "kick":
@@ -658,6 +659,14 @@ 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() }));
@@ -3544,11 +3553,6 @@ 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) =>
{
@@ -3582,14 +3586,6 @@ namespace Barotrauma
}
}));*/
AssignOnClientExecute(
"ShowServerPerf",
(string[] args) =>
{
GameMain.Client?.SendConsoleCommand("ShowServerPerf");
}
);
AssignOnClientExecute(
"giveperm",
(string[] args) =>
@@ -4227,8 +4223,51 @@ 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,14 +277,6 @@ 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;
@@ -348,8 +340,7 @@ namespace Barotrauma
if (speaker?.Info != null && drawChathead)
{
// chathead
int chatHeadWidth = (int)(content.RectTransform.Rect.Width * 0.15f);
new GUICustomComponent(new RectTransform(new Point(chatHeadWidth, chatHeadWidth), content.RectTransform, isFixedSize: true), onDraw: (sb, customComponent) =>
new GUICustomComponent(new RectTransform(new Vector2(0.15f, 0.8f), content.RectTransform), onDraw: (sb, customComponent) =>
{
speaker.Info.DrawIcon(sb, customComponent.Rect.Center.ToVector2(), customComponent.Rect.Size.ToVector2());
});
@@ -391,7 +382,7 @@ namespace Barotrauma
}
textContent.RectTransform.MinSize = new Point(0, textContent.Children.Sum(c => c.Rect.Height + textContent.AbsoluteSpacing) + GUI.IntScale(16));
content.RectTransform.MinSize = textContent.RectTransform.MinSize;
content.RectTransform.MinSize = new Point(0, content.Children.Sum(c => c.Rect.Height));
// 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,9 +61,17 @@ namespace Barotrauma
{
Item.ReadSpawnData(msg);
}
if (character.AIController is EnemyAIController enemyAi && character.Submarine is Submarine ownSub)
if (character.Submarine != null && character.AIController is EnemyAIController enemyAi)
{
enemyAi.SetUnattackableSubmarines(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);
}
}
}
}
if (characters.Contains(null))

View File

@@ -1,8 +0,0 @@
#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(CampaignMode.TransitionType.None))
if (DetermineCompleted())
{
HandleMessage(ref allRetrievedMessage);
}

View File

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

View File

@@ -1435,15 +1435,8 @@ namespace Barotrauma
Uri baseAddress = new Uri(url);
Uri remoteDirectory = new Uri(baseAddress, ".");
string remoteFileName = Path.GetFileName(baseAddress.LocalPath);
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;
}
IRestClient client = new RestClient(remoteDirectory);
var response = client.Execute(new RestRequest(remoteFileName, Method.GET));
if (response.ResponseStatus != ResponseStatus.Completed) { return null; }
if (response.StatusCode != HttpStatusCode.OK) { return null; }

View File

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

View File

@@ -710,24 +710,19 @@ namespace Barotrauma
if (listBox == pendingList || listBox == crewList)
{
//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)
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)
{
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
};
}
Enabled = CanHire(characterInfo),
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,6 +244,8 @@ 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();
@@ -295,8 +297,6 @@ namespace Barotrauma
MainThread = Thread.CurrentThread;
Window.FileDropped += OnFileDropped;
LuaCsSetup.Instance.GetType();
}
public static void ExecuteAfterContentFinishedLoading(Action action)
@@ -636,6 +636,9 @@ namespace Barotrauma
HasLoaded = true;
log("LOADING COROUTINE FINISHED");
#if CLIENT
LuaCsInstaller.CheckUpdate();
#endif
contentLoaded = true;
while (postContentLoadActions.TryDequeue(out Action action))
@@ -983,6 +986,8 @@ namespace Barotrauma
Screen.Selected.AddToGUIUpdateList();
LuaCsLogger.AddToGUIUpdateList();
Client?.AddToGUIUpdateList();
SubmarinePreview.AddToGUIUpdateList();
@@ -1049,6 +1054,8 @@ namespace Barotrauma
SoundManager?.Update();
GameMain.LuaCs.Update();
Timing.Accumulator -= Timing.Step;
updateCount++;
@@ -1230,6 +1237,8 @@ namespace Barotrauma
GUIMessageBox.CloseAll();
MainMenuScreen.Select();
GameSession = null;
GameMain.LuaCs.Stop();
}
public void ShowBugReporter()
@@ -1292,18 +1301,6 @@ 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,6 +179,8 @@ namespace Barotrauma.Tutorials
public void Start()
{
GameMain.LuaCs.CheckInitialize();
GameMain.Instance.ShowLoading(Loading());
ObjectiveManager.ResetObjectives();

View File

@@ -487,21 +487,7 @@ namespace Barotrauma.Items.Components
return 0.0f;
}
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)
public virtual bool ShouldDrawHUD(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 (targetSlotIndex < containedSpriteDepths.Length)
if (i < containedSpriteDepths.Length)
{
containedSpriteDepth = containedSpriteDepths[targetSlotIndex];
containedSpriteDepth = containedSpriteDepths[i];
}
containedSpriteDepth = itemDepth + (containedSpriteDepth - (item.Sprite?.Depth ?? item.SpriteDepth)) / 10000.0f;

View File

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

View File

@@ -8,30 +8,6 @@ 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);
@@ -93,33 +69,21 @@ 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;
// 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;
}
user = newUser;
IsActive = true;
}
}

View File

@@ -434,13 +434,18 @@ 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 };
@@ -452,7 +457,7 @@ namespace Barotrauma.Items.Components
itemIcon, scaleToFit: true)
{
Color = itemIcon == fi.TargetItem.Sprite ? fi.TargetItem.SpriteColor : fi.TargetItem.InventoryIconColor,
CanBeFocused = false
ToolTip = recipeTooltip
};
}
@@ -461,7 +466,7 @@ namespace Barotrauma.Items.Components
{
Padding = Vector4.Zero,
AutoScaleVertical = true,
CanBeFocused = false
ToolTip = recipeTooltip
};
new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), frame.RectTransform, Anchor.BottomRight),
@@ -473,20 +478,6 @@ 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)
@@ -936,24 +927,16 @@ namespace Barotrauma.Items.Components
}
}
if (recipe.RequiresRecipe)
if (recipe.RequiresRecipe && recipe.HideIfNoRecipe)
{
if (recipe.HideIfNoRecipe)
if (Character.Controlled != null)
{
bool anyOneHasRecipe = AnyOneHasRecipeForItem(Character.Controlled, recipe.TargetItem);
if (Character.Controlled != null)
if (!AnyOneHasRecipeForItem(Character.Controlled, recipe.TargetItem))
{
if (!anyOneHasRecipe)
{
child.Visible = false;
continue;
}
child.Visible = false;
continue;
}
}
else
{
SetRecipeTooltip(child, recipe);
}
}
child.Visible =
@@ -1164,16 +1147,7 @@ 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));
if (newString.Length > 4)
{
description.Text = newString.Substring(0, newString.Length - 4) + "...";
}
else
{
description.Text = newString + "...";
}
description.Text = newString.Substring(0, newString.Length - 4) + "...";
description.CalculateHeightFromText();
description.ToolTip = richDescription;
}

View File

@@ -443,7 +443,6 @@ 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,22 +575,6 @@ 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
{
@@ -839,20 +823,18 @@ 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 > sonarSoundRange * sonarSoundRange * 2) { continue; }
if (distSqr > t.SoundRange * t.SoundRange * 2) { continue; }
float dist = (float)Math.Sqrt(distSqr);
if (dist > prevPassivePingRadius * Range && dist <= passivePingRadius * Range && Rand.Int(sonarBlips.Count) < 500)
{
Ping(t.WorldPosition, transducerCenter,
sonarSoundRange * DisplayScale, 0, DisplayScale, range,
t.SoundRange * 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(sonarSoundRange / 2000, 1.0f, 5.0f)));
sonarBlips.Add(new SonarBlip(t.WorldPosition, fadeTimer: 1.0f, scale: MathHelper.Clamp(t.SoundRange / 2000, 1.0f, 5.0f)));
}
}
}
@@ -995,9 +977,7 @@ namespace Barotrauma.Items.Components
if (aiTarget.InDetectable) { continue; }
if (aiTarget.SonarLabel.IsNullOrEmpty() || aiTarget.SoundRange <= 0.0f) { continue; }
float sonarSoundRange = aiTarget.SoundRange * aiTarget.SoundRangeOnSonarMultiplier;
if (Vector2.DistanceSquared(aiTarget.WorldPosition, transducerCenter) < sonarSoundRange * sonarSoundRange)
if (Vector2.DistanceSquared(aiTarget.WorldPosition, transducerCenter) < aiTarget.SoundRange * aiTarget.SoundRange)
{
DrawMarker(spriteBatch,
aiTarget.SonarLabel.Value,

View File

@@ -58,7 +58,7 @@ namespace Barotrauma.Items.Components
get { return Vector2.Zero; }
}
protected override bool ShouldDrawHUDComponentSpecific(Character character)
public override bool ShouldDrawHUD(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
}
}
protected override bool ShouldDrawHUDComponentSpecific(Character character)
public override bool ShouldDrawHUD(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);
}
protected override bool ShouldDrawHUDComponentSpecific(Character character)
public override bool ShouldDrawHUD(Character character)
{
return character == Character.Controlled && character == user && (character.SelectedItem == item || character.SelectedSecondaryItem == item);
}

View File

@@ -287,24 +287,20 @@ namespace Barotrauma.Items.Components
texts.Add(target.CustomInteractHUDText);
textColors.Add(GUIStyle.Green);
}
if (equipper?.FocusedCharacter == target)
if (!target.IsIncapacitated && target.IsPet)
{
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);
}
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.IsUnconscious)

View File

@@ -1597,8 +1597,7 @@ namespace Barotrauma
{
if (DraggingSlot == null || (!DraggingSlot.MouseOn()))
{
Item firstDraggingItem = DraggingItems.First();
Sprite sprite = firstDraggingItem.OverrideInventorySprite ?? firstDraggingItem.Prefab.InventoryIcon ?? firstDraggingItem.Sprite;
Sprite sprite = DraggingItems.First().Prefab.InventoryIcon ?? DraggingItems.First().Sprite;
int iconSize = (int)(64 * GUI.Scale);
float scale = Math.Min(Math.Min(iconSize / sprite.size.X, iconSize / sprite.size.Y), 1.5f);
@@ -1855,7 +1854,7 @@ namespace Barotrauma
if (item != null && drawItem)
{
Sprite sprite = item.OverrideInventorySprite ?? item.Prefab.InventoryIcon ?? item.Sprite;
Sprite sprite = 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 = MathHelper.Clamp(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.0f, 0.999f);
float d = Math.Min(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 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 = MathHelper.Clamp(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.0f, 0.999f);
float d = Math.Min(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 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,12 +885,7 @@ namespace Barotrauma
Spacing = (int)(25 * GUI.Scale)
};
var itemEditor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true,
titleFont: GUIStyle.LargeFont,
dimOutDefaultValues: false)
{
UserData = this
};
var itemEditor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUIStyle.LargeFont) { UserData = this };
activeEditors.Add(itemEditor);
itemEditor.Children.First().Color = Color.Black * 0.7f;
if (!inGame)
@@ -1050,12 +1045,7 @@ 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,
dimOutDefaultValues: false)
{
UserData = ic
};
var componentEditor = new SerializableEntityEditor(listBox.Content.RectTransform, ic, inGame, showName: !inGame, titleFont: GUIStyle.SubHeadingFont) { UserData = ic };
componentEditor.Children.First().Color = Color.Black * 0.7f;
activeEditors.Add(componentEditor);
@@ -1074,12 +1064,7 @@ namespace Barotrauma
requiredItems.Add(relatedItem);
}
}
//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);
}
requiredItems.AddRange(ic.DisabledRequiredItems);
foreach (RelatedItem relatedItem in requiredItems)
{
@@ -1641,16 +1626,12 @@ namespace Barotrauma
activeComponents.Clear();
activeComponents.AddRange(components);
Controller controller = GetComponent<Controller>();
if (controller == null || controller.User != Character.Controlled || !controller.HideAllItemComponentHUDs)
foreach (MapEntity entity in linkedTo)
{
foreach (MapEntity entity in linkedTo)
if (Prefab.IsLinkAllowed(entity.Prefab) && entity is Item i)
{
if (Prefab.IsLinkAllowed(entity.Prefab) && entity is Item i)
{
if (!i.DisplaySideBySideWhenLinked) { continue; }
activeComponents.AddRange(i.components);
}
if (!i.DisplaySideBySideWhenLinked) { continue; }
activeComponents.AddRange(i.components);
}
}
@@ -1720,9 +1701,7 @@ namespace Barotrauma
foreach (Character otherCharacter in Character.CharacterList)
{
if (otherCharacter != character &&
otherCharacter.SelectedItem == this &&
// Prevent the in use message from being shown if a character is, for example, inside the deconstructor
!otherCharacter.IsAttachedToController())
otherCharacter.SelectedItem == this)
{
ItemInUseWarning.Visible = true;
if (mergedHUDRect.Width > GameMain.GraphicsWidth / 2) { mergedHUDRect.Inflate(-GameMain.GraphicsWidth / 4, 0); }
@@ -1772,11 +1751,6 @@ namespace Barotrauma
}
}
public void ClearActiveHUDs()
{
activeHUDs.Clear();
}
readonly List<ColoredText> texts = new();
public List<ColoredText> GetHUDTexts(Character character, bool recreateHudTexts = true)
{

View File

@@ -166,14 +166,6 @@ 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

@@ -1,33 +0,0 @@
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

@@ -1,9 +0,0 @@
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

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

View File

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

View File

@@ -1,252 +0,0 @@
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

@@ -1,17 +0,0 @@
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

@@ -1,143 +0,0 @@
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,7 +8,115 @@ 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

@@ -0,0 +1,143 @@
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

@@ -0,0 +1,115 @@
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,184 +1,75 @@
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.Collections.Generic;
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 PromptCSharpMods(Action<bool> onSelection, bool joiningServer)
{
public void AddToGUIUpdateList()
{
ImmutableArray<ContentPackage> contentPackages = PackageManagementService.GetLoadedUnrestrictedPackages()
.Where(p => p.Name != PackageName)
.ToImmutableArray();
if (_csRunPolicy?.Value is "Enabled")
if (!GameMain.LuaCs.Config.DisableErrorGUIOverlay)
{
IsCsEnabledForSession = true;
onSelection(true);
return;
LuaCsLogger.AddToGUIUpdateList();
}
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;
}
};
}
private void SetupServicesProviderClient(IServicesProvider serviceProvider)
public void CheckInitialize()
{
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(() =>
List<ContentPackage> csharpMods = new List<ContentPackage>();
foreach (ContentPackage cp in ContentPackageManager.EnabledPackages.All)
{
switch (screen)
if (Directory.Exists(cp.Dir + "/CSharp") || Directory.Exists(cp.Dir + "/bin"))
{
// 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;
csharpMods.Add(cp);
}
}, delay: 0f); // min is one tick delay.
}
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;
};
}
}
}

View File

@@ -1,22 +0,0 @@
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

@@ -1,50 +0,0 @@
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

@@ -1,44 +0,0 @@
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

@@ -1,163 +0,0 @@
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

@@ -1,239 +0,0 @@
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

@@ -1,350 +0,0 @@
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

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

View File

@@ -1,13 +0,0 @@
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

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

View File

@@ -1,75 +0,0 @@
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

@@ -1,57 +0,0 @@
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

@@ -1,22 +0,0 @@
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

@@ -1,458 +0,0 @@
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

@@ -1,42 +0,0 @@
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

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

View File

@@ -296,9 +296,13 @@ namespace Barotrauma.Lights
light.Priority = lightPriority(range, light);
activeLights.Add(light);
int i = 0;
while (i < activeLights.Count && light.Priority < activeLights[i].Priority)
{
i++;
}
activeLights.Insert(i, light);
}
activeLights.Sort(static (a, b) => b.Priority.CompareTo(a.Priority));
ActiveLightCount = activeLights.Count;
float lightPriority(float range, LightSource light)
@@ -328,7 +332,7 @@ namespace Barotrauma.Lights
activeLights.Remove(activeShadowCastingLights[i]);
}
}
activeLights.Sort(static (l1, l2) => l1.LastRecalculationTime.CompareTo(l2.LastRecalculationTime));
activeLights.Sort((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,14 +50,8 @@ 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 => MathHelper.ToDegrees(RotationRad);
set => RotationRad = MathHelper.ToRadians(value);
}
public float Rotation { get; set; }
[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.")]
@@ -320,10 +314,6 @@ 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; }
@@ -332,7 +322,7 @@ namespace Barotrauma.Lights
if (Math.Abs(value - rotation) < 0.001f) { return; }
rotation = value;
RefreshDirection();
dir = new Vector2(MathF.Cos(rotation), -MathF.Sin(rotation));
if (Math.Abs(rotation - prevCalculatedRotation) < RotationRecalculationThreshold && vertices != null)
{
@@ -496,9 +486,6 @@ namespace Barotrauma.Lights
break;
}
}
//make sure the rotation defined in the parameters is taken into account
RefreshDirection();
NeedsRecalculation = true;
}
public LightSource(LightSourceParams lightSourceParams)
@@ -510,9 +497,6 @@ 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)
@@ -527,14 +511,6 @@ 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;
@@ -797,6 +773,9 @@ 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;
@@ -811,11 +790,8 @@ namespace Barotrauma.Lights
origin *= TextureRange;
//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;
drawOffset.X = -origin.X * cosAngle - origin.Y * sinAngle;
drawOffset.Y = origin.X * sinAngle + origin.Y * cosAngle;
}
//add a square-shaped boundary to make sure we've got something to construct the triangles from
@@ -1560,6 +1536,7 @@ 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,12 +193,7 @@ namespace Barotrauma
{
CanTakeKeyBoardFocus = false
};
var editor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true,
titleFont: GUIStyle.LargeFont,
dimOutDefaultValues: false)
{
UserData = this
};
var editor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUIStyle.LargeFont) { UserData = this };
if (editor.Fields.TryGetValue(nameof(Scale).ToIdentifier(), out GUIComponent[] scaleFields) &&
scaleFields.FirstOrDefault() is GUINumberInput scaleInput)

View File

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

View File

@@ -30,13 +30,6 @@ 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,6 +495,8 @@ 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,9 +49,6 @@ 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;
@@ -534,6 +531,23 @@ 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);
@@ -689,6 +703,8 @@ namespace Barotrauma
#region Selection
public override void Select()
{
GameMain.LuaCs.Stop();
ResetModUpdateButton();
if (WorkshopItemsToUpdate.Any())
@@ -1028,7 +1044,7 @@ namespace Barotrauma
else
{
StartServer();
}
}
}
private IEnumerable<CoroutineStatus> WaitForSubmarineHashCalculations(GUIMessageBox messageBox)
@@ -1079,7 +1095,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()
};
@@ -1118,13 +1134,6 @@ 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
{
@@ -1305,6 +1314,8 @@ 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);
@@ -1320,6 +1331,8 @@ namespace Barotrauma
{
if (string.IsNullOrWhiteSpace(path)) return;
GameMain.LuaCs.CheckInitialize();
try
{
CampaignDataPath dataPath =
@@ -1379,7 +1392,7 @@ namespace Barotrauma
}
int maxPlayers = Math.Clamp(maxPlayersElement, min: 1, max: NetConfig.MaxPlayers);
var karmaEnabled = serverSettings.GetAttributeBool("karmaenabled", false);
var karmaEnabled = serverSettings.GetAttributeBool("karmaenabled", true);
var selectedPlayStyle = serverSettings.GetAttributeEnum("playstyle", PlayStyle.Casual);
Vector2 textLabelSize = new Vector2(1.0f, 0.05f);
@@ -1590,18 +1603,10 @@ 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
@@ -1690,8 +1695,8 @@ namespace Barotrauma
if (string.IsNullOrEmpty(remoteContentUrl)) { return; }
try
{
var client = RestFactory.CreateClient(remoteContentUrl);
var request = RestFactory.CreateRequest("MenuContent.xml");
var client = new RestClient(remoteContentUrl);
var request = new RestRequest("MenuContent.xml", Method.GET);
TaskPool.Add("RequestMainMenuRemoteContent", client.ExecuteAsync(request),
RemoteContentReceived);
}
@@ -1712,17 +1717,12 @@ 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. " +
$"The master server might be temporarily unavailable (HTTP error: {remoteContentResponse.StatusCode})");
"There may be an issue with your internet connection, or the master server might be temporarily unavailable " +
$"(error code: {remoteContentResponse.StatusCode})");
return;
}
string xml = remoteContentResponse.Content;

View File

@@ -5,7 +5,6 @@ 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;
@@ -119,6 +118,7 @@ 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,6 +379,7 @@ namespace Barotrauma
}
GameMain.NetLobbyScreen.UpdateSubList(GameMain.NetLobbyScreen.SubList, GameMain.Client.ServerSubmarines);
GameMain.NetLobbyScreen.Select();
GameMain.LuaCs.CheckInitialize();
}
}
else if (GameMain.Client.FileReceiver.ActiveTransfers.None())
@@ -399,7 +400,7 @@ namespace Barotrauma
string dir = path.RemoveFromEnd(ModReceiver.Extension, StringComparison.OrdinalIgnoreCase);
SaveUtil.DecompressToDirectory(path, dir);
var result = ContentPackage.TryLoad(Path.Combine(dir, ContentPackage.FileListFileName).CleanUpPathCrossPlatform());
var result = ContentPackage.TryLoad(Path.Combine(dir, ContentPackage.FileListFileName));
if (!result.TryUnwrapSuccess(out var newPackage))
{

View File

@@ -12,7 +12,6 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using Barotrauma.LuaCs.Events;
using Barotrauma.Sounds;
namespace Barotrauma
@@ -734,8 +733,6 @@ 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)
@@ -1534,6 +1531,8 @@ namespace Barotrauma
public override void Select()
{
Select(enableAutoSave: true);
GameMain.LuaCs.CheckInitialize();
}
public void Select(bool enableAutoSave = true)
@@ -3256,20 +3255,6 @@ 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),
@@ -3290,26 +3275,28 @@ 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");
if (ownerPkg == null && MainSub?.Info != null) { ownerPkg = GetLocalPackageThatOwnsSub(MainSub.Info); }
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); }
foreach (var p in ContentPackageManager.LocalPackages)
{
var packageListItem = addItemToPackageToSaveList(p.Name, p);
@@ -3864,10 +3851,6 @@ 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)
{
@@ -4505,7 +4488,6 @@ namespace Barotrauma
public void ReconstructLayers()
{
Dictionary<string, LayerData> previousLayers = Layers.ToDictionary();
ClearLayers();
foreach (MapEntity entity in MapEntity.MapEntityList)
{
@@ -4514,13 +4496,6 @@ 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,8 +26,6 @@ namespace Barotrauma
public static DateTime NextCommandPush;
public static Tuple<SerializableProperty, PropertyCommand> CommandBuffer;
private bool dimOutDefaultValues;
private bool isReadonly;
public bool Readonly
{
@@ -318,17 +316,16 @@ namespace Barotrauma
}
}
public SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, bool inGame, bool showName, string style = "", int elementHeight = 24, GUIFont titleFont = null, bool dimOutDefaultValues = true)
public SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, bool inGame, bool showName, string style = "", int elementHeight = 24, GUIFont titleFont = null)
: 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, dimOutDefaultValues)
: SerializableProperty.GetProperties<Editable>(entity).Where(p => p.GetAttribute<ConditionallyEditable>()?.IsEditable(entity) ?? true), showName, style, elementHeight, titleFont)
{
}
public SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, IEnumerable<SerializableProperty> properties, bool showName, string style = "", int elementHeight = 24, GUIFont titleFont = null, bool dimOutDefaultValues = true)
public SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, IEnumerable<SerializableProperty> properties, bool showName, string style = "", int elementHeight = 24, GUIFont titleFont = null)
: 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");
@@ -526,67 +523,9 @@ 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>();
@@ -625,7 +564,6 @@ namespace Barotrauma
tickBox.Selected = propertyValue;
tickBox.Flash(Color.Red);
}
UpdateTextColors(property, entity, tickBox);
return true;
}
};
@@ -673,7 +611,6 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
refresh += () =>
{
@@ -717,7 +654,6 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
HandleSetterValueTampering(numberInput, () => property.GetFloatValue(entity));
@@ -775,7 +711,6 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
return true;
};
refresh += () =>
@@ -894,7 +829,6 @@ 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();
@@ -1039,7 +973,6 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
fields[i] = numberInput;
}
@@ -1113,7 +1046,6 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
HandleSetterValueTampering(numberInput, () =>
{
@@ -1194,7 +1126,6 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
fields[i] = numberInput;
}
@@ -1275,7 +1206,6 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
fields[i] = numberInput;
}
@@ -1369,7 +1299,6 @@ 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;
@@ -1444,7 +1373,6 @@ namespace Barotrauma
{
TrySendNetworkUpdate(entity, property);
}
UpdateTextColors(property, entity, frame);
};
fields[i] = numberInput;
}
@@ -1509,7 +1437,6 @@ 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;
public readonly GUIFrame mainFrame;
private readonly GUIFrame mainFrame;
public readonly GUILayoutGroup tabber;
public readonly GUIFrame contentFrame;
private readonly GUILayoutGroup tabber;
private readonly GUIFrame contentFrame;
private readonly GUILayoutGroup bottom;
public readonly WorkshopMenu WorkshopMenu;
@@ -103,7 +103,7 @@ namespace Barotrauma
newContent.Visible = true;
}
public readonly Dictionary<Tab, (GUIButton Button, GUIFrame Content)> tabContents;
private readonly Dictionary<Tab, (GUIButton Button, GUIFrame Content)> tabContents;
public void SelectTab(Tab tab)
{
@@ -149,7 +149,7 @@ namespace Barotrauma
return content;
}
public static (GUILayoutGroup Left, GUILayoutGroup Right) CreateSidebars(GUIFrame parent, bool split = false)
private 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);
}
public static GUILayoutGroup CreateCenterLayout(GUIFrame parent)
private static GUILayoutGroup CreateCenterLayout(GUIFrame parent)
{
return new GUILayoutGroup(new RectTransform((0.5f, 1.0f), parent.RectTransform, Anchor.TopCenter, Pivot.TopCenter)) { ChildAnchor = Anchor.TopCenter };
}
public static RectTransform NewItemRectT(GUILayoutGroup parent)
private static RectTransform NewItemRectT(GUILayoutGroup parent)
=> new RectTransform((1.0f, 0.06f), parent.RectTransform, Anchor.CenterLeft);
public static void Spacer(GUILayoutGroup parent, float height = 0.03f)
private static void Spacer(GUILayoutGroup parent)
{
new GUIFrame(new RectTransform((1.0f, height), parent.RectTransform, Anchor.CenterLeft), style: null);
new GUIFrame(new RectTransform((1.0f, 0.03f), parent.RectTransform, Anchor.CenterLeft), style: null);
}
public static GUITextBlock Label(GUILayoutGroup parent, LocalizedString str, GUIFont font)
private static GUITextBlock Label(GUILayoutGroup parent, LocalizedString str, GUIFont font)
{
return new GUITextBlock(NewItemRectT(parent), str, font: font);
}
public static void DropdownEnum<T>(GUILayoutGroup parent, Func<T, LocalizedString> textFunc, Func<T, LocalizedString>? tooltipFunc, T currentValue,
private 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);
public static GUIDropDown Dropdown<T>(GUILayoutGroup parent, Func<T, LocalizedString> textFunc, Func<T, LocalizedString>? tooltipFunc, IReadOnlyList<T> values, T currentValue, Action<T> setter)
private 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;
}
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)
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)
{
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);
}
public static GUITickBox Tickbox(GUILayoutGroup parent, LocalizedString label, LocalizedString tooltip, bool currentValue, Action<bool> setter)
private 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
};
}
public string Percentage(float v) => ToolBox.GetFormattedPercentage(v);
private string Percentage(float v) => ToolBox.GetFormattedPercentage(v);
public static int Round(float v) => MathUtils.RoundToInt(v);
private static int Round(float v) => MathUtils.RoundToInt(v);
private void CreateGraphicsTab()
{
@@ -507,47 +507,6 @@ 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);
@@ -1006,4 +965,4 @@ namespace Barotrauma
GUI.SettingsMenuOpen = false;
}
}
}
}

View File

@@ -387,11 +387,13 @@ These will hide all servers that have a discord.gg link in their name or descrip
try
{
var client = RestFactory.CreateClient($"{remoteContentUrl}spamfilter");
client.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
var client = new RestClient($"{remoteContentUrl}spamfilter")
{
CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore)
};
client.AddDefaultHeader("Cache-Control", "no-cache");
client.AddDefaultHeader("Pragma", "no-cache");
var request = RestFactory.CreateRequest("serve_spamlist.php");
var request = new RestRequest("serve_spamlist.php", Method.GET);
TaskPool.Add("RequestGlobalSpamFilter", client.ExecuteAsync(request), RemoteContentReceived);
}
catch (Exception e)
@@ -408,18 +410,12 @@ 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. " +
$"The master server might be temporarily unavailable, HTTP status: {remoteContentResponse.StatusCode}");
"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})");
return;
}
string data = remoteContentResponse.Content;

View File

@@ -18,36 +18,18 @@ 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",
"mission",
"outpost",
"beacon station",
"wreck",
"ruin",
"weapons",
"medical",
"equipment",
"art",
"mission",
"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
@@ -131,14 +113,10 @@ namespace Barotrauma.Steam
string? thumbnailUrl = item.PreviewImageUrl;
if (thumbnailUrl.IsNullOrWhiteSpace()) { return null; }
var client = RestFactory.CreateClient(thumbnailUrl);
var request = RestFactory.CreateRequest(".");
var client = new RestClient(thumbnailUrl);
var request = new RestRequest(".", Method.GET);
IRestResponse response = await client.ExecuteAsync(request, cancellationToken);
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 })
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 = true,
ScrollBarEnabled = false,
ScrollBarVisible = false,
HideChildrenOutsideFrame = true,
HideChildrenOutsideFrame = false,
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.12.7.0</Version>
<Version>1.11.5.0</Version>
<Copyright>Copyright © FakeFish 2018-2024</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>
@@ -14,7 +14,6 @@
<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'">
@@ -216,5 +215,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.12.7.0</Version>
<Version>1.11.5.0</Version>
<Copyright>Copyright © FakeFish 2018-2024</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>
@@ -15,7 +15,6 @@
<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'">
@@ -221,6 +220,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.12.7.0</Version>
<Version>1.11.5.0</Version>
<Copyright>Copyright © FakeFish 2018-2024</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>
@@ -15,7 +15,6 @@
<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'">
@@ -248,5 +247,5 @@
<ItemGroup>
<None Include="../BarotraumaShared/Luatrauma.props" />
</ItemGroup>
<Import Project="../BarotraumaShared/LuatraumaBuild.props" />
</Project>

View File

@@ -1,2 +0,0 @@
<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,16 +6,14 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>1.12.7.0</Version>
<Version>1.11.5.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'">
@@ -55,9 +53,8 @@
<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" />
@@ -164,6 +161,4 @@
<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.12.7.0</Version>
<Version>1.11.5.0</Version>
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>
@@ -14,7 +14,6 @@
<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'">
@@ -167,6 +166,4 @@
<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 (!LuaCsSetup.Instance.Game.overrideTraitors)
if (!GameMain.LuaCs.Game.overrideTraitors)
{
GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("KilledByTraitorNotification"), owner, ChatMessageType.ServerMessageBoxInGame);
}

View File

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

View File

@@ -10,7 +10,6 @@ using System.Linq;
using System.Text;
using Barotrauma.Steam;
using Barotrauma.Extensions;
using Barotrauma.LuaCs.Events;
namespace Barotrauma
{
@@ -1288,6 +1287,41 @@ 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);
}));
#if DEBUG
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();
}));
#endif
commands.Add(new Command("randomizeseed", "randomizeseed: Toggles level seed randomization on/off.", (string[] args) =>
{
GameMain.Server.ServerSettings.RandomizeSeed = !GameMain.Server.ServerSettings.RandomizeSeed;
@@ -2737,17 +2771,10 @@ namespace Barotrauma
commands.Add(new Command("ShowServerPerf", "Immediately log server performance info in ServerMessage", (string[] args) =>
{
GameServer.Log(PerformanceMonitor.PM.ToString(), ServerLog.MessageType.ServerMessage);
GameServer.Log(PerformenceMonitor.PM.ToString(), ServerLog.MessageType.ServerMessage);
NewMessage(PerformenceMonitor.PM.ToString(), Color.Green);
}));
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,15 +79,6 @@ 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,7 +14,6 @@ using MoonSharp.Interpreter;
using System.Net;
using Barotrauma.Extensions;
using System.Threading.Tasks;
using Barotrauma.LuaCs.Events;
namespace Barotrauma
{
@@ -36,6 +35,8 @@ namespace Barotrauma
set { world = value; }
}
public static LuaCsSetup LuaCs;
public static GameServer Server;
public static NetworkMember NetworkMember
{
@@ -112,8 +113,6 @@ namespace Barotrauma
GameScreen = new GameScreen();
MainThread = Thread.CurrentThread;
LuaCsSetup.Instance.GetType();
}
public void Init()
@@ -132,6 +131,8 @@ namespace Barotrauma
NetLobbyScreen = new NetLobbyScreen();
CheckContentPackage();
LuaCs = new LuaCsSetup();
}
@@ -243,9 +244,6 @@ namespace Barotrauma
//handled in TryStartChildServerRelay
i += 2;
break;
case "-lenienthandshake":
NetConfig.UseLenientHandshake = true;
break;
}
}
@@ -332,7 +330,7 @@ namespace Barotrauma
Stopwatch performanceCounterTimer = Stopwatch.StartNew();
stopwatch = Stopwatch.StartNew();
PerformanceMonitor PM = new PerformanceMonitor();
PerformenceMonitor PM = new PerformenceMonitor();
long prevTicks = stopwatch.ElapsedTicks;
while (ShouldRun)
@@ -369,16 +367,12 @@ namespace Barotrauma
TaskPool.Update();
CoroutineManager.Update(paused: false, (float)Timing.Step);
GameMain.LuaCs.Update();
performanceCounterTimer.Stop();
if (LuaCsSetup.Instance.PerformanceCounterService.EnablePerformanceCounter)
if (GameMain.LuaCs.PerformanceCounter.EnablePerformanceCounter)
{
LuaCsSetup.Instance.PerformanceCounterService.AddElapsedTicks(new SimplePerformanceData("Update", performanceCounterTimer.ElapsedTicks));
GameMain.LuaCs.PerformanceCounter.UpdateElapsedTime = (double)performanceCounterTimer.ElapsedTicks / Stopwatch.Frequency;
}
if (LuaCsSetup.Instance.PerformanceCounter.EnablePerformanceCounter)
{
LuaCsSetup.Instance.PerformanceCounter.UpdateElapsedTime = (double)performanceCounterTimer.ElapsedTicks / Stopwatch.Frequency;
}
performanceCounterTimer.Reset();
Timing.Accumulator -= Timing.Step;
@@ -432,7 +426,7 @@ namespace Barotrauma
}
}
PerformanceMonitor.PM.Dispose();
PerformenceMonitor.PM.Dispose();
stopwatch.Stop();
@@ -465,17 +459,7 @@ namespace Barotrauma
public void Exit()
{
ShouldRun = false;
try
{
if (LuaCsSetup.Instance is not null)
{
LuaCsSetup.Instance.Dispose();
}
}
catch (Exception e)
{
DebugConsole.ThrowError($"Error while disposing of LuaCsForBarotrauma: {e.Message} | {e.StackTrace}");
}
GameMain.LuaCs.Stop();
}
}
}

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 || User.Removed ? (ushort)0 : User.ID);
msg.WriteUInt16(user == null ? (ushort)0 : user.ID);
}
}
}

View File

@@ -16,11 +16,8 @@ namespace Barotrauma.Items.Components
public void ServerEventRead(IReadMessage msg, Client c)
{
if (c.Character == null) { return; }
FixActions requestedFixAction = (FixActions)msg.ReadRangedInteger(0, 2);
bool QTESuccess = msg.ReadBoolean();
if (!item.CanClientAccess(c) || !HasRequiredItems(c.Character, addMessage: false)) { return; }
var requestedFixAction = (FixActions)msg.ReadRangedInteger(0, 2);
var QTESuccess = msg.ReadBoolean();
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(sender.Character);
droppedItem.Drop(null);
droppedItem.PreviousParentInventory = previousInventory;
var previousCharacterInventory = prevOwner switch
@@ -188,18 +188,9 @@ namespace Barotrauma
if (holdable != null && !holdable.CanBeDeattached()) { continue; }
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;
}
bool itemAccessDenied = !prevItems.Contains(item) && !itemAccessibility[item] &&
(sender.Character == null || item.PreviousParentInventory == null || !sender.Character.CanAccessInventory(item.PreviousParentInventory));
//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,6 +12,8 @@ namespace Barotrauma
{
private CoroutineHandle logPropertyChangeCoroutine;
public Inventory PreviousParentInventory;
public override Sprite Sprite
{
get { return base.Prefab?.Sprite; }

View File

@@ -0,0 +1,107 @@
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,7 +2,6 @@
using System;
using System.IO;
using System.Linq;
using Barotrauma.LuaCs;
namespace Barotrauma
{
@@ -10,11 +9,11 @@ namespace Barotrauma
{
public static void Install()
{
ContentPackage luaPackage = LuaCsSetup.GetLuaCsPackage();
ContentPackage luaPackage = LuaCsSetup.GetPackage(LuaCsSetup.LuaForBarotraumaId);
if (luaPackage == null)
{
GameMain.Server.SendChatMessage("Couldn't find the ProjectEP package.", ChatMessageType.ServerMessageBox);
GameMain.Server.SendChatMessage("Couldn't find the LuaCs For Barotrauma package.", ChatMessageType.ServerMessageBox);
return;
}
@@ -46,13 +45,15 @@ 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 ProjectEP installed. ${e}", LuaCsMessageOrigin.LuaCs);
LuaCsLogger.LogError($"Unauthorized file access exception. This usually means you already have LuaCs installed. ${e}", LuaCsMessageOrigin.LuaCs);
return;
}
@@ -63,7 +64,7 @@ namespace Barotrauma
return;
}
GameMain.Server.SendChatMessage("Client-Side ProjectEP installed, restart your game to apply changes.", ChatMessageType.ServerMessageBox);
GameMain.Server.SendChatMessage("Client-Side LuaCs installed, restart your game to apply changes.", ChatMessageType.ServerMessageBox);
}
}
}

View File

@@ -0,0 +1,194 @@
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

@@ -1,25 +0,0 @@
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

@@ -1,188 +0,0 @@
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,7 +112,6 @@ 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:
@@ -252,7 +251,7 @@ namespace Barotrauma
break;
case EventType.Decal:
byte decalIndex = msg.ReadByte();
float decalAlpha = msg.ReadRangedSingle(0f, 1f, 8);
float decalAlpha = msg.ReadRangedSingle(0.0f, 1.0f, 255);
if (decalIndex < 0 || decalIndex >= decals.Count) { return; }
if (c.Character != null && c.Character.AllowInput && c.Character.HeldItems.Any(it => it.GetComponent<Sprayer>() != null))
{

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