mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
236 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
977a840253 | ||
|
|
6051d4d358 | ||
|
|
0a00a8a109 | ||
|
|
9d1aff3a75 | ||
|
|
3332279e75 | ||
|
|
d8059cacf4 | ||
|
|
374e80aad3 | ||
|
|
2fd15b0375 | ||
|
|
9293421f41 | ||
|
|
4f358b3a5e | ||
|
|
76a804d427 | ||
|
|
f8bf877b61 | ||
|
|
1f99f43755 | ||
|
|
457abf323b | ||
|
|
35d81974a8 | ||
|
|
c6a9113144 | ||
|
|
c76d1d8c5c | ||
|
|
0e289873f5 | ||
|
|
7e59febb47 | ||
|
|
fb7a231b78 | ||
|
|
85ded7a5a5 | ||
|
|
db996b9fb3 | ||
|
|
52b09cef71 | ||
|
|
26dcef4a11 | ||
|
|
9133879bd3 | ||
|
|
76b2909116 | ||
|
|
9dab8e77a7 | ||
|
|
0464b89cfe | ||
|
|
877e64f6be | ||
|
|
4df03f859b | ||
|
|
66435e6022 | ||
|
|
dc4f3b8f6f | ||
|
|
8377acb672 | ||
|
|
84c14a4657 | ||
|
|
1d5476e01b | ||
|
|
73d21da0dd | ||
|
|
81672fbe29 | ||
|
|
5655dea1c7 | ||
|
|
2616f3cedd | ||
|
|
511f272e96 | ||
|
|
3c7f87b8c3 | ||
|
|
7f89aabbb0 | ||
|
|
d6c9420a74 | ||
|
|
43e67c0db9 | ||
|
|
2e1aa16d38 | ||
|
|
849bcaeb74 | ||
|
|
dce320d0f5 | ||
|
|
d40804b41e | ||
|
|
90596c8979 | ||
|
|
36f582a712 | ||
|
|
163deff564 | ||
|
|
ca68041199 | ||
|
|
850d7ba032 | ||
|
|
4c5fefc3b2 | ||
|
|
84ee039f62 | ||
|
|
a84b481143 | ||
|
|
69fa2eed64 | ||
|
|
71f8275ae2 | ||
|
|
b89bf5739c | ||
|
|
e9ec2ee1d3 | ||
|
|
a44a25fb96 | ||
|
|
5356523305 | ||
|
|
2004ee9040 | ||
|
|
7b6d9bd719 | ||
|
|
287e57f717 | ||
|
|
add06753e9 | ||
|
|
8aa6fb478b | ||
|
|
5e7d617736 | ||
|
|
fa6c37778e | ||
|
|
4f92301d25 | ||
|
|
bd6be35b4d | ||
|
|
c39f6d09eb | ||
|
|
5eda1b326d | ||
|
|
5c3b058e81 | ||
|
|
7a06db60cf | ||
|
|
aafb15aff5 | ||
|
|
c437fadd25 | ||
|
|
bab37587c5 | ||
|
|
c4cd1fb6ee | ||
|
|
94e6886a85 | ||
|
|
e93ae73e50 | ||
|
|
9739eaa34a | ||
|
|
2aa5e8c07f | ||
|
|
f8b2412855 | ||
|
|
04782b83ab | ||
|
|
e2f2b3a26d | ||
|
|
b44a1cde88 | ||
|
|
af964b52ed | ||
|
|
ebca38b57a | ||
|
|
86d05dfa1e | ||
|
|
e10bd576ff | ||
|
|
20ff1e86c0 | ||
|
|
9d2a2ef4ba | ||
|
|
70d2457514 | ||
|
|
ba8deea700 | ||
|
|
f44d797057 | ||
|
|
e72d29a677 | ||
|
|
5730465c36 | ||
|
|
19f50d60c9 | ||
|
|
bbf5e92f32 | ||
|
|
419f4f36ab | ||
|
|
7769252109 | ||
|
|
3785777983 | ||
|
|
61aa73ffe3 | ||
|
|
ce11bf4564 | ||
|
|
0cf97780f0 | ||
|
|
fc2f6d4910 | ||
|
|
69158de99b | ||
|
|
7bec76d8d1 | ||
|
|
6e86f98406 | ||
|
|
f782e76671 | ||
|
|
5a8464b518 | ||
|
|
5f1feb9bb1 | ||
|
|
196e2bb427 | ||
|
|
f8bebee904 | ||
|
|
6515b08b41 | ||
|
|
6e2f18d0d8 | ||
|
|
a2ecd63e9d | ||
|
|
6da9176410 | ||
|
|
ae9b771c8c | ||
|
|
d1e206864c | ||
|
|
f812eb1e27 | ||
|
|
1dec0dd980 | ||
|
|
7316d9e950 | ||
|
|
f7c2305bce | ||
|
|
ebc0fc9c60 | ||
|
|
d157aab786 | ||
|
|
70a5d1bad6 | ||
|
|
2471bf8b4b | ||
|
|
e7c5706b04 | ||
|
|
7b6d8a1465 | ||
|
|
31b750bd5b | ||
|
|
6ebd0eb4ae | ||
|
|
37eac8c73f | ||
|
|
44649eea1c | ||
|
|
caa0212282 | ||
|
|
7f9d08c8f9 | ||
|
|
1c128e6b74 | ||
|
|
099e7c5c48 | ||
|
|
2f76908efb | ||
|
|
5adde7d588 | ||
|
|
7b1bb7df47 | ||
|
|
8e5eb6ebbb | ||
|
|
87ef010348 | ||
|
|
229d1c248b | ||
|
|
8db606c4e4 | ||
|
|
6b5181269b | ||
|
|
17b84c3520 | ||
|
|
42875fc101 | ||
|
|
72a7bc2ae7 | ||
|
|
bef4f75419 | ||
|
|
216509f89d | ||
|
|
b7991204f1 | ||
|
|
02987ac703 | ||
|
|
5cf8cb262f | ||
|
|
49dfca169c | ||
|
|
33008a2bce | ||
|
|
c2e90132c0 | ||
|
|
9859b5b090 | ||
|
|
20aec0a8f9 | ||
|
|
a9ee78e40d | ||
|
|
69f36aac6f | ||
|
|
173b41ab9e | ||
|
|
7796d7f065 | ||
|
|
175c111be9 | ||
|
|
279cc0f83f | ||
|
|
b334d927a5 | ||
|
|
bdb9b9af2b | ||
|
|
5aa634b5eb | ||
|
|
60d7430fe7 | ||
|
|
a9aeff9b78 | ||
|
|
e7a0409645 | ||
|
|
3e718575ff | ||
|
|
746ec9eab7 | ||
|
|
60e6ecb0cc | ||
|
|
3a00e0d497 | ||
|
|
24a5020b42 | ||
|
|
bbb9e94ce9 | ||
|
|
2bea9576f0 | ||
|
|
6e0be2b8c9 | ||
|
|
0a348dd1be | ||
|
|
a95283913b | ||
|
|
ebc73a71c2 | ||
|
|
05321f0381 | ||
|
|
6554144c42 | ||
|
|
28e6cdc92f | ||
|
|
2ec715b70e | ||
|
|
424e0768c8 | ||
|
|
e1b9327ec0 | ||
|
|
80308a799f | ||
|
|
e30f8f3e69 | ||
|
|
302b910cf3 | ||
|
|
5a6a16e7dc | ||
|
|
8d84c56a72 | ||
|
|
13f821be5f | ||
|
|
d647ab1c61 | ||
|
|
b902ef290a | ||
|
|
058d897529 | ||
|
|
8e173a7a18 | ||
|
|
5443f77526 | ||
|
|
b406526592 | ||
|
|
48697da450 | ||
|
|
06f20ea722 | ||
|
|
427378f94d | ||
|
|
6cf5021efa | ||
|
|
ad9bda2efe | ||
|
|
f0bf251acf | ||
|
|
21f5fed32f | ||
|
|
cc67a47c32 | ||
|
|
e78e7bacfe | ||
|
|
6301008ac3 | ||
|
|
dfd572a0aa | ||
|
|
97fab99971 | ||
|
|
25d6bd908b | ||
|
|
fd5a8bf207 | ||
|
|
772076826a | ||
|
|
52d669e032 | ||
|
|
75fc9089c3 | ||
|
|
0972601a43 | ||
|
|
603c252c48 | ||
|
|
d5b1c044b7 | ||
|
|
4600f0531d | ||
|
|
c88498eca9 | ||
|
|
f15f1eb345 | ||
|
|
5be3ced05a | ||
|
|
7f03e88e97 | ||
|
|
8e3fa3e52d | ||
|
|
f9ae3e1fc2 | ||
|
|
bf9e95fa8a | ||
|
|
030a7d265b | ||
|
|
df70e94743 | ||
|
|
d68cd4d7eb | ||
|
|
d098881bff | ||
|
|
b8fbe32c27 | ||
|
|
02d2bd31e7 | ||
|
|
bd0dba0df0 |
Submodule Lidgren.Network/Lidgren.Network updated: dd285c9246...1dd5c1f333
4
MSBuild/Robust.Engine.Version.props
Normal file
4
MSBuild/Robust.Engine.Version.props
Normal file
@@ -0,0 +1,4 @@
|
||||
<Project>
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<PropertyGroup><Version>0.8.3</Version></PropertyGroup>
|
||||
</Project>
|
||||
@@ -6,4 +6,5 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<WarningsAsErrors>nullable</WarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<Import Project="Robust.Engine.Version.props" />
|
||||
</Project>
|
||||
|
||||
122
MSBuild/Robust.Trimming.targets
Normal file
122
MSBuild/Robust.Trimming.targets
Normal file
@@ -0,0 +1,122 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<!--
|
||||
Stuff for using ILLink trimming without self-contained deployments.
|
||||
This is not something officially supported by the .NET SDK currently, but we can simply run ILLink ourselves.
|
||||
-->
|
||||
|
||||
<!--
|
||||
A lot of stuff taken from Microsoft.NET.ILLink.targets in the SDK files.
|
||||
-->
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<RobustLinkRoots>
|
||||
<Visible>false</Visible>
|
||||
</RobustLinkRoots>
|
||||
<RobustLinkAssemblies>
|
||||
<Visible>false</Visible>
|
||||
</RobustLinkAssemblies>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<Target Name="RobustILLink"
|
||||
BeforeTargets="ILLink"
|
||||
Condition="'$(PublishTrimmed)' != 'true' And
|
||||
'$(RobustILLink)' == 'true'"
|
||||
DependsOnTargets="_ComputeAssembliesToPostprocessOnPublish">
|
||||
|
||||
<ComputeManagedAssemblies Assemblies="@(ResolvedFileToPublish)">
|
||||
<Output TaskParameter="ManagedAssemblies" ItemName="_ResolvedFileToPublishFiltered" />
|
||||
</ComputeManagedAssemblies>
|
||||
|
||||
<JoinItems Left="@(_ResolvedFileToPublishFiltered)" LeftKey="FileName" LeftMetadata="*"
|
||||
Right="@(RobustLinkRoots)"
|
||||
ItemSpecToUse="Left">
|
||||
<Output TaskParameter="JoinResult" ItemName="_RobustLinkRootsJoined" />
|
||||
</JoinItems>
|
||||
|
||||
<JoinItems Left="@(_ResolvedFileToPublishFiltered)" LeftKey="FileName" LeftMetadata="*"
|
||||
Right="@(RobustLinkAssemblies)"
|
||||
ItemSpecToUse="Left">
|
||||
<Output TaskParameter="JoinResult" ItemName="_RobustLinkAssembliesJoined" />
|
||||
</JoinItems>
|
||||
|
||||
<PropertyGroup>
|
||||
<TrimMode Condition=" '$(TrimMode)' == '' ">link</TrimMode>
|
||||
<TrimmerDefaultAction Condition=" '$(TrimmerDefaultAction)' == '' ">copy</TrimmerDefaultAction>
|
||||
<_ExtraTrimmerArgs>--skip-unresolved true $(_ExtraTrimmerArgs)</_ExtraTrimmerArgs>
|
||||
<ILLinkTreatWarningsAsErrors Condition=" '$(ILLinkTreatWarningsAsErrors)' == '' ">$(TreatWarningsAsErrors)</ILLinkTreatWarningsAsErrors>
|
||||
<TrimmerSingleWarn Condition=" '$(TrimmerSingleWarn)' == '' ">true</TrimmerSingleWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<RobustAssemblyToLink Include="@(_RobustLinkRootsJoined)">
|
||||
<TrimMode>Copy</TrimMode>
|
||||
</RobustAssemblyToLink>
|
||||
<RobustAssemblyToLink Include="@(_RobustLinkAssembliesJoined)">
|
||||
<TrimMode>Link</TrimMode>
|
||||
</RobustAssemblyToLink>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- The linker implicitly picks up PDBs next to input assemblies. We will filter these out of the publish set. -->
|
||||
<__PDBToLink Include="@(ResolvedFileToPublish)" Exclude="@(RobustAssemblyToLink->'%(RelativeDir)%(Filename).pdb')" />
|
||||
<_PDBToLink Include="@(ResolvedFileToPublish)" Exclude="@(__PDBToLink)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_LinkedResolvedFileToPublishCandidate Include="@(RobustAssemblyToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" />
|
||||
<_LinkedResolvedFileToPublishCandidate Include="@(_PDBToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--<Message Text="@(ResolvedFileToPublish)" Importance="high" />-->
|
||||
|
||||
<ItemGroup>
|
||||
<_TrimmerFeatureSettings Include="@(RuntimeHostConfigurationOption)"
|
||||
Condition="'%(RuntimeHostConfigurationOption.Trim)' == 'true'" />
|
||||
</ItemGroup>
|
||||
|
||||
<Delete Files="@(_LinkedResolvedFileToPublishCandidate)" />
|
||||
<ILLink AssemblyPaths="@(RobustAssemblyToLink)"
|
||||
ReferenceAssemblyPaths="@(ReferencePath)"
|
||||
RootAssemblyNames="@(RobustLinkRoots)"
|
||||
TrimMode="Skip"
|
||||
DefaultAction="$(TrimmerDefaultAction)"
|
||||
RemoveSymbols="false"
|
||||
FeatureSettings="@(_TrimmerFeatureSettings)"
|
||||
CustomData="@(_TrimmerCustomData)"
|
||||
|
||||
BeforeFieldInit="$(_TrimmerBeforeFieldInit)"
|
||||
OverrideRemoval="$(_TrimmerOverrideRemoval)"
|
||||
UnreachableBodies="$(_TrimmerUnreachableBodies)"
|
||||
UnusedInterfaces="$(_TrimmerUnusedInterfaces)"
|
||||
IPConstProp="$(_TrimmerIPConstProp)"
|
||||
Sealer="$(_TrimmerSealer)"
|
||||
|
||||
Warn="$(ILLinkWarningLevel)"
|
||||
NoWarn="$(NoWarn)"
|
||||
TreatWarningsAsErrors="$(ILLinkTreatWarningsAsErrors)"
|
||||
WarningsAsErrors="$(WarningsAsErrors)"
|
||||
WarningsNotAsErrors="$(WarningsNotAsErrors)"
|
||||
SingleWarn="$(TrimmerSingleWarn)"
|
||||
|
||||
CustomSteps="@(_TrimmerCustomSteps)"
|
||||
RootDescriptorFiles="@(TrimmerRootDescriptor)"
|
||||
OutputDirectory="$(IntermediateLinkDir)"
|
||||
DumpDependencies="$(_TrimmerDumpDependencies)"
|
||||
ExtraArgs="$(_ExtraTrimmerArgs)"
|
||||
ToolExe="$(_DotNetHostFileName)"
|
||||
ToolPath="$(_DotNetHostDirectory)"
|
||||
ContinueOnError="ErrorAndContinue">
|
||||
<Output TaskParameter="ExitCode" PropertyName="_ILLinkExitCode" />
|
||||
</ILLink>
|
||||
|
||||
<Touch Files="$(_LinkSemaphore)" AlwaysCreate="true" Condition=" '$(_ILLinkExitCode)' == '0' " />
|
||||
|
||||
<ItemGroup>
|
||||
<_LinkedResolvedFileToPublish Include="@(_LinkedResolvedFileToPublishCandidate)" Condition="Exists('%(Identity)')" />
|
||||
<ResolvedFileToPublish Remove="@(RobustAssemblyToLink)" />
|
||||
<ResolvedFileToPublish Remove="@(_PDBToLink)" />
|
||||
<ResolvedFileToPublish Include="@(_LinkedResolvedFileToPublish)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
@@ -18,20 +18,18 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
NativeLibrary.SetDllImportResolver(typeof(GLFWNative).Assembly, (name, assembly, path) =>
|
||||
{
|
||||
// Please keep in sync with what Robust.Shared/DllMapHelper.cs does.
|
||||
|
||||
if (name != "glfw3.dll")
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
return NativeLibrary.Load("libglfw.so.3", assembly, path);
|
||||
}
|
||||
string rName = null;
|
||||
if (OperatingSystem.IsLinux()) rName = "libglfw.so.3";
|
||||
else if (OperatingSystem.IsMacOS()) rName = "libglfw.3.dylib";
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
return NativeLibrary.Load("libglfw.3.dylib", assembly, path);
|
||||
}
|
||||
if ((rName != null) && NativeLibrary.TryLoad(rName, assembly, path, out var handle))
|
||||
return handle;
|
||||
|
||||
return IntPtr.Zero;
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ Use the [content repo](https://github.com/space-wizards/space-station-14) for ac
|
||||
|
||||
## Documentation/Wiki
|
||||
|
||||
The [HackMD Wiki](https://hackmd.io/@ss14/docs/wiki) has documentation on SS14s content, engine, game design and more. We also have lots of resources for new contributors to the project.
|
||||
The [wiki](https://docs.spacestation14.io/) has documentation on SS14s content, engine, game design and more. We also have lots of resources for new contributors to the project.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -7,3 +7,8 @@
|
||||
- type: shader
|
||||
id: shaded
|
||||
kind: canvas
|
||||
|
||||
- type: shader
|
||||
id: bgra
|
||||
kind: source
|
||||
path: "/Shaders/Internal/bgra.swsl"
|
||||
|
||||
6
Resources/Shaders/Internal/bgra.swsl
Normal file
6
Resources/Shaders/Internal/bgra.swsl
Normal file
@@ -0,0 +1,6 @@
|
||||
// Swaps B and R channel so you can render BGRA stuff without swizzling at upload time.
|
||||
// Currently used by CEF.
|
||||
|
||||
void fragment() {
|
||||
COLOR = zTexture(UV).bgra;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using BenchmarkDotNet.Running;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Running;
|
||||
using System;
|
||||
|
||||
namespace Robust.Benchmarks
|
||||
{
|
||||
@@ -8,7 +10,14 @@ namespace Robust.Benchmarks
|
||||
// --anyCategories=ctg1,ctg2
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
#if DEBUG
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine("\nWARNING: YOU ARE RUNNING A DEBUG BUILD, USE A RELEASE BUILD FOR AN ACCURATE BENCHMARK");
|
||||
Console.WriteLine("THE DEBUG BUILD IS ONLY GOOD FOR FIXING A CRASHING BENCHMARK\n");
|
||||
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig());
|
||||
#else
|
||||
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ namespace {nameSpace}
|
||||
foreach (var candidateClass in receiver.CandidateClasses)
|
||||
{
|
||||
var model = compilation.GetSemanticModel(candidateClass.SyntaxTree);
|
||||
var typeSymbol = (INamedTypeSymbol) model.GetDeclaredSymbol(candidateClass);
|
||||
var typeSymbol = model.GetDeclaredSymbol(candidateClass);
|
||||
var relevantAttribute = typeSymbol.GetAttributes().FirstOrDefault(attr =>
|
||||
attr.AttributeClass != null &&
|
||||
attr.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default));
|
||||
|
||||
@@ -9,14 +9,16 @@ namespace Robust.Client.WebView.Cef
|
||||
{
|
||||
internal sealed class ImageBuffer
|
||||
{
|
||||
public Image<Bgra32> Buffer { get; private set; } = new(1, 1);
|
||||
public Image<Rgba32> Buffer { get; private set; } = new(1, 1);
|
||||
|
||||
public unsafe void UpdateBuffer(int width, int height, IntPtr buffer, CefRectangle dirtyRect)
|
||||
{
|
||||
if (width != Buffer.Width || height != Buffer.Height)
|
||||
UpdateSize(width, height);
|
||||
|
||||
var span = new ReadOnlySpan<Bgra32>((void*) buffer, width * height);
|
||||
// NOTE: Image data from CEF is actually BGRA32, not RGBA32.
|
||||
// OpenGL ES does not allow uploading BGRA data, so we pretend it's RGBA32 and use a shader to swizzle it.
|
||||
var span = new ReadOnlySpan<Rgba32>((void*) buffer, width * height);
|
||||
|
||||
ImageSharpExt.Blit(
|
||||
span,
|
||||
@@ -28,7 +30,7 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
private void UpdateSize(int width, int height)
|
||||
{
|
||||
Buffer = new Image<Bgra32>(width, height);
|
||||
Buffer = new Image<Rgba32>(width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ namespace Robust.Client.WebView.Cef
|
||||
{
|
||||
public IWebViewControlImpl MakeControlImpl(WebViewControl owner)
|
||||
{
|
||||
var impl = new ControlImpl(owner);
|
||||
var shader = _prototypeManager.Index<ShaderPrototype>("bgra");
|
||||
var shaderInstance = shader.Instance();
|
||||
var impl = new ControlImpl(owner, shaderInstance);
|
||||
_dependencyCollection.InjectDependencies(impl);
|
||||
return impl;
|
||||
}
|
||||
@@ -131,10 +133,12 @@ namespace Robust.Client.WebView.Cef
|
||||
[Dependency] private readonly IInputManager _inputMgr = default!;
|
||||
|
||||
public readonly WebViewControl Owner;
|
||||
private readonly ShaderInstance _shaderInstance;
|
||||
|
||||
public ControlImpl(WebViewControl owner)
|
||||
public ControlImpl(WebViewControl owner, ShaderInstance shaderInstance)
|
||||
{
|
||||
Owner = owner;
|
||||
_shaderInstance = shaderInstance;
|
||||
}
|
||||
|
||||
private const int ScrollSpeed = 50;
|
||||
@@ -183,7 +187,7 @@ namespace Robust.Client.WebView.Cef
|
||||
// Create the web browser! And by default, we go to about:blank.
|
||||
var browser = CefBrowserHost.CreateBrowserSync(info, client, settings, _startUrl);
|
||||
|
||||
var texture = _clyde.CreateBlankTexture<Bgra32>(Vector2i.One);
|
||||
var texture = _clyde.CreateBlankTexture<Rgba32>(Vector2i.One);
|
||||
|
||||
_data = new LiveData(texture, client, browser, renderer);
|
||||
}
|
||||
@@ -386,7 +390,7 @@ namespace Robust.Client.WebView.Cef
|
||||
_data.Browser.GetHost().NotifyMoveOrResizeStarted();
|
||||
_data.Browser.GetHost().WasResized();
|
||||
_data.Texture.Dispose();
|
||||
_data.Texture = _clyde.CreateBlankTexture<Bgra32>((Owner.PixelWidth, Owner.PixelHeight));
|
||||
_data.Texture = _clyde.CreateBlankTexture<Rgba32>((Owner.PixelWidth, Owner.PixelHeight));
|
||||
}
|
||||
|
||||
public void Draw(DrawingHandleScreen handle)
|
||||
@@ -404,6 +408,7 @@ namespace Robust.Client.WebView.Cef
|
||||
Math.Min(Owner.PixelWidth, bufImg.Width),
|
||||
Math.Min(Owner.PixelHeight, bufImg.Height)));
|
||||
|
||||
handle.UseShader(_shaderInstance);
|
||||
handle.DrawTexture(_data.Texture, Vector2.Zero);
|
||||
}
|
||||
|
||||
@@ -533,8 +538,7 @@ namespace Robust.Client.WebView.Cef
|
||||
if (_control.Owner.Disposed)
|
||||
return false;
|
||||
|
||||
// TODO CEF: Get actual scale factor?
|
||||
screenInfo.DeviceScaleFactor = 1.0f;
|
||||
screenInfo.DeviceScaleFactor = _control.Owner.UIScale;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.IO;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Xilium.CefGlue;
|
||||
|
||||
namespace Robust.Client.WebView.Cef
|
||||
@@ -12,6 +13,7 @@ namespace Robust.Client.WebView.Cef
|
||||
private CefApp _app = default!;
|
||||
|
||||
[Dependency] private readonly IDependencyCollection _dependencyCollection = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
<Import Project="..\MSBuild\Robust.Engine.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<OutputType>WinExe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Animations
|
||||
{
|
||||
@@ -18,8 +19,8 @@ namespace Robust.Client.Animations
|
||||
throw new InvalidOperationException("Must set parameters to non-null values.");
|
||||
}
|
||||
|
||||
var entity = (IEntity) context;
|
||||
var component = entity.GetComponent(ComponentType);
|
||||
var entity = (EntityUid) context;
|
||||
var component = IoCManager.Resolve<IEntityManager>().GetComponent(entity, ComponentType);
|
||||
|
||||
if (component is IAnimationProperties properties)
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Robust.Client.Animations
|
||||
public override (int KeyFrameIndex, float FramePlayingTime)
|
||||
AdvancePlayback(object context, int prevKeyFrameIndex, float prevPlayingTime, float frameTime)
|
||||
{
|
||||
var entity = (IEntity) context;
|
||||
var entity = (EntityUid) context;
|
||||
|
||||
var playingTime = prevPlayingTime + frameTime;
|
||||
var keyFrameIndex = prevKeyFrameIndex;
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Animations
|
||||
@@ -38,8 +39,8 @@ namespace Robust.Client.Animations
|
||||
{
|
||||
DebugTools.AssertNotNull(LayerKey);
|
||||
|
||||
var entity = (IEntity) context;
|
||||
var sprite = entity.GetComponent<ISpriteComponent>();
|
||||
var entity = (EntityUid) context;
|
||||
var sprite = IoCManager.Resolve<IEntityManager>().GetComponent<ISpriteComponent>(entity);
|
||||
|
||||
var playingTime = prevPlayingTime + frameTime;
|
||||
var keyFrameIndex = prevKeyFrameIndex;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -299,7 +299,7 @@ namespace Robust.Client.Audio.Midi
|
||||
}
|
||||
else if (renderer.TrackingEntity != null)
|
||||
{
|
||||
mapPos = renderer.TrackingEntity.Transform.MapPosition;
|
||||
mapPos = _entityManager.GetComponent<TransformComponent>(renderer.TrackingEntity.Value).MapPosition;
|
||||
}
|
||||
|
||||
if (mapPos != null)
|
||||
@@ -335,7 +335,7 @@ namespace Robust.Client.Audio.Midi
|
||||
|
||||
if (renderer.TrackingEntity != null)
|
||||
{
|
||||
renderer.Source.SetVelocity(renderer.TrackingEntity.GlobalLinearVelocity());
|
||||
renderer.Source.SetVelocity(renderer.TrackingEntity.Value.GlobalLinearVelocity());
|
||||
}
|
||||
|
||||
if (float.IsNaN(pos.Position.X) || float.IsNaN(pos.Position.Y))
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace Robust.Client.Audio.Midi
|
||||
/// The entity whose position will be used for positional audio.
|
||||
/// This is only used if <see cref="Mono"/> is set to True.
|
||||
/// </summary>
|
||||
IEntity? TrackingEntity { get; set; }
|
||||
EntityUid? TrackingEntity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The position that will be used for positional audio.
|
||||
@@ -297,7 +297,7 @@ namespace Robust.Client.Audio.Midi
|
||||
set
|
||||
{
|
||||
lock (_playerStateLock)
|
||||
_player?.SetLoop(value ? -1 : 1);
|
||||
_player?.SetLoop(value ? -1 : 0);
|
||||
_loopMidi = value;
|
||||
}
|
||||
}
|
||||
@@ -306,7 +306,7 @@ namespace Robust.Client.Audio.Midi
|
||||
public bool VolumeBoost { get; set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public IEntity? TrackingEntity { get; set; } = null;
|
||||
public EntityUid? TrackingEntity { get; set; } = null;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public EntityCoordinates? TrackingCoordinates { get; set; } = null;
|
||||
|
||||
@@ -5,6 +5,7 @@ using Robust.Client.Debugging;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Graphics.Audio;
|
||||
using Robust.Client.Graphics.Clyde;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Map;
|
||||
@@ -82,8 +83,9 @@ namespace Robust.Client
|
||||
case GameController.DisplayMode.Headless:
|
||||
IoCManager.Register<IClyde, ClydeHeadless>();
|
||||
IoCManager.Register<IClipboardManager, ClydeHeadless>();
|
||||
IoCManager.Register<IClydeAudio, ClydeHeadless>();
|
||||
IoCManager.Register<IClydeInternal, ClydeHeadless>();
|
||||
IoCManager.Register<IClydeAudio, ClydeAudioHeadless>();
|
||||
IoCManager.Register<IClydeAudioInternal, ClydeAudioHeadless>();
|
||||
IoCManager.Register<IInputManager, InputManager>();
|
||||
IoCManager.Register<IFileDialogManager, DummyFileDialogManager>();
|
||||
IoCManager.Register<IUriOpener, UriOpenerDummy>();
|
||||
@@ -91,8 +93,9 @@ namespace Robust.Client
|
||||
case GameController.DisplayMode.Clyde:
|
||||
IoCManager.Register<IClyde, Clyde>();
|
||||
IoCManager.Register<IClipboardManager, Clyde>();
|
||||
IoCManager.Register<IClydeAudio, Clyde>();
|
||||
IoCManager.Register<IClydeInternal, Clyde>();
|
||||
IoCManager.Register<IClydeAudio, FallbackProxyClydeAudio>();
|
||||
IoCManager.Register<IClydeAudioInternal, FallbackProxyClydeAudio>();
|
||||
IoCManager.Register<IInputManager, ClydeInputManager>();
|
||||
IoCManager.Register<IFileDialogManager, FileDialogManager>();
|
||||
IoCManager.Register<IUriOpener, UriOpener>();
|
||||
|
||||
@@ -4,177 +4,186 @@ using Robust.Shared;
|
||||
using Robust.Shared.Utility;
|
||||
using C = System.Console;
|
||||
|
||||
namespace Robust.Client
|
||||
namespace Robust.Client;
|
||||
|
||||
internal sealed class CommandLineArgs
|
||||
{
|
||||
internal sealed class CommandLineArgs
|
||||
public MountOptions MountOptions { get; }
|
||||
public bool Headless { get; }
|
||||
public bool SelfContained { get; }
|
||||
public bool Connect { get; }
|
||||
public string ConnectAddress { get; }
|
||||
public string? Ss14Address { get; }
|
||||
public bool Launcher { get; }
|
||||
public string? Username { get; }
|
||||
public IReadOnlyCollection<(string key, string value)> CVars { get; }
|
||||
public IReadOnlyCollection<(string key, string value)> LogLevels { get; }
|
||||
public IReadOnlyList<string> ExecCommands { get; set; }
|
||||
|
||||
// Manual parser because C# has no good command line parsing libraries. Also dependencies bad.
|
||||
// Also I don't like spending 100ms parsing command line args. Do you?
|
||||
public static bool TryParse(IReadOnlyList<string> args, [NotNullWhen(true)] out CommandLineArgs? parsed)
|
||||
{
|
||||
public MountOptions MountOptions { get; }
|
||||
public bool Headless { get; }
|
||||
public bool SelfContained { get; }
|
||||
public bool Connect { get; }
|
||||
public string ConnectAddress { get; }
|
||||
public string? Ss14Address { get; }
|
||||
public bool Launcher { get; }
|
||||
public string? Username { get; }
|
||||
public IReadOnlyCollection<(string key, string value)> CVars { get; }
|
||||
public IReadOnlyCollection<(string key, string value)> LogLevels { get; }
|
||||
parsed = null;
|
||||
var headless = false;
|
||||
var selfContained = false;
|
||||
var connect = false;
|
||||
var connectAddress = "localhost";
|
||||
string? ss14Address = null;
|
||||
var launcher = false;
|
||||
string? username = null;
|
||||
var cvars = new List<(string, string)>();
|
||||
var logLevels = new List<(string, string)>();
|
||||
var mountOptions = new MountOptions();
|
||||
var execCommands = new List<string>();
|
||||
|
||||
// Manual parser because C# has no good command line parsing libraries. Also dependencies bad.
|
||||
// Also I don't like spending 100ms parsing command line args. Do you?
|
||||
public static bool TryParse(IReadOnlyList<string> args, [NotNullWhen(true)] out CommandLineArgs? parsed)
|
||||
using var enumerator = args.GetEnumerator();
|
||||
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
parsed = null;
|
||||
var headless = false;
|
||||
var selfContained = false;
|
||||
var connect = false;
|
||||
var connectAddress = "localhost";
|
||||
string? ss14Address = null;
|
||||
var launcher = false;
|
||||
string? username = null;
|
||||
var cvars = new List<(string, string)>();
|
||||
var logLevels = new List<(string, string)>();
|
||||
var mountOptions = new MountOptions();
|
||||
|
||||
using var enumerator = args.GetEnumerator();
|
||||
|
||||
while (enumerator.MoveNext())
|
||||
var arg = enumerator.Current;
|
||||
if (arg == "--connect")
|
||||
{
|
||||
var arg = enumerator.Current;
|
||||
if (arg == "--connect")
|
||||
connect = true;
|
||||
}
|
||||
else if (arg == "--connect-address")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
connect = true;
|
||||
}
|
||||
else if (arg == "--connect-address")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing connection address.");
|
||||
return false;
|
||||
}
|
||||
|
||||
connectAddress = enumerator.Current;
|
||||
}
|
||||
else if (arg == "--ss14-address")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing SS14 address.");
|
||||
return false;
|
||||
}
|
||||
|
||||
ss14Address = enumerator.Current;
|
||||
}
|
||||
else if (arg == "--self-contained")
|
||||
{
|
||||
selfContained = true;
|
||||
}
|
||||
else if (arg == "--launcher")
|
||||
{
|
||||
launcher = true;
|
||||
}
|
||||
else if (arg == "--headless")
|
||||
{
|
||||
headless = true;
|
||||
}
|
||||
else if (arg == "--username")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing username.");
|
||||
return false;
|
||||
}
|
||||
|
||||
username = enumerator.Current;
|
||||
}
|
||||
else if (arg == "--cvar")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing cvar value.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var cvar = enumerator.Current;
|
||||
DebugTools.AssertNotNull(cvar);
|
||||
var pos = cvar.IndexOf('=');
|
||||
|
||||
if (pos == -1)
|
||||
{
|
||||
C.WriteLine("Expected = in cvar.");
|
||||
return false;
|
||||
}
|
||||
|
||||
cvars.Add((cvar[..pos], cvar[(pos + 1)..]));
|
||||
}
|
||||
else if (arg == "--mount-zip")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing mount path");
|
||||
return false;
|
||||
}
|
||||
|
||||
mountOptions.ZipMounts.Add(enumerator.Current);
|
||||
}
|
||||
else if (arg == "--mount-dir")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing mount path");
|
||||
return false;
|
||||
}
|
||||
|
||||
mountOptions.DirMounts.Add(enumerator.Current);
|
||||
}
|
||||
else if (arg == "--loglevel")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing loglevel sawmill.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var loglevel = enumerator.Current;
|
||||
DebugTools.AssertNotNull(loglevel);
|
||||
var pos = loglevel.IndexOf('=');
|
||||
|
||||
if (pos == -1)
|
||||
{
|
||||
C.WriteLine("Expected = in loglevel.");
|
||||
return false;
|
||||
}
|
||||
|
||||
logLevels.Add((loglevel[..pos], loglevel[(pos + 1)..]));
|
||||
}
|
||||
else if (arg == "--help")
|
||||
{
|
||||
PrintHelp();
|
||||
C.WriteLine("Missing connection address.");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
C.WriteLine("Unknown argument: {0}", arg);
|
||||
}
|
||||
|
||||
connectAddress = enumerator.Current;
|
||||
}
|
||||
else if (arg == "--ss14-address")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing SS14 address.");
|
||||
return false;
|
||||
}
|
||||
|
||||
parsed = new CommandLineArgs(
|
||||
headless,
|
||||
selfContained,
|
||||
connect,
|
||||
launcher,
|
||||
username,
|
||||
cvars,
|
||||
logLevels,
|
||||
connectAddress,
|
||||
ss14Address,
|
||||
mountOptions);
|
||||
ss14Address = enumerator.Current;
|
||||
}
|
||||
else if (arg == "--self-contained")
|
||||
{
|
||||
selfContained = true;
|
||||
}
|
||||
else if (arg == "--launcher")
|
||||
{
|
||||
launcher = true;
|
||||
}
|
||||
else if (arg == "--headless")
|
||||
{
|
||||
headless = true;
|
||||
}
|
||||
else if (arg == "--username")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing username.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
username = enumerator.Current;
|
||||
}
|
||||
else if (arg == "--cvar")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing cvar value.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var cvar = enumerator.Current;
|
||||
DebugTools.AssertNotNull(cvar);
|
||||
var pos = cvar.IndexOf('=');
|
||||
|
||||
if (pos == -1)
|
||||
{
|
||||
C.WriteLine("Expected = in cvar.");
|
||||
return false;
|
||||
}
|
||||
|
||||
cvars.Add((cvar[..pos], cvar[(pos + 1)..]));
|
||||
}
|
||||
else if (arg == "--mount-zip")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing mount path");
|
||||
return false;
|
||||
}
|
||||
|
||||
mountOptions.ZipMounts.Add(enumerator.Current);
|
||||
}
|
||||
else if (arg == "--mount-dir")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing mount path");
|
||||
return false;
|
||||
}
|
||||
|
||||
mountOptions.DirMounts.Add(enumerator.Current);
|
||||
}
|
||||
else if (arg == "--loglevel")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing loglevel sawmill.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var loglevel = enumerator.Current;
|
||||
DebugTools.AssertNotNull(loglevel);
|
||||
var pos = loglevel.IndexOf('=');
|
||||
|
||||
if (pos == -1)
|
||||
{
|
||||
C.WriteLine("Expected = in loglevel.");
|
||||
return false;
|
||||
}
|
||||
|
||||
logLevels.Add((loglevel[..pos], loglevel[(pos + 1)..]));
|
||||
}
|
||||
else if (arg == "--help")
|
||||
{
|
||||
PrintHelp();
|
||||
return false;
|
||||
}
|
||||
else if (arg.StartsWith("+"))
|
||||
{
|
||||
execCommands.Add(arg[1..]);
|
||||
}
|
||||
else
|
||||
{
|
||||
C.WriteLine("Unknown argument: {0}", arg);
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrintHelp()
|
||||
{
|
||||
C.WriteLine(@"
|
||||
parsed = new CommandLineArgs(
|
||||
headless,
|
||||
selfContained,
|
||||
connect,
|
||||
launcher,
|
||||
username,
|
||||
cvars,
|
||||
logLevels,
|
||||
connectAddress,
|
||||
ss14Address,
|
||||
mountOptions,
|
||||
execCommands);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void PrintHelp()
|
||||
{
|
||||
C.WriteLine(@"
|
||||
Usage: Robust.Client [options] [+command [+command]]
|
||||
|
||||
Options:
|
||||
--headless Run without graphics/audio/input.
|
||||
--self-contained Store data relative to executable instead of user-global locations.
|
||||
@@ -189,30 +198,34 @@ Options:
|
||||
--mount-dir Resource directory to mount.
|
||||
--mount-zip Resource zip to mount.
|
||||
--help Display this help text and exit.
|
||||
");
|
||||
}
|
||||
|
||||
private CommandLineArgs(
|
||||
bool headless,
|
||||
bool selfContained,
|
||||
bool connect,
|
||||
bool launcher,
|
||||
string? username,
|
||||
IReadOnlyCollection<(string key, string value)> cVars,
|
||||
IReadOnlyCollection<(string key, string value)> logLevels,
|
||||
string connectAddress, string? ss14Address,
|
||||
MountOptions mountOptions)
|
||||
{
|
||||
Headless = headless;
|
||||
SelfContained = selfContained;
|
||||
Connect = connect;
|
||||
Launcher = launcher;
|
||||
Username = username;
|
||||
CVars = cVars;
|
||||
LogLevels = logLevels;
|
||||
ConnectAddress = connectAddress;
|
||||
Ss14Address = ss14Address;
|
||||
MountOptions = mountOptions;
|
||||
}
|
||||
+command: You can pass a set of commands, prefixed by +,
|
||||
to be executed in the console in order after the game has finished initializing.
|
||||
");
|
||||
}
|
||||
|
||||
private CommandLineArgs(
|
||||
bool headless,
|
||||
bool selfContained,
|
||||
bool connect,
|
||||
bool launcher,
|
||||
string? username,
|
||||
IReadOnlyCollection<(string key, string value)> cVars,
|
||||
IReadOnlyCollection<(string key, string value)> logLevels,
|
||||
string connectAddress, string? ss14Address,
|
||||
MountOptions mountOptions,
|
||||
IReadOnlyList<string> execCommands)
|
||||
{
|
||||
Headless = headless;
|
||||
SelfContained = selfContained;
|
||||
Connect = connect;
|
||||
Launcher = launcher;
|
||||
Username = username;
|
||||
CVars = cVars;
|
||||
LogLevels = logLevels;
|
||||
ConnectAddress = connectAddress;
|
||||
Ss14Address = ss14Address;
|
||||
MountOptions = mountOptions;
|
||||
ExecCommands = execCommands;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,12 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
var entityUid = EntityUid.Parse(args[0]);
|
||||
var entity = EntityUid.Parse(args[0]);
|
||||
var componentName = args[1];
|
||||
|
||||
var compFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
var entity = entityManager.GetEntity(entityUid);
|
||||
var component = (Component) compFactory.GetComponent(componentName);
|
||||
|
||||
component.Owner = entity;
|
||||
|
||||
@@ -15,15 +15,15 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var player = IoCManager.Resolve<IPlayerManager>().LocalPlayer;
|
||||
if (player?.ControlledEntity == null)
|
||||
var controlled = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
if (controlled == EntityUid.Invalid)
|
||||
{
|
||||
shell.WriteLine("You don't have an attached entity.");
|
||||
return;
|
||||
}
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
entityManager.SpawnEntity(args[0], player.ControlledEntity.Transform.Coordinates);
|
||||
entityManager.SpawnEntity(args[0], entityManager.GetComponent<TransformComponent>(controlled).Coordinates);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,12 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Debugging;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
@@ -41,9 +40,9 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
foreach (var e in entityManager.GetEntities().OrderBy(e => e.Uid))
|
||||
foreach (var e in entityManager.GetEntities().OrderBy(e => e))
|
||||
{
|
||||
shell.WriteLine($"entity {e.Uid}, {e.Prototype?.ID}, {e.Transform.Coordinates}.");
|
||||
shell.WriteLine($"entity {e}, {entityManager.GetComponent<MetaDataComponent>(e).EntityPrototype?.ID}, {entityManager.GetComponent<TransformComponent>(e).Coordinates}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,15 +237,15 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
var uid = EntityUid.Parse(args[0]);
|
||||
var entmgr = IoCManager.Resolve<IEntityManager>();
|
||||
if (!entmgr.TryGetEntity(uid, out var entity))
|
||||
if (!entmgr.EntityExists(uid))
|
||||
{
|
||||
shell.WriteError("That entity does not exist. Sorry lad.");
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteLine($"{entity.Uid}: {entity.Prototype?.ID}/{entity.Name}");
|
||||
shell.WriteLine($"init/del/lmt: {entity.Initialized}/{entity.Deleted}/{entity.LastModifiedTick}");
|
||||
foreach (var component in entity.GetAllComponents())
|
||||
var meta = entmgr.GetComponent<MetaDataComponent>(uid);
|
||||
shell.WriteLine($"{uid}: {meta.EntityPrototype?.ID}/{meta.EntityName}");
|
||||
shell.WriteLine($"init/del/lmt: {meta.EntityInitialized}/{meta.EntityDeleted}/{meta.EntityLastModifiedTick}");
|
||||
foreach (var component in entmgr.GetComponents(uid))
|
||||
{
|
||||
shell.WriteLine(component.ToString() ?? "");
|
||||
if (component is IComponentDebug debug)
|
||||
|
||||
@@ -24,6 +24,9 @@ namespace Robust.Client.Console.Commands
|
||||
case "aabbs":
|
||||
system.Flags ^= PhysicsDebugFlags.AABBs;
|
||||
break;
|
||||
case "com":
|
||||
system.Flags ^= PhysicsDebugFlags.COM;
|
||||
break;
|
||||
case "contactnormals":
|
||||
system.Flags ^= PhysicsDebugFlags.ContactNormals;
|
||||
break;
|
||||
|
||||
@@ -79,13 +79,13 @@ namespace Robust.Client.Debugging
|
||||
|
||||
foreach (var ent in grid.GetAnchoredEntities(spot))
|
||||
{
|
||||
if (!EntityManager.TryGetEntity(ent, out var entity))
|
||||
if (EntityManager.TryGetComponent<MetaDataComponent>(ent, out var meta))
|
||||
{
|
||||
text.AppendLine($"uid: {ent}, invalid");
|
||||
text.AppendLine($"uid: {ent}, {meta.EntityName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
text.AppendLine($"uid: {ent}, {entity.Name}");
|
||||
text.AppendLine($"uid: {ent}, invalid");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Robust.Client.Debugging
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IEntityLookup _lookup = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
private bool _debugPositions;
|
||||
|
||||
@@ -30,7 +31,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
if (value && !_overlayManager.HasOverlay<EntityPositionOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new EntityPositionOverlay(_lookup, _eyeManager));
|
||||
_overlayManager.AddOverlay(new EntityPositionOverlay(_lookup, _eyeManager, _entityManager));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -43,13 +44,15 @@ namespace Robust.Client.Debugging
|
||||
{
|
||||
private readonly IEntityLookup _lookup;
|
||||
private readonly IEyeManager _eyeManager;
|
||||
private readonly IEntityManager _entityManager;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public EntityPositionOverlay(IEntityLookup lookup, IEyeManager eyeManager)
|
||||
public EntityPositionOverlay(IEntityLookup lookup, IEyeManager eyeManager, IEntityManager entityManager)
|
||||
{
|
||||
_lookup = lookup;
|
||||
_eyeManager = eyeManager;
|
||||
_entityManager = entityManager;
|
||||
}
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
@@ -61,7 +64,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
foreach (var entity in _lookup.GetEntitiesIntersecting(_eyeManager.CurrentMap, viewport))
|
||||
{
|
||||
var transform = entity.Transform;
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(entity);
|
||||
|
||||
var center = transform.WorldPosition;
|
||||
var worldRotation = transform.WorldRotation;
|
||||
|
||||
@@ -162,6 +162,7 @@ namespace Robust.Client.Debugging
|
||||
ShapeInfo = 1 << 3,
|
||||
Joints = 1 << 4,
|
||||
AABBs = 1 << 5,
|
||||
COM = 1 << 6,
|
||||
}
|
||||
|
||||
internal sealed class PhysicsDebugOverlay : Overlay
|
||||
@@ -190,17 +191,17 @@ namespace Robust.Client.Debugging
|
||||
_font = new VectorFont(cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||
}
|
||||
|
||||
private void DrawWorld(DrawingHandleWorld worldHandle)
|
||||
private void DrawWorld(DrawingHandleWorld worldHandle, OverlayDrawArgs args)
|
||||
{
|
||||
var viewport = _eyeManager.GetWorldViewport();
|
||||
var viewBounds = _eyeManager.GetWorldViewbounds();
|
||||
var viewBounds = args.WorldBounds;
|
||||
var viewAABB = args.WorldAABB;
|
||||
var mapId = _eyeManager.CurrentMap;
|
||||
|
||||
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.Shapes) != 0 && !viewport.IsEmpty())
|
||||
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.Shapes) != 0)
|
||||
{
|
||||
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, viewBounds))
|
||||
{
|
||||
if (physBody.Owner.HasComponent<MapGridComponent>()) continue;
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner)) continue;
|
||||
|
||||
var xform = physBody.GetTransform();
|
||||
|
||||
@@ -237,11 +238,36 @@ namespace Robust.Client.Debugging
|
||||
}
|
||||
}
|
||||
|
||||
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.AABBs) != 0 && !viewport.IsEmpty())
|
||||
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.COM) != 0)
|
||||
{
|
||||
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, viewBounds))
|
||||
{
|
||||
if (physBody.Owner.HasComponent<MapGridComponent>()) continue;
|
||||
Color color;
|
||||
const float Alpha = 0.25f;
|
||||
float size;
|
||||
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner))
|
||||
{
|
||||
color = Color.Orange.WithAlpha(Alpha);
|
||||
size = 1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
color = Color.Purple.WithAlpha(Alpha);
|
||||
size = 0.2f;
|
||||
}
|
||||
|
||||
var transform = physBody.GetTransform();
|
||||
|
||||
worldHandle.DrawCircle(Transform.Mul(transform, physBody.LocalCenter), size, color);
|
||||
}
|
||||
}
|
||||
|
||||
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.AABBs) != 0)
|
||||
{
|
||||
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, viewBounds))
|
||||
{
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner)) continue;
|
||||
|
||||
var xform = physBody.GetTransform();
|
||||
|
||||
@@ -270,8 +296,8 @@ namespace Robust.Client.Debugging
|
||||
foreach (var jointComponent in _entityManager.EntityQuery<JointComponent>(true))
|
||||
{
|
||||
if (jointComponent.JointCount == 0 ||
|
||||
!_entityManager.TryGetComponent(jointComponent.Owner.Uid, out TransformComponent? xf1) ||
|
||||
!viewport.Contains(xf1.WorldPosition)) continue;
|
||||
!_entityManager.TryGetComponent(jointComponent.Owner, out TransformComponent? xf1) ||
|
||||
!viewAABB.Contains(xf1.WorldPosition)) continue;
|
||||
|
||||
foreach (var (_, joint) in jointComponent.Joints)
|
||||
{
|
||||
@@ -312,7 +338,7 @@ namespace Robust.Client.Debugging
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawScreen(DrawingHandleScreen screenHandle)
|
||||
private void DrawScreen(DrawingHandleScreen screenHandle, OverlayDrawArgs args)
|
||||
{
|
||||
var mapId = _eyeManager.CurrentMap;
|
||||
var mousePos = _inputManager.MouseScreenPosition;
|
||||
@@ -324,7 +350,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, bounds))
|
||||
{
|
||||
if (physBody.Owner.HasComponent<MapGridComponent>()) continue;
|
||||
if (_entityManager.HasComponent<MapGridComponent>(physBody.Owner)) continue;
|
||||
hoverBodies.Add(physBody);
|
||||
}
|
||||
|
||||
@@ -359,10 +385,10 @@ namespace Robust.Client.Debugging
|
||||
switch (args.Space)
|
||||
{
|
||||
case OverlaySpace.ScreenSpace:
|
||||
DrawScreen((DrawingHandleScreen) args.DrawingHandle);
|
||||
DrawScreen((DrawingHandleScreen) args.DrawingHandle, args);
|
||||
break;
|
||||
case OverlaySpace.WorldSpace:
|
||||
DrawWorld((DrawingHandleWorld) args.DrawingHandle);
|
||||
DrawWorld((DrawingHandleWorld) args.DrawingHandle, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -382,8 +408,8 @@ namespace Robust.Client.Debugging
|
||||
|
||||
if (edge.OneSided)
|
||||
{
|
||||
worldHandle.DrawCircle(v1, 0.5f, color);
|
||||
worldHandle.DrawCircle(v2, 0.5f, color);
|
||||
worldHandle.DrawCircle(v1, 0.1f, color);
|
||||
worldHandle.DrawCircle(v2, 0.1f, color);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -416,11 +442,45 @@ namespace Robust.Client.Debugging
|
||||
var p1 = matrix1.Transform(joint.LocalAnchorA);
|
||||
var p2 = matrix2.Transform(joint.LocalAnchorB);
|
||||
|
||||
var xfa = new Transform(xf1, xform1.WorldRotation);
|
||||
var xfb = new Transform(xf2, xform2.WorldRotation);
|
||||
|
||||
switch (joint)
|
||||
{
|
||||
case DistanceJoint:
|
||||
worldHandle.DrawLine(xf1, xf2, JointColor);
|
||||
break;
|
||||
case PrismaticJoint prisma:
|
||||
var pA = Transform.Mul(xfa, joint.LocalAnchorA);
|
||||
var pB = Transform.Mul(xfb, joint.LocalAnchorB);
|
||||
|
||||
var axis = Transform.Mul(xfa.Quaternion2D, prisma._localXAxisA);
|
||||
|
||||
Color c1 = new(0.7f, 0.7f, 0.7f);
|
||||
Color c2 = new(0.3f, 0.9f, 0.3f);
|
||||
Color c3 = new(0.9f, 0.3f, 0.3f);
|
||||
Color c4 = new(0.3f, 0.3f, 0.9f);
|
||||
Color c5 = new(0.4f, 0.4f, 0.4f);
|
||||
|
||||
worldHandle.DrawLine(pA, pB, c5);
|
||||
|
||||
if (prisma.EnableLimit)
|
||||
{
|
||||
var lower = pA + axis * prisma.LowerTranslation;
|
||||
var upper = pA + axis * prisma.UpperTranslation;
|
||||
var perp = Transform.Mul(xfa.Quaternion2D, prisma._localYAxisA);
|
||||
worldHandle.DrawLine(lower, upper, c1);
|
||||
worldHandle.DrawLine(lower - perp * 0.5f, lower + perp * 0.5f, c2);
|
||||
worldHandle.DrawLine(upper - perp * 0.5f, upper + perp * 0.5f, c3);
|
||||
}
|
||||
else
|
||||
{
|
||||
worldHandle.DrawLine(pA - axis * 1.0f, pA + axis * 1.0f, c1);
|
||||
}
|
||||
|
||||
worldHandle.DrawCircle(pA, 0.5f, c1);
|
||||
worldHandle.DrawCircle(pB, 0.5f, c4);
|
||||
break;
|
||||
default:
|
||||
worldHandle.DrawLine(xf1, p1, JointColor);
|
||||
worldHandle.DrawLine(p1, p2, JointColor);
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace Robust.Client
|
||||
[Dependency] private readonly IViewVariablesManagerInternal _viewVariablesManager = default!;
|
||||
[Dependency] private readonly IDiscordRichPresence _discord = default!;
|
||||
[Dependency] private readonly IClydeInternal _clyde = default!;
|
||||
[Dependency] private readonly IClydeAudioInternal _clydeAudio = default!;
|
||||
[Dependency] private readonly IFontManagerInternal _fontManager = default!;
|
||||
[Dependency] private readonly IModLoaderInternal _modLoader = default!;
|
||||
[Dependency] private readonly IScriptClient _scriptClient = default!;
|
||||
@@ -86,6 +87,7 @@ namespace Robust.Client
|
||||
internal bool StartupContinue(DisplayMode displayMode)
|
||||
{
|
||||
_clyde.InitializePostWindowing();
|
||||
_clydeAudio.InitializePostWindowing();
|
||||
_clyde.SetWindowTitle(Options.DefaultWindowTitle);
|
||||
|
||||
_taskManager.Initialize();
|
||||
@@ -198,6 +200,8 @@ namespace Robust.Client
|
||||
_client.ConnectToServer(LaunchState.ConnectEndpoint);
|
||||
}
|
||||
|
||||
ProgramShared.RunExecCommands(_console, _commandLineArgs?.ExecCommands);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -437,6 +441,7 @@ namespace Robust.Client
|
||||
private void Update(FrameEventArgs frameEventArgs)
|
||||
{
|
||||
_webViewHook?.Update();
|
||||
_clydeAudio.FrameProcess(frameEventArgs);
|
||||
_clyde.FrameProcess(frameEventArgs);
|
||||
_modLoader.BroadcastUpdate(ModUpdateLevel.FramePreEngine, frameEventArgs);
|
||||
_stateManager.FrameUpdate(frameEventArgs);
|
||||
@@ -535,6 +540,7 @@ namespace Robust.Client
|
||||
IoCManager.Resolve<IEntityLookup>().Shutdown();
|
||||
_entityManager.Shutdown();
|
||||
_clyde.Shutdown();
|
||||
_clydeAudio.Shutdown();
|
||||
}
|
||||
|
||||
private sealed record ResourceManifestData(string[] Modules);
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace Robust.Client.GameObjects
|
||||
RegisterClass<ClientOccluderComponent>();
|
||||
RegisterClass<OccluderTreeComponent>();
|
||||
RegisterClass<EyeComponent>();
|
||||
RegisterClass<AppearanceComponent>();
|
||||
RegisterClass<AnimationPlayerComponent>();
|
||||
RegisterClass<TimerComponent>();
|
||||
|
||||
|
||||
@@ -33,19 +33,19 @@ namespace Robust.Client.GameObjects
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
IEntity IClientEntityManagerInternal.CreateEntity(string? prototypeName, EntityUid? uid)
|
||||
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName, EntityUid uid)
|
||||
{
|
||||
return base.CreateEntity(prototypeName, uid);
|
||||
}
|
||||
|
||||
void IClientEntityManagerInternal.InitializeEntity(IEntity entity)
|
||||
void IClientEntityManagerInternal.InitializeEntity(EntityUid entity)
|
||||
{
|
||||
base.InitializeEntity((Entity)entity);
|
||||
base.InitializeEntity(entity);
|
||||
}
|
||||
|
||||
void IClientEntityManagerInternal.StartEntity(IEntity entity)
|
||||
void IClientEntityManagerInternal.StartEntity(EntityUid entity)
|
||||
{
|
||||
base.StartEntity((Entity)entity);
|
||||
base.StartEntity(entity);
|
||||
}
|
||||
|
||||
#region IEntityNetworkManager impl
|
||||
@@ -107,7 +107,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
public void SendComponentNetworkMessage(INetChannel? channel, IEntity entity, IComponent component, ComponentMessage message)
|
||||
public void SendComponentNetworkMessage(INetChannel? channel, EntityUid entity, IComponent component, ComponentMessage message)
|
||||
{
|
||||
var netId = ComponentFactory.GetRegistration(component.GetType()).NetID;
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var msg = _networkManager.CreateNetMessage<MsgEntity>();
|
||||
msg.Type = EntityMessageType.ComponentMessage;
|
||||
msg.EntityUid = entity.Uid;
|
||||
msg.EntityUid = entity;
|
||||
msg.NetId = netId.Value;
|
||||
msg.ComponentMessage = message;
|
||||
msg.SourceTick = _gameTiming.CurTick;
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[ComponentReference(typeof(SharedAppearanceComponent))]
|
||||
public sealed class AppearanceComponent : SharedAppearanceComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
private Dictionary<object, object> data = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("visuals")]
|
||||
internal List<AppearanceVisualizer> Visualizers = new();
|
||||
|
||||
[ViewVariables]
|
||||
private bool _appearanceDirty;
|
||||
|
||||
public override void SetData(string key, object value)
|
||||
{
|
||||
SetData(key, value);
|
||||
}
|
||||
|
||||
public override void SetData(Enum key, object value)
|
||||
{
|
||||
SetData(key, value);
|
||||
}
|
||||
|
||||
public override T GetData<T>(string key)
|
||||
{
|
||||
return (T) data[key];
|
||||
}
|
||||
|
||||
public override T GetData<T>(Enum key)
|
||||
{
|
||||
return (T) data[key];
|
||||
}
|
||||
|
||||
internal T GetData<T>(object key)
|
||||
{
|
||||
return (T) data[key];
|
||||
}
|
||||
|
||||
public override bool TryGetData<T>(Enum key, [NotNullWhen(true)] out T data)
|
||||
{
|
||||
return TryGetData(key, out data);
|
||||
}
|
||||
|
||||
public override bool TryGetData<T>(string key, [NotNullWhen(true)] out T data)
|
||||
{
|
||||
return TryGetData(key, out data);
|
||||
}
|
||||
|
||||
internal bool TryGetData<T>(object key, [NotNullWhen(true)] out T data)
|
||||
{
|
||||
if (this.data.TryGetValue(key, out var dat))
|
||||
{
|
||||
data = (T) dat;
|
||||
return true;
|
||||
}
|
||||
|
||||
data = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetData(object key, object value)
|
||||
{
|
||||
if (data.TryGetValue(key, out var existing) && existing.Equals(value)) return;
|
||||
|
||||
data[key] = value;
|
||||
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if (curState is not AppearanceComponentState actualState)
|
||||
return;
|
||||
|
||||
var stateDiff = data.Count != actualState.Data.Count;
|
||||
|
||||
if (!stateDiff)
|
||||
{
|
||||
foreach (var (key, value) in data)
|
||||
{
|
||||
if (!actualState.Data.TryGetValue(key, out var stateValue) ||
|
||||
!value.Equals(stateValue))
|
||||
{
|
||||
stateDiff = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!stateDiff) return;
|
||||
|
||||
data = actualState.Data;
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
internal void MarkDirty()
|
||||
{
|
||||
if (_appearanceDirty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EntitySystem.Get<AppearanceSystem>().EnqueueUpdate(this);
|
||||
_appearanceDirty = true;
|
||||
}
|
||||
|
||||
internal void UnmarkDirty()
|
||||
{
|
||||
_appearanceDirty = false;
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
foreach (var visual in Visualizers)
|
||||
{
|
||||
visual.InitializeEntity(Owner);
|
||||
}
|
||||
|
||||
MarkDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the visualization of data inside of an appearance component.
|
||||
/// Implementations of this class are NOT bound to a specific entity, they are flyweighted across multiple.
|
||||
/// </summary>
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public abstract class AppearanceVisualizer
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes an entity to be managed by this appearance controller.
|
||||
/// DO NOT assume this is your only entity. Visualizers are shared.
|
||||
/// </summary>
|
||||
public virtual void InitializeEntity(IEntity entity)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever appearance data for an entity changes.
|
||||
/// Update its visuals here.
|
||||
/// </summary>
|
||||
/// <param name="component">The appearance component of the entity that might need updating.</param>
|
||||
public virtual void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the visualization of data inside of an appearance component.
|
||||
/// Implementations of this class are NOT bound to a specific entity, they are flyweighted across multiple.
|
||||
/// </summary>
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public abstract class AppearanceVisualizer
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes an entity to be managed by this appearance controller.
|
||||
/// DO NOT assume this is your only entity. Visualizers are shared.
|
||||
/// </summary>
|
||||
public virtual void InitializeEntity(EntityUid entity)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever appearance data for an entity changes.
|
||||
/// Update its visuals here.
|
||||
/// </summary>
|
||||
/// <param name="component">The appearance component of the entity that might need updating.</param>
|
||||
public virtual void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
/// <summary>
|
||||
/// This is the client instance of <see cref="AppearanceComponent"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(AppearanceComponent))]
|
||||
public sealed class ClientAppearanceComponent : AppearanceComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
private bool _appearanceDirty;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("visuals")]
|
||||
internal List<AppearanceVisualizer> Visualizers = new();
|
||||
|
||||
protected override void MarkDirty()
|
||||
{
|
||||
if (_appearanceDirty)
|
||||
return;
|
||||
|
||||
EntitySystem.Get<AppearanceSystem>().EnqueueUpdate(this);
|
||||
_appearanceDirty = true;
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
foreach (var visual in Visualizers)
|
||||
{
|
||||
visual.InitializeEntity(Owner);
|
||||
}
|
||||
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
internal void UnmarkDirty()
|
||||
{
|
||||
_appearanceDirty = false;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,6 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -14,6 +12,7 @@ namespace Robust.Client.GameObjects
|
||||
public class EyeComponent : SharedEyeComponent
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Eye";
|
||||
@@ -118,7 +117,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
_eye = new Eye
|
||||
{
|
||||
Position = Owner.Transform.MapPosition,
|
||||
Position = _entityManager.GetComponent<TransformComponent>(Owner).MapPosition,
|
||||
Zoom = _setZoomOnInitialize,
|
||||
DrawFov = _setDrawFovOnInitialize
|
||||
};
|
||||
@@ -166,7 +165,7 @@ namespace Robust.Client.GameObjects
|
||||
public void UpdateEyePosition()
|
||||
{
|
||||
if (_eye == null) return;
|
||||
var mapPos = Owner.Transform.MapPosition;
|
||||
var mapPos = _entityManager.GetComponent<TransformComponent>(Owner).MapPosition;
|
||||
_eye.Position = new MapCoordinates(mapPos.Position, mapPos.MapId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
@@ -12,6 +11,7 @@ namespace Robust.Client.GameObjects
|
||||
internal sealed class ClientOccluderComponent : OccluderComponent
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
[ViewVariables] private (GridId, Vector2i) _lastPosition;
|
||||
[ViewVariables] internal OccluderDir Occluding { get; private set; }
|
||||
@@ -32,7 +32,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
if (Owner.Transform.Anchored)
|
||||
if (_entityManager.GetComponent<TransformComponent>(Owner).Anchored)
|
||||
{
|
||||
AnchorStateChanged();
|
||||
}
|
||||
@@ -42,11 +42,11 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
SendDirty();
|
||||
|
||||
if(!Owner.Transform.Anchored)
|
||||
if(!_entityManager.GetComponent<TransformComponent>(Owner).Anchored)
|
||||
return;
|
||||
|
||||
var grid = _mapManager.GetGrid(Owner.Transform.GridID);
|
||||
_lastPosition = (Owner.Transform.GridID, grid.TileIndicesFor(Owner.Transform.Coordinates));
|
||||
var grid = _mapManager.GetGrid(_entityManager.GetComponent<TransformComponent>(Owner).GridID);
|
||||
_lastPosition = (_entityManager.GetComponent<TransformComponent>(Owner).GridID, grid.TileIndicesFor(_entityManager.GetComponent<TransformComponent>(Owner).Coordinates));
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
@@ -58,9 +58,9 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private void SendDirty()
|
||||
{
|
||||
if (Owner.Transform.Anchored)
|
||||
if (_entityManager.GetComponent<TransformComponent>(Owner).Anchored)
|
||||
{
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local,
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local,
|
||||
new OccluderDirtyEvent(Owner, _lastPosition));
|
||||
}
|
||||
}
|
||||
@@ -69,18 +69,18 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
Occluding = OccluderDir.None;
|
||||
|
||||
if (Deleted || !Owner.Transform.Anchored)
|
||||
if (Deleted || !_entityManager.GetComponent<TransformComponent>(Owner).Anchored)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var grid = _mapManager.GetGrid(_entityManager.GetComponent<TransformComponent>(Owner).GridID);
|
||||
var position = _entityManager.GetComponent<TransformComponent>(Owner).Coordinates;
|
||||
void CheckDir(Direction dir, OccluderDir oclDir)
|
||||
{
|
||||
var grid = _mapManager.GetGrid(Owner.Transform.GridID);
|
||||
var position = Owner.Transform.Coordinates;
|
||||
foreach (var neighbor in grid.GetInDir(position, dir))
|
||||
{
|
||||
if (Owner.EntityManager.TryGetComponent(neighbor, out ClientOccluderComponent? comp) && comp.Enabled)
|
||||
if (_entityManager.TryGetComponent(neighbor, out ClientOccluderComponent? comp) && comp.Enabled)
|
||||
{
|
||||
Occluding |= oclDir;
|
||||
break;
|
||||
@@ -88,10 +88,20 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
CheckDir(Direction.North, OccluderDir.North);
|
||||
CheckDir(Direction.East, OccluderDir.East);
|
||||
CheckDir(Direction.South, OccluderDir.South);
|
||||
CheckDir(Direction.West, OccluderDir.West);
|
||||
var angle = _entityManager.GetComponent<TransformComponent>(Owner).LocalRotation;
|
||||
var dirRolling = angle.GetCardinalDir();
|
||||
// dirRolling starts at effective south
|
||||
|
||||
CheckDir(dirRolling, OccluderDir.South);
|
||||
dirRolling = dirRolling.GetClockwise90Degrees();
|
||||
|
||||
CheckDir(dirRolling, OccluderDir.West);
|
||||
dirRolling = dirRolling.GetClockwise90Degrees();
|
||||
|
||||
CheckDir(dirRolling, OccluderDir.North);
|
||||
dirRolling = dirRolling.GetClockwise90Degrees();
|
||||
|
||||
CheckDir(dirRolling, OccluderDir.East);
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -12,6 +13,8 @@ namespace Robust.Client.GameObjects
|
||||
[ComponentReference(typeof(SharedPointLightComponent))]
|
||||
public class PointLightComponent : SharedPointLightComponent, ISerializationHooks
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
internal bool TreeUpdateQueued { get; set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
@@ -31,7 +34,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
if (_enabled == value) return;
|
||||
base.Enabled = value;
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PointLightUpdateEvent());
|
||||
_entityManager.EventBus.RaiseLocalEvent(Owner, new PointLightUpdateEvent());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +47,7 @@ namespace Robust.Client.GameObjects
|
||||
if (_containerOccluded == value) return;
|
||||
|
||||
_containerOccluded = value;
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PointLightUpdateEvent());
|
||||
_entityManager.EventBus.RaiseLocalEvent(Owner, new PointLightUpdateEvent());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +154,7 @@ namespace Robust.Client.GameObjects
|
||||
if (MathHelper.CloseToPercent(value, _radius)) return;
|
||||
|
||||
base.Radius = value;
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PointLightRadiusChangedEvent(this));
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local, new PointLightRadiusChangedEvent(this));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,11 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public sealed class SpriteBoundsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eye = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly RenderingTreeSystem _renderingTree = default!;
|
||||
|
||||
private SpriteBoundsOverlay? _overlay;
|
||||
|
||||
public bool Enabled
|
||||
@@ -37,15 +42,13 @@ namespace Robust.Client.GameObjects
|
||||
if (_enabled)
|
||||
{
|
||||
DebugTools.AssertNull(_overlay);
|
||||
var overlayManager = IoCManager.Resolve<IOverlayManager>();
|
||||
_overlay = new SpriteBoundsOverlay(EntitySystem.Get<RenderingTreeSystem>(), IoCManager.Resolve<IEyeManager>());
|
||||
overlayManager.AddOverlay(_overlay);
|
||||
_overlay = new SpriteBoundsOverlay(_renderingTree, _eye, _entityManager);
|
||||
_overlayManager.AddOverlay(_overlay);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_overlay == null) return;
|
||||
var overlayManager = IoCManager.Resolve<IOverlayManager>();
|
||||
overlayManager.RemoveOverlay(_overlay);
|
||||
_overlayManager.RemoveOverlay(_overlay);
|
||||
_overlay = null;
|
||||
}
|
||||
}
|
||||
@@ -58,13 +61,15 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
private readonly IEyeManager _eyeManager = default!;
|
||||
private readonly IEyeManager _eyeManager;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private RenderingTreeSystem _renderTree;
|
||||
|
||||
public SpriteBoundsOverlay(RenderingTreeSystem renderTree, IEyeManager eyeManager)
|
||||
public SpriteBoundsOverlay(RenderingTreeSystem renderTree, IEyeManager eyeManager, IEntityManager entityManager)
|
||||
{
|
||||
_renderTree = renderTree;
|
||||
_eyeManager = eyeManager;
|
||||
_entityManager = entityManager;
|
||||
}
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
@@ -75,11 +80,11 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
foreach (var comp in _renderTree.GetRenderTrees(currentMap, viewport))
|
||||
{
|
||||
var localAABB = comp.Owner.Transform.InvWorldMatrix.TransformBox(viewport);
|
||||
var localAABB = _entityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(viewport);
|
||||
|
||||
foreach (var sprite in comp.SpriteTree.QueryAabb(localAABB))
|
||||
{
|
||||
var worldPos = sprite.Owner.Transform.WorldPosition;
|
||||
var worldPos = _entityManager.GetComponent<TransformComponent>(sprite.Owner).WorldPosition;
|
||||
var bounds = sprite.CalculateBoundingBox(worldPos);
|
||||
handle.DrawRect(bounds, Color.Red.WithAlpha(0.2f));
|
||||
handle.DrawRect(bounds.Scale(0.2f).Translated(-new Vector2(0f, bounds.Extents.Y)), Color.Blue.WithAlpha(0.5f));
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -13,14 +12,11 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using DrawDepthTag = Robust.Shared.GameObjects.DrawDepth;
|
||||
@@ -34,6 +30,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
[Dependency] private readonly IResourceCache resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager prototypes = default!;
|
||||
[Dependency] private readonly IEntityManager entities = default!;
|
||||
|
||||
[DataField("visible")]
|
||||
private bool _visible = true;
|
||||
@@ -47,7 +44,7 @@ namespace Robust.Client.GameObjects
|
||||
if (_visible == value) return;
|
||||
_visible = value;
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(OwnerUid, new SpriteUpdateEvent());
|
||||
entities.EventBus.RaiseLocalEvent(Owner, new SpriteUpdateEvent());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +210,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
layer.Color = layerDatum.Color;
|
||||
layer.Rotation = layerDatum.Rotation;
|
||||
layer._offset = layerDatum.Offset;
|
||||
// If neither state: nor texture: were provided we assume that they want a blank invisible layer.
|
||||
layer.Visible = anyTextureAttempted && layerDatum.Visible;
|
||||
layer.Scale = layerDatum.Scale;
|
||||
@@ -302,7 +300,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
if (_containerOccluded == value) return;
|
||||
_containerOccluded = value;
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(OwnerUid, new SpriteUpdateEvent());
|
||||
entities.EventBus.RaiseLocalEvent(Owner, new SpriteUpdateEvent());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1259,7 +1257,7 @@ namespace Robust.Client.GameObjects
|
||||
if (worldRotation.Theta < 0)
|
||||
worldRotation = new Angle(worldRotation.Theta + Math.Tau);
|
||||
|
||||
var localMatrix = GetLocalMatrix();
|
||||
var spriteMatrix = GetLocalMatrix();
|
||||
|
||||
foreach (var layer in Layers)
|
||||
{
|
||||
@@ -1270,9 +1268,10 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var numDirs = GetLayerDirectionCount(layer);
|
||||
var layerRotation = worldRotation + layer.Rotation;
|
||||
var layerPosition = worldPosition + layerRotation.RotateVec(layer._offset);
|
||||
|
||||
CalcModelMatrix(numDirs, eyeRotation, layerRotation, worldPosition, out var modelMatrix);
|
||||
Matrix3.Multiply(ref localMatrix, ref modelMatrix, out var transformMatrix);
|
||||
CalcModelMatrix(numDirs, eyeRotation, layerRotation, layerPosition, out var modelMatrix);
|
||||
Matrix3.Multiply(ref spriteMatrix, ref modelMatrix, out var transformMatrix);
|
||||
drawingHandle.SetTransform(in transformMatrix);
|
||||
|
||||
RenderLayer(drawingHandle, layer, eyeRotation, layerRotation, overrideDirection);
|
||||
@@ -1290,7 +1289,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var layerColor = color * layer.Color;
|
||||
|
||||
var position = -(Vector2)texture.Size / (2f * EyeManager.PixelsPerMeter) + layer.Offset;
|
||||
var position = -(Vector2)texture.Size / (2f * EyeManager.PixelsPerMeter);
|
||||
var textureSize = texture.Size / (float)EyeManager.PixelsPerMeter;
|
||||
var quad = Box2.FromDimensions(position, textureSize);
|
||||
|
||||
@@ -1503,7 +1502,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
// Look this was an easy way to get bounds checks for layer updates.
|
||||
// If you really want it optimal you'll need to comb through all 2k lines of spritecomponent.
|
||||
if (Owner?.EntityManager?.EventBus != null)
|
||||
if ((Owner != default ? entities : null)?.EventBus != null)
|
||||
UpdateBounds();
|
||||
|
||||
if (_inertUpdateQueued)
|
||||
@@ -1512,7 +1511,7 @@ namespace Robust.Client.GameObjects
|
||||
_inertUpdateQueued = true;
|
||||
// Yes that null check is valid because of that stupid fucking dummy IEntity.
|
||||
// Who thought that was a good idea.
|
||||
Owner?.EntityManager?.EventBus?.RaiseEvent(EventSource.Local, new SpriteUpdateInertEvent {Sprite = this});
|
||||
(Owner != default ? entities : null)?.EventBus?.RaiseEvent(EventSource.Local, new SpriteUpdateInertEvent {Sprite = this});
|
||||
}
|
||||
|
||||
internal void DoUpdateIsInert()
|
||||
@@ -1599,7 +1598,7 @@ namespace Robust.Client.GameObjects
|
||||
builder.AppendFormat(
|
||||
"vis/depth/scl/rot/ofs/col/norot/override/dir: {0}/{1}/{2}/{3}/{4}/{5}/{6}/{8}/{7}\n",
|
||||
Visible, DrawDepth, Scale, Rotation, Offset,
|
||||
Color, NoRotation, GetDir(RSI.State.DirectionType.Dir8, Owner.Transform.WorldRotation),
|
||||
Color, NoRotation, GetDir(RSI.State.DirectionType.Dir8, entities.GetComponent<TransformComponent>(Owner).WorldRotation),
|
||||
DirectionOverride
|
||||
);
|
||||
|
||||
@@ -1653,7 +1652,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
internal void UpdateBounds()
|
||||
{
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(OwnerUid, new SpriteUpdateEvent());
|
||||
entities.EventBus.RaiseLocalEvent(Owner, new SpriteUpdateEvent());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1723,7 +1722,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 _offset;
|
||||
internal Vector2 _offset;
|
||||
|
||||
[ViewVariables]
|
||||
public DirectionOffset DirOffset { get; set; }
|
||||
@@ -2072,7 +2071,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
var dummy = entityManager.SpawnEntity(prototype.ID, MapCoordinates.Nullspace).Uid;
|
||||
var dummy = entityManager.SpawnEntity(prototype.ID, MapCoordinates.Nullspace);
|
||||
var spriteComponent = entityManager.EnsureComponent<SpriteComponent>(dummy);
|
||||
|
||||
var anyTexture = false;
|
||||
@@ -2111,7 +2110,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
var dummy = entityManager.SpawnEntity(prototype.ID, MapCoordinates.Nullspace).Uid;
|
||||
var dummy = entityManager.SpawnEntity(prototype.ID, MapCoordinates.Nullspace);
|
||||
var spriteComponent = entityManager.EnsureComponent<SpriteComponent>(dummy);
|
||||
var result = spriteComponent.Icon ?? GetFallbackState(resourceCache);
|
||||
entityManager.DeleteEntity(dummy);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -9,55 +8,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
public override string Name => "RenderingTree";
|
||||
|
||||
internal DynamicTree<SpriteComponent> SpriteTree { get; private set; } = new(SpriteAabbFunc);
|
||||
internal DynamicTree<PointLightComponent> LightTree { get; private set; } = new(LightAabbFunc);
|
||||
|
||||
private static Box2 SpriteAabbFunc(in SpriteComponent value)
|
||||
{
|
||||
var worldPos = value.Owner.Transform.WorldPosition;
|
||||
var worldRot = value.Owner.Transform.WorldRotation;
|
||||
var bounds = new Box2Rotated(value.CalculateBoundingBox(worldPos), worldRot, worldPos);
|
||||
var tree = RenderingTreeSystem.GetRenderTree(value.Owner);
|
||||
|
||||
var localAABB = tree?.Owner.Transform.InvWorldMatrix.TransformBox(bounds) ?? bounds.CalcBoundingBox();
|
||||
|
||||
return localAABB;
|
||||
}
|
||||
|
||||
private static Box2 LightAabbFunc(in PointLightComponent value)
|
||||
{
|
||||
var worldPos = value.Owner.Transform.WorldPosition;
|
||||
var tree = RenderingTreeSystem.GetRenderTree(value.Owner);
|
||||
var boxSize = value.Radius * 2;
|
||||
|
||||
var localPos = tree?.Owner.Transform.InvWorldMatrix.Transform(worldPos) ?? worldPos;
|
||||
|
||||
return Box2.CenteredAround(localPos, (boxSize, boxSize));
|
||||
}
|
||||
|
||||
internal static Box2 SpriteAabbFunc(SpriteComponent value, Vector2? worldPos = null, Angle? worldRot = null)
|
||||
{
|
||||
worldPos ??= value.Owner.Transform.WorldPosition;
|
||||
worldRot ??= value.Owner.Transform.WorldRotation;
|
||||
var bounds = new Box2Rotated(value.CalculateBoundingBox(worldPos.Value), worldRot.Value, worldPos.Value);
|
||||
var tree = RenderingTreeSystem.GetRenderTree(value.Owner);
|
||||
|
||||
var localAABB = tree?.Owner.Transform.InvWorldMatrix.TransformBox(bounds) ?? bounds.CalcBoundingBox();
|
||||
|
||||
return localAABB;
|
||||
}
|
||||
|
||||
internal static Box2 LightAabbFunc(PointLightComponent value, Vector2? worldPos = null)
|
||||
{
|
||||
// Lights are circles so don't need entity's rotation
|
||||
|
||||
worldPos ??= value.Owner.Transform.WorldPosition;
|
||||
var tree = RenderingTreeSystem.GetRenderTree(value.Owner);
|
||||
var boxSize = value.Radius * 2;
|
||||
|
||||
var localPos = tree?.Owner.Transform.InvWorldMatrix.Transform(worldPos.Value) ?? worldPos.Value;
|
||||
|
||||
return Box2.CenteredAround(localPos, (boxSize, boxSize));
|
||||
}
|
||||
internal DynamicTree<SpriteComponent> SpriteTree { get; set; } = default!;
|
||||
internal DynamicTree<PointLightComponent> LightTree { get; set; } = default!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactory _dynamicTypeFactory = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
private readonly Dictionary<object, BoundUserInterface> _openInterfaces =
|
||||
new();
|
||||
@@ -87,15 +89,15 @@ namespace Robust.Client.GameObjects
|
||||
_openInterfaces.Remove(uiKey);
|
||||
boundUserInterface.Dispose();
|
||||
|
||||
var playerSession = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.Session;
|
||||
var playerSession = _playerManager.LocalPlayer?.Session;
|
||||
if(playerSession != null)
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new BoundUIClosedEvent(uiKey, Owner.Uid, playerSession));
|
||||
_entityManager.EventBus.RaiseLocalEvent(Owner, new BoundUIClosedEvent(uiKey, Owner, playerSession));
|
||||
}
|
||||
|
||||
internal void SendMessage(BoundUserInterfaceMessage message, object uiKey)
|
||||
{
|
||||
EntitySystem.Get<UserInterfaceSystem>()
|
||||
.Send(new BoundUIWrapMessage(Owner.Uid, message, uiKey));
|
||||
.Send(new BoundUIWrapMessage(Owner, message, uiKey));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Robust.Client.GameObjects
|
||||
foreach (var key in remie)
|
||||
{
|
||||
component.PlayingAnimations.Remove(key);
|
||||
EntityManager.EventBus.RaiseLocalEvent(component.Owner.Uid, new AnimationCompletedEvent {Uid = component.Owner.Uid, Key = key});
|
||||
EntityManager.EventBus.RaiseLocalEvent(component.Owner, new AnimationCompletedEvent {Uid = component.Owner, Key = key});
|
||||
component.AnimationComplete(key);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
public void Play(EntityUid uid, Animation animation, string key)
|
||||
{
|
||||
var component = EntityManager.EnsureComponent<AnimationPlayerComponent>(EntityManager.GetEntity(uid));
|
||||
var component = EntityManager.EnsureComponent<AnimationPlayerComponent>(uid);
|
||||
Play(component, animation, key);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -7,9 +7,9 @@ namespace Robust.Client.GameObjects
|
||||
[UsedImplicitly]
|
||||
internal sealed class AppearanceSystem : EntitySystem
|
||||
{
|
||||
private readonly Queue<AppearanceComponent> _queuedUpdates = new();
|
||||
private readonly Queue<ClientAppearanceComponent> _queuedUpdates = new();
|
||||
|
||||
public void EnqueueUpdate(AppearanceComponent component)
|
||||
public void EnqueueUpdate(ClientAppearanceComponent component)
|
||||
{
|
||||
_queuedUpdates.Enqueue(component);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -82,8 +81,8 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private void PlayAudioEntityHandler(PlayAudioEntityMessage ev)
|
||||
{
|
||||
var stream = EntityManager.TryGetEntity(ev.EntityUid, out var entity) ?
|
||||
(PlayingStream?) Play(ev.FileName, entity, ev.FallbackCoordinates, ev.AudioParams)
|
||||
var stream = EntityManager.EntityExists(ev.EntityUid) ?
|
||||
(PlayingStream?) Play(ev.FileName, ev.EntityUid, ev.FallbackCoordinates, ev.AudioParams)
|
||||
: (PlayingStream?) Play(ev.FileName, ev.Coordinates, ev.FallbackCoordinates, ev.AudioParams);
|
||||
|
||||
if (stream != null)
|
||||
@@ -123,15 +122,15 @@ namespace Robust.Client.GameObjects
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (stream.TrackingEntity != null)
|
||||
else if (stream.TrackingEntity != default)
|
||||
{
|
||||
if (stream.TrackingEntity.Deleted)
|
||||
if (EntityManager.Deleted(stream.TrackingEntity))
|
||||
{
|
||||
StreamDone(stream);
|
||||
continue;
|
||||
}
|
||||
|
||||
mapPos = stream.TrackingEntity.Transform.MapPosition;
|
||||
mapPos = EntityManager.GetComponent<TransformComponent>(stream.TrackingEntity).MapPosition;
|
||||
}
|
||||
|
||||
// TODO Remove when coordinates can't be NaN
|
||||
@@ -211,7 +210,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
if (stream.TrackingEntity != null)
|
||||
if (stream.TrackingEntity != default)
|
||||
{
|
||||
stream.Source.SetVelocity(stream.TrackingEntity.GlobalLinearVelocity());
|
||||
}
|
||||
@@ -281,7 +280,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <param name="entity">The entity "emitting" the audio.</param>
|
||||
/// <param name="fallbackCoordinates">The map or grid coordinates at which to play the audio when entity is invalid.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
private IPlayingAudioStream? Play(string filename, IEntity entity, EntityCoordinates fallbackCoordinates,
|
||||
private IPlayingAudioStream? Play(string filename, EntityUid entity, EntityCoordinates fallbackCoordinates,
|
||||
AudioParams? audioParams = null)
|
||||
{
|
||||
if (_resourceCache.TryGetResource<AudioResource>(new ResourcePath(filename), out var audio))
|
||||
@@ -300,11 +299,11 @@ namespace Robust.Client.GameObjects
|
||||
/// <param name="entity">The entity "emitting" the audio.</param>
|
||||
/// <param name="fallbackCoordinates">The map or grid coordinates at which to play the audio when entity is invalid.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
private IPlayingAudioStream? Play(AudioStream stream, IEntity entity, EntityCoordinates fallbackCoordinates,
|
||||
private IPlayingAudioStream? Play(AudioStream stream, EntityUid entity, EntityCoordinates fallbackCoordinates,
|
||||
AudioParams? audioParams = null)
|
||||
{
|
||||
var source = _clyde.CreateAudioSource(stream);
|
||||
if (!source.SetPosition(entity.Transform.WorldPosition))
|
||||
if (!source.SetPosition(EntityManager.GetComponent<TransformComponent>(entity).WorldPosition))
|
||||
{
|
||||
return Play(stream, fallbackCoordinates, fallbackCoordinates, audioParams);
|
||||
}
|
||||
@@ -407,7 +406,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
public uint? NetIdentifier;
|
||||
public IClydeAudioSource Source = default!;
|
||||
public IEntity TrackingEntity = default!;
|
||||
public EntityUid TrackingEntity = default!;
|
||||
public EntityCoordinates? TrackingCoordinates;
|
||||
public EntityCoordinates? TrackingFallbackCoordinates;
|
||||
public bool Done;
|
||||
@@ -440,9 +439,6 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int DefaultSoundRange => 25;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int OcclusionCollisionMask { get; set; }
|
||||
|
||||
@@ -453,15 +449,9 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPlayingAudioStream? Play(Filter playerFilter, string filename, IEntity entity, AudioParams? audioParams = null)
|
||||
public IPlayingAudioStream? Play(Filter playerFilter, string filename, EntityUid entity, AudioParams? audioParams = null)
|
||||
{
|
||||
return Play(filename, entity, GetFallbackCoordinates(entity.Transform.MapPosition), audioParams);
|
||||
}
|
||||
|
||||
public IPlayingAudioStream? Play(Filter playerFilter, string filename, EntityUid uid, AudioParams? audioParams = null)
|
||||
{
|
||||
return EntityManager.TryGetEntity(uid, out var entity)
|
||||
? Play(filename, entity, GetFallbackCoordinates(entity.Transform.MapPosition), audioParams) : null;
|
||||
return Play(filename, entity, GetFallbackCoordinates(EntityManager.GetComponent<TransformComponent>(entity).MapPosition), audioParams);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -70,14 +70,14 @@ namespace Robust.Client.GameObjects
|
||||
private void HandleDirtyEvent(OccluderDirtyEvent ev)
|
||||
{
|
||||
var sender = ev.Sender;
|
||||
if (sender.IsValid() &&
|
||||
sender.TryGetComponent(out ClientOccluderComponent? iconSmooth)
|
||||
if (EntityManager.EntityExists(sender) &&
|
||||
EntityManager.TryGetComponent(sender, out ClientOccluderComponent? iconSmooth)
|
||||
&& iconSmooth.Running)
|
||||
{
|
||||
var grid1 = _mapManager.GetGrid(sender.Transform.GridID);
|
||||
var coords = sender.Transform.Coordinates;
|
||||
var grid1 = _mapManager.GetGrid(EntityManager.GetComponent<TransformComponent>(sender).GridID);
|
||||
var coords = EntityManager.GetComponent<TransformComponent>(sender).Coordinates;
|
||||
|
||||
_dirtyEntities.Enqueue(sender.Uid);
|
||||
_dirtyEntities.Enqueue(sender);
|
||||
AddValidEntities(grid1.GetInDir(coords, Direction.North));
|
||||
AddValidEntities(grid1.GetInDir(coords, Direction.South));
|
||||
AddValidEntities(grid1.GetInDir(coords, Direction.East));
|
||||
@@ -113,13 +113,13 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
internal sealed class OccluderDirtyEvent : EntityEventArgs
|
||||
{
|
||||
public OccluderDirtyEvent(IEntity sender, (GridId grid, Vector2i pos)? lastPosition)
|
||||
public OccluderDirtyEvent(EntityUid sender, (GridId grid, Vector2i pos)? lastPosition)
|
||||
{
|
||||
LastPosition = lastPosition;
|
||||
Sender = sender;
|
||||
}
|
||||
|
||||
public (GridId grid, Vector2i pos)? LastPosition { get; }
|
||||
public IEntity Sender { get; }
|
||||
public EntityUid Sender { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Robust.Client.GameObjects
|
||||
[Dependency] private readonly IRobustSerializer _serializer = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _dynFactory = default!;
|
||||
|
||||
private readonly HashSet<IEntity> _updateQueue = new();
|
||||
private readonly HashSet<EntityUid> _updateQueue = new();
|
||||
|
||||
public readonly Dictionary<EntityUid, IContainer> ExpectedEntities = new();
|
||||
|
||||
@@ -37,10 +37,10 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private void HandleEntityInitialized(EntityInitializedMessage ev)
|
||||
{
|
||||
if (!ExpectedEntities.TryGetValue(ev.Entity.Uid, out var container))
|
||||
if (!ExpectedEntities.TryGetValue(ev.Entity, out var container))
|
||||
return;
|
||||
|
||||
RemoveExpectedEntity(ev.Entity.Uid);
|
||||
RemoveExpectedEntity(ev.Entity);
|
||||
|
||||
if (container.Deleted)
|
||||
return;
|
||||
@@ -89,12 +89,12 @@ namespace Robust.Client.GameObjects
|
||||
container.OccludesLight = occludesLight;
|
||||
|
||||
// Remove gone entities.
|
||||
List<IEntity>? toRemove = null;
|
||||
List<EntityUid>? toRemove = null;
|
||||
foreach (var entity in container.ContainedEntities)
|
||||
{
|
||||
if (!entityUids.Contains(entity.Uid))
|
||||
if (!entityUids.Contains(entity))
|
||||
{
|
||||
toRemove ??= new List<IEntity>();
|
||||
toRemove ??= new List<EntityUid>();
|
||||
toRemove.Add(entity);
|
||||
}
|
||||
}
|
||||
@@ -123,11 +123,11 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
// Add new entities.
|
||||
foreach (var entityUid in entityUids)
|
||||
foreach (var entity in entityUids)
|
||||
{
|
||||
if (!EntityManager.TryGetEntity(entityUid, out var entity))
|
||||
if (!EntityManager.EntityExists(entity))
|
||||
{
|
||||
AddExpectedEntity(entityUid, container);
|
||||
AddExpectedEntity(entity, container);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
foreach (var toUpdate in _updateQueue)
|
||||
{
|
||||
if (toUpdate.Deleted)
|
||||
if (EntityManager.Deleted(toUpdate))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -183,22 +183,22 @@ namespace Robust.Client.GameObjects
|
||||
_updateQueue.Clear();
|
||||
}
|
||||
|
||||
private static void UpdateEntityRecursively(IEntity entity)
|
||||
private void UpdateEntityRecursively(EntityUid entity)
|
||||
{
|
||||
// TODO: Since we are recursing down,
|
||||
// we could cache ShowContents data here to speed it up for children.
|
||||
// Am lazy though.
|
||||
UpdateEntity(entity);
|
||||
|
||||
foreach (var child in entity.Transform.Children)
|
||||
foreach (var child in EntityManager.GetComponent<TransformComponent>(entity).Children)
|
||||
{
|
||||
UpdateEntityRecursively(child.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateEntity(IEntity entity)
|
||||
private void UpdateEntity(EntityUid entity)
|
||||
{
|
||||
if (entity.TryGetComponent(out SpriteComponent? sprite))
|
||||
if (EntityManager.TryGetComponent(entity, out SpriteComponent? sprite))
|
||||
{
|
||||
sprite.ContainerOccluded = false;
|
||||
|
||||
@@ -216,7 +216,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out PointLightComponent? light))
|
||||
if (EntityManager.TryGetComponent(entity, out PointLightComponent? light))
|
||||
{
|
||||
light.ContainerOccluded = false;
|
||||
|
||||
|
||||
@@ -77,11 +77,11 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
for (var i = 0; i < message.Entities.Count; i++)
|
||||
{
|
||||
var uid = message.Entities[i];
|
||||
var entity = message.Entities[i];
|
||||
|
||||
if (!EntityManager.TryGetEntity(uid, out var entity)) continue;
|
||||
if (!EntityManager.EntityExists(entity)) continue;
|
||||
|
||||
text.AppendLine(entity.ToString());
|
||||
text.AppendLine((string) EntityManager.ToPrettyString(entity));
|
||||
}
|
||||
|
||||
_label.Text = text.ToString();
|
||||
|
||||
@@ -23,7 +23,6 @@ namespace Robust.Client.GameObjects
|
||||
[Dependency] private readonly IOverlayManager overlayManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager prototypeManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
private readonly List<Effect> _Effects = new();
|
||||
@@ -35,7 +34,7 @@ namespace Robust.Client.GameObjects
|
||||
SubscribeNetworkEvent<EffectSystemMessage>(CreateEffect);
|
||||
SubscribeLocalEvent<EffectSystemMessage>(CreateEffect);
|
||||
|
||||
var overlay = new EffectOverlay(this, prototypeManager, _mapManager, _playerManager, _entityManager);
|
||||
var overlay = new EffectOverlay(this, prototypeManager, _playerManager, EntityManager);
|
||||
overlayManager.AddOverlay(overlay);
|
||||
}
|
||||
|
||||
@@ -65,13 +64,8 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
//Create effect from creation message
|
||||
var effect = new Effect(message, resourceCache, _mapManager, _entityManager);
|
||||
var effect = new Effect(message, resourceCache, _mapManager, EntityManager);
|
||||
effect.Deathtime = gameTiming.CurTime + message.LifeTime;
|
||||
if (effect.AttachedEntityUid != null
|
||||
&& _entityManager.TryGetEntity(effect.AttachedEntityUid.Value, out var attachedEntity))
|
||||
{
|
||||
effect.AttachedEntity = attachedEntity;
|
||||
}
|
||||
|
||||
_Effects.Add(effect);
|
||||
}
|
||||
@@ -119,8 +113,6 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Entity that the effect is attached to
|
||||
/// </summary>
|
||||
public IEntity? AttachedEntity { get; set; }
|
||||
|
||||
public EntityUid? AttachedEntityUid { get; }
|
||||
|
||||
/// <summary>
|
||||
@@ -335,14 +327,12 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private readonly ShaderInstance _unshadedShader;
|
||||
private readonly EffectSystem _owner;
|
||||
private readonly IMapManager _mapManager;
|
||||
private readonly IEntityManager _entityManager;
|
||||
|
||||
public EffectOverlay(EffectSystem owner, IPrototypeManager protoMan, IMapManager mapMan, IPlayerManager playerMan, IEntityManager entityManager)
|
||||
public EffectOverlay(EffectSystem owner, IPrototypeManager protoMan, IPlayerManager playerMan, IEntityManager entityManager)
|
||||
{
|
||||
_owner = owner;
|
||||
_unshadedShader = protoMan.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_mapManager = mapMan;
|
||||
_playerManager = playerMan;
|
||||
_entityManager = entityManager;
|
||||
}
|
||||
@@ -353,12 +343,15 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var worldHandle = args.WorldHandle;
|
||||
ShaderInstance? currentShader = null;
|
||||
var player = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
|
||||
if (_playerManager.LocalPlayer?.ControlledEntity is not {} playerEnt)
|
||||
return;
|
||||
|
||||
foreach (var effect in _owner._Effects)
|
||||
{
|
||||
if (effect.AttachedEntity?.Transform.MapID != player?.Transform.MapID &&
|
||||
effect.Coordinates.GetMapId(_entityManager) != map)
|
||||
if(effect.AttachedEntityUid is {} attached
|
||||
&& _entityManager.GetComponent<TransformComponent>(attached).MapID != _entityManager.GetComponent<TransformComponent>(playerEnt).MapID
|
||||
&& effect.Coordinates.GetMapId(_entityManager) != map)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -371,13 +364,19 @@ namespace Robust.Client.GameObjects
|
||||
currentShader = newShader;
|
||||
}
|
||||
|
||||
// TODO: Should be doing matrix transformations
|
||||
var effectSprite = effect.EffectSprite;
|
||||
var effectOrigin = effect.AttachedEntity?.Transform.MapPosition.Position + effect.AttachedOffset ??
|
||||
effect.Coordinates.ToMapPos(_entityManager);
|
||||
|
||||
var tempQualifier1 = effect.AttachedEntityUid;
|
||||
var coordinates =
|
||||
(tempQualifier1 != null ? _entityManager.GetComponent<TransformComponent>(tempQualifier1.Value).Coordinates : effect.Coordinates)
|
||||
.Offset(effect.AttachedOffset);
|
||||
|
||||
var rotation = _entityManager.GetComponent<TransformComponent>(coordinates.EntityId).WorldRotation;
|
||||
var effectOrigin = coordinates.ToMapPos(_entityManager);
|
||||
|
||||
var effectArea = Box2.CenteredAround(effectOrigin, effect.Size);
|
||||
|
||||
var rotatedBox = new Box2Rotated(effectArea, effect.Rotation, effectOrigin);
|
||||
var rotatedBox = new Box2Rotated(effectArea, effect.Rotation - rotation, effectOrigin);
|
||||
|
||||
worldHandle.DrawTextureRect(effectSprite, rotatedBox, ToColor(effect.Color));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Physics;
|
||||
@@ -9,7 +9,6 @@ using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
#nullable enable
|
||||
|
||||
@@ -70,15 +69,23 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
// TODO: Content should have its own way of handling this. We should have a default behavior that they can overwrite.
|
||||
|
||||
var playerTransform = _playerManager.LocalPlayer?.ControlledEntity?.Transform;
|
||||
EntityUid tempQualifier = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
var playerTransform = (tempQualifier != EntityUid.Invalid ? EntityManager.GetComponent<TransformComponent>(tempQualifier) : null);
|
||||
|
||||
if (playerTransform == null) return;
|
||||
|
||||
var gridId = playerTransform.GridID;
|
||||
|
||||
var parent = gridId != GridId.Invalid && EntityManager.TryGetEntity(_mapManager.GetGrid(gridId).GridEntityId, out var gridEnt) ?
|
||||
gridEnt.Transform
|
||||
: _mapManager.GetMapEntity(playerTransform.MapID).Transform;
|
||||
TransformComponent parent;
|
||||
if (gridId != GridId.Invalid &&
|
||||
_mapManager.GetGrid(gridId).GridEntityId is var gridEnt &&
|
||||
EntityManager.EntityExists(gridEnt))
|
||||
parent = EntityManager.GetComponent<TransformComponent>(gridEnt);
|
||||
else
|
||||
{
|
||||
parent = EntityManager.GetComponent<TransformComponent>(
|
||||
_mapManager.GetMapEntityId(playerTransform.MapID));
|
||||
}
|
||||
|
||||
// Make sure that we don't fire the vomit carousel when we spawn in
|
||||
if (_lastParent is null)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -25,7 +28,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
DebugTools.Assert(_overlay == null);
|
||||
_overlay = new GridChunkBoundsOverlay(
|
||||
IoCManager.Resolve<IEntityManager>(),
|
||||
EntityManager,
|
||||
IoCManager.Resolve<IEyeManager>(),
|
||||
IoCManager.Resolve<IMapManager>());
|
||||
|
||||
@@ -61,23 +64,38 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
var currentMap = _eyeManager.CurrentMap;
|
||||
var viewport = _eyeManager.GetWorldViewport();
|
||||
var worldHandle = args.WorldHandle;
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(currentMap, viewport))
|
||||
{
|
||||
var gridEnt = _entityManager.GetEntity(grid.GridEntityId);
|
||||
var gridInternal = (IMapGridInternal)grid;
|
||||
var worldMatrix = _entityManager.GetComponent<TransformComponent>(grid.GridEntityId).WorldMatrix;
|
||||
worldHandle.SetTransform(worldMatrix);
|
||||
var transform = new Transform(Vector2.Zero, Angle.Zero);
|
||||
|
||||
if (!_entityManager.TryGetComponent<PhysicsComponent>(gridEnt.Uid, out var body)) continue;
|
||||
gridInternal.GetMapChunks(viewport, out var chunkEnumerator);
|
||||
|
||||
var transform = body.GetTransform();
|
||||
|
||||
foreach (var fixture in body.Fixtures)
|
||||
while (chunkEnumerator.MoveNext(out var chunk))
|
||||
{
|
||||
for (var i = 0; i < fixture.Shape.ChildCount; i++)
|
||||
foreach (var fixture in chunk.Fixtures)
|
||||
{
|
||||
var aabb = fixture.Shape.ComputeAABB(transform, i);
|
||||
var poly = (PolygonShape) fixture.Shape;
|
||||
|
||||
args.WorldHandle.DrawRect(aabb, Color.Green.WithAlpha(0.2f));
|
||||
args.WorldHandle.DrawRect(aabb, Color.Red.WithAlpha(0.5f), false);
|
||||
var verts = new Vector2[poly.Vertices.Length];
|
||||
|
||||
for (var i = 0; i < poly.Vertices.Length; i++)
|
||||
{
|
||||
verts[i] = Transform.Mul(transform, poly.Vertices[i]);
|
||||
}
|
||||
|
||||
worldHandle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts, Color.Green.WithAlpha(0.2f));
|
||||
|
||||
for (var i = 0; i < fixture.Shape.ChildCount; i++)
|
||||
{
|
||||
var aabb = fixture.Shape.ComputeAABB(transform, i);
|
||||
|
||||
args.WorldHandle.DrawRect(aabb, Color.Red.WithAlpha(0.5f), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
@@ -107,7 +107,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private void OnAttachedEntityChanged(PlayerAttachSysMessage message)
|
||||
{
|
||||
if (message.AttachedEntity != null) // attach
|
||||
if (message.AttachedEntity != default) // attach
|
||||
{
|
||||
SetEntityContextActive(_inputManager, message.AttachedEntity);
|
||||
}
|
||||
@@ -117,14 +117,14 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetEntityContextActive(IInputManager inputMan, IEntity entity)
|
||||
private void SetEntityContextActive(IInputManager inputMan, EntityUid entity)
|
||||
{
|
||||
if(entity == null || !entity.IsValid())
|
||||
if(entity == default || !EntityManager.EntityExists(entity))
|
||||
throw new ArgumentNullException(nameof(entity));
|
||||
|
||||
if (!entity.TryGetComponent(out InputComponent? inputComp))
|
||||
if (!EntityManager.TryGetComponent(entity, out InputComponent? inputComp))
|
||||
{
|
||||
Logger.DebugS("input.context", $"AttachedEnt has no InputComponent: entId={entity.Uid}, entProto={entity.Prototype}. Setting default \"{InputContextContainer.DefaultContextName}\" context...");
|
||||
Logger.DebugS("input.context", $"AttachedEnt has no InputComponent: entId={entity}, entProto={EntityManager.GetComponent<MetaDataComponent>(entity).EntityPrototype}. Setting default \"{InputContextContainer.DefaultContextName}\" context...");
|
||||
inputMan.Contexts.SetActiveContext(InputContextContainer.DefaultContextName);
|
||||
return;
|
||||
}
|
||||
@@ -135,7 +135,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.ErrorS("input.context", $"Unknown context: entId={entity.Uid}, entProto={entity.Prototype}, context={inputComp.ContextName}. . Setting default \"{InputContextContainer.DefaultContextName}\" context...");
|
||||
Logger.ErrorS("input.context", $"Unknown context: entId={entity}, entProto={EntityManager.GetComponent<MetaDataComponent>(entity).EntityPrototype}, context={inputComp.ContextName}. . Setting default \"{InputContextContainer.DefaultContextName}\" context...");
|
||||
inputMan.Contexts.SetActiveContext(InputContextContainer.DefaultContextName);
|
||||
}
|
||||
}
|
||||
@@ -145,12 +145,13 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
public void SetEntityContextActive()
|
||||
{
|
||||
if (_playerManager.LocalPlayer?.ControlledEntity == null)
|
||||
var controlled = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
if (controlled == EntityUid.Invalid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetEntityContextActive(_inputManager, _playerManager.LocalPlayer.ControlledEntity);
|
||||
SetEntityContextActive(_inputManager, controlled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,13 +163,13 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// New entity the player is attached to.
|
||||
/// </summary>
|
||||
public IEntity? AttachedEntity { get; }
|
||||
public EntityUid AttachedEntity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="PlayerAttachSysMessage"/>.
|
||||
/// </summary>
|
||||
/// <param name="attachedEntity">New entity the player is attached to.</param>
|
||||
public PlayerAttachSysMessage(IEntity? attachedEntity)
|
||||
public PlayerAttachSysMessage(EntityUid attachedEntity)
|
||||
{
|
||||
AttachedEntity = attachedEntity;
|
||||
}
|
||||
@@ -176,21 +177,21 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public class PlayerAttachedEvent : EntityEventArgs
|
||||
{
|
||||
public PlayerAttachedEvent(IEntity entity)
|
||||
public PlayerAttachedEvent(EntityUid entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
public IEntity Entity { get; }
|
||||
public EntityUid Entity { get; }
|
||||
}
|
||||
|
||||
public class PlayerDetachedEvent : EntityEventArgs
|
||||
{
|
||||
public PlayerDetachedEvent(IEntity entity)
|
||||
public PlayerDetachedEvent(EntityUid entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
public IEntity Entity { get; }
|
||||
public EntityUid Entity { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private void HandleRemove(EntityUid uid, PointLightComponent component, ComponentRemove args)
|
||||
{
|
||||
var ent = EntityManager.GetEntity(uid);
|
||||
var map = ent.Transform.MapID;
|
||||
var map = EntityManager.GetComponent<TransformComponent>(uid).MapID;
|
||||
// TODO: Just make this update the tree directly and not allocate
|
||||
if (map != MapId.Nullspace)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Physics;
|
||||
using Robust.Shared;
|
||||
@@ -11,7 +9,6 @@ using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
@@ -30,7 +27,7 @@ namespace Robust.Client.GameObjects
|
||||
private readonly List<SpriteComponent> _spriteQueue = new();
|
||||
private readonly List<PointLightComponent> _lightQueue = new();
|
||||
|
||||
private HashSet<EntityUid> _checkedChildren = new();
|
||||
private readonly HashSet<EntityUid> _checkedChildren = new();
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="CVars.MaxLightRadius"/>
|
||||
@@ -43,10 +40,12 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldBounds))
|
||||
{
|
||||
yield return EntityManager.GetEntity(grid.GridEntityId).GetComponent<RenderingTreeComponent>();
|
||||
var tempQualifier = grid.GridEntityId;
|
||||
yield return EntityManager.GetComponent<RenderingTreeComponent>(tempQualifier);
|
||||
}
|
||||
|
||||
yield return _mapManager.GetMapEntity(mapId).GetComponent<RenderingTreeComponent>();
|
||||
var tempQualifier1 = _mapManager.GetMapEntityId(mapId);
|
||||
yield return EntityManager.GetComponent<RenderingTreeComponent>(tempQualifier1);
|
||||
}
|
||||
|
||||
internal IEnumerable<RenderingTreeComponent> GetRenderTrees(MapId mapId, Box2 worldAABB)
|
||||
@@ -55,10 +54,12 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldAABB))
|
||||
{
|
||||
yield return EntityManager.GetEntity(grid.GridEntityId).GetComponent<RenderingTreeComponent>();
|
||||
var tempQualifier = grid.GridEntityId;
|
||||
yield return EntityManager.GetComponent<RenderingTreeComponent>(tempQualifier);
|
||||
}
|
||||
|
||||
yield return _mapManager.GetMapEntity(mapId).GetComponent<RenderingTreeComponent>();
|
||||
var tempQualifier1 = _mapManager.GetMapEntityId(mapId);
|
||||
yield return EntityManager.GetComponent<RenderingTreeComponent>(tempQualifier1);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
@@ -86,12 +87,19 @@ namespace Robust.Client.GameObjects
|
||||
SubscribeLocalEvent<PointLightComponent, RenderTreeRemoveLightEvent>(RemoveLight);
|
||||
SubscribeLocalEvent<PointLightComponent, PointLightUpdateEvent>(HandleLightUpdate);
|
||||
|
||||
SubscribeLocalEvent<RenderingTreeComponent, ComponentRemove>(HandleTreeRemove);
|
||||
SubscribeLocalEvent<RenderingTreeComponent, ComponentInit>(OnTreeInit);
|
||||
SubscribeLocalEvent<RenderingTreeComponent, ComponentRemove>(OnTreeRemove);
|
||||
|
||||
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
configManager.OnValueChanged(CVars.MaxLightRadius, value => MaxLightRadius = value, true);
|
||||
}
|
||||
|
||||
private void OnTreeInit(EntityUid uid, RenderingTreeComponent component, ComponentInit args)
|
||||
{
|
||||
component.LightTree = new DynamicTree<PointLightComponent>(LightAabbFunc);
|
||||
component.SpriteTree = new DynamicTree<SpriteComponent>(SpriteAabbFunc);
|
||||
}
|
||||
|
||||
private void HandleLightUpdate(EntityUid uid, PointLightComponent component, PointLightUpdateEvent args)
|
||||
{
|
||||
if (component.TreeUpdateQueued) return;
|
||||
@@ -106,23 +114,23 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private void AnythingMoved(ref MoveEvent args)
|
||||
{
|
||||
AnythingMovedSubHandler(args.Sender.Transform);
|
||||
AnythingMovedSubHandler(EntityManager.GetComponent<TransformComponent>(args.Sender));
|
||||
}
|
||||
|
||||
private void AnythingMovedSubHandler(TransformComponent sender)
|
||||
{
|
||||
// To avoid doing redundant updates (and we don't need to update a grid's children ever)
|
||||
if (!_checkedChildren.Add(sender.Owner.Uid) ||
|
||||
sender.Owner.HasComponent<RenderingTreeComponent>()) return;
|
||||
if (!_checkedChildren.Add(sender.Owner) ||
|
||||
EntityManager.HasComponent<RenderingTreeComponent>(sender.Owner)) return;
|
||||
|
||||
// This recursive search is needed, as MoveEvent is defined to not care about indirect events like children.
|
||||
// WHATEVER YOU DO, DON'T REPLACE THIS WITH SPAMMING EVENTS UNLESS YOU HAVE A GUARANTEE IT WON'T LAG THE GC.
|
||||
// (Struct-based events ok though)
|
||||
// Ironically this was lagging the GC lolz
|
||||
if (sender.Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
if (EntityManager.TryGetComponent(sender.Owner, out SpriteComponent? sprite))
|
||||
QueueSpriteUpdate(sprite);
|
||||
|
||||
if (sender.Owner.TryGetComponent(out PointLightComponent? light))
|
||||
if (EntityManager.TryGetComponent(sender.Owner, out PointLightComponent? light))
|
||||
QueueLightUpdate(light);
|
||||
|
||||
foreach (TransformComponent child in sender.Children)
|
||||
@@ -214,7 +222,7 @@ namespace Robust.Client.GameObjects
|
||||
_mapManager.OnGridCreated -= MapManagerOnGridCreated;
|
||||
}
|
||||
|
||||
private void HandleTreeRemove(EntityUid uid, RenderingTreeComponent component, ComponentRemove args)
|
||||
private void OnTreeRemove(EntityUid uid, RenderingTreeComponent component, ComponentRemove args)
|
||||
{
|
||||
foreach (var sprite in component.SpriteTree)
|
||||
{
|
||||
@@ -237,27 +245,31 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
_mapManager.GetMapEntity(e.Map).EnsureComponent<RenderingTreeComponent>();
|
||||
EntityManager.EnsureComponent<RenderingTreeComponent>(_mapManager.GetMapEntityId(e.Map));
|
||||
}
|
||||
|
||||
private void MapManagerOnGridCreated(MapId mapId, GridId gridId)
|
||||
{
|
||||
EntityManager.GetEntity(_mapManager.GetGrid(gridId).GridEntityId).EnsureComponent<RenderingTreeComponent>();
|
||||
EntityManager.EnsureComponent<RenderingTreeComponent>(_mapManager.GetGrid(gridId).GridEntityId);
|
||||
}
|
||||
|
||||
internal static RenderingTreeComponent? GetRenderTree(IEntity entity)
|
||||
// TODO: Pass in TransformComponent directly: mainly interested in getting this shit working atm.
|
||||
private RenderingTreeComponent? GetRenderTree(EntityUid entity)
|
||||
{
|
||||
if (entity.Transform.MapID == MapId.Nullspace ||
|
||||
entity.HasComponent<RenderingTreeComponent>()) return null;
|
||||
if (!EntityManager.EntityExists(entity) ||
|
||||
!EntityManager.TryGetComponent(entity, out TransformComponent? xform) ||
|
||||
xform.MapID == MapId.Nullspace ||
|
||||
EntityManager.HasComponent<RenderingTreeComponent>(entity)) return null;
|
||||
|
||||
var parent = entity.Transform.Parent?.Owner;
|
||||
var parent = xform.ParentUid;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (parent == null) break;
|
||||
if (!parent.IsValid())
|
||||
break;
|
||||
|
||||
if (parent.TryGetComponent(out RenderingTreeComponent? comp)) return comp;
|
||||
parent = parent.Transform.Parent?.Owner;
|
||||
if (EntityManager.TryGetComponent(parent, out RenderingTreeComponent? comp)) return comp;
|
||||
parent = EntityManager.GetComponent<TransformComponent>(parent).ParentUid;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -265,7 +277,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private bool IsVisible(SpriteComponent component)
|
||||
{
|
||||
return component.Visible && !component.ContainerOccluded;
|
||||
return component.Visible && !component.ContainerOccluded && !component.Deleted;
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
@@ -284,7 +296,8 @@ namespace Robust.Client.GameObjects
|
||||
var oldMapTree = sprite.RenderTree;
|
||||
var newMapTree = GetRenderTree(sprite.Owner);
|
||||
// TODO: Temp PVS guard
|
||||
var worldPos = sprite.Owner.Transform.WorldPosition;
|
||||
var xform = EntityManager.GetComponent<TransformComponent>(sprite.Owner);
|
||||
var (worldPos, worldRot) = xform.GetWorldPositionRotation();
|
||||
|
||||
if (float.IsNaN(worldPos.X) || float.IsNaN(worldPos.Y))
|
||||
{
|
||||
@@ -292,7 +305,7 @@ namespace Robust.Client.GameObjects
|
||||
continue;
|
||||
}
|
||||
|
||||
var aabb = RenderingTreeComponent.SpriteAabbFunc(sprite, worldPos);
|
||||
var aabb = SpriteAabbFunc(sprite, worldPos, worldRot);
|
||||
|
||||
// If we're on a new map then clear the old one.
|
||||
if (oldMapTree != newMapTree)
|
||||
@@ -312,7 +325,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
light.TreeUpdateQueued = false;
|
||||
|
||||
if (!light.Enabled || light.ContainerOccluded)
|
||||
if (light.Deleted || !light.Enabled || light.ContainerOccluded)
|
||||
{
|
||||
ClearLight(light);
|
||||
continue;
|
||||
@@ -321,7 +334,7 @@ namespace Robust.Client.GameObjects
|
||||
var oldMapTree = light.RenderTree;
|
||||
var newMapTree = GetRenderTree(light.Owner);
|
||||
// TODO: Temp PVS guard
|
||||
var worldPos = light.Owner.Transform.WorldPosition;
|
||||
var worldPos = EntityManager.GetComponent<TransformComponent>(light.Owner).WorldPosition;
|
||||
|
||||
if (float.IsNaN(worldPos.X) || float.IsNaN(worldPos.Y))
|
||||
{
|
||||
@@ -336,7 +349,7 @@ namespace Robust.Client.GameObjects
|
||||
Logger.WarningS(LoggerSawmill, $"Light radius for {light.Owner} set above max radius of {MaxLightRadius}. This may lead to pop-in.");
|
||||
}
|
||||
|
||||
var aabb = RenderingTreeComponent.LightAabbFunc(light, worldPos);
|
||||
var aabb = LightAabbFunc(light, worldPos);
|
||||
|
||||
// If we're on a new map then clear the old one.
|
||||
if (oldMapTree != newMapTree)
|
||||
@@ -355,6 +368,74 @@ namespace Robust.Client.GameObjects
|
||||
_spriteQueue.Clear();
|
||||
_lightQueue.Clear();
|
||||
}
|
||||
|
||||
private Box2 SpriteAabbFunc(in SpriteComponent value)
|
||||
{
|
||||
var xform = EntityManager.GetComponent<TransformComponent>(value.Owner);
|
||||
var (worldPos, worldRot) = xform.GetWorldPositionRotation();
|
||||
var bounds = new Box2Rotated(value.CalculateBoundingBox(worldPos), worldRot, worldPos);
|
||||
var tree = GetRenderTree(value.Owner);
|
||||
|
||||
if (tree == null)
|
||||
{
|
||||
return bounds.CalcBoundingBox();
|
||||
}
|
||||
else
|
||||
{
|
||||
return EntityManager.GetComponent<TransformComponent>(tree.Owner).InvWorldMatrix.TransformBox(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
private Box2 LightAabbFunc(in PointLightComponent value)
|
||||
{
|
||||
var worldPos = EntityManager.GetComponent<TransformComponent>(value.Owner).WorldPosition;
|
||||
var tree = GetRenderTree(value.Owner);
|
||||
var boxSize = value.Radius * 2;
|
||||
|
||||
Vector2 localPos;
|
||||
if (tree == null)
|
||||
{
|
||||
localPos = worldPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Need a way to just cache this InvWorldMatrix
|
||||
localPos = EntityManager.GetComponent<TransformComponent>(tree.Owner).InvWorldMatrix.Transform(worldPos);
|
||||
}
|
||||
return Box2.CenteredAround(localPos, (boxSize, boxSize));
|
||||
}
|
||||
|
||||
private Box2 SpriteAabbFunc(SpriteComponent value, Vector2 worldPos, Angle worldRot)
|
||||
{
|
||||
var bounds = new Box2Rotated(value.CalculateBoundingBox(worldPos), worldRot, worldPos);
|
||||
var tree = GetRenderTree(value.Owner);
|
||||
|
||||
if (tree == null)
|
||||
{
|
||||
return bounds.CalcBoundingBox();
|
||||
}
|
||||
else
|
||||
{
|
||||
return EntityManager.GetComponent<TransformComponent>(tree.Owner).InvWorldMatrix.TransformBox(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
private Box2 LightAabbFunc(PointLightComponent value, Vector2 worldPos)
|
||||
{
|
||||
// Lights are circles so don't need entity's rotation
|
||||
var tree = GetRenderTree(value.Owner);
|
||||
var boxSize = value.Radius * 2;
|
||||
|
||||
Vector2 localPos;
|
||||
if (tree == null)
|
||||
{
|
||||
localPos = worldPos;
|
||||
} else
|
||||
{
|
||||
localPos = EntityManager.GetComponent<TransformComponent>(tree.Owner).InvWorldMatrix.Transform(worldPos);
|
||||
}
|
||||
return Box2.CenteredAround(localPos, (boxSize, boxSize));
|
||||
}
|
||||
}
|
||||
|
||||
internal class RenderTreeRemoveLightEvent : EntityEventArgs
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
foreach (var comp in _treeSystem.GetRenderTrees(currentMap, pvsBounds))
|
||||
{
|
||||
var bounds = comp.Owner.Transform.InvWorldMatrix.TransformBox(pvsBounds);
|
||||
var bounds = EntityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(pvsBounds);
|
||||
|
||||
comp.SpriteTree.QueryAabb(ref frameTime, (ref float state, in SpriteComponent value) =>
|
||||
{
|
||||
|
||||
@@ -35,13 +35,13 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var player = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
|
||||
if (player == null || !player.TryGetComponent(out PhysicsComponent? body))
|
||||
if (player == null || !EntityManager.TryGetComponent(player.Value, out PhysicsComponent? body))
|
||||
{
|
||||
_label.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var screenPos = _eyeManager.WorldToScreen(player.Transform.WorldPosition);
|
||||
var screenPos = _eyeManager.WorldToScreen(EntityManager.GetComponent<TransformComponent>(player.Value).WorldPosition);
|
||||
LayoutContainer.SetPosition(_label, screenPos + new Vector2(0, 50));
|
||||
_label.Visible = true;
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
// These methods are used by the Game State Manager.
|
||||
|
||||
IEntity CreateEntity(string? prototypeName, EntityUid? uid = null);
|
||||
EntityUid CreateEntity(string? prototypeName, EntityUid uid = default);
|
||||
|
||||
void InitializeEntity(IEntity entity);
|
||||
void InitializeEntity(EntityUid entity);
|
||||
|
||||
void StartEntity(IEntity entity);
|
||||
void StartEntity(EntityUid entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
@@ -39,6 +40,8 @@ namespace Robust.Client.GameStates
|
||||
_pendingSystemMessages
|
||||
= new();
|
||||
|
||||
private readonly Dictionary<EntityUid, MapId> _hiddenEntities = new();
|
||||
|
||||
private uint _metaCompNetId;
|
||||
|
||||
[Dependency] private readonly IComponentFactory _compFactory = default!;
|
||||
@@ -140,8 +143,7 @@ namespace Robust.Client.GameStates
|
||||
message.InputSequence = _nextInputCmdSeq;
|
||||
_pendingInputs.Enqueue(message);
|
||||
|
||||
var inputMan = IoCManager.Resolve<IInputManager>();
|
||||
inputMan.NetworkBindMap.TryGetKeyFunction(message.InputFunctionId, out var boundFunc);
|
||||
_inputManager.NetworkBindMap.TryGetKeyFunction(message.InputFunctionId, out var boundFunc);
|
||||
Logger.DebugS(CVars.NetPredict.Name,
|
||||
$"CL> SENT tick={_timing.CurTick}, sub={_timing.TickFraction}, seq={_nextInputCmdSeq}, func={boundFunc.FunctionName}, state={message.State}");
|
||||
_nextInputCmdSeq++;
|
||||
@@ -338,21 +340,21 @@ namespace Robust.Client.GameStates
|
||||
foreach (var entity in _entities.GetEntities())
|
||||
{
|
||||
// TODO: 99% there's an off-by-one here.
|
||||
if (entity.Uid.IsClientSide() || entity.LastModifiedTick < curTick)
|
||||
if (entity.IsClientSide() || _entityManager.GetComponent<MetaDataComponent>(entity).EntityLastModifiedTick < curTick)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.DebugS(CVars.NetPredict.Name, $"Entity {entity.Uid} was made dirty.");
|
||||
Logger.DebugS(CVars.NetPredict.Name, $"Entity {entity} was made dirty.");
|
||||
|
||||
if (!_processor.TryGetLastServerStates(entity.Uid, out var last))
|
||||
if (!_processor.TryGetLastServerStates(entity, out var last))
|
||||
{
|
||||
// Entity was probably deleted on the server so do nothing.
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: handle component deletions/creations.
|
||||
foreach (var (netId, comp) in _entityManager.GetNetComponents(entity.Uid))
|
||||
foreach (var (netId, comp) in _entityManager.GetNetComponents(entity))
|
||||
{
|
||||
DebugTools.AssertNotNull(netId);
|
||||
|
||||
@@ -379,7 +381,6 @@ namespace Robust.Client.GameStates
|
||||
var outputData = new Dictionary<EntityUid, Dictionary<uint, ComponentState>>();
|
||||
|
||||
Debug.Assert(_players.LocalPlayer != null, "_players.LocalPlayer != null");
|
||||
var player = _players.LocalPlayer.Session;
|
||||
|
||||
var bus = _entityManager.EventBus;
|
||||
|
||||
@@ -390,7 +391,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var (netId, component) in _entityManager.GetNetComponents(createdEntity))
|
||||
{
|
||||
var state = _entityManager.GetComponentState(bus, component, player);
|
||||
var state = _entityManager.GetComponentState(bus, component);
|
||||
|
||||
if(state.GetType() == typeof(ComponentState))
|
||||
continue;
|
||||
@@ -425,44 +426,55 @@ namespace Robust.Client.GameStates
|
||||
private List<EntityUid> ApplyEntityStates(ReadOnlySpan<EntityState> curEntStates, ReadOnlySpan<EntityUid> deletions,
|
||||
ReadOnlySpan<EntityState> nextEntStates)
|
||||
{
|
||||
var toApply = new Dictionary<IEntity, (EntityState?, EntityState?)>();
|
||||
var toInitialize = new List<Entity>();
|
||||
var toApply = new Dictionary<EntityUid, (EntityState?, EntityState?)>();
|
||||
var toInitialize = new List<EntityUid>();
|
||||
var created = new List<EntityUid>();
|
||||
var toHide = new List<EntityUid>();
|
||||
var toShow = new List<EntityUid>();
|
||||
|
||||
foreach (var es in curEntStates)
|
||||
{
|
||||
var uid = es.Uid;
|
||||
//Known entities
|
||||
if (_entities.TryGetEntity(es.Uid, out var entity))
|
||||
if (_entities.EntityExists(uid))
|
||||
{
|
||||
// Logger.Debug($"[{IGameTiming.TickStampStatic}] MOD {es.Uid}");
|
||||
toApply.Add(entity, (es, null));
|
||||
toApply.Add(uid, (es, null));
|
||||
if(_hiddenEntities.ContainsKey(uid))
|
||||
toShow.Add(uid);
|
||||
uid = es.Uid;
|
||||
}
|
||||
else //Unknown entities
|
||||
{
|
||||
var metaState = (MetaDataComponentState?) es.ComponentChanges.Value?.FirstOrDefault(c => c.NetID == _metaCompNetId).State;
|
||||
if (metaState == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Server sent new entity state for {es.Uid} without metadata component!");
|
||||
throw new InvalidOperationException($"Server sent new entity state for {uid} without metadata component!");
|
||||
}
|
||||
// Logger.Debug($"[{IGameTiming.TickStampStatic}] CREATE {es.Uid} {metaState.PrototypeId}");
|
||||
var newEntity = (Entity)_entities.CreateEntity(metaState.PrototypeId, es.Uid);
|
||||
var newEntity = _entities.CreateEntity(metaState.PrototypeId, uid);
|
||||
toApply.Add(newEntity, (es, null));
|
||||
toInitialize.Add(newEntity);
|
||||
created.Add(newEntity.Uid);
|
||||
created.Add(newEntity);
|
||||
uid = newEntity;
|
||||
}
|
||||
if(es.Hide)
|
||||
toHide.Add(uid);
|
||||
}
|
||||
|
||||
foreach (var es in nextEntStates)
|
||||
{
|
||||
if (_entities.TryGetEntity(es.Uid, out var entity))
|
||||
var uid = es.Uid;
|
||||
|
||||
if (_entities.EntityExists(uid))
|
||||
{
|
||||
if (toApply.TryGetValue(entity, out var state))
|
||||
if (toApply.TryGetValue(uid, out var state))
|
||||
{
|
||||
toApply[entity] = (state.Item1, es);
|
||||
toApply[uid] = (state.Item1, es);
|
||||
}
|
||||
else
|
||||
{
|
||||
toApply[entity] = (null, es);
|
||||
toApply[uid] = (null, es);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -471,7 +483,7 @@ namespace Robust.Client.GameStates
|
||||
foreach (var kvStates in toApply)
|
||||
{
|
||||
var ent = kvStates.Key;
|
||||
var entity = (Entity) ent;
|
||||
var entity = ent;
|
||||
HandleEntityState(entity, _entities.EventBus, kvStates.Value.Item1,
|
||||
kvStates.Value.Item2);
|
||||
}
|
||||
@@ -483,7 +495,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
HashSet<Entity> brokenEnts = new HashSet<Entity>();
|
||||
HashSet<EntityUid> brokenEnts = new HashSet<EntityUid>();
|
||||
#endif
|
||||
|
||||
foreach (var entity in toInitialize)
|
||||
@@ -497,7 +509,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorS("state", $"Server entity threw in Init: uid={entity.Uid}, proto={entity.Prototype}\n{e}");
|
||||
Logger.ErrorS("state", $"Server entity threw in Init: ent={_entityManager.ToPrettyString(entity)}\n{e}");
|
||||
brokenEnts.Add(entity);
|
||||
}
|
||||
#endif
|
||||
@@ -517,34 +529,42 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorS("state", $"Server entity threw in Start: uid={entity.Uid}, proto={entity.Prototype}\n{e}");
|
||||
Logger.ErrorS("state", $"Server entity threw in Start: ent={_entityManager.ToPrettyString(entity)}\n{e}");
|
||||
brokenEnts.Add(entity);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
foreach (var entity in toInitialize)
|
||||
{
|
||||
#if EXCEPTION_TOLERANCE
|
||||
if (brokenEnts.Contains(entity))
|
||||
continue;
|
||||
#endif
|
||||
}
|
||||
#if EXCEPTION_TOLERANCE
|
||||
foreach (var entity in brokenEnts)
|
||||
{
|
||||
entity.Delete();
|
||||
_entityManager.DeleteEntity(entity);
|
||||
}
|
||||
#endif
|
||||
|
||||
foreach (var entityUid in toHide)
|
||||
{
|
||||
if(_entityManager.HasComponent<MapGridComponent>(entityUid)) continue;
|
||||
|
||||
var xform = _entityManager.GetComponent<TransformComponent>(entityUid);
|
||||
_hiddenEntities.Add(entityUid, xform.MapID);
|
||||
xform.ChangeMapId(MapId.Nullspace);
|
||||
}
|
||||
|
||||
foreach (var entityUid in toShow)
|
||||
{
|
||||
_entityManager.GetComponent<TransformComponent>(entityUid).ChangeMapId(_hiddenEntities[entityUid]);
|
||||
_hiddenEntities.Remove(entityUid);
|
||||
}
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
private void HandleEntityState(IEntity entity, IEventBus bus, EntityState? curState,
|
||||
private void HandleEntityState(EntityUid entity, IEventBus bus, EntityState? curState,
|
||||
EntityState? nextState)
|
||||
{
|
||||
var compStateWork = new Dictionary<ushort, (ComponentState? curState, ComponentState? nextState)>();
|
||||
var entityUid = entity.Uid;
|
||||
var entityUid = entity;
|
||||
|
||||
if (curState != null)
|
||||
{
|
||||
@@ -595,7 +615,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var (netId, (cur, next)) in compStateWork)
|
||||
{
|
||||
if (_entityManager.TryGetComponent(entityUid, (ushort) netId, out var component))
|
||||
if (_entityManager.TryGetComponent(entityUid, netId, out var component))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -109,7 +110,8 @@ namespace Robust.Client.GameStates
|
||||
if(_entityManager.EntityExists(netEnt.Id))
|
||||
{
|
||||
//TODO: Whoever is working on PVS remake, change the InPVS detection.
|
||||
var position = _entityManager.GetEntity(netEnt.Id).Transform.MapPosition;
|
||||
var uid = netEnt.Id;
|
||||
var position = _entityManager.GetComponent<TransformComponent>(uid).MapPosition;
|
||||
netEnt.InPVS = !pvsEnabled || (pvsBox.Contains(position.Position) && position.MapId == pvsCenter.MapId);
|
||||
_netEnts[i] = netEnt; // copy struct back
|
||||
continue;
|
||||
@@ -166,8 +168,9 @@ namespace Robust.Client.GameStates
|
||||
for (int i = 0; i < _netEnts.Count; i++)
|
||||
{
|
||||
var netEnt = _netEnts[i];
|
||||
var uid = netEnt.Id;
|
||||
|
||||
if (!_entityManager.TryGetEntity(netEnt.Id, out var ent))
|
||||
if (!_entityManager.EntityExists(uid))
|
||||
{
|
||||
_netEnts.RemoveSwap(i);
|
||||
i--;
|
||||
@@ -176,7 +179,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
var xPos = 100;
|
||||
var yPos = 10 + _lineHeight * i;
|
||||
var name = $"({netEnt.Id}) {ent.Prototype?.ID}";
|
||||
var name = $"({netEnt.Id}) {_entityManager.GetComponent<MetaDataComponent>(uid).EntityPrototype?.ID}";
|
||||
var color = CalcTextColor(ref netEnt);
|
||||
screenHandle.DrawString(_font, new Vector2(xPos + (TrafficHistorySize + 4), yPos), name, color);
|
||||
DrawTrafficBox(screenHandle, ref netEnt, xPos, yPos);
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Robust.Client.GameStates
|
||||
foreach (var boundingBox in _entityManager.EntityQuery<IPhysBody>(true))
|
||||
{
|
||||
// all entities have a TransformComponent
|
||||
var transform = boundingBox.Owner.Transform;
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(boundingBox.Owner);
|
||||
|
||||
// if not on the same map, continue
|
||||
if (transform.MapID != _eyeManager.CurrentMap || boundingBox.Owner.IsInContainer())
|
||||
|
||||
59
Robust.Client/Graphics/Audio/ClydeAudio.ALDisposeQueues.cs
Normal file
59
Robust.Client/Graphics/Audio/ClydeAudio.ALDisposeQueues.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.Collections.Concurrent;
|
||||
using OpenToolkit.Audio.OpenAL;
|
||||
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
internal partial class ClydeAudio
|
||||
{
|
||||
// Used to track audio sources that were disposed in the finalizer thread,
|
||||
// so we need to properly send them off in the main thread.
|
||||
private readonly ConcurrentQueue<(int sourceHandle, int filterHandle)> _sourceDisposeQueue = new();
|
||||
private readonly ConcurrentQueue<(int sourceHandle, int filterHandle)> _bufferedSourceDisposeQueue = new();
|
||||
private readonly ConcurrentQueue<int> _bufferDisposeQueue = new();
|
||||
|
||||
private void _flushALDisposeQueues()
|
||||
{
|
||||
// Clear out finalized audio sources.
|
||||
while (_sourceDisposeQueue.TryDequeue(out var handles))
|
||||
{
|
||||
_openALSawmill.Debug("Cleaning out source {0} which finalized in another thread.", handles.sourceHandle);
|
||||
if (IsEfxSupported) RemoveEfx(handles);
|
||||
AL.DeleteSource(handles.sourceHandle);
|
||||
_checkAlError();
|
||||
_audioSources.Remove(handles.sourceHandle);
|
||||
}
|
||||
|
||||
// Clear out finalized buffered audio sources.
|
||||
while (_bufferedSourceDisposeQueue.TryDequeue(out var handles))
|
||||
{
|
||||
_openALSawmill.Debug("Cleaning out buffered source {0} which finalized in another thread.", handles.sourceHandle);
|
||||
if (IsEfxSupported) RemoveEfx(handles);
|
||||
AL.DeleteSource(handles.sourceHandle);
|
||||
_checkAlError();
|
||||
_bufferedAudioSources.Remove(handles.sourceHandle);
|
||||
}
|
||||
|
||||
// Clear out finalized audio buffers.
|
||||
while (_bufferDisposeQueue.TryDequeue(out var handle))
|
||||
{
|
||||
AL.DeleteBuffer(handle);
|
||||
_checkAlError();
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteSourceOnMainThread(int sourceHandle, int filterHandle)
|
||||
{
|
||||
_sourceDisposeQueue.Enqueue((sourceHandle, filterHandle));
|
||||
}
|
||||
|
||||
private void DeleteBufferedSourceOnMainThread(int bufferedSourceHandle, int filterHandle)
|
||||
{
|
||||
_bufferedSourceDisposeQueue.Enqueue((bufferedSourceHandle, filterHandle));
|
||||
}
|
||||
|
||||
private void DeleteAudioBufferOnMainThread(int bufferHandle)
|
||||
{
|
||||
_bufferDisposeQueue.Enqueue(bufferHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,436 +10,20 @@ using OpenToolkit.Audio.OpenAL.Extensions.Creative.EFX;
|
||||
using OpenToolkit.Mathematics;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Log;
|
||||
using Vector2 = Robust.Shared.Maths.Vector2;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
internal partial class Clyde
|
||||
internal partial class ClydeAudio
|
||||
{
|
||||
private ALDevice _openALDevice;
|
||||
private ALContext _openALContext;
|
||||
|
||||
private readonly List<LoadedAudioSample> _audioSampleBuffers = new();
|
||||
|
||||
private readonly Dictionary<int, WeakReference<AudioSource>> _audioSources =
|
||||
new();
|
||||
|
||||
private readonly Dictionary<int, WeakReference<BufferedAudioSource>> _bufferedAudioSources =
|
||||
new();
|
||||
|
||||
private readonly HashSet<string> _alcDeviceExtensions = new();
|
||||
private readonly HashSet<string> _alContextExtensions = new();
|
||||
|
||||
// Used to track audio sources that were disposed in the finalizer thread,
|
||||
// so we need to properly send them off in the main thread.
|
||||
private readonly ConcurrentQueue<(int sourceHandle, int filterHandle)> _sourceDisposeQueue = new();
|
||||
private readonly ConcurrentQueue<(int sourceHandle, int filterHandle)> _bufferedSourceDisposeQueue = new();
|
||||
private readonly ConcurrentQueue<int> _bufferDisposeQueue = new();
|
||||
|
||||
// The base gain value for a listener, used to boost the default volume.
|
||||
private const float _baseGain = 2f;
|
||||
|
||||
public bool HasAlDeviceExtension(string extension) => _alcDeviceExtensions.Contains(extension);
|
||||
public bool HasAlContextExtension(string extension) => _alContextExtensions.Contains(extension);
|
||||
|
||||
internal bool IsEfxSupported;
|
||||
|
||||
private ISawmill _openALSawmill = default!;
|
||||
|
||||
private void _initializeAudio()
|
||||
{
|
||||
_openALSawmill = Logger.GetSawmill("clyde.oal");
|
||||
|
||||
_audioOpenDevice();
|
||||
|
||||
// Create OpenAL context.
|
||||
_audioCreateContext();
|
||||
|
||||
IsEfxSupported = HasAlDeviceExtension("ALC_EXT_EFX");
|
||||
|
||||
_cfg.OnValueChanged(CVars.AudioMasterVolume, SetMasterVolume, true);
|
||||
_cfg.OnValueChanged(CVars.AudioAttenuation, SetAudioAttenuation, true);
|
||||
}
|
||||
|
||||
private void _audioCreateContext()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
_openALContext = ALC.CreateContext(_openALDevice, (int*) 0);
|
||||
}
|
||||
|
||||
ALC.MakeContextCurrent(_openALContext);
|
||||
_checkAlcError(_openALDevice);
|
||||
_checkAlError();
|
||||
|
||||
// Load up AL context extensions.
|
||||
var s = ALC.GetString(ALDevice.Null, AlcGetString.Extensions) ?? "";
|
||||
foreach (var extension in s.Split(' '))
|
||||
{
|
||||
_alContextExtensions.Add(extension);
|
||||
}
|
||||
|
||||
_openALSawmill.Debug("OpenAL Vendor: {0}", AL.Get(ALGetString.Vendor));
|
||||
_openALSawmill.Debug("OpenAL Renderer: {0}", AL.Get(ALGetString.Renderer));
|
||||
_openALSawmill.Debug("OpenAL Version: {0}", AL.Get(ALGetString.Version));
|
||||
}
|
||||
|
||||
private void _audioOpenDevice()
|
||||
{
|
||||
var preferredDevice = _cfg.GetCVar(CVars.AudioDevice);
|
||||
|
||||
// Open device.
|
||||
if (!string.IsNullOrEmpty(preferredDevice))
|
||||
{
|
||||
_openALDevice = ALC.OpenDevice(preferredDevice);
|
||||
if (_openALDevice == IntPtr.Zero)
|
||||
{
|
||||
_openALSawmill.Warning("Unable to open preferred audio device '{0}': {1}. Falling back default.",
|
||||
preferredDevice, ALC.GetError(ALDevice.Null));
|
||||
|
||||
_openALDevice = ALC.OpenDevice(null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_openALDevice = ALC.OpenDevice(null);
|
||||
}
|
||||
|
||||
_checkAlcError(_openALDevice);
|
||||
|
||||
if (_openALDevice == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to open OpenAL device! {ALC.GetError(ALDevice.Null)}");
|
||||
}
|
||||
|
||||
// Load up ALC extensions.
|
||||
var s = ALC.GetString(_openALDevice, AlcGetString.Extensions) ?? "";
|
||||
foreach (var extension in s.Split(' '))
|
||||
{
|
||||
_alcDeviceExtensions.Add(extension);
|
||||
}
|
||||
}
|
||||
|
||||
private void _shutdownAudio()
|
||||
{
|
||||
foreach (var source in _audioSources.Values.ToArray())
|
||||
{
|
||||
if (source.TryGetTarget(out var target))
|
||||
{
|
||||
target.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var source in _bufferedAudioSources.Values.ToArray())
|
||||
{
|
||||
if (source.TryGetTarget(out var target))
|
||||
{
|
||||
target.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
if (_openALContext != ALContext.Null)
|
||||
{
|
||||
ALC.DestroyContext(_openALContext);
|
||||
}
|
||||
|
||||
if (_openALDevice != IntPtr.Zero)
|
||||
{
|
||||
ALC.CloseDevice(_openALDevice);
|
||||
}
|
||||
}
|
||||
|
||||
private void _updateAudio()
|
||||
{
|
||||
var eye = _eyeManager.CurrentEye;
|
||||
var (x, y) = eye.Position.Position;
|
||||
AL.Listener(ALListener3f.Position, x, y, -5);
|
||||
var rot2d = eye.Rotation.ToVec();
|
||||
AL.Listener(ALListenerfv.Orientation, new []{0, 0, -1, rot2d.X, rot2d.Y, 0});
|
||||
|
||||
// Default orientation: at: (0, 0, -1) up: (0, 1, 0)
|
||||
var (rotX, rotY) = eye.Rotation.ToVec();
|
||||
var at = new Vector3(0f, 0f, -1f);
|
||||
var up = new Vector3(rotY, rotX, 0f);
|
||||
AL.Listener(ALListenerfv.Orientation, ref at, ref up);
|
||||
|
||||
// Clear out finalized audio sources.
|
||||
while (_sourceDisposeQueue.TryDequeue(out var handles))
|
||||
{
|
||||
_openALSawmill.Debug("Cleaning out source {0} which finalized in another thread.", handles.sourceHandle);
|
||||
if (IsEfxSupported) RemoveEfx(handles);
|
||||
AL.DeleteSource(handles.sourceHandle);
|
||||
_checkAlError();
|
||||
_audioSources.Remove(handles.sourceHandle);
|
||||
}
|
||||
|
||||
// Clear out finalized buffered audio sources.
|
||||
while (_bufferedSourceDisposeQueue.TryDequeue(out var handles))
|
||||
{
|
||||
_openALSawmill.Debug("Cleaning out buffered source {0} which finalized in another thread.", handles.sourceHandle);
|
||||
if (IsEfxSupported) RemoveEfx(handles);
|
||||
AL.DeleteSource(handles.sourceHandle);
|
||||
_checkAlError();
|
||||
_bufferedAudioSources.Remove(handles.sourceHandle);
|
||||
}
|
||||
|
||||
// Clear out finalized audio buffers.
|
||||
while (_bufferDisposeQueue.TryDequeue(out var handle))
|
||||
{
|
||||
AL.DeleteBuffer((int) handle);
|
||||
_checkAlError();
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveEfx((int sourceHandle, int filterHandle) handles)
|
||||
{
|
||||
if (handles.filterHandle != 0) EFX.DeleteFilter(handles.filterHandle);
|
||||
}
|
||||
|
||||
public void SetMasterVolume(float newVolume)
|
||||
{
|
||||
AL.Listener(ALListenerf.Gain, _baseGain * newVolume);
|
||||
}
|
||||
|
||||
public void SetAudioAttenuation(int value)
|
||||
{
|
||||
var attenuation = (Attenuation) value;
|
||||
|
||||
switch (attenuation)
|
||||
{
|
||||
case Attenuation.NoAttenuation:
|
||||
AL.DistanceModel(ALDistanceModel.None);
|
||||
break;
|
||||
case Attenuation.InverseDistance:
|
||||
AL.DistanceModel(ALDistanceModel.InverseDistance);
|
||||
break;
|
||||
case Attenuation.Default:
|
||||
case Attenuation.InverseDistanceClamped:
|
||||
AL.DistanceModel(ALDistanceModel.InverseDistanceClamped);
|
||||
break;
|
||||
case Attenuation.LinearDistance:
|
||||
AL.DistanceModel(ALDistanceModel.LinearDistance);
|
||||
break;
|
||||
case Attenuation.LinearDistanceClamped:
|
||||
AL.DistanceModel(ALDistanceModel.LinearDistanceClamped);
|
||||
break;
|
||||
case Attenuation.ExponentDistance:
|
||||
AL.DistanceModel(ALDistanceModel.ExponentDistance);
|
||||
break;
|
||||
case Attenuation.ExponentDistanceClamped:
|
||||
AL.DistanceModel(ALDistanceModel.ExponentDistanceClamped);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"No implementation to set {attenuation.ToString()} for DistanceModel!");
|
||||
}
|
||||
|
||||
var attToString = attenuation == Attenuation.Default ? Attenuation.InverseDistanceClamped : attenuation;
|
||||
|
||||
_openALSawmill.Info($"Set audio attenuation to {attToString.ToString()}");
|
||||
}
|
||||
|
||||
public IClydeAudioSource CreateAudioSource(AudioStream stream)
|
||||
{
|
||||
var source = AL.GenSource();
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
// TODO: This really shouldn't be indexing based on the ClydeHandle...
|
||||
AL.Source(source, ALSourcei.Buffer, _audioSampleBuffers[(int) stream.ClydeHandle!.Value.Value].BufferHandle);
|
||||
|
||||
var audioSource = new AudioSource(this, source, stream);
|
||||
_audioSources.Add(source, new WeakReference<AudioSource>(audioSource));
|
||||
return audioSource;
|
||||
}
|
||||
|
||||
public IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio=false)
|
||||
{
|
||||
var source = AL.GenSource();
|
||||
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
|
||||
var audioSource = new BufferedAudioSource(this, source, AL.GenBuffers(buffers), floatAudio);
|
||||
_bufferedAudioSources.Add(source, new WeakReference<BufferedAudioSource>(audioSource));
|
||||
return audioSource;
|
||||
}
|
||||
|
||||
private void _checkAlcError(ALDevice device,
|
||||
[CallerMemberName] string callerMember = "",
|
||||
[CallerLineNumber] int callerLineNumber = -1)
|
||||
{
|
||||
var error = ALC.GetError(device);
|
||||
if (error != AlcError.NoError)
|
||||
{
|
||||
_openALSawmill.Error("[{0}:{1}] ALC error: {2}", callerMember, callerLineNumber, error);
|
||||
}
|
||||
}
|
||||
|
||||
private void _checkAlError([CallerMemberName] string callerMember = "",
|
||||
[CallerLineNumber] int callerLineNumber = -1)
|
||||
{
|
||||
var error = AL.GetError();
|
||||
if (error != ALError.NoError)
|
||||
{
|
||||
_openALSawmill.Error("[{0}:{1}] AL error: {2}", callerMember, callerLineNumber, error);
|
||||
}
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
|
||||
{
|
||||
var vorbis = _readOggVorbis(stream);
|
||||
|
||||
var buffer = AL.GenBuffer();
|
||||
|
||||
ALFormat format;
|
||||
// NVorbis only supports loading into floats.
|
||||
// If this becomes a problem due to missing extension support (doubt it but ok),
|
||||
// check the git history, I originally used libvorbisfile which worked and loaded 16 bit LPCM.
|
||||
if (vorbis.Channels == 1)
|
||||
{
|
||||
format = ALFormat.MonoFloat32Ext;
|
||||
}
|
||||
else if (vorbis.Channels == 2)
|
||||
{
|
||||
format = ALFormat.StereoFloat32Ext;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Unable to load audio with more than 2 channels.");
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (float* ptr = vorbis.Data.Span)
|
||||
{
|
||||
AL.BufferData(buffer, format, (IntPtr) ptr, vorbis.Data.Length * sizeof(float),
|
||||
(int) vorbis.SampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
_checkAlError();
|
||||
|
||||
var handle = new ClydeHandle(_audioSampleBuffers.Count);
|
||||
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
|
||||
var length = TimeSpan.FromSeconds(vorbis.TotalSamples / (double) vorbis.SampleRate);
|
||||
return new AudioStream(handle, length, (int) vorbis.Channels, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioWav(Stream stream, string? name = null)
|
||||
{
|
||||
var wav = _readWav(stream);
|
||||
|
||||
var buffer = AL.GenBuffer();
|
||||
|
||||
ALFormat format;
|
||||
if (wav.BitsPerSample == 16)
|
||||
{
|
||||
if (wav.NumChannels == 1)
|
||||
{
|
||||
format = ALFormat.Mono16;
|
||||
}
|
||||
else if (wav.NumChannels == 2)
|
||||
{
|
||||
format = ALFormat.Stereo16;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Unable to load audio with more than 2 channels.");
|
||||
}
|
||||
}
|
||||
else if (wav.BitsPerSample == 8)
|
||||
{
|
||||
if (wav.NumChannels == 1)
|
||||
{
|
||||
format = ALFormat.Mono8;
|
||||
}
|
||||
else if (wav.NumChannels == 2)
|
||||
{
|
||||
format = ALFormat.Stereo8;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Unable to load audio with more than 2 channels.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Unable to load wav with bits per sample different from 8 or 16");
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = wav.Data.Span)
|
||||
{
|
||||
AL.BufferData(buffer, format, (IntPtr) ptr, wav.Data.Length, wav.SampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
_checkAlError();
|
||||
|
||||
var handle = new ClydeHandle(_audioSampleBuffers.Count);
|
||||
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
|
||||
var length = TimeSpan.FromSeconds(wav.Data.Length / (double) wav.BlockAlign / wav.SampleRate);
|
||||
return new AudioStream(handle, length, wav.NumChannels, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
|
||||
{
|
||||
var fmt = channels switch
|
||||
{
|
||||
1 => ALFormat.Mono16,
|
||||
2 => ALFormat.Stereo16,
|
||||
_ => throw new ArgumentOutOfRangeException(
|
||||
nameof(channels), "Only stereo and mono is currently supported")
|
||||
};
|
||||
|
||||
var buffer = AL.GenBuffer();
|
||||
_checkAlError();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (short* ptr = samples)
|
||||
{
|
||||
AL.BufferData(buffer, fmt, (IntPtr) ptr, samples.Length * sizeof(short), sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
_checkAlError();
|
||||
|
||||
var handle = new ClydeHandle(_audioSampleBuffers.Count);
|
||||
var length = TimeSpan.FromSeconds((double) samples.Length / channels / sampleRate);
|
||||
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
|
||||
return new AudioStream(handle, length, channels, name);
|
||||
}
|
||||
|
||||
private sealed class LoadedAudioSample
|
||||
{
|
||||
public readonly int BufferHandle;
|
||||
|
||||
public LoadedAudioSample(int bufferHandle)
|
||||
{
|
||||
BufferHandle = bufferHandle;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteSourceOnMainThread(int sourceHandle, int filterHandle)
|
||||
{
|
||||
_sourceDisposeQueue.Enqueue((sourceHandle, filterHandle));
|
||||
}
|
||||
|
||||
private void DeleteBufferedSourceOnMainThread(int bufferedSourceHandle, int filterHandle)
|
||||
{
|
||||
_bufferedSourceDisposeQueue.Enqueue((bufferedSourceHandle, filterHandle));
|
||||
}
|
||||
|
||||
private void DeleteAudioBufferOnMainThread(int bufferHandle)
|
||||
{
|
||||
_bufferDisposeQueue.Enqueue(bufferHandle);
|
||||
}
|
||||
|
||||
private sealed class AudioSource : IClydeAudioSource
|
||||
{
|
||||
private int SourceHandle;
|
||||
private readonly Clyde _master;
|
||||
private readonly ClydeAudio _master;
|
||||
private readonly AudioStream _sourceStream;
|
||||
private int FilterHandle;
|
||||
#if DEBUG
|
||||
@@ -450,7 +34,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private bool IsEfxSupported => _master.IsEfxSupported;
|
||||
|
||||
public AudioSource(Clyde master, int sourceHandle, AudioStream sourceStream)
|
||||
public AudioSource(ClydeAudio master, int sourceHandle, AudioStream sourceStream)
|
||||
{
|
||||
_master = master;
|
||||
SourceHandle = sourceHandle;
|
||||
@@ -700,7 +284,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private int? SourceHandle = null;
|
||||
private int[] BufferHandles;
|
||||
private Dictionary<int, int> BufferMap = new();
|
||||
private readonly Clyde _master;
|
||||
private readonly ClydeAudio _master;
|
||||
private bool _mono = true;
|
||||
private bool _float = false;
|
||||
private int FilterHandle;
|
||||
@@ -711,7 +295,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private bool IsEfxSupported => _master.IsEfxSupported;
|
||||
|
||||
public BufferedAudioSource(Clyde master, int sourceHandle, int[] bufferHandles, bool floatAudio = false)
|
||||
public BufferedAudioSource(ClydeAudio master, int sourceHandle, int[] bufferHandles, bool floatAudio = false)
|
||||
{
|
||||
_master = master;
|
||||
SourceHandle = sourceHandle;
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
internal partial class Clyde
|
||||
internal partial class ClydeAudio
|
||||
{
|
||||
private OggVorbisData _readOggVorbis(Stream stream)
|
||||
{
|
||||
@@ -3,9 +3,9 @@ using System.IO;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
internal partial class Clyde
|
||||
internal partial class ClydeAudio
|
||||
{
|
||||
/// <summary>
|
||||
/// Load up a WAVE file.
|
||||
50
Robust.Client/Graphics/Audio/ClydeAudio.IoC.cs
Normal file
50
Robust.Client/Graphics/Audio/ClydeAudio.IoC.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using OpenToolkit.Audio.OpenAL;
|
||||
using OpenToolkit.Audio.OpenAL.Extensions.Creative.EFX;
|
||||
using OpenToolkit.Mathematics;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Timing;
|
||||
using Vector2 = Robust.Shared.Maths.Vector2;
|
||||
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
internal partial class ClydeAudio
|
||||
{
|
||||
[Robust.Shared.IoC.Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Robust.Shared.IoC.Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
|
||||
private Thread? _gameThread;
|
||||
|
||||
public bool InitializePostWindowing()
|
||||
{
|
||||
_gameThread = Thread.CurrentThread;
|
||||
return _initializeAudio();
|
||||
}
|
||||
|
||||
public void FrameProcess(FrameEventArgs eventArgs)
|
||||
{
|
||||
_updateAudio();
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
_shutdownAudio();
|
||||
}
|
||||
|
||||
private bool IsMainThread()
|
||||
{
|
||||
return Thread.CurrentThread == _gameThread;
|
||||
}
|
||||
}
|
||||
}
|
||||
399
Robust.Client/Graphics/Audio/ClydeAudio.cs
Normal file
399
Robust.Client/Graphics/Audio/ClydeAudio.cs
Normal file
@@ -0,0 +1,399 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using OpenToolkit.Audio.OpenAL;
|
||||
using OpenToolkit.Audio.OpenAL.Extensions.Creative.EFX;
|
||||
using OpenToolkit.Mathematics;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Log;
|
||||
using Vector2 = Robust.Shared.Maths.Vector2;
|
||||
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
internal partial class ClydeAudio : IClydeAudio, IClydeAudioInternal
|
||||
{
|
||||
private ALDevice _openALDevice;
|
||||
private ALContext _openALContext;
|
||||
|
||||
private readonly List<LoadedAudioSample> _audioSampleBuffers = new();
|
||||
|
||||
private readonly Dictionary<int, WeakReference<AudioSource>> _audioSources =
|
||||
new();
|
||||
|
||||
private readonly Dictionary<int, WeakReference<BufferedAudioSource>> _bufferedAudioSources =
|
||||
new();
|
||||
|
||||
private readonly HashSet<string> _alcDeviceExtensions = new();
|
||||
private readonly HashSet<string> _alContextExtensions = new();
|
||||
|
||||
// The base gain value for a listener, used to boost the default volume.
|
||||
private const float _baseGain = 2f;
|
||||
|
||||
public bool HasAlDeviceExtension(string extension) => _alcDeviceExtensions.Contains(extension);
|
||||
public bool HasAlContextExtension(string extension) => _alContextExtensions.Contains(extension);
|
||||
|
||||
internal bool IsEfxSupported;
|
||||
|
||||
private ISawmill _openALSawmill = default!;
|
||||
|
||||
private bool _initializeAudio()
|
||||
{
|
||||
_openALSawmill = Logger.GetSawmill("clyde.oal");
|
||||
|
||||
if (!_audioOpenDevice())
|
||||
return false;
|
||||
|
||||
// Create OpenAL context.
|
||||
_audioCreateContext();
|
||||
|
||||
IsEfxSupported = HasAlDeviceExtension("ALC_EXT_EFX");
|
||||
|
||||
_cfg.OnValueChanged(CVars.AudioMasterVolume, SetMasterVolume, true);
|
||||
_cfg.OnValueChanged(CVars.AudioAttenuation, SetAudioAttenuation, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void _audioCreateContext()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
_openALContext = ALC.CreateContext(_openALDevice, (int*) 0);
|
||||
}
|
||||
|
||||
ALC.MakeContextCurrent(_openALContext);
|
||||
_checkAlcError(_openALDevice);
|
||||
_checkAlError();
|
||||
|
||||
// Load up AL context extensions.
|
||||
var s = ALC.GetString(ALDevice.Null, AlcGetString.Extensions) ?? "";
|
||||
foreach (var extension in s.Split(' '))
|
||||
{
|
||||
_alContextExtensions.Add(extension);
|
||||
}
|
||||
|
||||
_openALSawmill.Debug("OpenAL Vendor: {0}", AL.Get(ALGetString.Vendor));
|
||||
_openALSawmill.Debug("OpenAL Renderer: {0}", AL.Get(ALGetString.Renderer));
|
||||
_openALSawmill.Debug("OpenAL Version: {0}", AL.Get(ALGetString.Version));
|
||||
}
|
||||
|
||||
private bool _audioOpenDevice()
|
||||
{
|
||||
var preferredDevice = _cfg.GetCVar(CVars.AudioDevice);
|
||||
|
||||
// Open device.
|
||||
if (!string.IsNullOrEmpty(preferredDevice))
|
||||
{
|
||||
_openALDevice = ALC.OpenDevice(preferredDevice);
|
||||
if (_openALDevice == IntPtr.Zero)
|
||||
{
|
||||
_openALSawmill.Warning("Unable to open preferred audio device '{0}': {1}. Falling back default.",
|
||||
preferredDevice, ALC.GetError(ALDevice.Null));
|
||||
|
||||
_openALDevice = ALC.OpenDevice(null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_openALDevice = ALC.OpenDevice(null);
|
||||
}
|
||||
|
||||
_checkAlcError(_openALDevice);
|
||||
|
||||
if (_openALDevice == IntPtr.Zero)
|
||||
{
|
||||
_openALSawmill.Error("Unable to open OpenAL device! {1}", ALC.GetError(ALDevice.Null));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load up ALC extensions.
|
||||
var s = ALC.GetString(_openALDevice, AlcGetString.Extensions) ?? "";
|
||||
foreach (var extension in s.Split(' '))
|
||||
{
|
||||
_alcDeviceExtensions.Add(extension);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void _shutdownAudio()
|
||||
{
|
||||
foreach (var source in _audioSources.Values.ToArray())
|
||||
{
|
||||
if (source.TryGetTarget(out var target))
|
||||
{
|
||||
target.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var source in _bufferedAudioSources.Values.ToArray())
|
||||
{
|
||||
if (source.TryGetTarget(out var target))
|
||||
{
|
||||
target.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
if (_openALContext != ALContext.Null)
|
||||
{
|
||||
ALC.DestroyContext(_openALContext);
|
||||
}
|
||||
|
||||
if (_openALDevice != IntPtr.Zero)
|
||||
{
|
||||
ALC.CloseDevice(_openALDevice);
|
||||
}
|
||||
}
|
||||
|
||||
private void _updateAudio()
|
||||
{
|
||||
var eye = _eyeManager.CurrentEye;
|
||||
var (x, y) = eye.Position.Position;
|
||||
AL.Listener(ALListener3f.Position, x, y, -5);
|
||||
var rot2d = eye.Rotation.ToVec();
|
||||
AL.Listener(ALListenerfv.Orientation, new []{0, 0, -1, rot2d.X, rot2d.Y, 0});
|
||||
|
||||
// Default orientation: at: (0, 0, -1) up: (0, 1, 0)
|
||||
var (rotX, rotY) = eye.Rotation.ToVec();
|
||||
var at = new Vector3(0f, 0f, -1f);
|
||||
var up = new Vector3(rotY, rotX, 0f);
|
||||
AL.Listener(ALListenerfv.Orientation, ref at, ref up);
|
||||
|
||||
_flushALDisposeQueues();
|
||||
}
|
||||
|
||||
private static void RemoveEfx((int sourceHandle, int filterHandle) handles)
|
||||
{
|
||||
if (handles.filterHandle != 0) EFX.DeleteFilter(handles.filterHandle);
|
||||
}
|
||||
|
||||
public void SetMasterVolume(float newVolume)
|
||||
{
|
||||
AL.Listener(ALListenerf.Gain, _baseGain * newVolume);
|
||||
}
|
||||
|
||||
public void SetAudioAttenuation(int value)
|
||||
{
|
||||
var attenuation = (Attenuation) value;
|
||||
|
||||
switch (attenuation)
|
||||
{
|
||||
case Attenuation.NoAttenuation:
|
||||
AL.DistanceModel(ALDistanceModel.None);
|
||||
break;
|
||||
case Attenuation.InverseDistance:
|
||||
AL.DistanceModel(ALDistanceModel.InverseDistance);
|
||||
break;
|
||||
case Attenuation.Default:
|
||||
case Attenuation.InverseDistanceClamped:
|
||||
AL.DistanceModel(ALDistanceModel.InverseDistanceClamped);
|
||||
break;
|
||||
case Attenuation.LinearDistance:
|
||||
AL.DistanceModel(ALDistanceModel.LinearDistance);
|
||||
break;
|
||||
case Attenuation.LinearDistanceClamped:
|
||||
AL.DistanceModel(ALDistanceModel.LinearDistanceClamped);
|
||||
break;
|
||||
case Attenuation.ExponentDistance:
|
||||
AL.DistanceModel(ALDistanceModel.ExponentDistance);
|
||||
break;
|
||||
case Attenuation.ExponentDistanceClamped:
|
||||
AL.DistanceModel(ALDistanceModel.ExponentDistanceClamped);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"No implementation to set {attenuation.ToString()} for DistanceModel!");
|
||||
}
|
||||
|
||||
var attToString = attenuation == Attenuation.Default ? Attenuation.InverseDistanceClamped : attenuation;
|
||||
|
||||
_openALSawmill.Info($"Set audio attenuation to {attToString.ToString()}");
|
||||
}
|
||||
|
||||
public IClydeAudioSource CreateAudioSource(AudioStream stream)
|
||||
{
|
||||
var source = AL.GenSource();
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
// TODO: This really shouldn't be indexing based on the ClydeHandle...
|
||||
AL.Source(source, ALSourcei.Buffer, _audioSampleBuffers[(int) stream.ClydeHandle!.Value.Value].BufferHandle);
|
||||
|
||||
var audioSource = new AudioSource(this, source, stream);
|
||||
_audioSources.Add(source, new WeakReference<AudioSource>(audioSource));
|
||||
return audioSource;
|
||||
}
|
||||
|
||||
public IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio=false)
|
||||
{
|
||||
var source = AL.GenSource();
|
||||
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
|
||||
var audioSource = new BufferedAudioSource(this, source, AL.GenBuffers(buffers), floatAudio);
|
||||
_bufferedAudioSources.Add(source, new WeakReference<BufferedAudioSource>(audioSource));
|
||||
return audioSource;
|
||||
}
|
||||
|
||||
private void _checkAlcError(ALDevice device,
|
||||
[CallerMemberName] string callerMember = "",
|
||||
[CallerLineNumber] int callerLineNumber = -1)
|
||||
{
|
||||
var error = ALC.GetError(device);
|
||||
if (error != AlcError.NoError)
|
||||
{
|
||||
_openALSawmill.Error("[{0}:{1}] ALC error: {2}", callerMember, callerLineNumber, error);
|
||||
}
|
||||
}
|
||||
|
||||
private void _checkAlError([CallerMemberName] string callerMember = "",
|
||||
[CallerLineNumber] int callerLineNumber = -1)
|
||||
{
|
||||
var error = AL.GetError();
|
||||
if (error != ALError.NoError)
|
||||
{
|
||||
_openALSawmill.Error("[{0}:{1}] AL error: {2}", callerMember, callerLineNumber, error);
|
||||
}
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
|
||||
{
|
||||
var vorbis = _readOggVorbis(stream);
|
||||
|
||||
var buffer = AL.GenBuffer();
|
||||
|
||||
ALFormat format;
|
||||
// NVorbis only supports loading into floats.
|
||||
// If this becomes a problem due to missing extension support (doubt it but ok),
|
||||
// check the git history, I originally used libvorbisfile which worked and loaded 16 bit LPCM.
|
||||
if (vorbis.Channels == 1)
|
||||
{
|
||||
format = ALFormat.MonoFloat32Ext;
|
||||
}
|
||||
else if (vorbis.Channels == 2)
|
||||
{
|
||||
format = ALFormat.StereoFloat32Ext;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Unable to load audio with more than 2 channels.");
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (float* ptr = vorbis.Data.Span)
|
||||
{
|
||||
AL.BufferData(buffer, format, (IntPtr) ptr, vorbis.Data.Length * sizeof(float),
|
||||
(int) vorbis.SampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
_checkAlError();
|
||||
|
||||
var handle = new ClydeHandle(_audioSampleBuffers.Count);
|
||||
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
|
||||
var length = TimeSpan.FromSeconds(vorbis.TotalSamples / (double) vorbis.SampleRate);
|
||||
return new AudioStream(handle, length, (int) vorbis.Channels, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioWav(Stream stream, string? name = null)
|
||||
{
|
||||
var wav = _readWav(stream);
|
||||
|
||||
var buffer = AL.GenBuffer();
|
||||
|
||||
ALFormat format;
|
||||
if (wav.BitsPerSample == 16)
|
||||
{
|
||||
if (wav.NumChannels == 1)
|
||||
{
|
||||
format = ALFormat.Mono16;
|
||||
}
|
||||
else if (wav.NumChannels == 2)
|
||||
{
|
||||
format = ALFormat.Stereo16;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Unable to load audio with more than 2 channels.");
|
||||
}
|
||||
}
|
||||
else if (wav.BitsPerSample == 8)
|
||||
{
|
||||
if (wav.NumChannels == 1)
|
||||
{
|
||||
format = ALFormat.Mono8;
|
||||
}
|
||||
else if (wav.NumChannels == 2)
|
||||
{
|
||||
format = ALFormat.Stereo8;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Unable to load audio with more than 2 channels.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Unable to load wav with bits per sample different from 8 or 16");
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = wav.Data.Span)
|
||||
{
|
||||
AL.BufferData(buffer, format, (IntPtr) ptr, wav.Data.Length, wav.SampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
_checkAlError();
|
||||
|
||||
var handle = new ClydeHandle(_audioSampleBuffers.Count);
|
||||
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
|
||||
var length = TimeSpan.FromSeconds(wav.Data.Length / (double) wav.BlockAlign / wav.SampleRate);
|
||||
return new AudioStream(handle, length, wav.NumChannels, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
|
||||
{
|
||||
var fmt = channels switch
|
||||
{
|
||||
1 => ALFormat.Mono16,
|
||||
2 => ALFormat.Stereo16,
|
||||
_ => throw new ArgumentOutOfRangeException(
|
||||
nameof(channels), "Only stereo and mono is currently supported")
|
||||
};
|
||||
|
||||
var buffer = AL.GenBuffer();
|
||||
_checkAlError();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (short* ptr = samples)
|
||||
{
|
||||
AL.BufferData(buffer, fmt, (IntPtr) ptr, samples.Length * sizeof(short), sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
_checkAlError();
|
||||
|
||||
var handle = new ClydeHandle(_audioSampleBuffers.Count);
|
||||
var length = TimeSpan.FromSeconds((double) samples.Length / channels / sampleRate);
|
||||
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
|
||||
return new AudioStream(handle, length, channels, name);
|
||||
}
|
||||
|
||||
private sealed class LoadedAudioSample
|
||||
{
|
||||
public readonly int BufferHandle;
|
||||
|
||||
public LoadedAudioSample(int bufferHandle)
|
||||
{
|
||||
BufferHandle = bufferHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
70
Robust.Client/Graphics/Audio/ClydeAudioHeadless.cs
Normal file
70
Robust.Client/Graphics/Audio/ClydeAudioHeadless.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using Color = Robust.Shared.Maths.Color;
|
||||
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// Hey look, it's ClydeAudio's evil twin brother!
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
internal sealed class ClydeAudioHeadless : IClydeAudio, IClydeAudioInternal
|
||||
{
|
||||
public bool InitializePostWindowing()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void FrameProcess(FrameEventArgs eventArgs)
|
||||
{
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
|
||||
{
|
||||
// TODO: Might wanna actually load this so the length gets reported correctly.
|
||||
return new(default, default, 1, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioWav(Stream stream, string? name = null)
|
||||
{
|
||||
// TODO: Might wanna actually load this so the length gets reported correctly.
|
||||
return new(default, default, 1, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
|
||||
{
|
||||
// TODO: Might wanna actually load this so the length gets reported correctly.
|
||||
return new(default, default, channels, name);
|
||||
}
|
||||
|
||||
public IClydeAudioSource CreateAudioSource(AudioStream stream)
|
||||
{
|
||||
return DummyAudioSource.Instance;
|
||||
}
|
||||
|
||||
public IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio = false)
|
||||
{
|
||||
return DummyBufferedAudioSource.Instance;
|
||||
}
|
||||
|
||||
public void SetMasterVolume(float newVolume)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
}
|
||||
}
|
||||
98
Robust.Client/Graphics/Audio/DummyAudioSource.cs
Normal file
98
Robust.Client/Graphics/Audio/DummyAudioSource.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using Color = Robust.Shared.Maths.Color;
|
||||
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// Hey look, it's ClydeAudio.AudioSource's evil twin brother!
|
||||
/// </summary>
|
||||
internal class DummyAudioSource : IClydeAudioSource
|
||||
{
|
||||
public static DummyAudioSource Instance { get; } = new();
|
||||
|
||||
public bool IsPlaying => default;
|
||||
public bool IsLooping { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void StartPlaying()
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void StopPlaying()
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public bool SetPosition(Vector2 position)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetPitch(float pitch)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetGlobal()
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetVolume(float decibels)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetVolumeDirect(float scale)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetMaxDistance(float maxDistance)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetRolloffFactor(float rolloffFactor)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetReferenceDistance(float refDistance)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetOcclusion(float blocks)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetPlaybackPosition(float seconds)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetVelocity(Vector2 velocity)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Robust.Client/Graphics/Audio/DummyBufferedAudioSource.cs
Normal file
56
Robust.Client/Graphics/Audio/DummyBufferedAudioSource.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using Color = Robust.Shared.Maths.Color;
|
||||
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// Hey look, it's ClydeAudio.BufferedAudioSource's evil twin brother!
|
||||
/// </summary>
|
||||
internal sealed class DummyBufferedAudioSource : DummyAudioSource, IClydeBufferedAudioSource
|
||||
{
|
||||
public new static DummyBufferedAudioSource Instance { get; } = new();
|
||||
public int SampleRate { get; set; } = 0;
|
||||
|
||||
public void WriteBuffer(int handle, ReadOnlySpan<ushort> data)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void WriteBuffer(int handle, ReadOnlySpan<float> data)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void QueueBuffers(ReadOnlySpan<int> handles)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void EmptyBuffers()
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void GetBuffersProcessed(Span<int> handles)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public int GetNumberOfBuffersProcessed()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
Robust.Client/Graphics/Audio/FallbackProxyClydeAudio.cs
Normal file
34
Robust.Client/Graphics/Audio/FallbackProxyClydeAudio.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// For "start ss14 with no audio devices" Smugleaf
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
internal sealed class FallbackProxyClydeAudio : ProxyClydeAudio
|
||||
{
|
||||
public override bool InitializePostWindowing()
|
||||
{
|
||||
// Deliberate lack of base call here (see base implementation for comments as to why there even is a base)
|
||||
|
||||
ActualImplementation = new ClydeAudio();
|
||||
IoCManager.InjectDependencies(ActualImplementation);
|
||||
if (ActualImplementation.InitializePostWindowing())
|
||||
return true;
|
||||
|
||||
// If we get here, that failed, so use the fallback
|
||||
ActualImplementation = new ClydeAudioHeadless();
|
||||
IoCManager.InjectDependencies(ActualImplementation);
|
||||
return ActualImplementation.InitializePostWindowing();
|
||||
}
|
||||
}
|
||||
}
|
||||
72
Robust.Client/Graphics/Audio/ProxyClydeAudio.cs
Normal file
72
Robust.Client/Graphics/Audio/ProxyClydeAudio.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using Color = Robust.Shared.Maths.Color;
|
||||
|
||||
namespace Robust.Client.Graphics.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// For "start ss14 with no audio devices" Smugleaf
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
internal abstract class ProxyClydeAudio : IClydeAudio, IClydeAudioInternal
|
||||
{
|
||||
protected IClydeAudioInternal ActualImplementation = default!;
|
||||
|
||||
public virtual bool InitializePostWindowing()
|
||||
{
|
||||
// This particular implementation exists to be overridden because removing this method causes C# to complain
|
||||
return ActualImplementation.InitializePostWindowing();
|
||||
}
|
||||
|
||||
public void FrameProcess(FrameEventArgs eventArgs)
|
||||
{
|
||||
ActualImplementation.FrameProcess(eventArgs);
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
ActualImplementation.Shutdown();
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
|
||||
{
|
||||
return ActualImplementation.LoadAudioOggVorbis(stream, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioWav(Stream stream, string? name = null)
|
||||
{
|
||||
return ActualImplementation.LoadAudioWav(stream, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
|
||||
{
|
||||
return ActualImplementation.LoadAudioRaw(samples, channels, sampleRate, name);
|
||||
}
|
||||
|
||||
public IClydeAudioSource CreateAudioSource(AudioStream stream)
|
||||
{
|
||||
return ActualImplementation.CreateAudioSource(stream);
|
||||
}
|
||||
|
||||
public IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio = false)
|
||||
{
|
||||
return ActualImplementation.CreateBufferedAudioSource(buffers, floatAudio);
|
||||
}
|
||||
|
||||
public void SetMasterVolume(float newVolume)
|
||||
{
|
||||
ActualImplementation.SetMasterVolume(newVolume);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,17 +15,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
RuntimeInformation.ProcessArchitecture == Architecture.X64 &&
|
||||
Environment.GetEnvironmentVariable("ROBUST_INTEGRATED_GPU") != "1")
|
||||
{
|
||||
try
|
||||
{
|
||||
// We force load nvapi64.dll so nvidia gives us the dedicated GPU on optimus laptops.
|
||||
// This is 100x easier than nvidia's documented approach of NvOptimusEnablement,
|
||||
// and works while developing.
|
||||
NativeLibrary.Load("nvapi64.dll");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// If this fails whatever.
|
||||
}
|
||||
// We force load nvapi64.dll so nvidia gives us the dedicated GPU on optimus laptops.
|
||||
// This is 100x easier than nvidia's documented approach of NvOptimusEnablement,
|
||||
// and works while developing.
|
||||
NativeLibrary.TryLoad("nvapi64.dll", out _);
|
||||
// If this fails whatever.
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
|
||||
@@ -74,7 +74,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
case DEventWindowFocus(var args):
|
||||
OnWindowFocused?.Invoke(args);
|
||||
break;
|
||||
case DEventWindowResized(var args):
|
||||
case DEventWindowResized(var reg, var args):
|
||||
reg.Resized?.Invoke(args);
|
||||
OnWindowResized?.Invoke(args);
|
||||
break;
|
||||
}
|
||||
@@ -112,7 +113,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
reg.FramebufferSize,
|
||||
reg.Handle);
|
||||
|
||||
_eventDispatchQueue.Enqueue(new DEventWindowResized(eventArgs));
|
||||
_eventDispatchQueue.Enqueue(new DEventWindowResized(reg, eventArgs));
|
||||
}
|
||||
|
||||
private void SendWindowContentScaleChanged(WindowContentScaleEventArgs ev)
|
||||
@@ -151,7 +152,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private sealed record DEventWindowClosed(WindowReg Reg, WindowRequestClosedEventArgs Args) : DEventBase;
|
||||
|
||||
private sealed record DEventWindowResized(WindowResizedEventArgs Args) : DEventBase;
|
||||
private sealed record DEventWindowResized(WindowReg Reg, WindowResizedEventArgs Args) : DEventBase;
|
||||
|
||||
private sealed record DEventWindowContentScaleChanged(WindowContentScaleEventArgs Args) : DEventBase;
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Advanced GL contexts currently disabled due to lack of testing etc.
|
||||
if (OperatingSystem.IsWindows() && _cfg.GetCVar(CVars.DisplayAngle))
|
||||
{
|
||||
/*
|
||||
if (_cfg.GetCVar(CVars.DisplayAngleCustomSwapChain))
|
||||
{
|
||||
_sawmillOgl.Debug("Trying custom swap chain ANGLE.");
|
||||
@@ -31,7 +30,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
if (_cfg.GetCVar(CVars.DisplayEgl))
|
||||
|
||||
@@ -12,6 +12,8 @@ using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -372,14 +374,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
foreach (var comp in _entitySystemManager.GetEntitySystem<RenderingTreeSystem>().GetRenderTrees(map, worldBounds))
|
||||
{
|
||||
var bounds = comp.Owner.Transform.InvWorldMatrix.TransformBox(worldBounds);
|
||||
var bounds = _entityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(worldBounds);
|
||||
|
||||
comp.SpriteTree.QueryAabb(ref list, (
|
||||
ref RefList<(SpriteComponent sprite, Matrix3 matrix, Angle worldRot, float yWorldPos)> state,
|
||||
in SpriteComponent value) =>
|
||||
{
|
||||
var entity = value.Owner;
|
||||
var transform = entity.Transform;
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(entity);
|
||||
|
||||
ref var entry = ref state.AllocAdd();
|
||||
entry.sprite = value;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Buffers;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
@@ -6,6 +6,7 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -329,7 +330,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var mapId = eye.Position.MapId;
|
||||
|
||||
// If this map has lighting disabled, return
|
||||
if (!_mapManager.GetMapEntity(mapId).GetComponent<IMapComponent>().LightingEnabled)
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
if (!_entityManager.GetComponent<IMapComponent>(mapUid).LightingEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -413,7 +415,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
var (component, lightPos, _) = lights[i];
|
||||
|
||||
var transform = component.Owner.Transform;
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(component.Owner);
|
||||
|
||||
Texture? mask = null;
|
||||
var rotation = Angle.Zero;
|
||||
@@ -516,7 +518,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
foreach (var comp in renderingTreeSystem.GetRenderTrees(map, enlargedBounds))
|
||||
{
|
||||
var bounds = comp.Owner.Transform.InvWorldMatrix.TransformBox(worldBounds);
|
||||
var bounds = _entityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(worldBounds);
|
||||
|
||||
comp.LightTree.QueryAabb(ref state, (ref (Clyde clyde, Box2 worldAABB, int count) state, in PointLightComponent light) =>
|
||||
{
|
||||
@@ -526,7 +528,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return false;
|
||||
}
|
||||
|
||||
var transform = light.Owner.Transform;
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(light.Owner);
|
||||
|
||||
if (float.IsNaN(transform.LocalPosition.X) || float.IsNaN(transform.LocalPosition.Y)) return true;
|
||||
|
||||
@@ -871,12 +873,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
foreach (var comp in occluderSystem.GetOccluderTrees(map, expandedBounds))
|
||||
{
|
||||
var treeBounds = comp.Owner.Transform.InvWorldMatrix.TransformBox(expandedBounds);
|
||||
var treeBounds = _entityManager.GetComponent<TransformComponent>(comp.Owner).InvWorldMatrix.TransformBox(expandedBounds);
|
||||
|
||||
comp.Tree.QueryAabb((in OccluderComponent sOccluder) =>
|
||||
{
|
||||
var occluder = (ClientOccluderComponent)sOccluder;
|
||||
var transform = occluder.Owner.Transform;
|
||||
var transform = _entityManager.GetComponent<TransformComponent>(occluder.Owner);
|
||||
if (!occluder.Enabled)
|
||||
{
|
||||
return true;
|
||||
@@ -927,10 +929,10 @@ namespace Robust.Client.Graphics.Clyde
|
||||
//
|
||||
|
||||
// Calculate delta positions from camera.
|
||||
var (dTlX, dTlY) = eyeTransform.Transform(tl);
|
||||
var (dTrX, dTrY) = eyeTransform.Transform(tr);
|
||||
var (dBlX, dBlY) = eyeTransform.Transform(bl);
|
||||
var (dBrX, dBrY) = eyeTransform.Transform(br);
|
||||
var dTl = eyeTransform.Transform(tl);
|
||||
var dTr = eyeTransform.Transform(tr);
|
||||
var dBl = eyeTransform.Transform(bl);
|
||||
var dBr = eyeTransform.Transform(br);
|
||||
|
||||
// Get which neighbors are occluding.
|
||||
var no = (occluder.Occluding & OccluderDir.North) != 0;
|
||||
@@ -939,10 +941,26 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var wo = (occluder.Occluding & OccluderDir.West) != 0;
|
||||
|
||||
// Do visibility tests for occluders (described above).
|
||||
var tlV = dTlX > 0 && !wo || dTlY < 0 && !no;
|
||||
var trV = dTrX < 0 && !eo || dTrY < 0 && !no;
|
||||
var blV = dBlX > 0 && !wo || dBlY > 0 && !so;
|
||||
var brV = dBrX < 0 && !eo || dBrY > 0 && !so;
|
||||
bool CheckFaceEyeVis(Vector2 a, Vector2 b)
|
||||
{
|
||||
// get normal
|
||||
var alongNormal = b - a;
|
||||
var normal = alongNormal.Rotated90DegreesAnticlockwiseWorld.Normalized;
|
||||
// determine which side of the plane the face is on
|
||||
// the plane is at the origin of this coordinate system, which is also the eye
|
||||
// the normal of the plane is that of the face
|
||||
// therefore, if the dot <= 0, the face is facing the camera
|
||||
// I don't like this, but rotated occluders started happening
|
||||
return Vector2.Dot(normal, a) <= 0;
|
||||
}
|
||||
var nV = ((!no) && CheckFaceEyeVis(dTl, dTr));
|
||||
var sV = ((!so) && CheckFaceEyeVis(dBr, dBl));
|
||||
var eV = ((!eo) && CheckFaceEyeVis(dTr, dBr));
|
||||
var wV = ((!wo) && CheckFaceEyeVis(dBl, dTl));
|
||||
var tlV = nV || wV;
|
||||
var trV = nV || eV;
|
||||
var blV = sV || wV;
|
||||
var brV = sV || eV;
|
||||
|
||||
// Handle faces, rules described above.
|
||||
// Note that "from above" it should be clockwise.
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
@@ -122,14 +123,16 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_clyde.DrawSetScissor(scissorBox);
|
||||
}
|
||||
|
||||
public void DrawEntity(IEntity entity, Vector2 position, Vector2 scale, Direction? overrideDirection)
|
||||
public void DrawEntity(EntityUid entity, Vector2 position, Vector2 scale, Direction? overrideDirection)
|
||||
{
|
||||
if (entity.Deleted)
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (entMan.Deleted(entity))
|
||||
{
|
||||
throw new ArgumentException("Tried to draw an entity has been deleted.", nameof(entity));
|
||||
}
|
||||
|
||||
var sprite = entity.GetComponent<SpriteComponent>();
|
||||
var sprite = entMan.GetComponent<SpriteComponent>(entity);
|
||||
|
||||
var oldProj = _clyde._currentMatrixProj;
|
||||
var oldView = _clyde._currentMatrixView;
|
||||
@@ -162,7 +165,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
DrawingHandleWorld,
|
||||
Angle.Zero,
|
||||
overrideDirection == null
|
||||
? entity.Transform.WorldRotation
|
||||
? entMan.GetComponent<TransformComponent>(entity).WorldRotation
|
||||
: Angle.Zero,
|
||||
overrideDirection);
|
||||
|
||||
@@ -346,7 +349,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
rect.BottomLeft, rect.BottomRight, color, subRegion);
|
||||
}
|
||||
|
||||
public override void DrawEntity(IEntity entity, Vector2 position, Vector2 scale, Direction? overrideDirection)
|
||||
public override void DrawEntity(EntityUid entity, Vector2 position, Vector2 scale, Direction? overrideDirection)
|
||||
{
|
||||
_renderHandle.DrawEntity(entity, position, scale, overrideDirection);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Color = Robust.Shared.Maths.Color;
|
||||
using TKStencilOp = OpenToolkit.Graphics.OpenGL4.StencilOp;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
@@ -448,7 +447,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
//function! If passing in textures as uniforms ever stops working it might be since someone made it use all the way up to Texture6 too.
|
||||
//Might change this in the future?
|
||||
TextureUnit cTarget = TextureUnit.Texture6 + textureUnitVal;
|
||||
SetTexture(cTarget, ((ClydeTexture) clydeTexture).TextureId);
|
||||
SetTexture(cTarget, clydeTexture.TextureId);
|
||||
program.SetUniformTexture(name, cTarget);
|
||||
textureUnitVal++;
|
||||
break;
|
||||
@@ -1027,7 +1026,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return a.Owner.Uid.CompareTo(b.Owner.Uid);
|
||||
return a.Owner.CompareTo(b.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,10 +155,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
isActuallySrgb = loadParams.Srgb;
|
||||
}
|
||||
else if (pixelType == typeof(Bgra32))
|
||||
{
|
||||
isActuallySrgb = loadParams.Srgb;
|
||||
}
|
||||
else if (pixelType == typeof(A8))
|
||||
{
|
||||
DebugTools.Assert(_hasGLTextureSwizzle);
|
||||
@@ -264,7 +260,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Note that if _hasGLSrgb is off, we import an sRGB texture as non-sRGB.
|
||||
// Shaders are expected to compensate for this
|
||||
Rgba32 => (srgb && _hasGLSrgb ? PIF.Srgb8Alpha8 : PIF.Rgba8, PF.Rgba, PT.UnsignedByte),
|
||||
Bgra32 => (srgb && _hasGLSrgb ? PIF.Srgb8Alpha8 : PIF.Rgba8, PF.Bgra, PT.UnsignedByte),
|
||||
A8 or L8 => (PIF.R8, PF.Red, PT.UnsignedByte),
|
||||
_ => throw new NotSupportedException("Unsupported pixel type."),
|
||||
};
|
||||
@@ -445,7 +440,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
return default(T) switch
|
||||
{
|
||||
Rgba32 or Bgra32 => TexturePixelType.Rgba32,
|
||||
Rgba32 => TexturePixelType.Rgba32,
|
||||
L8 => TexturePixelType.L8,
|
||||
A8 => TexturePixelType.A8,
|
||||
_ => throw new NotSupportedException("Unsupported pixel type."),
|
||||
|
||||
@@ -218,7 +218,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return false;
|
||||
}
|
||||
|
||||
InitOpenGL();
|
||||
if (!_earlyGLInit)
|
||||
InitOpenGL();
|
||||
|
||||
_sawmillOgl.Debug("Setting viewport and rendering splash...");
|
||||
|
||||
@@ -458,6 +459,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public RenderWindow RenderTarget = default!;
|
||||
public Action<WindowRequestClosedEventArgs>? RequestClosed;
|
||||
public Action<WindowDestroyedEventArgs>? Closed;
|
||||
public Action<WindowResizedEventArgs>? Resized;
|
||||
}
|
||||
|
||||
private sealed class WindowHandle : IClydeWindowInternal
|
||||
@@ -523,6 +525,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
remove => Reg.Closed -= value;
|
||||
}
|
||||
|
||||
public event Action<WindowResizedEventArgs>? Resized
|
||||
{
|
||||
add => Reg.Resized += value;
|
||||
remove => Reg.Resized -= value;
|
||||
}
|
||||
|
||||
public nint? WindowsHWnd => _clyde._windowing!.WindowGetWin32Window(Reg);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
/// <summary>
|
||||
/// Responsible for most things rendering on OpenGL mode.
|
||||
/// </summary>
|
||||
internal sealed partial class Clyde : IClydeInternal, IClydeAudio, IPostInjectInit
|
||||
internal sealed partial class Clyde : IClydeInternal, IPostInjectInit
|
||||
{
|
||||
[Dependency] private readonly IClydeTileDefinitionManager _tileDefinitionManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
@@ -66,6 +66,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private ISawmill _sawmillOgl = default!;
|
||||
|
||||
private IBindingsContext _glBindingsContext = default!;
|
||||
private bool _earlyGLInit;
|
||||
|
||||
public Clyde()
|
||||
{
|
||||
@@ -97,7 +98,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (!InitMainWindowAndRenderer())
|
||||
return false;
|
||||
|
||||
_initializeAudio();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -115,8 +115,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
public void FrameProcess(FrameEventArgs eventArgs)
|
||||
{
|
||||
_updateAudio();
|
||||
|
||||
_windowing?.FlushDispose();
|
||||
FlushShaderInstanceDispose();
|
||||
FlushRenderTargetDispose();
|
||||
@@ -509,7 +507,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
_glContext?.Shutdown();
|
||||
ShutdownWindowing();
|
||||
_shutdownAudio();
|
||||
}
|
||||
|
||||
private bool IsMainThread()
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
/// Hey look, it's Clyde's evil twin brother!
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
internal sealed class ClydeHeadless : IClydeInternal, IClydeAudio
|
||||
internal sealed class ClydeHeadless : IClydeInternal
|
||||
{
|
||||
// Would it make sense to report a fake resolution like 720p here so code doesn't break? idk.
|
||||
public IClydeWindow MainWindow { get; }
|
||||
@@ -236,34 +236,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
|
||||
{
|
||||
// TODO: Might wanna actually load this so the length gets reported correctly.
|
||||
return new(default, default, 1, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioWav(Stream stream, string? name = null)
|
||||
{
|
||||
// TODO: Might wanna actually load this so the length gets reported correctly.
|
||||
return new(default, default, 1, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
|
||||
{
|
||||
// TODO: Might wanna actually load this so the length gets reported correctly.
|
||||
return new(default, default, channels, name);
|
||||
}
|
||||
|
||||
public IClydeAudioSource CreateAudioSource(AudioStream stream)
|
||||
{
|
||||
return DummyAudioSource.Instance;
|
||||
}
|
||||
|
||||
public IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio = false)
|
||||
{
|
||||
return DummyBufferedAudioSource.Instance;
|
||||
}
|
||||
|
||||
public Task<string> GetText()
|
||||
{
|
||||
return Task.FromResult(string.Empty);
|
||||
@@ -274,11 +246,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetMasterVolume(float newVolume)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
private class DummyCursor : ICursor
|
||||
{
|
||||
public void Dispose()
|
||||
@@ -627,6 +594,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public bool DisposeOnClose { get; set; }
|
||||
public event Action<WindowRequestClosedEventArgs>? RequestClosed { add { } remove { } }
|
||||
public event Action<WindowDestroyedEventArgs>? Destroyed;
|
||||
public event Action<WindowResizedEventArgs>? Resized { add { } remove { } }
|
||||
|
||||
public void MaximizeOnMonitor(IClydeMonitor monitor)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
// Commented out because I can't be bothered to figure out trimming for TerraFX.
|
||||
/*
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
@@ -10,13 +8,18 @@ using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using TerraFX.Interop;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
using static Robust.Client.Graphics.Clyde.Egl;
|
||||
using static TerraFX.Interop.D3D_DRIVER_TYPE;
|
||||
using static TerraFX.Interop.D3D_FEATURE_LEVEL;
|
||||
using static TerraFX.Interop.DXGI_FORMAT;
|
||||
using static TerraFX.Interop.DXGI_SWAP_EFFECT;
|
||||
using static TerraFX.Interop.Windows;
|
||||
using static TerraFX.Interop.DirectX.D3D_DRIVER_TYPE;
|
||||
using static TerraFX.Interop.DirectX.D3D_FEATURE_LEVEL;
|
||||
using static TerraFX.Interop.DirectX.DXGI_FORMAT;
|
||||
using static TerraFX.Interop.DirectX.DXGI_SWAP_EFFECT;
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
using static TerraFX.Interop.DirectX.DirectX;
|
||||
using static TerraFX.Interop.DirectX.D3D11;
|
||||
using static TerraFX.Interop.DirectX.DXGI;
|
||||
using GL = OpenToolkit.Graphics.OpenGL4.GL;
|
||||
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
@@ -77,7 +80,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
};
|
||||
_windowData[reg.Id] = data;
|
||||
|
||||
var hWnd = Clyde._windowing!.WindowGetWin32Window(reg)!.Value;
|
||||
var hWnd = (HWND) Clyde._windowing!.WindowGetWin32Window(reg)!.Value;
|
||||
|
||||
// todo: exception management.
|
||||
CreateSwapChain1(hWnd, data);
|
||||
@@ -98,7 +101,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
if (data.EglBackbuffer != null)
|
||||
{
|
||||
eglMakeCurrent(_eglDisplay, null, null, null);
|
||||
if (data.Reg.IsMainWindow)
|
||||
eglMakeCurrent(_eglDisplay, null, null, null);
|
||||
eglDestroySurface(_eglDisplay, data.EglBackbuffer);
|
||||
|
||||
data.EglBackbuffer = null;
|
||||
@@ -115,8 +119,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
fixed (ID3D11Texture2D** texPtr = &data.Backbuffer)
|
||||
{
|
||||
var iid = IID_ID3D11Texture2D;
|
||||
ThrowIfFailed("GetBuffer", data.SwapChain->GetBuffer(0, &iid, (void**) texPtr));
|
||||
ThrowIfFailed("GetBuffer", data.SwapChain->GetBuffer(0, __uuidof<ID3D11Texture2D>(), (void**) texPtr));
|
||||
}
|
||||
|
||||
var attributes = stackalloc int[]
|
||||
@@ -134,7 +137,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
attributes);
|
||||
}
|
||||
|
||||
private void CreateSwapChain1(nint hWnd, WindowData data)
|
||||
private void CreateSwapChain1(HWND hWnd, WindowData data)
|
||||
{
|
||||
var desc = new DXGI_SWAP_CHAIN_DESC
|
||||
{
|
||||
@@ -199,6 +202,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// and so that we can know _hasGLSrgb in window creation.
|
||||
eglMakeCurrent(_eglDisplay, null, null, _eglContext);
|
||||
Clyde.InitOpenGL();
|
||||
Clyde._earlyGLInit = true;
|
||||
}
|
||||
|
||||
private void TryInitializeCore()
|
||||
@@ -294,11 +298,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
try
|
||||
{
|
||||
var iid = IID_IDXGIFactory1;
|
||||
|
||||
fixed (IDXGIFactory1** ptr = &_factory)
|
||||
{
|
||||
ThrowIfFailed(nameof(CreateDXGIFactory1), CreateDXGIFactory1(&iid, (void**) ptr));
|
||||
ThrowIfFailed(nameof(CreateDXGIFactory1), CreateDXGIFactory1(__uuidof<IDXGIFactory1>(), (void**) ptr));
|
||||
}
|
||||
|
||||
// Try to find the correct adapter if specified.
|
||||
@@ -317,6 +319,38 @@ namespace Robust.Client.Graphics.Clyde
|
||||
Logger.DebugS("clyde.ogl.angle", $"Found display adapter with name: {adapterName}");
|
||||
}
|
||||
|
||||
#pragma warning disable CA1416
|
||||
IDXGIFactory6* factory6;
|
||||
if (_adapter == null && _factory->QueryInterface(__uuidof<IDXGIFactory6>(), (void**) &factory6) == 0)
|
||||
{
|
||||
var gpuPref = (DXGI_GPU_PREFERENCE) Clyde._cfg.GetCVar(CVars.DisplayGpuPreference);
|
||||
IDXGIAdapter1* adapter;
|
||||
for (var adapterIndex = 0u;
|
||||
factory6->EnumAdapterByGpuPreference(
|
||||
adapterIndex,
|
||||
gpuPref,
|
||||
__uuidof<IDXGIAdapter1>(),
|
||||
(void**)&adapter) != DXGI_ERROR_NOT_FOUND;
|
||||
adapterIndex++)
|
||||
{
|
||||
/*
|
||||
DXGI_ADAPTER_DESC1 aDesc;
|
||||
ThrowIfFailed("GetDesc1", adapter->GetDesc1(&aDesc));
|
||||
|
||||
var aDescName = new ReadOnlySpan<char>(aDesc.Description, 128);
|
||||
|
||||
Logger.DebugS("clyde.ogl.angle", aDescName.ToString());
|
||||
|
||||
adapter->Release();
|
||||
*/
|
||||
_adapter = adapter;
|
||||
break;
|
||||
}
|
||||
|
||||
factory6->Release();
|
||||
}
|
||||
#pragma warning restore CA1416
|
||||
|
||||
Span<D3D_FEATURE_LEVEL> featureLevels = stackalloc D3D_FEATURE_LEVEL[]
|
||||
{
|
||||
// 11_0 can do GLES3
|
||||
@@ -335,7 +369,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
ThrowIfFailed("D3D11CreateDevice", D3D11CreateDevice(
|
||||
(IDXGIAdapter*) _adapter,
|
||||
_adapter == null ? D3D_DRIVER_TYPE_HARDWARE : D3D_DRIVER_TYPE_UNKNOWN,
|
||||
IntPtr.Zero,
|
||||
HMODULE.NULL,
|
||||
0,
|
||||
fl,
|
||||
(uint) featureLevels.Length,
|
||||
@@ -348,13 +382,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
// Get adapter from the device.
|
||||
|
||||
iid = IID_IDXGIDevice1;
|
||||
ThrowIfFailed("QueryInterface", _device->QueryInterface(&iid, (void**) &dxgiDevice));
|
||||
ThrowIfFailed("QueryInterface", _device->QueryInterface(__uuidof<IDXGIDevice1>(), (void**) &dxgiDevice));
|
||||
|
||||
fixed (IDXGIAdapter1** ptrAdapter = &_adapter)
|
||||
{
|
||||
iid = IID_IDXGIAdapter1;
|
||||
ThrowIfFailed("GetParent", dxgiDevice->GetParent(&iid, (void**) ptrAdapter));
|
||||
ThrowIfFailed("GetParent", dxgiDevice->GetParent(__uuidof<IDXGIAdapter1>(), (void**) ptrAdapter));
|
||||
}
|
||||
|
||||
_deviceFl = _device->GetFeatureLevel();
|
||||
@@ -527,4 +559,3 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,28 +1,32 @@
|
||||
/*
|
||||
using System;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Utility;
|
||||
using TerraFX.Interop;
|
||||
using static TerraFX.Interop.D3D_DRIVER_TYPE;
|
||||
using static TerraFX.Interop.D3D_FEATURE_LEVEL;
|
||||
using static TerraFX.Interop.DXGI_MEMORY_SEGMENT_GROUP;
|
||||
using static TerraFX.Interop.DXGI_SWAP_EFFECT;
|
||||
using static TerraFX.Interop.Windows;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
using static TerraFX.Interop.DirectX.DXGI_MEMORY_SEGMENT_GROUP;
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
using static TerraFX.Interop.DirectX.DirectX;
|
||||
using static TerraFX.Interop.DirectX.DXGI;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
public sealed class VramCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "vram";
|
||||
public string Description => "Checks vram";
|
||||
public string Description => "Displays video memory usage statics by the game.";
|
||||
public string Help => "Usage: vram";
|
||||
|
||||
public unsafe void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
shell.WriteError("This command is only supported on Windows.");
|
||||
return;
|
||||
}
|
||||
|
||||
IDXGIFactory1* dxgiFactory;
|
||||
var iid = IID_IDXGIFactory1;
|
||||
ThrowIfFailed(nameof(CreateDXGIFactory1), CreateDXGIFactory1(&iid, (void**) &dxgiFactory));
|
||||
ThrowIfFailed(nameof(CreateDXGIFactory1), CreateDXGIFactory1(__uuidof<IDXGIFactory1>(), (void**) &dxgiFactory));
|
||||
|
||||
uint idx = 0;
|
||||
IDXGIAdapter* adapter;
|
||||
@@ -30,8 +34,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
DXGI_ADAPTER_DESC2 desc;
|
||||
IDXGIAdapter3* adapter3;
|
||||
iid = IID_IDXGIAdapter3;
|
||||
adapter->QueryInterface(&iid, (void**) &adapter3);
|
||||
adapter->QueryInterface(__uuidof<IDXGIAdapter3>(), (void**) &adapter3);
|
||||
adapter->Release();
|
||||
ThrowIfFailed("GetDesc", adapter3->GetDesc2(&desc));
|
||||
|
||||
var descString = new ReadOnlySpan<char>(desc.Description, 128).TrimEnd('\0');
|
||||
@@ -46,6 +50,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
shell.WriteLine($"Usage (non local): {ByteHelpers.FormatBytes((long) memInfo.CurrentUsage)}");
|
||||
|
||||
idx += 1;
|
||||
|
||||
adapter3->Release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,4 +64,3 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared;
|
||||
using System.Threading;
|
||||
using OpenToolkit.GraphicsLibraryFramework;
|
||||
using Robust.Client.Input;
|
||||
@@ -8,6 +9,8 @@ using GlfwKey = OpenToolkit.GraphicsLibraryFramework.Keys;
|
||||
using GlfwButton = OpenToolkit.GraphicsLibraryFramework.MouseButton;
|
||||
using static Robust.Client.Input.Mouse;
|
||||
using static Robust.Client.Input.Keyboard;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -22,6 +25,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private void InitKeyMap()
|
||||
{
|
||||
_printableKeyNameMap.Clear();
|
||||
// From GLFW's source code: this is the actual list of "printable" keys
|
||||
// that GetKeyName returns something for.
|
||||
CacheKey(Keys.KeyPadEqual);
|
||||
@@ -41,8 +45,18 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (rKey == Key.Unknown)
|
||||
return;
|
||||
|
||||
var name = GLFW.GetKeyName(key, 0);
|
||||
if (name != null)
|
||||
string name;
|
||||
|
||||
if (!_clyde._cfg.GetCVar(CVars.DisplayUSQWERTYHotkeys))
|
||||
{
|
||||
name = GLFW.GetKeyName(key, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
name = key.ToString();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
_printableKeyNameMap.Add(rKey, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
@@ -268,7 +268,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
var (desc, errCode) = errorResult!.Value;
|
||||
return (null, $"[{errCode}]: {desc}");
|
||||
return (null, (string)$"[{errCode}]: {desc}");
|
||||
}
|
||||
|
||||
public void WindowDestroy(WindowReg window)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using OpenToolkit.GraphicsLibraryFramework;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -16,6 +17,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly ILocalizationManager _loc = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
|
||||
private readonly Clyde _clyde;
|
||||
|
||||
@@ -39,6 +41,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
#if DEBUG
|
||||
_cfg.OnValueChanged(CVars.DisplayWin32Experience, b => _win32Experience = b, true);
|
||||
#endif
|
||||
_cfg.OnValueChanged(CVars.DisplayUSQWERTYHotkeys, ReInitKeyMap);
|
||||
|
||||
InitChannels();
|
||||
|
||||
@@ -60,6 +63,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (_glfwInitialized)
|
||||
{
|
||||
_sawmill.Debug("Terminating GLFW.");
|
||||
_cfg.UnsubValueChanged(CVars.DisplayUSQWERTYHotkeys, ReInitKeyMap);
|
||||
GLFW.Terminate();
|
||||
}
|
||||
}
|
||||
@@ -69,6 +73,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Not currently used
|
||||
}
|
||||
|
||||
private void ReInitKeyMap(bool onValueChanged)
|
||||
{
|
||||
InitKeyMap();
|
||||
_inputManager.InputModeChanged();
|
||||
}
|
||||
|
||||
private bool InitGlfw()
|
||||
{
|
||||
StoreCallbacks();
|
||||
|
||||
@@ -94,6 +94,6 @@ namespace Robust.Client.Graphics
|
||||
public Vector2 DrawString(Font font, Vector2 pos, string str)
|
||||
=> DrawString(font, pos, str, Color.White);
|
||||
|
||||
public abstract void DrawEntity(IEntity entity, Vector2 position, Vector2 scale, Direction? overrideDirection);
|
||||
public abstract void DrawEntity(EntityUid entity, Vector2 position, Vector2 scale, Direction? overrideDirection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,11 @@ namespace Robust.Client.Graphics
|
||||
public abstract void DrawTextureRectRegion(Texture texture, in Box2Rotated quad,
|
||||
Color? modulate = null, UIBox2? subRegion = null);
|
||||
|
||||
private Box2 GetQuad(Texture texture, Vector2 position)
|
||||
{
|
||||
return Box2.FromDimensions(position, texture.Size / (float)Ppm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a full texture sprite to the world. The coordinate system is right handed.
|
||||
/// Make sure to set <see cref="DrawingHandleBase.SetTransform"/>
|
||||
@@ -67,7 +72,28 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
DrawTextureRect(texture, Box2.FromDimensions(position, texture.Size / (float) Ppm), modulate);
|
||||
DrawTextureRect(texture, GetQuad(texture, position), modulate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a full texture sprite to the world. The coordinate system is right handed.
|
||||
/// Make sure to set <see cref="DrawingHandleBase.SetTransform"/>
|
||||
/// to set the model matrix if needed.
|
||||
/// </summary>
|
||||
/// <param name="texture">Texture to draw.</param>
|
||||
/// <param name="position">The coordinates of the quad in object space (or world if the transform is identity.).</param>
|
||||
/// <param name="angle">The angle of the quad in object space.</param>
|
||||
/// <param name="modulate">A color to multiply the texture by when shading.</param>
|
||||
/// <remarks>
|
||||
/// The sprite will have it's local dimensions calculated so that it has <see cref="EyeManager.PixelsPerMeter"/> texels per meter in the world.
|
||||
/// </remarks>
|
||||
public void DrawTexture(Texture texture, Vector2 position, Angle angle, Color? modulate = null)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
var quad = GetQuad(texture, position);
|
||||
|
||||
DrawTextureRect(texture, new Box2Rotated(quad, angle, quad.Center), modulate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
16
Robust.Client/Graphics/IClydeAudioInternal.cs
Normal file
16
Robust.Client/Graphics/IClydeAudioInternal.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
internal interface IClydeAudioInternal : IClydeAudio
|
||||
{
|
||||
bool InitializePostWindowing();
|
||||
void FrameProcess(FrameEventArgs eventArgs);
|
||||
void Shutdown();
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,12 @@ namespace Robust.Client.Graphics
|
||||
/// This means the window must not be used anymore (it is disposed).
|
||||
/// </summary>
|
||||
event Action<WindowDestroyedEventArgs> Destroyed;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the window has been definitively closed.
|
||||
/// This means the window must not be used anymore (it is disposed).
|
||||
/// </summary>
|
||||
event Action<WindowResizedEventArgs> Resized;
|
||||
}
|
||||
|
||||
public interface IClydeWindowInternal : IClydeWindow
|
||||
|
||||
@@ -12,6 +12,6 @@ namespace Robust.Client.Graphics
|
||||
void RenderInRenderTarget(IRenderTarget target, Action a, Color clearColor=default);
|
||||
|
||||
void SetScissor(UIBox2i? scissorBox);
|
||||
void DrawEntity(IEntity entity, Vector2 position, Vector2 scale, Direction? overrideDirection);
|
||||
void DrawEntity(EntityUid entity, Vector2 position, Vector2 scale, Direction? overrideDirection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,7 @@ namespace Robust.Client.Input
|
||||
|
||||
event Action<IKeyBinding> OnKeyBindingAdded;
|
||||
event Action<IKeyBinding> OnKeyBindingRemoved;
|
||||
event Action OnInputModeChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the keybinds bound to a specific function.
|
||||
@@ -131,5 +132,7 @@ namespace Robust.Client.Input
|
||||
bool IsKeyFunctionModified(BoundKeyFunction function);
|
||||
|
||||
bool IsKeyDown(Keyboard.Key key);
|
||||
|
||||
void InputModeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Robust.Client.Input
|
||||
{
|
||||
BoundKeyState State { get; }
|
||||
BoundKeyFunction Function { get; }
|
||||
string FunctionCommand { get; }
|
||||
KeyBindingType BindingType { get; }
|
||||
|
||||
Keyboard.Key BaseKey { get; }
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace Robust.Client.Input
|
||||
[Dependency] private readonly IResourceManager _resourceMan = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IUserInterfaceManagerInternal _uiMgr = default!;
|
||||
[Dependency] private readonly IConsoleHost _console = default!;
|
||||
|
||||
private bool _currentlyFindingViewport;
|
||||
|
||||
@@ -98,6 +99,7 @@ namespace Robust.Client.Input
|
||||
public event KeyEventAction? FirstChanceOnKeyEvent;
|
||||
public event Action<IKeyBinding>? OnKeyBindingAdded;
|
||||
public event Action<IKeyBinding>? OnKeyBindingRemoved;
|
||||
public event Action? OnInputModeChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
@@ -131,7 +133,7 @@ namespace Robust.Client.Input
|
||||
.SelectMany(p => p)
|
||||
.Select(p => new KeyBindingRegistration
|
||||
{
|
||||
Function = p.Function,
|
||||
Function = p.Function.FunctionName,
|
||||
BaseKey = p.BaseKey,
|
||||
Mod1 = p.Mod1,
|
||||
Mod2 = p.Mod2,
|
||||
@@ -213,7 +215,7 @@ namespace Robust.Client.Input
|
||||
foreach (var binding in _bindings)
|
||||
{
|
||||
// check if our binding is even in the active context
|
||||
if (!Contexts.ActiveContext.FunctionExistsHierarchy(binding.Function))
|
||||
if (binding.BindingType != KeyBindingType.Command && !Contexts.ActiveContext.FunctionExistsHierarchy(binding.Function))
|
||||
continue;
|
||||
|
||||
if (PackedMatchesPressedState(binding.PackedKeyCombo))
|
||||
@@ -359,6 +361,12 @@ namespace Robust.Client.Input
|
||||
|
||||
private bool SetBindState(KeyBinding binding, BoundKeyState state, bool uiOnly = false)
|
||||
{
|
||||
if (binding.BindingType == KeyBindingType.Command && state == BoundKeyState.Down)
|
||||
{
|
||||
_console.ExecuteCommand(binding.FunctionCommand);
|
||||
return true;
|
||||
}
|
||||
|
||||
// christ this crap *is* re-entrant thanks to PlacementManager and
|
||||
// I honestly have no idea what the best solution here is.
|
||||
// note from the future: context switches won't cause re-entrancy anymore because InputContextContainer defers context switches
|
||||
@@ -492,10 +500,10 @@ namespace Robust.Client.Input
|
||||
|
||||
foreach (var reg in baseKeyRegs)
|
||||
{
|
||||
if (!NetworkBindMap.FunctionExists(reg.Function.FunctionName))
|
||||
if (reg.Type != KeyBindingType.Command && !NetworkBindMap.FunctionExists(reg.Function.FunctionName))
|
||||
{
|
||||
Logger.ErrorS("input", "Key function in {0} does not exist: '{1}'", file,
|
||||
reg.Function.FunctionName);
|
||||
reg.Function);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -531,6 +539,17 @@ namespace Robust.Client.Input
|
||||
/// <inheritdoc />
|
||||
public IKeyBinding RegisterBinding(BoundKeyFunction function, KeyBindingType bindingType,
|
||||
Key baseKey, Key? mod1, Key? mod2, Key? mod3)
|
||||
{
|
||||
var binding = new KeyBinding(this, function.FunctionName, bindingType, baseKey, false, false, false,
|
||||
0, mod1 ?? Key.Unknown, mod2 ?? Key.Unknown, mod3 ?? Key.Unknown);
|
||||
|
||||
RegisterBinding(binding);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
public IKeyBinding RegisterBinding(string function, KeyBindingType bindingType,
|
||||
Key baseKey, Key? mod1, Key? mod2, Key? mod3)
|
||||
{
|
||||
var binding = new KeyBinding(this, function, bindingType, baseKey, false, false, false,
|
||||
0, mod1 ?? Key.Unknown, mod2 ?? Key.Unknown, mod3 ?? Key.Unknown);
|
||||
@@ -542,7 +561,7 @@ namespace Robust.Client.Input
|
||||
|
||||
public IKeyBinding RegisterBinding(in KeyBindingRegistration reg, bool markModified = true)
|
||||
{
|
||||
var binding = new KeyBinding(this, reg.Function, reg.Type, reg.BaseKey, reg.CanFocus, reg.CanRepeat,
|
||||
var binding = new KeyBinding(this, reg.Function.FunctionName, reg.Type, reg.BaseKey, reg.CanFocus, reg.CanRepeat,
|
||||
reg.AllowSubCombs, reg.Priority, reg.Mod1, reg.Mod2, reg.Mod3);
|
||||
|
||||
RegisterBinding(binding, markModified);
|
||||
@@ -569,6 +588,8 @@ namespace Robust.Client.Input
|
||||
OnKeyBindingRemoved?.Invoke(binding);
|
||||
}
|
||||
|
||||
public void InputModeChanged() => OnInputModeChanged?.Invoke();
|
||||
|
||||
private void RegisterBinding(KeyBinding binding, bool markModified = true)
|
||||
{
|
||||
// we sort larger combos first so they take priority over smaller (single key) combos,
|
||||
@@ -684,6 +705,7 @@ namespace Robust.Client.Input
|
||||
[ViewVariables] public BoundKeyState State { get; set; }
|
||||
public PackedKeyCombo PackedKeyCombo { get; }
|
||||
[ViewVariables] public BoundKeyFunction Function { get; }
|
||||
[ViewVariables] public string FunctionCommand => Function.FunctionName;
|
||||
[ViewVariables] public KeyBindingType BindingType { get; }
|
||||
|
||||
[ViewVariables] public Key BaseKey => PackedKeyCombo.BaseKey;
|
||||
@@ -711,7 +733,9 @@ namespace Robust.Client.Input
|
||||
|
||||
[ViewVariables] public int Priority { get; internal set; }
|
||||
|
||||
public KeyBinding(InputManager inputManager, BoundKeyFunction function,
|
||||
public KeyBinding(
|
||||
InputManager inputManager,
|
||||
string function,
|
||||
KeyBindingType bindingType,
|
||||
Key baseKey,
|
||||
bool canFocus, bool canRepeat, bool allowSubCombs, int priority, Key mod1 = Key.Unknown,
|
||||
@@ -771,7 +795,7 @@ namespace Robust.Client.Input
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("{0}: {1}", Function.FunctionName, BaseKey);
|
||||
sb.AppendFormat("{0}: {1}", Function, BaseKey);
|
||||
if (Mod1 != Key.Unknown)
|
||||
{
|
||||
sb.AppendFormat("+{0}", Mod1);
|
||||
@@ -868,6 +892,10 @@ namespace Robust.Client.Input
|
||||
Unknown = 0,
|
||||
State,
|
||||
Toggle,
|
||||
/// <summary>
|
||||
/// This keybind does not execute a real key function but instead causes a console command to be executed.
|
||||
/// </summary>
|
||||
Command,
|
||||
}
|
||||
|
||||
public enum CommandState : byte
|
||||
@@ -922,7 +950,7 @@ namespace Robust.Client.Input
|
||||
|
||||
var registration = new KeyBindingRegistration
|
||||
{
|
||||
Function = new BoundKeyFunction(inputCommand),
|
||||
Function = inputCommand,
|
||||
BaseKey = keyId,
|
||||
Type = keyMode
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -35,7 +35,8 @@ namespace Robust.Client.Physics
|
||||
protected override void HandleMapCreated(object? sender, MapEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Map == MapId.Nullspace) return;
|
||||
MapManager.GetMapEntity(eventArgs.Map).AddComponent<PhysicsMapComponent>();
|
||||
var mapUid = MapManager.GetMapEntityId(eventArgs.Map);
|
||||
EntityManager.AddComponent<PhysicsMapComponent>(mapUid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Client.Placement.Modes
|
||||
{
|
||||
@@ -31,8 +32,8 @@ namespace Robust.Client.Placement.Modes
|
||||
var mapId = MouseCoords.GetMapId(pManager.EntityManager);
|
||||
|
||||
var snapToEntities = IoCManager.Resolve<IEntityLookup>().GetEntitiesInRange(MouseCoords, SnapToRange)
|
||||
.Where(entity => entity.Prototype == pManager.CurrentPrototype && entity.Transform.MapID == mapId)
|
||||
.OrderBy(entity => (entity.Transform.WorldPosition - MouseCoords.ToMapPos(pManager.EntityManager)).LengthSquared)
|
||||
.Where(entity => pManager.EntityManager.GetComponent<MetaDataComponent>(entity).EntityPrototype == pManager.CurrentPrototype && pManager.EntityManager.GetComponent<TransformComponent>(entity).MapID == mapId)
|
||||
.OrderBy(entity => (pManager.EntityManager.GetComponent<TransformComponent>(entity).WorldPosition - MouseCoords.ToMapPos(pManager.EntityManager)).LengthSquared)
|
||||
.ToList();
|
||||
|
||||
if (snapToEntities.Count == 0)
|
||||
@@ -41,7 +42,8 @@ namespace Robust.Client.Placement.Modes
|
||||
}
|
||||
|
||||
var closestEntity = snapToEntities[0];
|
||||
if (!closestEntity.TryGetComponent<ISpriteComponent>(out var component) || component.BaseRSI == null)
|
||||
var closestTransform = pManager.EntityManager.GetComponent<TransformComponent>(closestEntity);
|
||||
if (!pManager.EntityManager.TryGetComponent<ISpriteComponent?>(closestEntity, out var component) || component.BaseRSI == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -50,8 +52,8 @@ namespace Robust.Client.Placement.Modes
|
||||
|
||||
var closestRect =
|
||||
Box2.FromDimensions(
|
||||
closestEntity.Transform.WorldPosition.X - closestBounds.X / 2f,
|
||||
closestEntity.Transform.WorldPosition.Y - closestBounds.Y / 2f,
|
||||
closestTransform.WorldPosition.X - closestBounds.X / 2f,
|
||||
closestTransform.WorldPosition.Y - closestBounds.Y / 2f,
|
||||
closestBounds.X, closestBounds.Y);
|
||||
|
||||
var sides = new[]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -34,8 +33,8 @@ namespace Robust.Client.Placement.Modes
|
||||
GridDistancing = SnapSize;
|
||||
|
||||
var mouselocal = new Vector2( //Round local coordinates onto the snap grid
|
||||
(float) MathF.Round(MouseCoords.X / SnapSize, MidpointRounding.AwayFromZero) * SnapSize,
|
||||
(float) MathF.Round(MouseCoords.Y / SnapSize, MidpointRounding.AwayFromZero) * SnapSize);
|
||||
MathF.Round(MouseCoords.X / SnapSize, MidpointRounding.AwayFromZero) * SnapSize,
|
||||
MathF.Round(MouseCoords.Y / SnapSize, MidpointRounding.AwayFromZero) * SnapSize);
|
||||
|
||||
//Convert back to original world and screen coordinates after applying offset
|
||||
MouseCoords =
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Robust.Client.Placement
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool HijackDeletion(IEntity entity)
|
||||
public virtual bool HijackDeletion(EntityUid entity)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -15,7 +15,6 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -99,7 +98,7 @@ namespace Robust.Client.Placement
|
||||
/// Colour of this gets swapped around in PlacementMode.
|
||||
/// This entity needs to stay in nullspace.
|
||||
/// </summary>
|
||||
public IEntity? CurrentPlacementOverlayEntity { get; set; }
|
||||
public EntityUid? CurrentPlacementOverlayEntity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A BAD way to explicitly control the icons used!!!
|
||||
@@ -242,7 +241,7 @@ namespace Robust.Client.Placement
|
||||
return false;
|
||||
}
|
||||
|
||||
HandleDeletion(EntityManager.GetEntity(uid));
|
||||
HandleDeletion(uid);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -414,14 +413,14 @@ namespace Robust.Client.Placement
|
||||
return false;
|
||||
}
|
||||
|
||||
public void HandleDeletion(IEntity entity)
|
||||
public void HandleDeletion(EntityUid entity)
|
||||
{
|
||||
if (!IsActive || !Eraser) return;
|
||||
if (Hijack != null && Hijack.HijackDeletion(entity)) return;
|
||||
|
||||
var msg = NetworkManager.CreateNetMessage<MsgPlacement>();
|
||||
msg.PlaceType = PlacementManagerMessage.RequestEntRemove;
|
||||
msg.EntityUid = entity.Uid;
|
||||
msg.EntityUid = entity;
|
||||
NetworkManager.ClientSendMessage(msg);
|
||||
}
|
||||
|
||||
@@ -493,10 +492,9 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
// Try to get current map.
|
||||
var map = MapId.Nullspace;
|
||||
var ent = PlayerManager.LocalPlayer!.ControlledEntity;
|
||||
if (ent != null)
|
||||
if (PlayerManager.LocalPlayer!.ControlledEntity is {Valid: true} ent)
|
||||
{
|
||||
map = ent.Transform.MapID;
|
||||
map = EntityManager.GetComponent<TransformComponent>(ent).MapID;
|
||||
}
|
||||
|
||||
if (map == MapId.Nullspace || CurrentPermission == null || CurrentMode == null)
|
||||
@@ -511,15 +509,15 @@ namespace Robust.Client.Placement
|
||||
|
||||
private bool CurrentEraserMouseCoordinates(out EntityCoordinates coordinates)
|
||||
{
|
||||
var ent = PlayerManager.LocalPlayer?.ControlledEntity;
|
||||
if (ent == null)
|
||||
var ent = PlayerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
if (ent == EntityUid.Invalid)
|
||||
{
|
||||
coordinates = new EntityCoordinates();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var map = ent.Transform.MapID;
|
||||
var map = EntityManager.GetComponent<TransformComponent>(ent).MapID;
|
||||
if (map == MapId.Nullspace || !Eraser)
|
||||
{
|
||||
coordinates = new EntityCoordinates();
|
||||
@@ -634,11 +632,12 @@ namespace Robust.Client.Placement
|
||||
|
||||
CurrentMode.Render(handle);
|
||||
|
||||
if (CurrentPermission == null || CurrentPermission.Range <= 0 || !CurrentMode.RangeRequired
|
||||
|| PlayerManager.LocalPlayer?.ControlledEntity == null)
|
||||
if (CurrentPermission is not {Range: > 0} ||
|
||||
!CurrentMode.RangeRequired ||
|
||||
PlayerManager.LocalPlayer?.ControlledEntity is not {Valid: true} controlled)
|
||||
return;
|
||||
|
||||
var worldPos = PlayerManager.LocalPlayer.ControlledEntity.Transform.WorldPosition;
|
||||
var worldPos = EntityManager.GetComponent<TransformComponent>(controlled).WorldPosition;
|
||||
|
||||
handle.DrawCircle(worldPos, CurrentPermission.Range, new Color(1, 1, 1, 0.25f));
|
||||
}
|
||||
@@ -661,8 +660,8 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
if (CurrentPlacementOverlayEntity != null)
|
||||
{
|
||||
if (!CurrentPlacementOverlayEntity.Deleted)
|
||||
CurrentPlacementOverlayEntity.Delete();
|
||||
if (!EntityManager.Deleted(CurrentPlacementOverlayEntity))
|
||||
EntityManager.DeleteEntity(CurrentPlacementOverlayEntity.Value);
|
||||
CurrentPlacementOverlayEntity = null;
|
||||
}
|
||||
}
|
||||
@@ -671,7 +670,7 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
EnsureNoPlacementOverlayEntity();
|
||||
CurrentPlacementOverlayEntity = EntityManager.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
return CurrentPlacementOverlayEntity.EnsureComponent<SpriteComponent>();
|
||||
return EntityManager.EnsureComponent<SpriteComponent>(CurrentPlacementOverlayEntity.Value);
|
||||
}
|
||||
|
||||
private void PreparePlacement(string templateName)
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Placement
|
||||
@@ -87,9 +84,9 @@ namespace Robust.Client.Placement
|
||||
public virtual void Render(DrawingHandleWorld handle)
|
||||
{
|
||||
var sce = pManager.CurrentPlacementOverlayEntity;
|
||||
if (sce == null || sce.Deleted)
|
||||
if (sce is not {} scent || pManager.EntityManager.Deleted(scent))
|
||||
return;
|
||||
var sc = sce.GetComponent<SpriteComponent>();
|
||||
var sc = pManager.EntityManager.GetComponent<SpriteComponent>(sce!.Value);
|
||||
|
||||
IEnumerable<EntityCoordinates> locationcollection;
|
||||
switch (pManager.PlacementType)
|
||||
@@ -113,9 +110,8 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
if (!coordinate.IsValid(pManager.EntityManager))
|
||||
return; // Just some paranoia just in case
|
||||
var entity = coordinate.GetEntity(pManager.EntityManager);
|
||||
var worldPos = coordinate.ToMapPos(pManager.EntityManager);
|
||||
var worldRot = entity.Transform.WorldRotation + dirAng;
|
||||
var worldRot = pManager.EntityManager.GetComponent<TransformComponent>(coordinate.EntityId).WorldRotation + dirAng;
|
||||
|
||||
sc.Color = IsValidPosition(coordinate) ? ValidPlaceColor : InvalidPlaceColor;
|
||||
sc.Render(handle, pManager.eyeManager.CurrentEye.Rotation, worldRot, worldPos);
|
||||
@@ -198,14 +194,14 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
if (!RangeRequired)
|
||||
return true;
|
||||
|
||||
if (pManager.PlayerManager.LocalPlayer?.ControlledEntity == null)
|
||||
var controlled = pManager.PlayerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
if (controlled == EntityUid.Invalid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var range = pManager.CurrentPermission!.Range;
|
||||
if (range > 0 && !pManager.PlayerManager.LocalPlayer.ControlledEntity.Transform.Coordinates.InRange(pManager.EntityManager, coordinates, range))
|
||||
if (range > 0 && !pManager.EntityManager.GetComponent<TransformComponent>(controlled).Coordinates.InRange(pManager.EntityManager, coordinates, range))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user