mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
195 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aacf6522b4 | ||
|
|
c73d27b9ae | ||
|
|
f068b30a7c | ||
|
|
5400dddcfc | ||
|
|
6cf5fdc5d6 | ||
|
|
5d46663881 | ||
|
|
8e0f227940 | ||
|
|
73a13fff9a | ||
|
|
de2e505a12 | ||
|
|
a9f7c7a76f | ||
|
|
37401c26c9 | ||
|
|
528cd1e0e5 | ||
|
|
2959456bec | ||
|
|
8951712495 | ||
|
|
d8612aff64 | ||
|
|
e16732eb7b | ||
|
|
91f61bb9de | ||
|
|
ddc91d05ec | ||
|
|
ef22842b90 | ||
|
|
303e2152d2 | ||
|
|
37fc0d0d2a | ||
|
|
53987e1e5d | ||
|
|
3216d7770b | ||
|
|
3203ca2ff4 | ||
|
|
e22254cd51 | ||
|
|
7ed722f669 | ||
|
|
4864096b2a | ||
|
|
5161385de4 | ||
|
|
98e009b38f | ||
|
|
3863ab8f62 | ||
|
|
f576eb5125 | ||
|
|
314742ccd8 | ||
|
|
f9074811f9 | ||
|
|
5f3e1eb378 | ||
|
|
3c1ee20ca1 | ||
|
|
3768f5e68e | ||
|
|
765a560380 | ||
|
|
39ae3ac653 | ||
|
|
e48f4027e5 | ||
|
|
2fa1e98faf | ||
|
|
cedfa0ee2f | ||
|
|
92f44b390e | ||
|
|
65a42f9209 | ||
|
|
ebf53248cf | ||
|
|
289f637e8a | ||
|
|
d7c13f30c8 | ||
|
|
0dac17ae5e | ||
|
|
9a19a774fa | ||
|
|
81f49d5eb2 | ||
|
|
4f3b4ac2d2 | ||
|
|
e428056b52 | ||
|
|
8dc9d2989a | ||
|
|
fd8c90dcbb | ||
|
|
ffe4e5a8ab | ||
|
|
6e5026d270 | ||
|
|
946c4166dc | ||
|
|
7d2fb85a04 | ||
|
|
d6ec078519 | ||
|
|
32256fc4d9 | ||
|
|
37bbdfe7ff | ||
|
|
c906675cdf | ||
|
|
90bb5574c1 | ||
|
|
7b50dcd969 | ||
|
|
8d82f48a8f | ||
|
|
469f9fd219 | ||
|
|
1a5783ab4e | ||
|
|
3d25886d79 | ||
|
|
516b2cd372 | ||
|
|
3cfcfa0be2 | ||
|
|
69328087bd | ||
|
|
1bf8b2a52b | ||
|
|
fc6dc6f4e1 | ||
|
|
31c1feca4e | ||
|
|
3ed1eef2ab | ||
|
|
1394a017bb | ||
|
|
6b0670d5f1 | ||
|
|
f573331541 | ||
|
|
a7218cd3b8 | ||
|
|
f7e8178736 | ||
|
|
31f921e4aa | ||
|
|
aa1c25637c | ||
|
|
71f2c48463 | ||
|
|
d65f4ca898 | ||
|
|
b35568ffe5 | ||
|
|
a0d241e551 | ||
|
|
33a6934582 | ||
|
|
f237a8bbbc | ||
|
|
4bc775c01c | ||
|
|
93b4d81505 | ||
|
|
0afb85a09e | ||
|
|
7b9315cea4 | ||
|
|
dc3af45096 | ||
|
|
00ce0179ae | ||
|
|
81947ba3d8 | ||
|
|
49327279d0 | ||
|
|
0936cf3c7f | ||
|
|
43b75a69c2 | ||
|
|
c17c8d7a11 | ||
|
|
223fd8126f | ||
|
|
1d5559be4a | ||
|
|
0b749ff8bb | ||
|
|
069fa89fcb | ||
|
|
80f9f24243 | ||
|
|
93018c9843 | ||
|
|
e2675271d0 | ||
|
|
d1f7edecef | ||
|
|
b5a3c0b988 | ||
|
|
06e62b031a | ||
|
|
24707b7385 | ||
|
|
ab95f39f9f | ||
|
|
cdd38abab5 | ||
|
|
d751c0b3ab | ||
|
|
2ace0e9e5a | ||
|
|
31716f5104 | ||
|
|
fefcc7cba3 | ||
|
|
30df989e8d | ||
|
|
86bfea6bd4 | ||
|
|
d890f168c2 | ||
|
|
f888a810bf | ||
|
|
16249a4dde | ||
|
|
e33488ba55 | ||
|
|
bfe6eeddb1 | ||
|
|
7f540e741c | ||
|
|
b7855f5af5 | ||
|
|
91391e1205 | ||
|
|
d5199ec459 | ||
|
|
e1e6f4fd10 | ||
|
|
e5b6fccf67 | ||
|
|
95a912c329 | ||
|
|
2b4833fc4e | ||
|
|
b814fc851a | ||
|
|
e87863203b | ||
|
|
33b66d9e18 | ||
|
|
fd406f7897 | ||
|
|
7a836d1018 | ||
|
|
393c15c44a | ||
|
|
a7f31f9ebf | ||
|
|
b332644d48 | ||
|
|
510f7c0e7c | ||
|
|
fdd05e3d3a | ||
|
|
6d41958853 | ||
|
|
cecc4dfcf2 | ||
|
|
4ac40f2e90 | ||
|
|
3e12d44173 | ||
|
|
a42b39bd84 | ||
|
|
22affccf24 | ||
|
|
028724c47b | ||
|
|
0114bff2fc | ||
|
|
4ddbd644eb | ||
|
|
f0366531ef | ||
|
|
6bd5814f4a | ||
|
|
78c132fdab | ||
|
|
460cf57d7c | ||
|
|
a3190a8aca | ||
|
|
6921fb2fbf | ||
|
|
9954d571de | ||
|
|
17fe000a1e | ||
|
|
fba415e765 | ||
|
|
583b7ebf38 | ||
|
|
771a256925 | ||
|
|
ae79e89347 | ||
|
|
6c7eeb95eb | ||
|
|
de0bd1887f | ||
|
|
eb3a815d48 | ||
|
|
e2a4dcdff1 | ||
|
|
68b0d7bf2e | ||
|
|
a9b163992b | ||
|
|
2409965cf8 | ||
|
|
eada37378a | ||
|
|
0f1da1ba2a | ||
|
|
e0cdcd228e | ||
|
|
fdb5e014b5 | ||
|
|
cefcad775b | ||
|
|
e40feac1f1 | ||
|
|
3ef4ac7452 | ||
|
|
93bf1b09e7 | ||
|
|
a1e557e870 | ||
|
|
864adb7445 | ||
|
|
9e3f3f0c1c | ||
|
|
a40c4a435c | ||
|
|
17182dd0e8 | ||
|
|
d8b50044a2 | ||
|
|
4dc396e73d | ||
|
|
6ae0b0e892 | ||
|
|
7162ca3456 | ||
|
|
1b44c1a1b8 | ||
|
|
5b80b33e00 | ||
|
|
f05c1b2395 | ||
|
|
d9b2c73440 | ||
|
|
29a39f8e0a | ||
|
|
2d72a2bdb5 | ||
|
|
91da635631 | ||
|
|
68ab3d504a | ||
|
|
5187040a64 | ||
|
|
e0c63e7ce6 |
17
.github/CODEOWNERS
vendored
17
.github/CODEOWNERS
vendored
@@ -1,14 +1,7 @@
|
||||
# Lines starting with '#' are comments.
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
# Last match in file takes precedence.
|
||||
|
||||
# These owners will be the default owners for everything in the repo.
|
||||
# * @defunkt
|
||||
* @Acruid @PJB3005 @Silvertorch5
|
||||
# Be they Fluent translations or Freemarker templates, I know them both!
|
||||
*.ftl @RemieRichards
|
||||
|
||||
# Order is important. The last matching pattern has the most precedence.
|
||||
# So if a pull request only touches javascript files, only these owners
|
||||
# will be requested to review.
|
||||
# *.js @octocat @github/js
|
||||
|
||||
# You can also use email addresses if you prefer.
|
||||
# docs/* docs@example.com
|
||||
# Ping for all PRs
|
||||
* @Acruid @PJB3005 @Silvertorch5
|
||||
Submodule Lidgren.Network/Lidgren.Network updated: 73554e6061...5fc11c2b2b
Submodule NetSerializer updated: 1e103e8e29...2d4f8b5611
@@ -574,29 +574,22 @@
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
- name: NGetText
|
||||
- name: Fluent.Net
|
||||
license: |
|
||||
The MIT License (MIT)
|
||||
blushingpenguin and Contributors
|
||||
|
||||
Copyright (c) 2012 Vitaly Zilnik
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
|
||||
- name: NetSerializer
|
||||
license: |
|
||||
|
||||
1
Resources/Locale/en-US/ss14window.ftl
Normal file
1
Resources/Locale/en-US/ss14window.ftl
Normal file
@@ -0,0 +1 @@
|
||||
ss14window-placeholder-title = Exemplary Window Title Here
|
||||
40
Resources/Prototypes/dbg_rotationMarker.yml
Normal file
40
Resources/Prototypes/dbg_rotationMarker.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
- type: entity
|
||||
id: debugRotation1
|
||||
name: dbg_rotation1
|
||||
components:
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
visible: true
|
||||
sprite: debugRotation.rsi
|
||||
state: direction1
|
||||
placement:
|
||||
mode: AlignTileAny
|
||||
|
||||
- type: entity
|
||||
id: debugRotation4
|
||||
name: dbg_rotation4
|
||||
components:
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
visible: true
|
||||
sprite: debugRotation.rsi
|
||||
state: direction4
|
||||
placement:
|
||||
mode: AlignTileAny
|
||||
|
||||
- type: entity
|
||||
id: debugRotationTex
|
||||
name: dbg_rotationTex
|
||||
components:
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
visible: true
|
||||
texture: debugRotation.rsi/direction1.png
|
||||
placement:
|
||||
mode: AlignTileAny
|
||||
4
Resources/Prototypes/dummy_entity.yml
Normal file
4
Resources/Prototypes/dummy_entity.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
- type: entity
|
||||
name: blank entity
|
||||
id: BlankEntity
|
||||
abstract: true
|
||||
@@ -28,5 +28,10 @@ void fragment()
|
||||
|
||||
highp float occlusion = ChebyshevUpperBound(occlDist, ourDist);
|
||||
|
||||
if (occlusion >= 1.0)
|
||||
{
|
||||
discard;
|
||||
}
|
||||
|
||||
COLOR = vec4(0.0, 0.0, 0.0, 1.0 - occlusion);
|
||||
}
|
||||
|
||||
BIN
Resources/Textures/debugRotation.rsi/direction1.png
Normal file
BIN
Resources/Textures/debugRotation.rsi/direction1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 359 B |
BIN
Resources/Textures/debugRotation.rsi/direction1.xcf
Normal file
BIN
Resources/Textures/debugRotation.rsi/direction1.xcf
Normal file
Binary file not shown.
BIN
Resources/Textures/debugRotation.rsi/direction4.png
Normal file
BIN
Resources/Textures/debugRotation.rsi/direction4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Resources/Textures/debugRotation.rsi/direction4.xcf
Normal file
BIN
Resources/Textures/debugRotation.rsi/direction4.xcf
Normal file
Binary file not shown.
17
Resources/Textures/debugRotation.rsi/meta.json
Normal file
17
Resources/Textures/debugRotation.rsi/meta.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"version": 1,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [{
|
||||
"name": "direction1",
|
||||
"directions": 1,
|
||||
"delays": [[1.0]]
|
||||
}, {
|
||||
"name": "direction4",
|
||||
"directions": 4,
|
||||
"delays": [[1.0], [1.0], [1.0], [1.0]]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/debugRotation.rsi/uvmap.xcf
Normal file
BIN
Resources/Textures/debugRotation.rsi/uvmap.xcf
Normal file
Binary file not shown.
10
Robust.Analyzers/Diagnostics.cs
Normal file
10
Robust.Analyzers/Diagnostics.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Robust.Generators
|
||||
{
|
||||
public static class Diagnostics
|
||||
{
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
}
|
||||
}
|
||||
42
Robust.Analyzers/MeansImplicitAssigmentSuppressor.cs
Normal file
42
Robust.Analyzers/MeansImplicitAssigmentSuppressor.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Generators;
|
||||
|
||||
namespace Robust.Analyzers
|
||||
{
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class MeansImplicitAssigmentSuppressor : DiagnosticSuppressor
|
||||
{
|
||||
const string MeansImplicitAssignmentAttribute = "Robust.Shared.MeansImplicitAssignmentAttribute";
|
||||
|
||||
public override void ReportSuppressions(SuppressionAnalysisContext context)
|
||||
{
|
||||
var implAttr = context.Compilation.GetTypeByMetadataName(MeansImplicitAssignmentAttribute);
|
||||
foreach (var reportedDiagnostic in context.ReportedDiagnostics)
|
||||
{
|
||||
if(reportedDiagnostic.Id != Diagnostics.MeansImplicitAssignment.SuppressedDiagnosticId) continue;
|
||||
|
||||
var node = reportedDiagnostic.Location.SourceTree?.GetRoot(context.CancellationToken).FindNode(reportedDiagnostic.Location.SourceSpan);
|
||||
if (node == null) continue;
|
||||
|
||||
var symbol = context.GetSemanticModel(reportedDiagnostic.Location.SourceTree).GetDeclaredSymbol(node);
|
||||
|
||||
if (symbol == null || !symbol.GetAttributes().Any(a =>
|
||||
a.AttributeClass?.GetAttributes().Any(attr =>
|
||||
SymbolEqualityComparer.Default.Equals(attr.AttributeClass, implAttr)) == true))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
context.ReportSuppression(Suppression.Create(
|
||||
Diagnostics.MeansImplicitAssignment,
|
||||
reportedDiagnostic));
|
||||
}
|
||||
}
|
||||
|
||||
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions => ImmutableArray.Create(Diagnostics.MeansImplicitAssignment);
|
||||
}
|
||||
}
|
||||
37
Robust.Client.Injectors/MathParsing.cs
Normal file
37
Robust.Client.Injectors/MathParsing.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Linq;
|
||||
using Pidgin;
|
||||
using static Pidgin.Parser;
|
||||
|
||||
|
||||
namespace Robust.Build.Tasks
|
||||
{
|
||||
public static class MathParsing
|
||||
{
|
||||
public static Parser<char, float> Single { get; } = Real.Select(c => (float) c);
|
||||
|
||||
public static Parser<char, float> Single1 { get; }
|
||||
= Single.Between(SkipWhitespaces);
|
||||
|
||||
public static Parser<char, (float, float)> Single2 { get; }
|
||||
= Single.Before(SkipWhitespaces).Repeat(2).Select(e =>
|
||||
{
|
||||
var arr = e.ToArray();
|
||||
return (arr[0], arr[1]);
|
||||
});
|
||||
|
||||
public static Parser<char, (float, float, float, float)> Single4 { get; }
|
||||
= Single.Before(SkipWhitespaces).Repeat(4).Select(e =>
|
||||
{
|
||||
var arr = e.ToArray();
|
||||
return (arr[0], arr[1], arr[2], arr[3]);
|
||||
});
|
||||
|
||||
public static Parser<char, float[]> Thickness { get; }
|
||||
= SkipWhitespaces.Then(
|
||||
OneOf(
|
||||
Try(Single4.Select(c => new[] {c.Item1, c.Item2, c.Item3, c.Item4})),
|
||||
Try(Single2.Select(c => new[] {c.Item1, c.Item2})),
|
||||
Try(Single1.Select(c => new[] {c}))
|
||||
));
|
||||
}
|
||||
}
|
||||
32
Robust.Client.Injectors/RXamlColorAstNode.cs
Normal file
32
Robust.Client.Injectors/RXamlColorAstNode.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Reflection.Emit;
|
||||
using XamlX.Ast;
|
||||
using XamlX.Emit;
|
||||
using XamlX.IL;
|
||||
using XamlX.TypeSystem;
|
||||
|
||||
namespace Robust.Build.Tasks
|
||||
{
|
||||
internal class RXamlColorAstNode
|
||||
: XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
|
||||
{
|
||||
private readonly IXamlMethod _method;
|
||||
private readonly string _color;
|
||||
|
||||
public RXamlColorAstNode(IXamlLineInfo lineInfo, RXamlWellKnownTypes types, string color) : base(lineInfo)
|
||||
{
|
||||
_color = color;
|
||||
Type = new XamlAstClrTypeReference(lineInfo, types.Color, false);
|
||||
_method = types.ColorFromXaml;
|
||||
}
|
||||
|
||||
public IXamlAstTypeReference Type { get; }
|
||||
|
||||
public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
|
||||
{
|
||||
codeGen.Ldstr(_color);
|
||||
codeGen.EmitCall(_method);
|
||||
|
||||
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
|
||||
}
|
||||
}
|
||||
}
|
||||
93
Robust.Client.Injectors/RXamlVecLikeConstAstNode.cs
Normal file
93
Robust.Client.Injectors/RXamlVecLikeConstAstNode.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using XamlX.Ast;
|
||||
using XamlX.Emit;
|
||||
using XamlX.IL;
|
||||
using XamlX.TypeSystem;
|
||||
|
||||
namespace Robust.Build.Tasks
|
||||
{
|
||||
public abstract class RXamlVecLikeConstAstNode<T>
|
||||
: XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
|
||||
where T : unmanaged
|
||||
{
|
||||
private readonly IXamlConstructor _constructor;
|
||||
protected readonly T[] Values;
|
||||
|
||||
public RXamlVecLikeConstAstNode(
|
||||
IXamlLineInfo lineInfo,
|
||||
IXamlType type, IXamlConstructor constructor,
|
||||
IXamlType componentType, T[] values)
|
||||
: base(lineInfo)
|
||||
{
|
||||
_constructor = constructor;
|
||||
Values = values;
|
||||
|
||||
var @params = constructor.Parameters;
|
||||
|
||||
if (@params.Count != values.Length)
|
||||
throw new ArgumentException("Invalid amount of parameters");
|
||||
|
||||
if (@params.Any(c => c != componentType))
|
||||
throw new ArgumentException("Invalid constructor: not all parameters match component type");
|
||||
|
||||
Type = new XamlAstClrTypeReference(lineInfo, type, false);
|
||||
}
|
||||
|
||||
public IXamlAstTypeReference Type { get; }
|
||||
|
||||
public virtual XamlILNodeEmitResult Emit(
|
||||
XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context,
|
||||
IXamlILEmitter codeGen)
|
||||
{
|
||||
codeGen.Newobj(_constructor);
|
||||
|
||||
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RXamlSingleVecLikeConstAstNode : RXamlVecLikeConstAstNode<float>
|
||||
{
|
||||
public RXamlSingleVecLikeConstAstNode(
|
||||
IXamlLineInfo lineInfo,
|
||||
IXamlType type, IXamlConstructor constructor,
|
||||
IXamlType componentType, float[] values)
|
||||
: base(lineInfo, type, constructor, componentType, values)
|
||||
{
|
||||
}
|
||||
|
||||
public override XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context,
|
||||
IXamlILEmitter codeGen)
|
||||
{
|
||||
foreach (var value in Values)
|
||||
{
|
||||
codeGen.Emit(OpCodes.Ldc_R4, value);
|
||||
}
|
||||
|
||||
return base.Emit(context, codeGen);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RXamlInt32VecLikeConstAstNode : RXamlVecLikeConstAstNode<int>
|
||||
{
|
||||
public RXamlInt32VecLikeConstAstNode(
|
||||
IXamlLineInfo lineInfo,
|
||||
IXamlType type, IXamlConstructor constructor,
|
||||
IXamlType componentType, int[] values)
|
||||
: base(lineInfo, type, constructor, componentType, values)
|
||||
{
|
||||
}
|
||||
|
||||
public override XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context,
|
||||
IXamlILEmitter codeGen)
|
||||
{
|
||||
foreach (var value in Values)
|
||||
{
|
||||
codeGen.Emit(OpCodes.Ldc_I4, value);
|
||||
}
|
||||
|
||||
return base.Emit(context, codeGen);
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Robust.Client.Injectors/RXamlWellKnownTypes.cs
Normal file
58
Robust.Client.Injectors/RXamlWellKnownTypes.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Linq;
|
||||
using XamlX.Transform;
|
||||
using XamlX.TypeSystem;
|
||||
|
||||
namespace Robust.Build.Tasks
|
||||
{
|
||||
class RXamlWellKnownTypes
|
||||
{
|
||||
public XamlTypeWellKnownTypes XamlIlTypes { get; }
|
||||
public IXamlType Single { get; }
|
||||
public IXamlType Int32 { get; }
|
||||
public IXamlType Vector2 { get; }
|
||||
public IXamlConstructor Vector2ConstructorFull { get; }
|
||||
public IXamlType Vector2i { get; }
|
||||
public IXamlConstructor Vector2iConstructorFull { get; }
|
||||
public IXamlType Thickness { get; }
|
||||
public IXamlConstructor ThicknessConstructorFull { get; }
|
||||
public IXamlType Color { get; }
|
||||
public IXamlMethod ColorFromXaml { get; }
|
||||
|
||||
public RXamlWellKnownTypes(TransformerConfiguration cfg)
|
||||
{
|
||||
var ts = cfg.TypeSystem;
|
||||
XamlIlTypes = cfg.WellKnownTypes;
|
||||
Single = ts.GetType("System.Single");
|
||||
Int32 = ts.GetType("System.Int32");
|
||||
|
||||
(Vector2, Vector2ConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Vector2", Single, 2);
|
||||
(Vector2i, Vector2iConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Vector2i", Int32, 2);
|
||||
(Thickness, ThicknessConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Thickness", Single, 4);
|
||||
|
||||
(IXamlType, IXamlConstructor) GetNumericTypeInfo(string name, IXamlType componentType, int componentCount)
|
||||
{
|
||||
var type = cfg.TypeSystem.GetType(name);
|
||||
var ctor = type.GetConstructor(Enumerable.Repeat(componentType, componentCount).ToList());
|
||||
|
||||
return (type, ctor);
|
||||
}
|
||||
|
||||
Color = cfg.TypeSystem.GetType("Robust.Shared.Maths.Color");
|
||||
ColorFromXaml = Color.GetMethod(new FindMethodMethodSignature("FromXaml", Color, XamlIlTypes.String)
|
||||
{
|
||||
IsStatic = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static class RXamlWellKnownTypesExtensions
|
||||
{
|
||||
public static RXamlWellKnownTypes GetRobustTypes(this AstTransformationContext ctx)
|
||||
{
|
||||
if (ctx.TryGetItem<RXamlWellKnownTypes>(out var rv))
|
||||
return rv;
|
||||
ctx.SetItem(rv = new RXamlWellKnownTypes(ctx.Configuration));
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build.Framework" Version="16.8.0" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
|
||||
<PackageReference Include="Pidgin" Version="2.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -6,6 +6,7 @@ using Microsoft.Build.Framework;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Cecil.Rocks;
|
||||
using Pidgin;
|
||||
using XamlX;
|
||||
using XamlX.Ast;
|
||||
using XamlX.Emit;
|
||||
@@ -70,7 +71,7 @@ namespace Robust.Build.Tasks
|
||||
{
|
||||
XmlnsAttributes =
|
||||
{
|
||||
typeSystem.GetType("Robust.Client.UserInterface.XAML.XmlnsDefinitionAttribute"),
|
||||
typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute"),
|
||||
|
||||
},
|
||||
ContentAttributes =
|
||||
@@ -98,7 +99,7 @@ namespace Robust.Build.Tasks
|
||||
typeSystem,
|
||||
typeSystem.TargetAssembly,
|
||||
xamlLanguage,
|
||||
XamlXmlnsMappings.Resolve(typeSystem, xamlLanguage));
|
||||
XamlXmlnsMappings.Resolve(typeSystem, xamlLanguage), CustomValueConverter);
|
||||
|
||||
var contextDef = new TypeDefinition("CompiledRobustXaml", "XamlIlContext",
|
||||
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
|
||||
@@ -297,6 +298,112 @@ namespace Robust.Build.Tasks
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool CustomValueConverter(
|
||||
AstTransformationContext context,
|
||||
IXamlAstValueNode node,
|
||||
IXamlType type,
|
||||
out IXamlAstValueNode result)
|
||||
{
|
||||
if (!(node is XamlAstTextNode textNode))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var text = textNode.Text;
|
||||
var types = context.GetRobustTypes();
|
||||
|
||||
if (type.Equals(types.Vector2))
|
||||
{
|
||||
var foo = MathParsing.Single2.Parse(text);
|
||||
|
||||
if (!foo.Success)
|
||||
throw new XamlLoadException($"Unable to parse \"{text}\" as a Vector2", node);
|
||||
|
||||
var (x, y) = foo.Value;
|
||||
|
||||
result = new RXamlSingleVecLikeConstAstNode(
|
||||
node,
|
||||
types.Vector2, types.Vector2ConstructorFull,
|
||||
types.Single, new[] {x, y});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.Equals(types.Thickness))
|
||||
{
|
||||
var foo = MathParsing.Thickness.Parse(text);
|
||||
|
||||
if (!foo.Success)
|
||||
throw new XamlLoadException($"Unable to parse \"{text}\" as a Thickness", node);
|
||||
|
||||
var val = foo.Value;
|
||||
float[] full;
|
||||
if (val.Length == 1)
|
||||
{
|
||||
var u = val[0];
|
||||
full = new[] {u, u, u, u};
|
||||
}
|
||||
else if (val.Length == 2)
|
||||
{
|
||||
var h = val[0];
|
||||
var v = val[1];
|
||||
full = new[] {h, v, h, v};
|
||||
}
|
||||
else // 4
|
||||
{
|
||||
full = val;
|
||||
}
|
||||
|
||||
result = new RXamlSingleVecLikeConstAstNode(
|
||||
node,
|
||||
types.Thickness, types.ThicknessConstructorFull,
|
||||
types.Single, full);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.Equals(types.Thickness))
|
||||
{
|
||||
var foo = MathParsing.Thickness.Parse(text);
|
||||
|
||||
if (!foo.Success)
|
||||
throw new XamlLoadException($"Unable to parse \"{text}\" as a Thickness", node);
|
||||
|
||||
var val = foo.Value;
|
||||
float[] full;
|
||||
if (val.Length == 1)
|
||||
{
|
||||
var u = val[0];
|
||||
full = new[] {u, u, u, u};
|
||||
}
|
||||
else if (val.Length == 2)
|
||||
{
|
||||
var h = val[0];
|
||||
var v = val[1];
|
||||
full = new[] {h, v, h, v};
|
||||
}
|
||||
else // 4
|
||||
{
|
||||
full = val;
|
||||
}
|
||||
|
||||
result = new RXamlSingleVecLikeConstAstNode(
|
||||
node,
|
||||
types.Thickness, types.ThicknessConstructorFull,
|
||||
types.Single, full);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.Equals(types.Color))
|
||||
{
|
||||
// TODO: Interpret these colors at XAML compile time instead of at runtime.
|
||||
result = new RXamlColorAstNode(node, types, text);
|
||||
return true;
|
||||
}
|
||||
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public const string ContextNameScopeFieldName = "RobustNameScope";
|
||||
|
||||
private static void EmitNameScopeField(XamlLanguageTypeMappings xamlLanguage, CecilTypeSystem typeSystem, IXamlTypeBuilder<IXamlILEmitter> typeBuilder, IXamlILEmitter constructor)
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace Robust.Client.NameGenerator
|
||||
{
|
||||
private const string AttributeName = "Robust.Client.AutoGenerated.GenerateTypedNameReferencesAttribute";
|
||||
private const string AttributeFile = "GenerateTypedNameReferencesAttribute";
|
||||
|
||||
private const string AttributeCode = @"// <auto-generated />
|
||||
using System;
|
||||
namespace Robust.Client.AutoGenerated
|
||||
@@ -54,8 +55,8 @@ namespace Robust.Client.AutoGenerated
|
||||
{
|
||||
var clrtype = objectNode.Type.GetClrType();
|
||||
var isControl = IsControl(clrtype);
|
||||
//clrtype.Interfaces.Any(i =>
|
||||
//i.IsInterface && i.FullName == "Robust.Client.UserInterface.IControl");
|
||||
//clrtype.Interfaces.Any(i =>
|
||||
//i.IsInterface && i.FullName == "Robust.Client.UserInterface.IControl");
|
||||
|
||||
if (!isControl)
|
||||
return node;
|
||||
@@ -80,9 +81,13 @@ namespace Robust.Client.AutoGenerated
|
||||
return node;
|
||||
}
|
||||
|
||||
public void Push(IXamlAstNode node) { }
|
||||
public void Push(IXamlAstNode node)
|
||||
{
|
||||
}
|
||||
|
||||
public void Pop() { }
|
||||
public void Pop()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static string GenerateSourceCode(
|
||||
@@ -97,16 +102,20 @@ namespace Robust.Client.AutoGenerated
|
||||
var compiler =
|
||||
new XamlILCompiler(
|
||||
new TransformerConfiguration(typeSystem, typeSystem.Assemblies[0],
|
||||
new XamlLanguageTypeMappings(typeSystem)),
|
||||
new XamlLanguageTypeMappings(typeSystem)
|
||||
{
|
||||
XmlnsAttributes = {typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute")}
|
||||
}),
|
||||
new XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult>(), false);
|
||||
compiler.Transformers.Add(new TypeReferenceResolver());
|
||||
compiler.Transform(parsed);
|
||||
var initialRoot = (XamlAstObjectNode) parsed.Root;
|
||||
var names = NameVisitor.GetNames(initialRoot);
|
||||
var fieldAccess = classSymbol.IsSealed ? "private" : "protected";
|
||||
//var names = NameVisitor.GetNames((XamlAstObjectNode)XDocumentXamlParser.Parse(xamlFile).Root);
|
||||
var namedControls = names.Select(info => " " +
|
||||
$"protected global::{info.type} {info.name} => " +
|
||||
$"this.FindControl<global::{info.type}>(\"{info.name}\");");
|
||||
$"{fieldAccess} global::{info.type} {info.name} => " +
|
||||
$"this.FindControl<global::{info.type}>(\"{info.name}\");");
|
||||
return $@"// <auto-generated />
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
@@ -124,7 +133,7 @@ namespace {nameSpace}
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
var comp = (CSharpCompilation) context.Compilation;
|
||||
if(comp.GetTypeByMetadataName(AttributeName) == null)
|
||||
if (comp.GetTypeByMetadataName(AttributeName) == null)
|
||||
context.AddSource(AttributeFile, SourceText.From(AttributeCode, Encoding.UTF8));
|
||||
if (!(context.SyntaxReceiver is NameReferenceSyntaxReceiver receiver))
|
||||
{
|
||||
@@ -132,7 +141,7 @@ namespace {nameSpace}
|
||||
}
|
||||
|
||||
var symbols = UnpackAnnotatedTypes(context, comp, receiver);
|
||||
if(symbols == null)
|
||||
if (symbols == null)
|
||||
return;
|
||||
|
||||
foreach (var typeSymbol in symbols)
|
||||
@@ -168,7 +177,8 @@ namespace {nameSpace}
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true),
|
||||
Location.Create(xamlFileName, new TextSpan(0,0), new LinePositionSpan(new LinePosition(0,0),new LinePosition(0,0)))));
|
||||
Location.Create(xamlFileName, new TextSpan(0, 0),
|
||||
new LinePositionSpan(new LinePosition(0, 0), new LinePosition(0, 0)))));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -194,7 +204,8 @@ namespace {nameSpace}
|
||||
}
|
||||
}
|
||||
|
||||
private IReadOnlyList<INamedTypeSymbol> UnpackAnnotatedTypes(in GeneratorExecutionContext context, CSharpCompilation comp, NameReferenceSyntaxReceiver receiver)
|
||||
private IReadOnlyList<INamedTypeSymbol> UnpackAnnotatedTypes(in GeneratorExecutionContext context,
|
||||
CSharpCompilation comp, NameReferenceSyntaxReceiver receiver)
|
||||
{
|
||||
var options = (CSharpParseOptions) comp.SyntaxTrees[0].Options;
|
||||
var compilation =
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Robust.Client.Animations
|
||||
/// <seealso cref="AnimationPlayerComponent"/>
|
||||
public sealed class Animation
|
||||
{
|
||||
public readonly List<AnimationTrack> AnimationTracks = new();
|
||||
public List<AnimationTrack> AnimationTracks { get; private set; } = new();
|
||||
|
||||
public TimeSpan Length { get; set; }
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Robust.Client.Animations
|
||||
{
|
||||
@@ -14,7 +15,7 @@ namespace Robust.Client.Animations
|
||||
/// <summary>
|
||||
/// A list of key frames for when to fire flicks.
|
||||
/// </summary>
|
||||
public readonly List<KeyFrame> KeyFrames = new();
|
||||
public List<KeyFrame> KeyFrames { get; private set; } = new();
|
||||
|
||||
public override (int KeyFrameIndex, float FramePlayingTime) InitPlayback()
|
||||
{
|
||||
@@ -36,8 +37,7 @@ namespace Robust.Client.Animations
|
||||
|
||||
var keyFrame = KeyFrames[keyFrameIndex];
|
||||
|
||||
EntitySystem.Get<AudioSystem>()
|
||||
.Play(keyFrame.Resource, entity, keyFrame.AudioParamsFunc.Invoke());
|
||||
SoundSystem.Play(Filter.Local(), keyFrame.Resource, entity, keyFrame.AudioParamsFunc.Invoke());
|
||||
}
|
||||
|
||||
return (keyFrameIndex, playingTime);
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Robust.Client.Animations
|
||||
/// </summary>
|
||||
public abstract class AnimationTrackProperty : AnimationTrack
|
||||
{
|
||||
public readonly List<KeyFrame> KeyFrames = new();
|
||||
public List<KeyFrame> KeyFrames { get; protected set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// How to interpolate values when between two keyframes.
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Robust.Client.Animations
|
||||
/// <summary>
|
||||
/// A list of key frames for when to fire flicks.
|
||||
/// </summary>
|
||||
public readonly List<KeyFrame> KeyFrames = new();
|
||||
public List<KeyFrame> KeyFrames { get; private set; } = new();
|
||||
|
||||
// TODO: Should this layer key be per keyframe maybe?
|
||||
/// <summary>
|
||||
|
||||
@@ -11,8 +11,8 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Utility;
|
||||
using Logger = Robust.Shared.Log.Logger;
|
||||
|
||||
@@ -63,14 +63,18 @@ namespace Robust.Client.Audio.Midi
|
||||
bool IsAvailable { get; }
|
||||
|
||||
public int OcclusionCollisionMask { get; set; }
|
||||
|
||||
void Shutdown();
|
||||
}
|
||||
|
||||
internal class MidiManager : IDisposable, IMidiManager
|
||||
internal class MidiManager : IMidiManager
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
private SharedBroadPhaseSystem _broadPhaseSystem = default!;
|
||||
|
||||
public bool IsAvailable
|
||||
{
|
||||
get
|
||||
@@ -154,6 +158,7 @@ namespace Robust.Client.Audio.Midi
|
||||
_midiThread = new Thread(ThreadUpdate);
|
||||
_midiThread.Start();
|
||||
|
||||
_broadPhaseSystem = EntitySystem.Get<SharedBroadPhaseSystem>();
|
||||
FluidsynthInitialized = true;
|
||||
}
|
||||
|
||||
@@ -298,7 +303,7 @@ namespace Robust.Client.Audio.Midi
|
||||
var occlusion = 0f;
|
||||
if (sourceRelative.Length > 0)
|
||||
{
|
||||
occlusion = IoCManager.Resolve<IPhysicsManager>().IntersectRayPenetration(
|
||||
occlusion = _broadPhaseSystem.IntersectRayPenetration(
|
||||
pos.MapId,
|
||||
new CollisionRay(
|
||||
pos.Position,
|
||||
@@ -315,6 +320,11 @@ namespace Robust.Client.Audio.Midi
|
||||
continue;
|
||||
}
|
||||
|
||||
if (renderer.TrackingEntity != null)
|
||||
{
|
||||
renderer.Source.SetVelocity(renderer.TrackingEntity.GlobalLinearVelocity());
|
||||
}
|
||||
|
||||
if (float.IsNaN(pos.Position.X) || float.IsNaN(pos.Position.Y))
|
||||
{
|
||||
// just duck out instead of move to NaN
|
||||
@@ -352,7 +362,7 @@ namespace Robust.Client.Audio.Midi
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public void Shutdown()
|
||||
{
|
||||
_alive = false;
|
||||
_midiThread?.Join();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Client.Audio.Midi;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Debugging;
|
||||
@@ -10,6 +10,7 @@ using Robust.Client.Input;
|
||||
using Robust.Client.Map;
|
||||
using Robust.Client.Placement;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Prototypes;
|
||||
using Robust.Client.Reflection;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.State;
|
||||
@@ -17,11 +18,13 @@ using Robust.Client.UserInterface;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Client.ViewVariables;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
|
||||
@@ -33,7 +36,7 @@ namespace Robust.Client
|
||||
{
|
||||
SharedIoC.RegisterIoC();
|
||||
|
||||
IoCManager.Register<IPrototypeManager, PrototypeManager>();
|
||||
IoCManager.Register<IPrototypeManager, ClientPrototypeManager>();
|
||||
IoCManager.Register<IEntityManager, ClientEntityManager>();
|
||||
IoCManager.Register<IComponentFactory, ClientComponentFactory>();
|
||||
IoCManager.Register<ITileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
@@ -51,6 +54,7 @@ namespace Robust.Client
|
||||
IoCManager.Register<IClientGameStateManager, ClientGameStateManager>();
|
||||
IoCManager.Register<IBaseClient, BaseClient>();
|
||||
IoCManager.Register<IPlayerManager, PlayerManager>();
|
||||
IoCManager.Register<ISharedPlayerManager, PlayerManager>();
|
||||
IoCManager.Register<IStateManager, StateManager>();
|
||||
IoCManager.Register<IUserInterfaceManager, UserInterfaceManager>();
|
||||
IoCManager.Register<IUserInterfaceManagerInternal, UserInterfaceManager>();
|
||||
@@ -59,8 +63,7 @@ namespace Robust.Client
|
||||
IoCManager.Register<ILightManager, LightManager>();
|
||||
IoCManager.Register<IDiscordRichPresence, DiscordRichPresence>();
|
||||
IoCManager.Register<IClientConsoleHost, ClientConsoleHost>();
|
||||
IoCManager.Register<IFontManager, FontManager>();
|
||||
IoCManager.Register<IFontManagerInternal, FontManager>();
|
||||
IoCManager.Register<IConsoleHost, ClientConsoleHost>();
|
||||
IoCManager.Register<IMidiManager, MidiManager>();
|
||||
IoCManager.Register<IAuthManager, AuthManager>();
|
||||
switch (mode)
|
||||
@@ -87,8 +90,9 @@ namespace Robust.Client
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
IoCManager.Register<IFontManager, FontManager>();
|
||||
IoCManager.Register<IFontManagerInternal, FontManager>();
|
||||
IoCManager.Register<IEyeManager, EyeManager>();
|
||||
|
||||
IoCManager.Register<IPlacementManager, PlacementManager>();
|
||||
IoCManager.Register<IOverlayManager, OverlayManager>();
|
||||
IoCManager.Register<IOverlayManagerInternal, OverlayManager>();
|
||||
@@ -96,7 +100,6 @@ namespace Robust.Client
|
||||
IoCManager.Register<IViewVariablesManagerInternal, ViewVariablesManager>();
|
||||
IoCManager.Register<IClientConGroupController, ClientConGroupController>();
|
||||
IoCManager.Register<IScriptClient, ScriptClient>();
|
||||
//IoCManager.Register<IXamlCompiler, XamlCompiler>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ using Robust.Client.Input;
|
||||
using Robust.Client.Debugging;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.ResourceManagement.ResourceTypes;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
@@ -481,6 +480,8 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
_writeNode(root, 0, writer);
|
||||
}
|
||||
|
||||
shell.WriteLine("Saved guidump");
|
||||
}
|
||||
|
||||
private static void _writeNode(Control control, int indents, TextWriter writer)
|
||||
@@ -542,7 +543,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var window = new SS14Window { CustomMinimumSize = (500, 400)};
|
||||
var window = new SS14Window { MinSize = (500, 400)};
|
||||
var tabContainer = new TabContainer();
|
||||
window.Contents.AddChild(tabContainer);
|
||||
var scroll = new ScrollContainer();
|
||||
@@ -562,7 +563,7 @@ namespace Robust.Client.Console.Commands
|
||||
optionButton.OnItemSelected += eventArgs => optionButton.SelectId(eventArgs.Id);
|
||||
vBox.AddChild(optionButton);
|
||||
|
||||
var tree = new Tree { SizeFlagsVertical = Control.SizeFlags.FillExpand };
|
||||
var tree = new Tree { VerticalExpand = true };
|
||||
var root = tree.CreateItem();
|
||||
root.Text = "Honk!";
|
||||
var child = tree.CreateItem();
|
||||
@@ -599,7 +600,7 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
grid.AddChild(new Button
|
||||
{
|
||||
CustomMinimumSize = (50, 50),
|
||||
MinSize = (50, 50),
|
||||
Text = $"{x}, {y}"
|
||||
});
|
||||
}
|
||||
@@ -631,6 +632,29 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
});
|
||||
|
||||
tabContainer.AddChild(new HSplitContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.Red},
|
||||
Children =
|
||||
{
|
||||
new Label{ Text = "FOOBARBAZ"},
|
||||
}
|
||||
},
|
||||
new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.Blue},
|
||||
Children =
|
||||
{
|
||||
new Label{ Text = "FOOBARBAZ"},
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
window.OpenCentered();
|
||||
}
|
||||
}
|
||||
|
||||
44
Robust.Client/Console/Commands/MonitorCommands.cs
Normal file
44
Robust.Client/Console/Commands/MonitorCommands.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class LsMonitorCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "lsmonitor";
|
||||
public string Description => "";
|
||||
public string Help => "";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var clyde = IoCManager.Resolve<IClyde>();
|
||||
|
||||
foreach (var monitor in clyde.EnumerateMonitors())
|
||||
{
|
||||
shell.WriteLine(
|
||||
$"[{monitor.Id}] {monitor.Name}: {monitor.Size.X}x{monitor.Size.Y}@{monitor.RefreshRate}Hz");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class SetMonitorCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "setmonitor";
|
||||
public string Description => "";
|
||||
public string Help => "Usage: setmonitor <id>";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var clyde = IoCManager.Resolve<IClyde>();
|
||||
|
||||
var id = int.Parse(args[0]);
|
||||
var monitor = clyde.EnumerateMonitors().Single(m => m.Id == id);
|
||||
clyde.SetWindowMonitor(monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Robust.Client/Console/Commands/PhysicsOverlayCommands.cs
Normal file
41
Robust.Client/Console/Commands/PhysicsOverlayCommands.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Robust.Client.Debugging;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
public sealed class PhysicsOverlayCommands : IConsoleCommand
|
||||
{
|
||||
public string Command => "physics";
|
||||
public string Description => $"{Command} <contactnormals / contactpoints / shapes>";
|
||||
public string Help => $"{Command} <overlay>";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine($"Invalid number of args supplied");
|
||||
return;
|
||||
}
|
||||
|
||||
var system = EntitySystem.Get<DebugPhysicsSystem>();
|
||||
|
||||
switch (args[0])
|
||||
{
|
||||
case "contactnormals":
|
||||
system.Flags ^= PhysicsDebugFlags.ContactNormals;
|
||||
break;
|
||||
case "contactpoints":
|
||||
system.Flags ^= PhysicsDebugFlags.ContactPoints;
|
||||
break;
|
||||
case "shapes":
|
||||
system.Flags ^= PhysicsDebugFlags.Shapes;
|
||||
break;
|
||||
default:
|
||||
shell.WriteLine($"{args[0]} is not a recognised overlay");
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Robust.Client/Console/Commands/ReloadLocalizationsCommand.cs
Normal file
20
Robust.Client/Console/Commands/ReloadLocalizationsCommand.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
internal sealed class ReloadLocalizationsCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "rldloc";
|
||||
public string Description => "Reloads localization (client & server)";
|
||||
public string Help => "Usage: rldloc";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
IoCManager.Resolve<ILocalizationManager>().ReloadLocalizations();
|
||||
|
||||
shell.RemoteExecuteCommand("sudo rldloc");
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Robust.Client/Console/Commands/VelocitiesCommand.cs
Normal file
17
Robust.Client/Console/Commands/VelocitiesCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
public class VelocitiesCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "showvelocities";
|
||||
public string Description => "Displays your angular and linear velocities";
|
||||
public string Help => $"{Command}";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<VelocityDebugSystem>().Enabled ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
#if CLIENT_SCRIPTING
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.CodeAnalysis;
|
||||
@@ -36,6 +39,8 @@ namespace Robust.Client.Console
|
||||
private readonly ScriptGlobals _globals;
|
||||
private ScriptState? _state;
|
||||
|
||||
private (string[] imports, string code)? _autoImportRepeatBuffer;
|
||||
|
||||
public ScriptConsoleClient()
|
||||
{
|
||||
Title = Loc.GetString("Robust C# Interactive (CLIENT)");
|
||||
@@ -54,38 +59,56 @@ namespace Robust.Client.Console
|
||||
var code = InputBar.Text;
|
||||
InputBar.Clear();
|
||||
|
||||
// Remove > or . at the end of the output panel.
|
||||
OutputPanel.RemoveEntry(^1);
|
||||
|
||||
_inputBuffer.AppendLine(code);
|
||||
_linesEntered += 1;
|
||||
|
||||
var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(_inputBuffer.ToString()), ScriptInstanceShared.ParseOptions);
|
||||
|
||||
if (!SyntaxFactory.IsCompleteSubmission(tree))
|
||||
if (_autoImportRepeatBuffer.HasValue && code == "y")
|
||||
{
|
||||
if (_linesEntered == 1)
|
||||
var (imports, repeatCode) = _autoImportRepeatBuffer.Value;
|
||||
var sb = new StringBuilder();
|
||||
foreach (var import in imports)
|
||||
{
|
||||
OutputPanel.AddText($"> {code}");
|
||||
sb.AppendFormat("using {0};\n", import);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputPanel.AddText($". {code}");
|
||||
}
|
||||
OutputPanel.AddText(".");
|
||||
return;
|
||||
|
||||
sb.Append(repeatCode);
|
||||
|
||||
code = sb.ToString();
|
||||
}
|
||||
|
||||
code = _inputBuffer.ToString().Trim();
|
||||
|
||||
// Remove echo of partial submission from the output panel.
|
||||
for (var i = 1; i < _linesEntered; i++)
|
||||
else
|
||||
{
|
||||
// Remove > or . at the end of the output panel.
|
||||
OutputPanel.RemoveEntry(^1);
|
||||
}
|
||||
|
||||
_inputBuffer.Clear();
|
||||
_linesEntered = 0;
|
||||
_inputBuffer.AppendLine(code);
|
||||
_linesEntered += 1;
|
||||
|
||||
var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(_inputBuffer.ToString()),
|
||||
ScriptInstanceShared.ParseOptions);
|
||||
|
||||
if (!SyntaxFactory.IsCompleteSubmission(tree))
|
||||
{
|
||||
if (_linesEntered == 1)
|
||||
{
|
||||
OutputPanel.AddText($"> {code}");
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputPanel.AddText($". {code}");
|
||||
}
|
||||
|
||||
OutputPanel.AddText(".");
|
||||
return;
|
||||
}
|
||||
|
||||
code = _inputBuffer.ToString().Trim();
|
||||
|
||||
// Remove echo of partial submission from the output panel.
|
||||
for (var i = 1; i < _linesEntered; i++)
|
||||
{
|
||||
OutputPanel.RemoveEntry(^1);
|
||||
}
|
||||
|
||||
_inputBuffer.Clear();
|
||||
_linesEntered = 0;
|
||||
}
|
||||
|
||||
Script newScript;
|
||||
|
||||
@@ -135,6 +158,8 @@ namespace Robust.Client.Console
|
||||
|
||||
OutputPanel.AddMessage(msg);
|
||||
OutputPanel.AddText(">");
|
||||
|
||||
PromptAutoImports(e.Diagnostics, code);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -148,13 +173,23 @@ namespace Robust.Client.Console
|
||||
else if (ScriptInstanceShared.HasReturnValue(newScript))
|
||||
{
|
||||
var msg = new FormattedMessage();
|
||||
msg.AddText(CSharpObjectFormatter.Instance.FormatObject(_state.ReturnValue));
|
||||
msg.AddText(ScriptInstanceShared.SafeFormat(_state.ReturnValue));
|
||||
OutputPanel.AddMessage(msg);
|
||||
}
|
||||
|
||||
OutputPanel.AddText(">");
|
||||
}
|
||||
|
||||
private void PromptAutoImports(IEnumerable<Diagnostic> diags, string code)
|
||||
{
|
||||
if (!ScriptInstanceShared.CalcAutoImports(_reflectionManager, diags, out var found))
|
||||
return;
|
||||
|
||||
OutputPanel.AddText($"Auto-import {string.Join(", ", found)} (enter 'y')?");
|
||||
|
||||
_autoImportRepeatBuffer = (found.ToArray(), code);
|
||||
}
|
||||
|
||||
private sealed class ScriptGlobalsImpl : ScriptGlobals
|
||||
{
|
||||
private readonly ScriptConsoleClient _owner;
|
||||
@@ -180,7 +215,7 @@ namespace Robust.Client.Console
|
||||
|
||||
public override void show(object obj)
|
||||
{
|
||||
write(CSharpObjectFormatter.Instance.FormatObject(obj));
|
||||
write(ScriptInstanceShared.SafeFormat(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ namespace Robust.Client.Console
|
||||
{
|
||||
private readonly IReflectionManager _reflectionManager;
|
||||
|
||||
protected override Vector2? CustomSize => (300, 300);
|
||||
|
||||
private readonly VBoxContainer _watchesVBox;
|
||||
private readonly LineEdit _addWatchEdit;
|
||||
private readonly Button _addWatchButton;
|
||||
@@ -37,12 +35,12 @@ namespace Robust.Client.Console
|
||||
|
||||
var mainVBox = new VBoxContainer
|
||||
{
|
||||
CustomMinimumSize = (500, 300),
|
||||
MinSize = (500, 300),
|
||||
Children =
|
||||
{
|
||||
(_watchesVBox = new VBoxContainer
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.FillExpand
|
||||
VerticalExpand = true
|
||||
}),
|
||||
new HBoxContainer
|
||||
{
|
||||
@@ -50,7 +48,7 @@ namespace Robust.Client.Console
|
||||
{
|
||||
(_addWatchEdit = new HistoryLineEdit
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
HorizontalExpand = true,
|
||||
PlaceHolder = Loc.GetString("Add watch (C# interactive)")
|
||||
}),
|
||||
(_addWatchButton = new Button
|
||||
@@ -66,6 +64,8 @@ namespace Robust.Client.Console
|
||||
_addWatchEdit.OnTextEntered += _ => AddWatch();
|
||||
|
||||
Contents.AddChild(mainVBox);
|
||||
|
||||
SetSize = (300, 300);
|
||||
}
|
||||
|
||||
private void AddWatch()
|
||||
@@ -113,7 +113,7 @@ namespace Robust.Client.Console
|
||||
{
|
||||
(_outputLabel = new Label
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
HorizontalExpand = true,
|
||||
ClipText = true
|
||||
}),
|
||||
(delButton = new Button
|
||||
@@ -176,7 +176,7 @@ namespace Robust.Client.Console
|
||||
{
|
||||
Text = message,
|
||||
ClipText = true,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand
|
||||
HorizontalExpand = true
|
||||
},
|
||||
(delButton = new Button {Text = Loc.GetString("Remove")})
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Client.Debugging
|
||||
@@ -38,14 +42,14 @@ namespace Robust.Client.Debugging
|
||||
|
||||
_debugColliders = value;
|
||||
|
||||
if (value)
|
||||
if (value && !_overlayManager.HasOverlay<PhysicsOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new PhysicsOverlay(_componentManager, _eyeManager,
|
||||
_prototypeManager, _inputManager, _physicsManager));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay(nameof(PhysicsOverlay));
|
||||
_overlayManager.RemoveOverlay<PhysicsOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,13 +67,13 @@ namespace Robust.Client.Debugging
|
||||
|
||||
_debugPositions = value;
|
||||
|
||||
if (value)
|
||||
if (value && !_overlayManager.HasOverlay<EntityPositionOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new EntityPositionOverlay(_entityManager, _eyeManager));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay(nameof(EntityPositionOverlay));
|
||||
_overlayManager.RemoveOverlay<EntityPositionOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,8 +92,8 @@ namespace Robust.Client.Debugging
|
||||
private Vector2 _hoverStartScreen = Vector2.Zero;
|
||||
private List<IPhysBody> _hoverBodies = new();
|
||||
|
||||
|
||||
public PhysicsOverlay(IComponentManager compMan, IEyeManager eyeMan, IPrototypeManager protoMan, IInputManager inputManager, IPhysicsManager physicsManager)
|
||||
: base(nameof(PhysicsOverlay))
|
||||
{
|
||||
_componentManager = compMan;
|
||||
_eyeManager = eyeMan;
|
||||
@@ -136,7 +140,7 @@ namespace Robust.Client.Debugging
|
||||
row++;
|
||||
DrawString(screenHandle, _font, drawPos + new Vector2(0, row * lineHeight), $"Mask: {Convert.ToString(body.CollisionMask, 2)}");
|
||||
row++;
|
||||
DrawString(screenHandle, _font, drawPos + new Vector2(0, row * lineHeight), $"Enabled: {body.CanCollide}, Hard: {body.Hard}, Anchored: {((IPhysicsComponent)body).Anchored}");
|
||||
DrawString(screenHandle, _font, drawPos + new Vector2(0, row * lineHeight), $"Enabled: {body.CanCollide}, Hard: {body.Hard}, Anchored: {(body).BodyType == BodyType.Static}");
|
||||
row++;
|
||||
}
|
||||
|
||||
@@ -157,20 +161,22 @@ namespace Robust.Client.Debugging
|
||||
if (viewport.IsEmpty()) return;
|
||||
|
||||
var mapId = _eyeManager.CurrentMap;
|
||||
var sleepThreshold = IoCManager.Resolve<IConfigurationManager>().GetCVar(CVars.TimeToSleep);
|
||||
var colorEdge = Color.Red.WithAlpha(0.33f);
|
||||
|
||||
foreach (var physBody in _physicsManager.GetCollidingEntities(mapId, viewport))
|
||||
foreach (var physBody in EntitySystem.Get<SharedBroadPhaseSystem>().GetCollidingEntities(mapId, viewport))
|
||||
{
|
||||
// all entities have a TransformComponent
|
||||
var transform = physBody.Entity.Transform;
|
||||
|
||||
var worldBox = physBody.WorldAABB;
|
||||
var worldBox = physBody.GetWorldAABB();
|
||||
if (worldBox.IsEmpty()) continue;
|
||||
|
||||
var colorEdge = Color.Red.WithAlpha(0.33f);
|
||||
|
||||
foreach (var shape in physBody.PhysicsShapes)
|
||||
foreach (var fixture in physBody.Fixtures)
|
||||
{
|
||||
shape.DebugDraw(drawing, transform.WorldMatrix, in viewport, physBody.SleepAccumulator / (float) physBody.SleepThreshold);
|
||||
var shape = fixture.Shape;
|
||||
var sleepPercent = physBody.Awake ? physBody.SleepTime / sleepThreshold : 1.0f;
|
||||
shape.DebugDraw(drawing, transform.WorldMatrix, in viewport, sleepPercent);
|
||||
}
|
||||
|
||||
if (worldBox.Contains(mouseWorldPos))
|
||||
@@ -187,9 +193,9 @@ namespace Robust.Client.Debugging
|
||||
{
|
||||
var baseLine = new Vector2(pos.X, font.GetAscent(1) + pos.Y);
|
||||
|
||||
foreach (var chr in str)
|
||||
foreach (var rune in str.EnumerateRunes())
|
||||
{
|
||||
var advance = font.DrawChar(handle, chr, baseLine, 1, Color.White);
|
||||
var advance = font.DrawChar(handle, rune, baseLine, 1, Color.White);
|
||||
baseLine += new Vector2(advance, 0);
|
||||
}
|
||||
}
|
||||
@@ -233,6 +239,16 @@ namespace Robust.Client.Debugging
|
||||
_handle.DrawCircle(origin, radius, color);
|
||||
}
|
||||
|
||||
public override void DrawPolygonShape(Vector2[] vertices, in Color color)
|
||||
{
|
||||
_handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, vertices, color);
|
||||
}
|
||||
|
||||
public override void DrawLine(Vector2 start, Vector2 end, in Color color)
|
||||
{
|
||||
_handle.DrawLine(start, end, color);
|
||||
}
|
||||
|
||||
public override void SetTransform(in Matrix3 transform)
|
||||
{
|
||||
_handle.SetTransform(transform);
|
||||
@@ -249,7 +265,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public EntityPositionOverlay(IEntityManager entityManager, IEyeManager eyeManager) : base(nameof(EntityPositionOverlay))
|
||||
public EntityPositionOverlay(IEntityManager entityManager, IEyeManager eyeManager)
|
||||
{
|
||||
_entityManager = entityManager;
|
||||
_eyeManager = eyeManager;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Robust.Client.Debugging
|
||||
@@ -38,13 +39,13 @@ namespace Robust.Client.Debugging
|
||||
|
||||
_debugDrawRays = value;
|
||||
|
||||
if (value)
|
||||
if (value && !_overlayManager.HasOverlay<DebugDrawRayOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new DebugDrawRayOverlay(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay(nameof(DebugDrawRayOverlay));
|
||||
_overlayManager.RemoveOverlay<DebugDrawRayOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,7 +82,7 @@ namespace Robust.Client.Debugging
|
||||
private readonly DebugDrawingManager _owner;
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public DebugDrawRayOverlay(DebugDrawingManager owner) : base(nameof(DebugDrawRayOverlay))
|
||||
public DebugDrawRayOverlay(DebugDrawingManager owner)
|
||||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
164
Robust.Client/Debugging/DebugPhysicsSystem.cs
Normal file
164
Robust.Client/Debugging/DebugPhysicsSystem.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Farseer Physics Engine:
|
||||
* Copyright (c) 2012 Ian Qvist
|
||||
*
|
||||
* Original source Box2D:
|
||||
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/* Heavily inspired by Farseer */
|
||||
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Dynamics.Contacts;
|
||||
|
||||
namespace Robust.Client.Debugging
|
||||
{
|
||||
internal sealed class DebugPhysicsSystem : SharedDebugPhysicsSystem
|
||||
{
|
||||
/*
|
||||
* Used for debugging shapes, controllers, joints, contacts
|
||||
*/
|
||||
|
||||
private const int MaxContactPoints = 2048;
|
||||
internal int PointCount;
|
||||
|
||||
internal ContactPoint[] _points = new ContactPoint[MaxContactPoints];
|
||||
|
||||
public PhysicsDebugFlags Flags
|
||||
{
|
||||
get => _flags;
|
||||
set
|
||||
{
|
||||
if (value == _flags) return;
|
||||
|
||||
if (_flags == PhysicsDebugFlags.None)
|
||||
IoCManager.Resolve<IOverlayManager>().AddOverlay(new PhysicsDebugOverlay(this));
|
||||
|
||||
if (value == PhysicsDebugFlags.None)
|
||||
IoCManager.Resolve<IOverlayManager>().RemoveOverlay(typeof(PhysicsDebugOverlay));
|
||||
|
||||
_flags = value;
|
||||
}
|
||||
}
|
||||
|
||||
private PhysicsDebugFlags _flags;
|
||||
|
||||
public override void HandlePreSolve(Contact contact, in Manifold oldManifold)
|
||||
{
|
||||
if ((Flags & PhysicsDebugFlags.ContactPoints) != 0)
|
||||
{
|
||||
Manifold manifold = contact.Manifold;
|
||||
|
||||
if (manifold.PointCount == 0)
|
||||
return;
|
||||
|
||||
Fixture fixtureA = contact.FixtureA!;
|
||||
|
||||
PointState[] state1, state2;
|
||||
CollisionManager.GetPointStates(out state1, out state2, oldManifold, manifold);
|
||||
|
||||
Span<Vector2> points = stackalloc Vector2[2];
|
||||
Vector2 normal;
|
||||
contact.GetWorldManifold(out normal, points);
|
||||
|
||||
for (int i = 0; i < manifold.PointCount && PointCount < MaxContactPoints; ++i)
|
||||
{
|
||||
if (fixtureA == null)
|
||||
_points[i] = new ContactPoint();
|
||||
|
||||
ContactPoint cp = _points[PointCount];
|
||||
cp.Position = points[i];
|
||||
cp.Normal = normal;
|
||||
cp.State = state2[i];
|
||||
_points[PointCount] = cp;
|
||||
++PointCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal struct ContactPoint
|
||||
{
|
||||
public Vector2 Normal;
|
||||
public Vector2 Position;
|
||||
public PointState State;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum PhysicsDebugFlags : byte
|
||||
{
|
||||
None = 0,
|
||||
ContactPoints = 1 << 0,
|
||||
ContactNormals = 1 << 1,
|
||||
Shapes = 1 << 2,
|
||||
}
|
||||
|
||||
internal sealed class PhysicsDebugOverlay : Overlay
|
||||
{
|
||||
private DebugPhysicsSystem _physics = default!;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public PhysicsDebugOverlay(DebugPhysicsSystem system)
|
||||
{
|
||||
_physics = system;
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
|
||||
{
|
||||
if (_physics.Flags == PhysicsDebugFlags.None) return;
|
||||
|
||||
var worldHandle = (DrawingHandleWorld) handle;
|
||||
|
||||
if ((_physics.Flags & PhysicsDebugFlags.Shapes) != 0)
|
||||
{
|
||||
// Port DebugDrawing over.
|
||||
}
|
||||
|
||||
if ((_physics.Flags & PhysicsDebugFlags.ContactPoints) != 0)
|
||||
{
|
||||
const float axisScale = 0.3f;
|
||||
|
||||
for (int i = 0; i < _physics.PointCount; ++i)
|
||||
{
|
||||
DebugPhysicsSystem.ContactPoint point = _physics._points[i];
|
||||
|
||||
if (point.State == PointState.Add)
|
||||
worldHandle.DrawCircle(point.Position, 0.5f, new Color(255, 77, 243, 77));
|
||||
else if (point.State == PointState.Persist)
|
||||
worldHandle.DrawCircle(point.Position, 0.5f, new Color(255, 77, 77, 77));
|
||||
|
||||
if ((_physics.Flags & PhysicsDebugFlags.ContactNormals) != 0)
|
||||
{
|
||||
Vector2 p1 = point.Position;
|
||||
Vector2 p2 = p1 + point.Normal * axisScale;
|
||||
worldHandle.DrawLine(p1, p2, new Color(255, 102, 230, 102));
|
||||
}
|
||||
}
|
||||
|
||||
_physics.PointCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Management;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Client.Audio.Midi;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameStates;
|
||||
@@ -26,7 +28,7 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timers;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -44,7 +46,7 @@ namespace Robust.Client
|
||||
[Dependency] private readonly IUserInterfaceManagerInternal _userInterfaceManager = default!;
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
[Dependency] private readonly ITimerManager _timerManager = default!;
|
||||
[Dependency] private readonly IClientEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPlacementManager _placementManager = default!;
|
||||
@@ -62,6 +64,7 @@ namespace Robust.Client
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IRobustMappedStringSerializer _stringSerializer = default!;
|
||||
[Dependency] private readonly IAuthManager _authManager = default!;
|
||||
[Dependency] private readonly IMidiManager _midiManager = default!;
|
||||
|
||||
private CommandLineArgs? _commandLineArgs;
|
||||
private bool _disableAssemblyLoadContext;
|
||||
@@ -78,6 +81,74 @@ namespace Robust.Client
|
||||
}
|
||||
|
||||
public bool Startup(Func<ILogHandler>? logHandlerFactory = null)
|
||||
{
|
||||
if (!StartupSystemSplash(logHandlerFactory))
|
||||
return false;
|
||||
|
||||
// Disable load context usage on content start.
|
||||
// This prevents Content.Client being loaded twice and things like csi blowing up because of it.
|
||||
_modLoader.SetUseLoadContext(!_disableAssemblyLoadContext);
|
||||
_modLoader.SetEnableSandboxing(true);
|
||||
|
||||
if (!_modLoader.TryLoadModulesFrom(new ResourcePath("/Assemblies/"), "Content."))
|
||||
{
|
||||
Logger.Fatal("Errors while loading content assemblies.");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var loadedModule in _modLoader.LoadedModules)
|
||||
{
|
||||
_configurationManager.LoadCVarsFromAssembly(loadedModule);
|
||||
}
|
||||
|
||||
IoCManager.Resolve<ISerializationManager>().Initialize();
|
||||
|
||||
// Call Init in game assemblies.
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.PreInit);
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.Init);
|
||||
|
||||
_resourceCache.PreloadTextures();
|
||||
_userInterfaceManager.Initialize();
|
||||
_networkManager.Initialize(false);
|
||||
IoCManager.Resolve<INetConfigurationManager>().SetupNetworking();
|
||||
_serializer.Initialize();
|
||||
_inputManager.Initialize();
|
||||
_console.Initialize();
|
||||
_prototypeManager.Initialize();
|
||||
_prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes/"));
|
||||
_prototypeManager.Resync();
|
||||
_mapManager.Initialize();
|
||||
_entityManager.Initialize();
|
||||
_gameStateManager.Initialize();
|
||||
_placementManager.Initialize();
|
||||
_viewVariablesManager.Initialize();
|
||||
_scriptClient.Initialize();
|
||||
|
||||
_client.Initialize();
|
||||
_discord.Initialize();
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.PostInit);
|
||||
|
||||
if (_commandLineArgs?.Username != null)
|
||||
{
|
||||
_client.PlayerNameOverride = _commandLineArgs.Username;
|
||||
}
|
||||
|
||||
_authManager.LoadFromEnv();
|
||||
|
||||
GC.Collect();
|
||||
|
||||
_clyde.Ready();
|
||||
|
||||
if ((_commandLineArgs?.Connect == true || _commandLineArgs?.Launcher == true)
|
||||
&& LaunchState.ConnectEndpoint != null)
|
||||
{
|
||||
_client.ConnectToServer(LaunchState.ConnectEndpoint);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool StartupSystemSplash(Func<ILogHandler>? logHandlerFactory)
|
||||
{
|
||||
ReadInitialLaunchState();
|
||||
|
||||
@@ -117,6 +188,8 @@ namespace Robust.Client
|
||||
_configurationManager.OverrideConVars(_commandLineArgs.CVars);
|
||||
}
|
||||
|
||||
ProfileOptSetup.Setup(_configurationManager);
|
||||
|
||||
_resourceCache.Initialize(LoadConfigAndUserData ? userDataDir : null);
|
||||
|
||||
ProgramShared.DoMounts(_resourceCache, _commandLineArgs?.MountOptions, "Content.Client", _loaderArgs != null);
|
||||
@@ -127,6 +200,13 @@ namespace Robust.Client
|
||||
_modLoader.VerifierExtraLoadHandler = VerifierExtraLoadHandler;
|
||||
}
|
||||
|
||||
_clyde.TextEntered += TextEntered;
|
||||
_clyde.MouseMove += MouseMove;
|
||||
_clyde.KeyUp += KeyUp;
|
||||
_clyde.KeyDown += KeyDown;
|
||||
_clyde.MouseWheel += MouseWheel;
|
||||
_clyde.CloseWindow += Shutdown;
|
||||
|
||||
// Bring display up as soon as resources are mounted.
|
||||
if (!_clyde.Initialize())
|
||||
{
|
||||
@@ -135,62 +215,7 @@ namespace Robust.Client
|
||||
|
||||
_clyde.SetWindowTitle("Space Station 14");
|
||||
|
||||
_fontManager.Initialize();
|
||||
|
||||
// Disable load context usage on content start.
|
||||
// This prevents Content.Client being loaded twice and things like csi blowing up because of it.
|
||||
_modLoader.SetUseLoadContext(!_disableAssemblyLoadContext);
|
||||
_modLoader.SetEnableSandboxing(true);
|
||||
|
||||
if (!_modLoader.TryLoadModulesFrom(new ResourcePath("/Assemblies/"), "Content."))
|
||||
{
|
||||
Logger.Fatal("Errors while loading content assemblies.");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var loadedModule in _modLoader.LoadedModules)
|
||||
{
|
||||
_configurationManager.LoadCVarsFromAssembly(loadedModule);
|
||||
}
|
||||
|
||||
// Call Init in game assemblies.
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.PreInit);
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.Init);
|
||||
|
||||
_userInterfaceManager.Initialize();
|
||||
_networkManager.Initialize(false);
|
||||
IoCManager.Resolve<INetConfigurationManager>().SetupNetworking();
|
||||
_serializer.Initialize();
|
||||
_inputManager.Initialize();
|
||||
_consoleHost.Initialize();
|
||||
_prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes/"));
|
||||
_prototypeManager.Resync();
|
||||
_mapManager.Initialize();
|
||||
_entityManager.Initialize();
|
||||
_gameStateManager.Initialize();
|
||||
_placementManager.Initialize();
|
||||
_viewVariablesManager.Initialize();
|
||||
_scriptClient.Initialize();
|
||||
|
||||
_client.Initialize();
|
||||
_discord.Initialize();
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.PostInit);
|
||||
|
||||
if (_commandLineArgs?.Username != null)
|
||||
{
|
||||
_client.PlayerNameOverride = _commandLineArgs.Username;
|
||||
}
|
||||
|
||||
_authManager.LoadFromEnv();
|
||||
|
||||
_clyde.Ready();
|
||||
|
||||
if ((_commandLineArgs?.Connect == true || _commandLineArgs?.Launcher == true)
|
||||
&& LaunchState.ConnectEndpoint != null)
|
||||
{
|
||||
_client.ConnectToServer(LaunchState.ConnectEndpoint);
|
||||
}
|
||||
|
||||
_fontManager.SetFontDpi((uint) _configurationManager.GetCVar(CVars.DisplayFontDpi));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -267,17 +292,13 @@ namespace Robust.Client
|
||||
_modLoader.BroadcastUpdate(ModUpdateLevel.PreEngine, frameEventArgs);
|
||||
_timerManager.UpdateTimers(frameEventArgs);
|
||||
_taskManager.ProcessPendingTasks();
|
||||
_userInterfaceManager.Update(frameEventArgs);
|
||||
|
||||
// GameStateManager is in full control of the simulation update.
|
||||
if (_client.RunLevel >= ClientRunLevel.Connected)
|
||||
{
|
||||
_componentManager.CullRemovedComponents();
|
||||
_gameStateManager.ApplyGameState();
|
||||
_entityManager.Update(frameEventArgs.DeltaSeconds);
|
||||
_playerManager.Update(frameEventArgs.DeltaSeconds);
|
||||
}
|
||||
|
||||
_stateManager.Update(frameEventArgs);
|
||||
_modLoader.BroadcastUpdate(ModUpdateLevel.PostEngine, frameEventArgs);
|
||||
}
|
||||
|
||||
@@ -318,6 +339,7 @@ namespace Robust.Client
|
||||
logManager.GetSawmill("discord").Level = LogLevel.Warning;
|
||||
logManager.GetSawmill("net.predict").Level = LogLevel.Info;
|
||||
logManager.GetSawmill("szr").Level = LogLevel.Info;
|
||||
logManager.GetSawmill("loc").Level = LogLevel.Error;
|
||||
|
||||
#if DEBUG_ONLY_FCE_INFO
|
||||
#if DEBUG_ONLY_FCE_LOG
|
||||
@@ -376,6 +398,8 @@ namespace Robust.Client
|
||||
|
||||
private void Cleanup()
|
||||
{
|
||||
_networkManager.Shutdown("Client shutting down");
|
||||
_midiManager.Shutdown();
|
||||
_entityManager.Shutdown();
|
||||
_clyde.Shutdown();
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ namespace Robust.Client
|
||||
RegisterReflection();
|
||||
}
|
||||
|
||||
|
||||
internal static void RegisterReflection()
|
||||
{
|
||||
// Gets a handle to the shared and the current (client) dll.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -23,9 +24,13 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
Register<PhysicsComponent>();
|
||||
RegisterReference<PhysicsComponent, IPhysBody>();
|
||||
RegisterReference<PhysicsComponent, IPhysicsComponent>();
|
||||
|
||||
Register<CollisionWakeComponent>();
|
||||
|
||||
Register<ContainerManagerComponent>();
|
||||
RegisterReference<ContainerManagerComponent, IContainerManager>();
|
||||
|
||||
RegisterIgnore("KeyBindingInput");
|
||||
Register<PointLightComponent>();
|
||||
|
||||
Register<InputComponent>();
|
||||
|
||||
@@ -50,14 +55,10 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
Register<AnimationPlayerComponent>();
|
||||
|
||||
Register<ContainerManagerComponent>();
|
||||
RegisterReference<ContainerManagerComponent, IContainerManager>();
|
||||
|
||||
Register<TimerComponent>();
|
||||
|
||||
#if DEBUG
|
||||
Register<DebugExceptionOnAddComponent>();
|
||||
Register<DebugExceptionExposeDataComponent>();
|
||||
Register<DebugExceptionInitializeComponent>();
|
||||
Register<DebugExceptionStartupComponent>();
|
||||
#endif
|
||||
|
||||
@@ -212,12 +212,6 @@ namespace Robust.Client.GameObjects
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEntity SpawnEntityNoMapInit(string? protoName, EntityCoordinates coordinates)
|
||||
{
|
||||
return SpawnEntity(protoName, coordinates);
|
||||
}
|
||||
|
||||
protected override EntityUid GenerateEntityUid()
|
||||
{
|
||||
return new(_nextClientEntityUid++);
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Robust.Client.GameObjects
|
||||
_networkManager.RegisterNetMessage<MsgEntity>(MsgEntity.NAME, HandleEntityNetworkMessage);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
public void TickUpdate()
|
||||
{
|
||||
while (_queue.Count != 0 && _queue.Peek().msg.SourceTick <= _gameStateManager.CurServerTick)
|
||||
{
|
||||
@@ -45,12 +45,12 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SendSystemNetworkMessage(EntitySystemMessage message)
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message)
|
||||
{
|
||||
SendSystemNetworkMessage(message, default(uint));
|
||||
}
|
||||
|
||||
public void SendSystemNetworkMessage(EntitySystemMessage message, uint sequence)
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message, uint sequence)
|
||||
{
|
||||
var msg = _networkManager.CreateNetMessage<MsgEntity>();
|
||||
msg.Type = EntityMessageType.SystemMessage;
|
||||
@@ -62,7 +62,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SendSystemNetworkMessage(EntitySystemMessage message, INetChannel channel)
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message, INetChannel channel)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
@@ -15,13 +14,11 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
[ViewVariables]
|
||||
private Dictionary<object, object> data = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("visuals")]
|
||||
internal List<AppearanceVisualizer> Visualizers = new();
|
||||
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
|
||||
private static bool _didRegisterSerializer;
|
||||
|
||||
[ViewVariables]
|
||||
private bool _appearanceDirty;
|
||||
|
||||
@@ -107,18 +104,6 @@ namespace Robust.Client.GameObjects
|
||||
_appearanceDirty = false;
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
if (!_didRegisterSerializer)
|
||||
{
|
||||
YamlObjectSerializer.RegisterTypeSerializer(typeof(AppearanceVisualizer),
|
||||
new VisualizerTypeSerializer(_reflectionManager));
|
||||
_didRegisterSerializer = true;
|
||||
}
|
||||
|
||||
serializer.DataField(ref Visualizers, "visuals", new List<AppearanceVisualizer>());
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -131,78 +116,6 @@ namespace Robust.Client.GameObjects
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
class VisualizerTypeSerializer : YamlObjectSerializer.TypeSerializer
|
||||
{
|
||||
private readonly IReflectionManager _reflectionManager;
|
||||
|
||||
public VisualizerTypeSerializer(IReflectionManager reflectionManager)
|
||||
{
|
||||
_reflectionManager = reflectionManager;
|
||||
}
|
||||
|
||||
public override object NodeToType(Type type, YamlNode node, YamlObjectSerializer serializer)
|
||||
{
|
||||
var mapping = (YamlMappingNode) node;
|
||||
var nodeType = mapping.GetNode("type");
|
||||
switch (nodeType.AsString())
|
||||
{
|
||||
case SpriteLayerToggle.NAME:
|
||||
var keyString = mapping.GetNode("key").AsString();
|
||||
object key;
|
||||
if (_reflectionManager.TryParseEnumReference(keyString, out var @enum))
|
||||
{
|
||||
key = @enum;
|
||||
}
|
||||
else
|
||||
{
|
||||
key = keyString;
|
||||
}
|
||||
|
||||
var layer = mapping.GetNode("layer").AsInt();
|
||||
return new SpriteLayerToggle(key, layer);
|
||||
|
||||
default:
|
||||
var visType = _reflectionManager.LooseGetType(nodeType.AsString());
|
||||
if (!typeof(AppearanceVisualizer).IsAssignableFrom(visType))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
var vis = (AppearanceVisualizer) Activator.CreateInstance(visType)!;
|
||||
vis.LoadData(mapping);
|
||||
return vis;
|
||||
}
|
||||
}
|
||||
|
||||
public override YamlNode TypeToNode(object obj, YamlObjectSerializer serializer)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case SpriteLayerToggle spriteLayerToggle:
|
||||
YamlScalarNode key;
|
||||
if (spriteLayerToggle.Key is Enum)
|
||||
{
|
||||
var name = spriteLayerToggle.Key.GetType().FullName;
|
||||
key = new YamlScalarNode($"{name}.{spriteLayerToggle.Key}");
|
||||
}
|
||||
else
|
||||
{
|
||||
key = new YamlScalarNode(spriteLayerToggle.Key.ToString());
|
||||
}
|
||||
|
||||
return new YamlMappingNode
|
||||
{
|
||||
{new YamlScalarNode("type"), new YamlScalarNode(SpriteLayerToggle.NAME)},
|
||||
{new YamlScalarNode("key"), key},
|
||||
{new YamlScalarNode("layer"), new YamlScalarNode(spriteLayerToggle.SpriteLayer.ToString())},
|
||||
};
|
||||
default:
|
||||
// TODO: A proper way to do serialization here.
|
||||
// I can't use the ExposeData system here since that's specific to entity serializers.
|
||||
return new YamlMappingNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SpriteLayerToggle : AppearanceVisualizer
|
||||
{
|
||||
@@ -223,15 +136,9 @@ namespace Robust.Client.GameObjects
|
||||
/// 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>
|
||||
/// Load data from the prototype declaring this visualizer, to configure settings and such.
|
||||
/// </summary>
|
||||
public virtual void LoadData(YamlMappingNode node)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an entity to be managed by this appearance controller.
|
||||
/// DO NOT assume this is your only entity. Visualizers are shared.
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public sealed partial class ContainerManagerComponent
|
||||
{
|
||||
[DebuggerDisplay("ClientContainer {Owner.Uid}/{ID}")]
|
||||
private sealed class ClientContainer : IContainer
|
||||
{
|
||||
public List<IEntity> Entities { get; } = new List<IEntity>();
|
||||
|
||||
public ClientContainer(string id, ContainerManagerComponent manager)
|
||||
{
|
||||
ID = id;
|
||||
Manager = manager;
|
||||
}
|
||||
|
||||
[ViewVariables] public IContainerManager Manager { get; }
|
||||
[ViewVariables] public string ID { get; }
|
||||
[ViewVariables] public IEntity Owner => Manager.Owner;
|
||||
[ViewVariables] public bool Deleted { get; private set; }
|
||||
[ViewVariables] public IReadOnlyList<IEntity> ContainedEntities => Entities;
|
||||
[ViewVariables]
|
||||
public bool ShowContents { get; set; }
|
||||
[ViewVariables]
|
||||
public bool OccludesLight { get; set; }
|
||||
|
||||
public bool CanInsert(IEntity toinsert)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Insert(IEntity toinsert)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CanRemove(IEntity toremove)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Remove(IEntity toremove)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ForceRemove(IEntity toRemove)
|
||||
{
|
||||
throw new NotSupportedException("Cannot directly modify containers on the client");
|
||||
}
|
||||
|
||||
public bool Contains(IEntity contained)
|
||||
{
|
||||
return Entities.Contains(contained);
|
||||
}
|
||||
|
||||
public void DoInsert(IEntity entity)
|
||||
{
|
||||
Entities.Add(entity);
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(entity));
|
||||
}
|
||||
|
||||
public void DoRemove(IEntity entity)
|
||||
{
|
||||
Entities.Remove(entity);
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(entity));
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
Deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void InternalContainerShutdown(IContainer container)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public sealed partial class ContainerManagerComponent : SharedContainerManagerComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<string, ClientContainer> _containers = new();
|
||||
|
||||
public override T MakeContainer<T>(string id)
|
||||
{
|
||||
throw new NotSupportedException("Cannot modify containers on the client.");
|
||||
}
|
||||
|
||||
public override bool Remove(IEntity entity)
|
||||
{
|
||||
// TODO: This will probably need relaxing if we want to predict things like inventories.
|
||||
throw new NotSupportedException("Cannot modify containers on the client.");
|
||||
}
|
||||
|
||||
protected override IEnumerable<IContainer> GetAllContainersImpl()
|
||||
{
|
||||
return _containers.Values.Where(c => !c.Deleted);
|
||||
}
|
||||
|
||||
public override IContainer GetContainer(string id)
|
||||
{
|
||||
return _containers[id];
|
||||
}
|
||||
|
||||
public override bool HasContainer(string id)
|
||||
{
|
||||
return _containers.ContainsKey(id);
|
||||
}
|
||||
|
||||
public override bool TryGetContainer(string id, [NotNullWhen(true)] out IContainer? container)
|
||||
{
|
||||
var ret = _containers.TryGetValue(id, out var cont);
|
||||
container = cont!;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryGetContainer(IEntity entity, [NotNullWhen(true)] out IContainer? container)
|
||||
{
|
||||
foreach (var contain in _containers.Values)
|
||||
{
|
||||
if (!contain.Deleted && contain.Contains(entity))
|
||||
{
|
||||
container = contain;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
container = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ContainsEntity(IEntity entity)
|
||||
{
|
||||
foreach (var container in _containers.Values)
|
||||
{
|
||||
if (!container.Deleted && container.Contains(entity))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void ForceRemove(IEntity entity)
|
||||
{
|
||||
throw new NotSupportedException("Cannot modify containers on the client.");
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if(!(curState is ContainerManagerComponentState cast))
|
||||
return;
|
||||
|
||||
// Delete now-gone containers.
|
||||
List<string>? toDelete = null;
|
||||
foreach (var (id, container) in _containers)
|
||||
{
|
||||
if (!cast.Containers.ContainsKey(id))
|
||||
{
|
||||
container.Shutdown();
|
||||
toDelete ??= new List<string>();
|
||||
toDelete.Add(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (toDelete != null)
|
||||
{
|
||||
foreach (var dead in toDelete)
|
||||
{
|
||||
_containers.Remove(dead);
|
||||
}
|
||||
}
|
||||
|
||||
// Add new containers and update existing contents.
|
||||
foreach (var (id, data) in cast.Containers)
|
||||
{
|
||||
|
||||
if (!_containers.TryGetValue(id, out var container))
|
||||
{
|
||||
container = new ClientContainer(id, this);
|
||||
_containers.Add(id, container);
|
||||
}
|
||||
|
||||
// sync show flag
|
||||
container.ShowContents = data.ShowContents;
|
||||
container.OccludesLight = data.OccludesLight;
|
||||
|
||||
// Remove gone entities.
|
||||
List<IEntity>? toRemove = null;
|
||||
foreach (var entity in container.Entities)
|
||||
{
|
||||
if (!data.ContainedEntities.Contains(entity.Uid))
|
||||
{
|
||||
toRemove ??= new List<IEntity>();
|
||||
toRemove.Add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
if (toRemove != null)
|
||||
{
|
||||
foreach (var goner in toRemove)
|
||||
{
|
||||
container.DoRemove(goner);
|
||||
}
|
||||
}
|
||||
|
||||
// Add new entities.
|
||||
foreach (var uid in data.ContainedEntities)
|
||||
{
|
||||
var entity = Owner.EntityManager.GetEntity(uid);
|
||||
|
||||
if (!container.Entities.Contains(entity))
|
||||
{
|
||||
container.DoInsert(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
// On shutdown we won't get to process remove events in the containers so this has to be manually done.
|
||||
foreach (var container in _containers.Values)
|
||||
{
|
||||
foreach (var containerEntity in container.Entities)
|
||||
{
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local,
|
||||
new UpdateContainerOcclusionMessage(containerEntity));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Graphics;
|
||||
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;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -20,7 +22,9 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
// Horrible hack to get around ordering issues.
|
||||
private bool _setCurrentOnInitialize;
|
||||
private bool _setDrawFovOnInitialize;
|
||||
[DataField("drawFov")]
|
||||
private bool _setDrawFovOnInitialize = true;
|
||||
[DataField("zoom")]
|
||||
private Vector2 _setZoomOnInitialize = Vector2.One/2f;
|
||||
private Vector2 _offset = Vector2.Zero;
|
||||
|
||||
@@ -148,6 +152,7 @@ namespace Robust.Client.GameObjects
|
||||
Zoom = state.Zoom;
|
||||
Offset = state.Offset;
|
||||
Rotation = state.Rotation;
|
||||
VisibilityMask = state.VisibilityMask;
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
@@ -157,15 +162,6 @@ namespace Robust.Client.GameObjects
|
||||
Current = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataFieldCached(ref _setZoomOnInitialize, "zoom", Vector2.One/2f);
|
||||
serializer.DataFieldCached(ref _setDrawFovOnInitialize, "drawFov", true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the Eye of this entity with the transform position. This has to be called every frame to
|
||||
/// keep the view following the entity.
|
||||
|
||||
@@ -1,105 +1,51 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class IconComponent : Component
|
||||
public class IconComponent : Component, ISerializationHooks
|
||||
{
|
||||
public override string Name => "Icon";
|
||||
public IDirectionalTextureProvider? Icon { get; private set; }
|
||||
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[DataField("sprite")]
|
||||
private ResourcePath? rsi;
|
||||
[DataField("state")]
|
||||
private string? stateID;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
if (rsi != null && stateID != null)
|
||||
{
|
||||
Icon = new SpriteSpecifier.Rsi(rsi, stateID).Frame0();
|
||||
}
|
||||
}
|
||||
|
||||
public const string LogCategory = "go.comp.icon";
|
||||
const string SerializationCache = "icon";
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
private static IRsiStateLike TextureForConfig(IconComponent compData, IResourceCache resourceCache)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
// TODO: Does this need writing?
|
||||
if (serializer.Reading)
|
||||
{
|
||||
Icon = TextureForConfig(serializer, _resourceCache);
|
||||
}
|
||||
}
|
||||
|
||||
private static IRsiStateLike TextureForConfig(ObjectSerializer serializer, IResourceCache resourceCache)
|
||||
{
|
||||
DebugTools.Assert(serializer.Reading);
|
||||
|
||||
if (serializer.TryGetCacheData<IRsiStateLike>(SerializationCache, out var dirTex))
|
||||
{
|
||||
return dirTex;
|
||||
}
|
||||
|
||||
var tex = serializer.ReadDataField<string?>("texture", null);
|
||||
if (!string.IsNullOrWhiteSpace(tex))
|
||||
{
|
||||
dirTex = resourceCache.GetResource<TextureResource>(SpriteComponent.TextureRoot / tex).Texture;
|
||||
serializer.SetCacheData(SerializationCache, dirTex);
|
||||
return dirTex;
|
||||
}
|
||||
|
||||
RSI rsi;
|
||||
|
||||
var rsiPath = serializer.ReadDataField<string?>("sprite", null);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(rsiPath))
|
||||
{
|
||||
dirTex = resourceCache.GetFallback<TextureResource>().Texture;
|
||||
serializer.SetCacheData(SerializationCache, dirTex);
|
||||
return dirTex;
|
||||
}
|
||||
|
||||
var path = SpriteComponent.TextureRoot / rsiPath;
|
||||
|
||||
try
|
||||
{
|
||||
rsi = resourceCache.GetResource<RSIResource>(path).RSI;
|
||||
}
|
||||
catch
|
||||
{
|
||||
dirTex = resourceCache.GetFallback<TextureResource>().Texture;
|
||||
serializer.SetCacheData(SerializationCache, dirTex);
|
||||
return dirTex;
|
||||
}
|
||||
|
||||
var stateId = serializer.ReadDataField<string?>("state", null);
|
||||
if (string.IsNullOrWhiteSpace(stateId))
|
||||
{
|
||||
Logger.ErrorS(LogCategory, "No state specified.");
|
||||
dirTex = resourceCache.GetFallback<TextureResource>().Texture;
|
||||
serializer.SetCacheData(SerializationCache, dirTex);
|
||||
return dirTex;
|
||||
}
|
||||
|
||||
if (rsi.TryGetState(stateId, out var state))
|
||||
{
|
||||
serializer.SetCacheData(SerializationCache, state);
|
||||
return state;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.ErrorS(LogCategory, "State '{0}' does not exist on RSI.", stateId);
|
||||
return resourceCache.GetFallback<TextureResource>().Texture;
|
||||
}
|
||||
return compData.Icon?.Default ?? resourceCache.GetFallback<TextureResource>().Texture;
|
||||
}
|
||||
|
||||
public static IRsiStateLike? GetPrototypeIcon(EntityPrototype prototype, IResourceCache resourceCache)
|
||||
{
|
||||
if (!prototype.Components.TryGetValue("Icon", out var mapping))
|
||||
if (!prototype.Components.TryGetValue("Icon", out var compData))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return TextureForConfig(YamlObjectSerializer.NewReader(mapping), resourceCache);
|
||||
|
||||
return TextureForConfig((IconComponent)compData, resourceCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedIgnorePauseComponent))]
|
||||
public sealed class IgnorePauseComponent : SharedIgnorePauseComponent
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -17,14 +19,7 @@ namespace Robust.Client.GameObjects
|
||||
/// The context that will be made active for a client that attaches to this entity.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string ContextName { get; set; } = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataReadWriteFunction("context", InputContextContainer.DefaultContextName, value => ContextName = value, () => ContextName);
|
||||
}
|
||||
[DataField("context")]
|
||||
public string ContextName { get; set; } = InputContextContainer.DefaultContextName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,17 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public class PointLightComponent : Component
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IPointLightComponent))]
|
||||
public class PointLightComponent : Component, IPointLightComponent, ISerializationHooks
|
||||
{
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
|
||||
public override string Name => "PointLight";
|
||||
public override uint? NetID => NetIDs.POINT_LIGHT;
|
||||
|
||||
@@ -65,6 +70,21 @@ namespace Robust.Client.GameObjects
|
||||
set => _rotation = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// The resource path to the mask texture the light will use.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? MaskPath
|
||||
{
|
||||
get => _maskPath;
|
||||
set
|
||||
{
|
||||
_maskPath = value;
|
||||
UpdateMask();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a mask texture that will be applied to the light while rendering.
|
||||
/// The mask's red channel will be linearly multiplied.p
|
||||
@@ -115,16 +135,26 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
private float _radius = 5;
|
||||
[DataField("radius")]
|
||||
private float _radius = 5f;
|
||||
[DataField("nestedvisible")]
|
||||
private bool _visibleNested = true;
|
||||
private bool _lightOnParent = false;
|
||||
private bool _lightOnParent;
|
||||
[DataField("color")]
|
||||
private Color _color = Color.White;
|
||||
private Vector2 _offset;
|
||||
[DataField("offset")]
|
||||
private Vector2 _offset = Vector2.Zero;
|
||||
[DataField("enabled")]
|
||||
private bool _enabled = true;
|
||||
[DataField("autoRot")]
|
||||
private bool _maskAutoRotate;
|
||||
private Angle _rotation;
|
||||
private float _energy;
|
||||
private float _softness;
|
||||
[DataField("energy")]
|
||||
private float _energy = 1f;
|
||||
[DataField("softness")]
|
||||
private float _softness = 1f;
|
||||
[DataField("mask")]
|
||||
private string? _maskPath;
|
||||
|
||||
/// <summary>
|
||||
/// Radius, in meters.
|
||||
@@ -141,12 +171,34 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMask()
|
||||
{
|
||||
if (_maskPath is not null)
|
||||
Mask = _resourceCache.GetResource<TextureResource>(_maskPath);
|
||||
else
|
||||
Mask = null;
|
||||
}
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
if (_maskPath != null)
|
||||
{
|
||||
Mask = IoCManager.Resolve<IResourceCache>().GetResource<TextureResource>(_maskPath);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
UpdateMask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
|
||||
if ((message is ParentChangedMessage msg))
|
||||
if (message is ParentChangedMessage msg)
|
||||
{
|
||||
HandleTransformParentChanged(msg);
|
||||
}
|
||||
@@ -170,23 +222,6 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataFieldCached(ref _offset, "offset", Vector2.Zero);
|
||||
serializer.DataFieldCached(ref _radius, "radius", 5f);
|
||||
serializer.DataFieldCached(ref _color, "color", Color.White);
|
||||
serializer.DataFieldCached(ref _enabled, "enabled", true);
|
||||
serializer.DataFieldCached(ref _energy, "energy", 1f);
|
||||
serializer.DataFieldCached(ref _softness, "softness", 1f);
|
||||
serializer.DataFieldCached(ref _maskAutoRotate, "autoRot", false);
|
||||
serializer.DataFieldCached(ref _visibleNested, "nestedvisible", true);
|
||||
|
||||
if (serializer.Reading && serializer.TryReadDataField<string>("mask", out var value))
|
||||
{
|
||||
Mask = IoCManager.Resolve<IResourceCache>().GetResource<TextureResource>(value);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Animations;
|
||||
@@ -49,8 +49,26 @@ namespace Robust.Client.GameObjects
|
||||
/// Rotation transformations on individual layers still apply.
|
||||
/// If false, all layers get locked to south and rotation is a transformation.
|
||||
/// </summary>
|
||||
[Obsolete("Use NoRotation and/or DirectionOverride")]
|
||||
bool Directional { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// All sprite rotation is locked, and will always be drawn upright on
|
||||
/// the screen, regardless of world or view orientation.
|
||||
/// </summary>
|
||||
bool NoRotation {get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables overriding the calculated directional RSI state for this sprite.
|
||||
/// The state to use is defined in <see cref="DirectionOverride"/>.
|
||||
/// </summary>
|
||||
bool EnableDirectionOverride { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The directional RSI state that will always be displayed, regardless of orientation.
|
||||
/// </summary>
|
||||
Direction DirectionOverride { get; set; }
|
||||
|
||||
// NOTE: The below are ALL designed to NOT throw exceptions ever,
|
||||
// instead making a bunch of noisy error logs.
|
||||
|
||||
@@ -64,6 +82,8 @@ namespace Robust.Client.GameObjects
|
||||
uint RenderOrder { get; set; }
|
||||
bool IsInert { get; }
|
||||
|
||||
Matrix3 GetLocalMatrix();
|
||||
|
||||
/// <summary>
|
||||
/// Sets a layer key to the layer map, creating it if it does not exist.
|
||||
/// </summary>
|
||||
@@ -202,5 +222,12 @@ namespace Robust.Client.GameObjects
|
||||
ISpriteLayer this[object layerKey] { get; }
|
||||
|
||||
IEnumerable<ISpriteLayer> AllLayers { get; }
|
||||
|
||||
int GetLayerDirectionCount(ISpriteLayer layer);
|
||||
|
||||
/// <summary>
|
||||
/// Calculate sprite bounding box in world-space coordinates.
|
||||
/// </summary>
|
||||
Box2 CalculateBoundingBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,5 +26,17 @@ namespace Robust.Client.GameObjects
|
||||
RSI.State.Direction EffectiveDirection(Angle worldRotation);
|
||||
|
||||
Vector2 LocalToLayer(Vector2 localPos);
|
||||
|
||||
/// <summary>
|
||||
/// Layer size in pixels.
|
||||
/// Don't account layer scale or sprite world transform.
|
||||
/// </summary>
|
||||
Vector2i PixelSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Calculate layer bounding box in sprite local-space coordinates.
|
||||
/// </summary>
|
||||
/// <returns>Bounding box in sprite local-space coordinates.</returns>
|
||||
Box2 CalculateBoundingBox();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,41 +6,31 @@ using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public class ClientUserInterfaceComponent : SharedUserInterfaceComponent
|
||||
public class ClientUserInterfaceComponent : SharedUserInterfaceComponent, ISerializationHooks
|
||||
{
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactory _dynamicTypeFactory = default!;
|
||||
|
||||
private readonly Dictionary<object, BoundUserInterface> _openInterfaces =
|
||||
new();
|
||||
|
||||
private Dictionary<object, PrototypeData> _interfaceData = default!;
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactory _dynamicTypeFactory = default!;
|
||||
#pragma warning restore 649
|
||||
private readonly Dictionary<object, PrototypeData> _interfaces = new();
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
[DataField("interfaces", readOnly: true)]
|
||||
private List<PrototypeData> _interfaceData = new();
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
_interfaces.Clear();
|
||||
|
||||
const string cache = "ui_cache";
|
||||
|
||||
if (serializer.TryGetCacheData<Dictionary<object, PrototypeData>>(cache, out var interfaceData))
|
||||
foreach (var data in _interfaceData)
|
||||
{
|
||||
_interfaceData = interfaceData;
|
||||
return;
|
||||
_interfaces[data.UiKey] = data;
|
||||
}
|
||||
|
||||
var data = serializer.ReadDataFieldCached("interfaces", new List<PrototypeData>());
|
||||
interfaceData = new Dictionary<object, PrototypeData>();
|
||||
foreach (var prototypeData in data)
|
||||
{
|
||||
interfaceData[prototypeData.UiKey] = prototypeData;
|
||||
}
|
||||
|
||||
serializer.SetCacheData(cache, interfaceData);
|
||||
_interfaceData = interfaceData;
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel,
|
||||
@@ -81,7 +71,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private void OpenInterface(BoundInterfaceMessageWrapMessage wrapped)
|
||||
{
|
||||
var data = _interfaceData[wrapped.UiKey];
|
||||
var data = _interfaces[wrapped.UiKey];
|
||||
// TODO: This type should be cached, but I'm too lazy.
|
||||
var type = _reflectionManager.LooseGetType(data.ClientType);
|
||||
var boundInterface = (BoundUserInterface) _dynamicTypeFactory.CreateInstance(type, new[]{this, wrapped.UiKey});
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Client.GameObjects
|
||||
public static class EntityManagerExt
|
||||
{
|
||||
public static void RaisePredictiveEvent<T>(this IEntityManager entityManager, T msg)
|
||||
where T : EntitySystemMessage
|
||||
where T : EntityEventArgs
|
||||
{
|
||||
var localPlayer = IoCManager.Resolve<IPlayerManager>().LocalPlayer;
|
||||
DebugTools.AssertNotNull(localPlayer);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Audio;
|
||||
@@ -9,14 +8,15 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class AudioSystem : EntitySystem
|
||||
public class AudioSystem : EntitySystem, IAudioSystem
|
||||
{
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
@@ -24,17 +24,21 @@ namespace Robust.Client.GameObjects
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
private readonly List<PlayingStream> _playingClydeStreams = new();
|
||||
private SharedBroadPhaseSystem _broadPhaseSystem = default!;
|
||||
|
||||
public int OcclusionCollisionMask;
|
||||
private readonly List<PlayingStream> _playingClydeStreams = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeNetworkEvent<PlayAudioEntityMessage>(PlayAudioEntityHandler);
|
||||
SubscribeNetworkEvent<PlayAudioGlobalMessage>(PlayAudioGlobalHandler);
|
||||
SubscribeNetworkEvent<PlayAudioPositionalMessage>(PlayAudioPositionalHandler);
|
||||
SubscribeNetworkEvent<StopAudioMessageClient>(StopAudioMessageHandler);
|
||||
|
||||
SubscribeLocalEvent<SoundSystem.QueryAudioSystem>((ev => ev.Audio = this));
|
||||
_broadPhaseSystem = Get<SharedBroadPhaseSystem>();
|
||||
}
|
||||
|
||||
private void StopAudioMessageHandler(StopAudioMessageClient ev)
|
||||
@@ -141,7 +145,7 @@ namespace Robust.Client.GameObjects
|
||||
var occlusion = 0f;
|
||||
if (sourceRelative.Length > 0)
|
||||
{
|
||||
occlusion = IoCManager.Resolve<IPhysicsManager>().IntersectRayPenetration(
|
||||
occlusion = _broadPhaseSystem.IntersectRayPenetration(
|
||||
pos.MapId,
|
||||
new CollisionRay(
|
||||
pos.Position,
|
||||
@@ -160,6 +164,12 @@ namespace Robust.Client.GameObjects
|
||||
Logger.Warning("Interrupting positional audio, can't set position.");
|
||||
stream.Source.StopPlaying();
|
||||
}
|
||||
|
||||
if (stream.TrackingEntity != null)
|
||||
{
|
||||
stream.Source.SetVelocity(stream.TrackingEntity.GlobalLinearVelocity());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,7 +186,6 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
stream.Source.Dispose();
|
||||
stream.Done = true;
|
||||
stream.DoPlaybackDone();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -184,7 +193,7 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
public IPlayingAudioStream? Play(string filename, AudioParams? audioParams = null)
|
||||
private IPlayingAudioStream? Play(string filename, AudioParams? audioParams = null)
|
||||
{
|
||||
if (_resourceCache.TryGetResource<AudioResource>(new ResourcePath(filename), out var audio))
|
||||
{
|
||||
@@ -200,7 +209,7 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
/// <param name="stream">The audio stream to play.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
public IPlayingAudioStream Play(AudioStream stream, AudioParams? audioParams = null)
|
||||
private IPlayingAudioStream Play(AudioStream stream, AudioParams? audioParams = null)
|
||||
{
|
||||
var source = _clyde.CreateAudioSource(stream);
|
||||
ApplyAudioParams(audioParams, source);
|
||||
@@ -222,7 +231,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="entity">The entity "emitting" the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
public IPlayingAudioStream? Play(string filename, IEntity entity, AudioParams? audioParams = null)
|
||||
private IPlayingAudioStream? Play(string filename, IEntity entity, AudioParams? audioParams = null)
|
||||
{
|
||||
if (_resourceCache.TryGetResource<AudioResource>(new ResourcePath(filename), out var audio))
|
||||
{
|
||||
@@ -239,7 +248,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <param name="stream">The audio stream to play.</param>
|
||||
/// <param name="entity">The entity "emitting" the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
public IPlayingAudioStream? Play(AudioStream stream, IEntity entity, AudioParams? audioParams = null)
|
||||
private IPlayingAudioStream? Play(AudioStream stream, IEntity entity, AudioParams? audioParams = null)
|
||||
{
|
||||
var source = _clyde.CreateAudioSource(stream);
|
||||
if (!source.SetPosition(entity.Transform.WorldPosition))
|
||||
@@ -268,7 +277,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
public IPlayingAudioStream? Play(string filename, EntityCoordinates coordinates, AudioParams? audioParams = null)
|
||||
private IPlayingAudioStream? Play(string filename, EntityCoordinates coordinates, AudioParams? audioParams = null)
|
||||
{
|
||||
if (_resourceCache.TryGetResource<AudioResource>(new ResourcePath(filename), out var audio))
|
||||
{
|
||||
@@ -285,7 +294,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <param name="stream">The audio stream to play.</param>
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
public IPlayingAudioStream? Play(AudioStream stream, EntityCoordinates coordinates,
|
||||
private IPlayingAudioStream? Play(AudioStream stream, EntityCoordinates coordinates,
|
||||
AudioParams? audioParams = null)
|
||||
{
|
||||
var source = _clyde.CreateAudioSource(stream);
|
||||
@@ -335,92 +344,30 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
Source.StopPlaying();
|
||||
}
|
||||
|
||||
public event Action? PlaybackDone;
|
||||
|
||||
public void DoPlaybackDone()
|
||||
{
|
||||
PlaybackDone?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface IPlayingAudioStream
|
||||
{
|
||||
void Stop();
|
||||
|
||||
event Action PlaybackDone;
|
||||
}
|
||||
|
||||
public static class AudioSystemExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file following an entity.
|
||||
/// </summary>
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="entity">The entity "emitting" the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
|
||||
public static IPlayingAudioStream? Play(
|
||||
this IEntity entity,
|
||||
string filename,
|
||||
AudioParams? audioParams,
|
||||
AudioSystem? audioSystem = null)
|
||||
{
|
||||
audioSystem ??= EntitySystem.Get<AudioSystem>();
|
||||
return audioSystem.Play(filename, entity, audioParams);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio stream following an entity.
|
||||
/// </summary>
|
||||
/// <param name="stream">The audio stream to play.</param>
|
||||
/// <param name="entity">The entity "emitting" the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
|
||||
public static IPlayingAudioStream? Play(
|
||||
this IEntity entity,
|
||||
AudioStream stream,
|
||||
AudioParams? audioParams = null,
|
||||
AudioSystem? audioSystem = null)
|
||||
/// <inheritdoc />
|
||||
public int DefaultSoundRange => 25;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int OcclusionCollisionMask { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPlayingAudioStream? Play(Filter playerFilter, string filename, AudioParams? audioParams = null)
|
||||
{
|
||||
audioSystem ??= EntitySystem.Get<AudioSystem>();
|
||||
return audioSystem.Play(stream, entity, audioParams);
|
||||
return Play(filename, audioParams);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file at a static position.
|
||||
/// </summary>
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
|
||||
public static IPlayingAudioStream? Play(
|
||||
this EntityCoordinates coordinates,
|
||||
string filename,
|
||||
AudioParams? audioParams = null,
|
||||
AudioSystem? audioSystem = null)
|
||||
/// <inheritdoc />
|
||||
public IPlayingAudioStream? Play(Filter playerFilter, string filename, IEntity entity, AudioParams? audioParams = null)
|
||||
{
|
||||
audioSystem ??= EntitySystem.Get<AudioSystem>();
|
||||
return audioSystem.Play(filename, coordinates, audioParams);
|
||||
return Play(filename, entity, audioParams);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio stream at a static position.
|
||||
/// </summary>
|
||||
/// <param name="stream">The audio stream to play.</param>
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
/// <param name="audioSystem">A pre-fetched instance of <see cref="AudioSystem"/> to use, can be null.</param>
|
||||
public static IPlayingAudioStream? Play(
|
||||
this EntityCoordinates coordinates,
|
||||
AudioStream stream,
|
||||
AudioParams? audioParams = null,
|
||||
AudioSystem? audioSystem = null)
|
||||
/// <inheritdoc />
|
||||
public IPlayingAudioStream? Play(Filter playerFilter, string filename, EntityCoordinates coordinates, AudioParams? audioParams = null)
|
||||
{
|
||||
audioSystem ??= EntitySystem.Get<AudioSystem>();
|
||||
return audioSystem.Play(stream, coordinates, audioParams);
|
||||
return Play(filename, coordinates, audioParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public class ContainerSystem : EntitySystem
|
||||
public class ClientContainerSystem : ContainerSystem
|
||||
{
|
||||
private readonly HashSet<IEntity> _updateQueue = new();
|
||||
|
||||
@@ -91,14 +91,4 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal readonly struct UpdateContainerOcclusionMessage
|
||||
{
|
||||
public UpdateContainerOcclusionMessage(IEntity entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
public IEntity Entity { get; }
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Event raised by a <see cref="ClientOccluderComponent"/> when it needs to be recalculated.
|
||||
/// </summary>
|
||||
internal sealed class OccluderDirtyEvent : EntitySystemMessage
|
||||
internal sealed class OccluderDirtyEvent : EntityEventArgs
|
||||
{
|
||||
public OccluderDirtyEvent(IEntity sender, (GridId grid, Vector2i pos)? lastPosition, SnapGridOffset offset)
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
@@ -42,7 +43,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
overlayManager.RemoveOverlay("EffectSystem");
|
||||
overlayManager.RemoveOverlay(typeof(EffectOverlay));
|
||||
}
|
||||
|
||||
public void CreateEffect(EffectSystemMessage message)
|
||||
@@ -329,7 +330,6 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
private readonly IPlayerManager _playerManager;
|
||||
|
||||
public override bool AlwaysDirty => true;
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
private readonly ShaderInstance _unshadedShader;
|
||||
@@ -337,8 +337,7 @@ namespace Robust.Client.GameObjects
|
||||
private readonly IMapManager _mapManager;
|
||||
private readonly IEntityManager _entityManager;
|
||||
|
||||
public EffectOverlay(EffectSystem owner, IPrototypeManager protoMan, IMapManager mapMan, IPlayerManager playerMan, IEntityManager entityManager) : base(
|
||||
"EffectSystem")
|
||||
public EffectOverlay(EffectSystem owner, IPrototypeManager protoMan, IMapManager mapMan, IPlayerManager playerMan, IEntityManager entityManager)
|
||||
{
|
||||
_owner = owner;
|
||||
_unshadedShader = protoMan.Index<ShaderPrototype>("unshaded").Instance();
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <param name="message">Arguments for this event.</param>
|
||||
/// <param name="replay">if true, current cmd state will not be checked or updated - use this for "replaying" an
|
||||
/// old input that was saved or buffered until further processing could be done</param>
|
||||
public bool HandleInputCommand(ICommonSession session, BoundKeyFunction function, FullInputCmdMessage message, bool replay = false)
|
||||
public bool HandleInputCommand(ICommonSession? session, BoundKeyFunction function, FullInputCmdMessage message, bool replay = false)
|
||||
{
|
||||
#if DEBUG
|
||||
|
||||
@@ -157,7 +157,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Entity system message that is raised when the player changes attached entities.
|
||||
/// </summary>
|
||||
public class PlayerAttachSysMessage : EntitySystemMessage
|
||||
public class PlayerAttachSysMessage : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// New entity the player is attached to.
|
||||
|
||||
@@ -19,19 +19,19 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
[Dependency] private readonly IMapManagerInternal _mapManager = default!;
|
||||
|
||||
private readonly Dictionary<MapId, MapTrees> _mapTrees = new();
|
||||
private readonly Dictionary<MapId, Dictionary<GridId, MapTrees>> _gridTrees = new();
|
||||
|
||||
private readonly List<SpriteComponent> _spriteQueue = new();
|
||||
private readonly List<PointLightComponent> _lightQueue = new();
|
||||
|
||||
internal DynamicTree<SpriteComponent> GetSpriteTreeForMap(MapId map)
|
||||
internal DynamicTree<SpriteComponent> GetSpriteTreeForMap(MapId map, GridId grid)
|
||||
{
|
||||
return _mapTrees[map].SpriteTree;
|
||||
return _gridTrees[map][grid].SpriteTree;
|
||||
}
|
||||
|
||||
internal DynamicTree<PointLightComponent> GetLightTreeForMap(MapId map)
|
||||
internal DynamicTree<PointLightComponent> GetLightTreeForMap(MapId map, GridId grid)
|
||||
{
|
||||
return _mapTrees[map].LightTree;
|
||||
return _gridTrees[map][grid].LightTree;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
@@ -44,6 +44,8 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
_mapManager.MapCreated += MapManagerOnMapCreated;
|
||||
_mapManager.MapDestroyed += MapManagerOnMapDestroyed;
|
||||
_mapManager.OnGridCreated += MapManagerOnGridCreated;
|
||||
_mapManager.OnGridRemoved += MapManagerOnGridRemoved;
|
||||
|
||||
SubscribeLocalEvent<EntMapIdChangedMessage>(EntMapIdChanged);
|
||||
SubscribeLocalEvent<MoveEvent>(EntMoved);
|
||||
@@ -53,18 +55,40 @@ namespace Robust.Client.GameObjects
|
||||
SubscribeLocalEvent<RenderTreeRemoveLightMessage>(RemoveLight);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_mapManager.MapCreated -= MapManagerOnMapCreated;
|
||||
_mapManager.MapDestroyed -= MapManagerOnMapDestroyed;
|
||||
_mapManager.OnGridCreated -= MapManagerOnGridCreated;
|
||||
_mapManager.OnGridRemoved -= MapManagerOnGridRemoved;
|
||||
|
||||
UnsubscribeLocalEvent<EntMapIdChangedMessage>();
|
||||
UnsubscribeLocalEvent<MoveEvent>();
|
||||
UnsubscribeLocalEvent<EntParentChangedMessage>();
|
||||
UnsubscribeLocalEvent<PointLightRadiusChangedMessage>();
|
||||
UnsubscribeLocalEvent<RenderTreeRemoveSpriteMessage>();
|
||||
UnsubscribeLocalEvent<RenderTreeRemoveLightMessage>();
|
||||
}
|
||||
|
||||
// For these next 2 methods (the Remove* ones):
|
||||
// If the Transform is removed BEFORE the Sprite/Light,
|
||||
// then the MapIdChanged code will handle and remove it (because MapId gets set to nullspace).
|
||||
// Otherwise these will still have their past MapId and that's all we need..
|
||||
private void RemoveLight(RenderTreeRemoveLightMessage ev)
|
||||
{
|
||||
_mapTrees[ev.Map].LightTree.Remove(ev.Light);
|
||||
foreach (var gridId in _mapManager.FindGridIdsIntersecting(ev.Map, MapTrees.LightAabbFunc(ev.Light), true))
|
||||
{
|
||||
_gridTrees[ev.Map][gridId].LightTree.Remove(ev.Light);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveSprite(RenderTreeRemoveSpriteMessage ev)
|
||||
{
|
||||
_mapTrees[ev.Map].SpriteTree.Remove(ev.Sprite);
|
||||
foreach (var gridId in _mapManager.FindGridIdsIntersecting(ev.Map, MapTrees.SpriteAabbFunc(ev.Sprite), true))
|
||||
{
|
||||
_gridTrees[ev.Map][gridId].SpriteTree.Remove(ev.Sprite);
|
||||
}
|
||||
}
|
||||
|
||||
private void PointLightRadiusChanged(PointLightRadiusChangedMessage ev)
|
||||
@@ -119,27 +143,69 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
// Nullspace is a valid map ID for stuff to have but we also aren't gonna bother indexing it.
|
||||
// So that's why there's a GetValueOrDefault.
|
||||
var oldMapTrees = _mapTrees.GetValueOrDefault(ev.OldMapId);
|
||||
var newMapTrees = _mapTrees.GetValueOrDefault(ev.Entity.Transform.MapID);
|
||||
var oldMapTrees = _gridTrees.GetValueOrDefault(ev.OldMapId);
|
||||
|
||||
// TODO: MMMM probably a better way to do this.
|
||||
if (ev.Entity.TryGetComponent(out SpriteComponent? sprite))
|
||||
{
|
||||
oldMapTrees?.SpriteTree.Remove(sprite);
|
||||
if (oldMapTrees != null)
|
||||
{
|
||||
foreach (var (_, gridTree) in oldMapTrees)
|
||||
{
|
||||
gridTree.SpriteTree.Remove(sprite);
|
||||
}
|
||||
}
|
||||
|
||||
newMapTrees?.SpriteTree.AddOrUpdate(sprite);
|
||||
var bounds = MapTrees.SpriteAabbFunc(sprite);
|
||||
|
||||
foreach (var gridId in _mapManager.FindGridIdsIntersecting(ev.Entity.Transform.MapID, bounds, true))
|
||||
{
|
||||
Box2 gridBounds;
|
||||
|
||||
if (gridId == GridId.Invalid)
|
||||
{
|
||||
gridBounds = bounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
gridBounds = bounds.Translated(-_mapManager.GetGrid(gridId).WorldPosition);
|
||||
}
|
||||
_gridTrees[ev.Entity.Transform.MapID][gridId].SpriteTree.AddOrUpdate(sprite, gridBounds);
|
||||
}
|
||||
}
|
||||
|
||||
if (ev.Entity.TryGetComponent(out PointLightComponent? light))
|
||||
{
|
||||
oldMapTrees?.LightTree.Remove(light);
|
||||
if (oldMapTrees != null)
|
||||
{
|
||||
foreach (var (_, gridTree) in oldMapTrees)
|
||||
{
|
||||
gridTree.LightTree.Remove(light);
|
||||
}
|
||||
}
|
||||
|
||||
newMapTrees?.LightTree.AddOrUpdate(light);
|
||||
var bounds = MapTrees.LightAabbFunc(light);
|
||||
|
||||
foreach (var gridId in _mapManager.FindGridIdsIntersecting(ev.Entity.Transform.MapID, bounds, true))
|
||||
{
|
||||
Box2 gridBounds;
|
||||
|
||||
if (gridId == GridId.Invalid)
|
||||
{
|
||||
gridBounds = bounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
gridBounds = bounds.Translated(-_mapManager.GetGrid(gridId).WorldPosition);
|
||||
}
|
||||
_gridTrees[ev.Entity.Transform.MapID][gridId].LightTree.AddOrUpdate(light, gridBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MapManagerOnMapDestroyed(object? sender, MapEventArgs e)
|
||||
{
|
||||
_mapTrees.Remove(e.Map);
|
||||
_gridTrees.Remove(e.Map);
|
||||
}
|
||||
|
||||
private void MapManagerOnMapCreated(object? sender, MapEventArgs e)
|
||||
@@ -149,36 +215,59 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
_mapTrees.Add(e.Map, new MapTrees());
|
||||
_gridTrees.Add(e.Map, new Dictionary<GridId, MapTrees>
|
||||
{
|
||||
{GridId.Invalid, new MapTrees()}
|
||||
});
|
||||
}
|
||||
|
||||
private void MapManagerOnGridCreated(MapId mapId, GridId gridId)
|
||||
{
|
||||
_gridTrees[mapId].Add(gridId, new MapTrees());
|
||||
}
|
||||
|
||||
private void MapManagerOnGridRemoved(MapId mapId, GridId gridId)
|
||||
{
|
||||
_gridTrees[mapId].Remove(gridId);
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
foreach (var queuedUpdateSprite in _spriteQueue)
|
||||
{
|
||||
var transform = queuedUpdateSprite.Owner.Transform;
|
||||
var map = transform.MapID;
|
||||
var map = queuedUpdateSprite.Owner.Transform.MapID;
|
||||
if (map == MapId.Nullspace)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var updateMapTree = _mapTrees[map].SpriteTree;
|
||||
|
||||
updateMapTree.AddOrUpdate(queuedUpdateSprite);
|
||||
var mapTree = _gridTrees[map];
|
||||
|
||||
foreach (var gridId in _mapManager.FindGridIdsIntersecting(map,
|
||||
MapTrees.SpriteAabbFunc(queuedUpdateSprite), true))
|
||||
{
|
||||
mapTree[gridId].SpriteTree.AddOrUpdate(queuedUpdateSprite);
|
||||
}
|
||||
|
||||
queuedUpdateSprite.TreeUpdateQueued = false;
|
||||
}
|
||||
|
||||
foreach (var queuedUpdateLight in _lightQueue)
|
||||
{
|
||||
var transform = queuedUpdateLight.Owner.Transform;
|
||||
var map = transform.MapID;
|
||||
var map = queuedUpdateLight.Owner.Transform.MapID;
|
||||
if (map == MapId.Nullspace)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var updateMapTree = _mapTrees[map].LightTree;
|
||||
|
||||
updateMapTree.AddOrUpdate(queuedUpdateLight);
|
||||
var mapTree = _gridTrees[map];
|
||||
|
||||
foreach (var gridId in _mapManager.FindGridIdsIntersecting(map,
|
||||
MapTrees.LightAabbFunc(queuedUpdateLight), true))
|
||||
{
|
||||
mapTree[gridId].LightTree.AddOrUpdate(queuedUpdateLight);
|
||||
}
|
||||
|
||||
queuedUpdateLight.TreeUpdateQueued = false;
|
||||
}
|
||||
|
||||
@@ -197,14 +286,14 @@ namespace Robust.Client.GameObjects
|
||||
LightTree = new DynamicTree<PointLightComponent>(LightAabbFunc);
|
||||
}
|
||||
|
||||
private static Box2 SpriteAabbFunc(in SpriteComponent value)
|
||||
internal static Box2 SpriteAabbFunc(in SpriteComponent value)
|
||||
{
|
||||
var worldPos = value.Owner.Transform.WorldPosition;
|
||||
|
||||
return new Box2(worldPos, worldPos);
|
||||
}
|
||||
|
||||
private static Box2 LightAabbFunc(in PointLightComponent value)
|
||||
internal static Box2 LightAabbFunc(in PointLightComponent value)
|
||||
{
|
||||
var worldPos = value.Owner.Transform.WorldPosition;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
@@ -13,6 +14,7 @@ namespace Robust.Client.GameObjects
|
||||
public class SpriteSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void FrameUpdate(float frameTime)
|
||||
@@ -29,18 +31,32 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
var mapTree = renderTreeSystem.GetSpriteTreeForMap(currentMap);
|
||||
|
||||
mapTree.QueryAabb(ref frameTime, (ref float state, in SpriteComponent value) =>
|
||||
foreach (var gridId in _mapManager.FindGridIdsIntersecting(currentMap, pvsBounds, true))
|
||||
{
|
||||
if (value.IsInert)
|
||||
Box2 gridBounds;
|
||||
|
||||
if (gridId == GridId.Invalid)
|
||||
{
|
||||
return true;
|
||||
gridBounds = pvsBounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
gridBounds = pvsBounds.Translated(-_mapManager.GetGrid(gridId).WorldPosition);
|
||||
}
|
||||
|
||||
value.FrameUpdate(state);
|
||||
return true;
|
||||
}, pvsBounds, approx: true);
|
||||
var mapTree = renderTreeSystem.GetSpriteTreeForMap(currentMap, gridId);
|
||||
|
||||
mapTree.QueryAabb(ref frameTime, (ref float state, in SpriteComponent value) =>
|
||||
{
|
||||
if (value.IsInert)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
value.FrameUpdate(state);
|
||||
return true;
|
||||
}, gridBounds, approx: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public class VelocityDebugSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
internal bool Enabled { get; set; }
|
||||
|
||||
private Label _label = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_label = new Label();
|
||||
IoCManager.Resolve<IUserInterfaceManager>().StateRoot.AddChild(_label);
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
if (!Enabled)
|
||||
{
|
||||
_label.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var player = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
|
||||
if (player == null || !player.TryGetComponent(out PhysicsComponent? body))
|
||||
{
|
||||
_label.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var screenPos = _eyeManager.WorldToScreen(player.Transform.WorldPosition);
|
||||
LayoutContainer.SetPosition(_label, screenPos + new Vector2(0, 50));
|
||||
_label.Visible = true;
|
||||
|
||||
_label.Text = $"Speed: {body.LinearVelocity.Length}\nLinear: {body.LinearVelocity.X:0.00}, {body.LinearVelocity.Y:0.00}\nAngular:{body.AngularVelocity}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Shared.GameStates;
|
||||
@@ -26,7 +27,7 @@ namespace Robust.Client.GameStates
|
||||
private uint _nextInputCmdSeq = 1;
|
||||
private readonly Queue<FullInputCmdMessage> _pendingInputs = new();
|
||||
|
||||
private readonly Queue<(uint sequence, GameTick sourceTick, EntitySystemMessage msg, object sessionMsg)>
|
||||
private readonly Queue<(uint sequence, GameTick sourceTick, EntityEventArgs msg, object sessionMsg)>
|
||||
_pendingSystemMessages
|
||||
= new();
|
||||
|
||||
@@ -125,7 +126,7 @@ namespace Robust.Client.GameStates
|
||||
_nextInputCmdSeq++;
|
||||
}
|
||||
|
||||
public uint SystemMessageDispatched<T>(T message) where T : EntitySystemMessage
|
||||
public uint SystemMessageDispatched<T>(T message) where T : EntityEventArgs
|
||||
{
|
||||
if (!Predicting)
|
||||
{
|
||||
@@ -241,65 +242,67 @@ namespace Robust.Client.GameStates
|
||||
|
||||
if (!Predicting) return;
|
||||
|
||||
using var _ = _timing.StartPastPredictionArea();
|
||||
|
||||
|
||||
if (_pendingInputs.Count > 0)
|
||||
using(var _ = _timing.StartPastPredictionArea())
|
||||
{
|
||||
Logger.DebugS(CVars.NetPredict.Name, "CL> Predicted:");
|
||||
if (_pendingInputs.Count > 0)
|
||||
{
|
||||
Logger.DebugS(CVars.NetPredict.Name, "CL> Predicted:");
|
||||
}
|
||||
|
||||
var pendingInputEnumerator = _pendingInputs.GetEnumerator();
|
||||
var pendingMessagesEnumerator = _pendingSystemMessages.GetEnumerator();
|
||||
var hasPendingInput = pendingInputEnumerator.MoveNext();
|
||||
var hasPendingMessage = pendingMessagesEnumerator.MoveNext();
|
||||
|
||||
var ping = _network.ServerChannel!.Ping / 1000f + PredictLagBias; // seconds.
|
||||
var targetTick = _timing.CurTick.Value + _processor.TargetBufferSize +
|
||||
(int) Math.Ceiling(_timing.TickRate * ping) + PredictTickBias;
|
||||
|
||||
// Logger.DebugS("net.predict", $"Predicting from {_lastProcessedTick} to {targetTick}");
|
||||
|
||||
for (var t = _lastProcessedTick.Value + 1; t <= targetTick; t++)
|
||||
{
|
||||
var tick = new GameTick(t);
|
||||
_timing.CurTick = tick;
|
||||
|
||||
while (hasPendingInput && pendingInputEnumerator.Current.Tick <= tick)
|
||||
{
|
||||
var inputCmd = pendingInputEnumerator.Current;
|
||||
|
||||
_inputManager.NetworkBindMap.TryGetKeyFunction(inputCmd.InputFunctionId, out var boundFunc);
|
||||
|
||||
Logger.DebugS(CVars.NetPredict.Name,
|
||||
$" seq={inputCmd.InputSequence}, sub={inputCmd.SubTick}, dTick={tick}, func={boundFunc.FunctionName}, " +
|
||||
$"state={inputCmd.State}");
|
||||
|
||||
|
||||
input.PredictInputCommand(inputCmd);
|
||||
|
||||
hasPendingInput = pendingInputEnumerator.MoveNext();
|
||||
}
|
||||
|
||||
while (hasPendingMessage && pendingMessagesEnumerator.Current.sourceTick <= tick)
|
||||
{
|
||||
var msg = pendingMessagesEnumerator.Current.msg;
|
||||
|
||||
_entities.EventBus.RaiseEvent(EventSource.Local, msg);
|
||||
_entities.EventBus.RaiseEvent(EventSource.Local, pendingMessagesEnumerator.Current.sessionMsg);
|
||||
|
||||
hasPendingMessage = pendingMessagesEnumerator.MoveNext();
|
||||
}
|
||||
|
||||
if (t != targetTick)
|
||||
{
|
||||
// Don't run EntitySystemManager.TickUpdate if this is the target tick,
|
||||
// because the rest of the main loop will call into it with the target tick later,
|
||||
// and it won't be a past prediction.
|
||||
_entitySystemManager.TickUpdate((float) _timing.TickPeriod.TotalSeconds);
|
||||
((IBroadcastEventBusInternal) _entities.EventBus).ProcessEventQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var pendingInputEnumerator = _pendingInputs.GetEnumerator();
|
||||
var pendingMessagesEnumerator = _pendingSystemMessages.GetEnumerator();
|
||||
var hasPendingInput = pendingInputEnumerator.MoveNext();
|
||||
var hasPendingMessage = pendingMessagesEnumerator.MoveNext();
|
||||
|
||||
var ping = _network.ServerChannel!.Ping / 1000f + PredictLagBias; // seconds.
|
||||
var targetTick = _timing.CurTick.Value + _processor.TargetBufferSize +
|
||||
(int) Math.Ceiling(_timing.TickRate * ping) + PredictTickBias;
|
||||
|
||||
// Logger.DebugS("net.predict", $"Predicting from {_lastProcessedTick} to {targetTick}");
|
||||
|
||||
for (var t = _lastProcessedTick.Value + 1; t <= targetTick; t++)
|
||||
{
|
||||
var tick = new GameTick(t);
|
||||
_timing.CurTick = tick;
|
||||
|
||||
while (hasPendingInput && pendingInputEnumerator.Current.Tick <= tick)
|
||||
{
|
||||
var inputCmd = pendingInputEnumerator.Current;
|
||||
|
||||
_inputManager.NetworkBindMap.TryGetKeyFunction(inputCmd.InputFunctionId, out var boundFunc);
|
||||
|
||||
Logger.DebugS(CVars.NetPredict.Name,
|
||||
$" seq={inputCmd.InputSequence}, sub={inputCmd.SubTick}, dTick={tick}, func={boundFunc.FunctionName}, " +
|
||||
$"state={inputCmd.State}");
|
||||
|
||||
|
||||
input.PredictInputCommand(inputCmd);
|
||||
|
||||
hasPendingInput = pendingInputEnumerator.MoveNext();
|
||||
}
|
||||
|
||||
while (hasPendingMessage && pendingMessagesEnumerator.Current.sourceTick <= tick)
|
||||
{
|
||||
var msg = pendingMessagesEnumerator.Current.msg;
|
||||
|
||||
_entities.EventBus.RaiseEvent(EventSource.Local, msg);
|
||||
_entities.EventBus.RaiseEvent(EventSource.Local, pendingMessagesEnumerator.Current.sessionMsg);
|
||||
|
||||
hasPendingMessage = pendingMessagesEnumerator.MoveNext();
|
||||
}
|
||||
|
||||
if (t != targetTick)
|
||||
{
|
||||
// Don't run EntitySystemManager.Update if this is the target tick,
|
||||
// because the rest of the main loop will call into it with the target tick later,
|
||||
// and it won't be a past prediction.
|
||||
_entitySystemManager.Update((float) _timing.TickPeriod.TotalSeconds);
|
||||
((IEntityEventBus) _entities.EventBus).ProcessEventQueue();
|
||||
}
|
||||
}
|
||||
_entities.TickUpdate((float) _timing.TickPeriod.TotalSeconds);
|
||||
}
|
||||
|
||||
private void ResetPredictedEntities(GameTick curTick)
|
||||
@@ -352,7 +355,10 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var component in _componentManager.GetNetComponents(createdEntity))
|
||||
{
|
||||
var state = component.GetComponentState();
|
||||
Debug.Assert(_players.LocalPlayer != null, "_players.LocalPlayer != null");
|
||||
|
||||
var player = _players.LocalPlayer.Session;
|
||||
var state = component.GetComponentState(player);
|
||||
|
||||
if (state.GetType() == typeof(ComponentState))
|
||||
{
|
||||
|
||||
@@ -70,6 +70,6 @@ namespace Robust.Client.GameStates
|
||||
/// <param name="message">Message being dispatched.</param>
|
||||
void InputCommandDispatched(FullInputCmdMessage message);
|
||||
|
||||
uint SystemMessageDispatched<T>(T message) where T : EntitySystemMessage;
|
||||
uint SystemMessageDispatched<T>(T message) where T : EntityEventArgs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameStates
|
||||
{
|
||||
@@ -27,13 +30,13 @@ namespace Robust.Client.GameStates
|
||||
private const int TrafficHistorySize = 64; // Size of the traffic history bar in game ticks.
|
||||
|
||||
/// <inheritdoc />
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace | OverlaySpace.WorldSpace;
|
||||
|
||||
private readonly Font _font;
|
||||
private readonly int _lineHeight;
|
||||
private readonly List<NetEntity> _netEnts = new();
|
||||
|
||||
public NetEntityOverlay() : base(nameof(NetEntityOverlay))
|
||||
public NetEntityOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
var cache = IoCManager.Resolve<IResourceCache>();
|
||||
@@ -42,12 +45,12 @@ namespace Robust.Client.GameStates
|
||||
|
||||
_gameStateManager.GameStateApplied += HandleGameStateApplied;
|
||||
}
|
||||
|
||||
|
||||
private void HandleGameStateApplied(GameStateAppliedArgs args)
|
||||
{
|
||||
if(_gameTiming.InPrediction) // we only care about real server states.
|
||||
return;
|
||||
|
||||
|
||||
// Shift traffic history down one
|
||||
for (var i = 0; i < _netEnts.Count; i++)
|
||||
{
|
||||
@@ -74,7 +77,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
if (netEnt.Id != entityState.Uid)
|
||||
continue;
|
||||
|
||||
|
||||
//TODO: calculate size of state and record it here.
|
||||
netEnt.Traffic[^1] = 1;
|
||||
netEnt.LastUpdate = gameState.ToSequence;
|
||||
@@ -94,15 +97,15 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
bool pvsEnabled = _configurationManager.GetCVar<bool>("net.pvs");
|
||||
float pvsSize = _configurationManager.GetCVar<float>("net.maxupdaterange");
|
||||
float pvsRange = _configurationManager.GetCVar<float>("net.maxupdaterange");
|
||||
var pvsCenter = _eyeManager.CurrentEye.Position;
|
||||
Box2 pvsBox = Box2.CenteredAround(pvsCenter.Position, new Vector2(pvsSize*2, pvsSize*2));
|
||||
Box2 pvsBox = Box2.CenteredAround(pvsCenter.Position, new Vector2(pvsRange*2, pvsRange*2));
|
||||
|
||||
int timeout = _gameTiming.TickRate * 3;
|
||||
for (int i = 0; i < _netEnts.Count; i++)
|
||||
{
|
||||
var netEnt = _netEnts[i];
|
||||
|
||||
|
||||
if(_entityManager.EntityExists(netEnt.Id))
|
||||
{
|
||||
//TODO: Whoever is working on PVS remake, change the InPVS detection.
|
||||
@@ -123,22 +126,58 @@ namespace Robust.Client.GameStates
|
||||
_netEnts[i] = netEnt; // copy struct back
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
|
||||
{
|
||||
if (!_netManager.IsConnected)
|
||||
return;
|
||||
|
||||
switch (currentSpace)
|
||||
{
|
||||
case OverlaySpace.ScreenSpace:
|
||||
DrawScreen(handle);
|
||||
break;
|
||||
case OverlaySpace.WorldSpace:
|
||||
DrawWorld(handle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawWorld(DrawingHandleBase handle)
|
||||
{
|
||||
bool pvsEnabled = _configurationManager.GetCVar<bool>("net.pvs");
|
||||
|
||||
if(!pvsEnabled)
|
||||
return;
|
||||
|
||||
float pvsSize = _configurationManager.GetCVar<float>("net.maxupdaterange");
|
||||
var pvsCenter = _eyeManager.CurrentEye.Position;
|
||||
Box2 pvsBox = Box2.CenteredAround(pvsCenter.Position, new Vector2(pvsSize, pvsSize));
|
||||
|
||||
var worldHandle = (DrawingHandleWorld)handle;
|
||||
|
||||
worldHandle.DrawRect(pvsBox, Color.Red, false);
|
||||
}
|
||||
|
||||
private void DrawScreen(DrawingHandleBase handle)
|
||||
{
|
||||
// remember, 0,0 is top left of ui with +X right and +Y down
|
||||
var screenHandle = (DrawingHandleScreen)handle;
|
||||
|
||||
var screenHandle = (DrawingHandleScreen) handle;
|
||||
|
||||
for (int i = 0; i < _netEnts.Count; i++)
|
||||
{
|
||||
var netEnt = _netEnts[i];
|
||||
|
||||
if (!_entityManager.TryGetEntity(netEnt.Id, out var ent))
|
||||
{
|
||||
_netEnts.RemoveSwap(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
var xPos = 100;
|
||||
var yPos = 10 + _lineHeight * i;
|
||||
var name = $"({netEnt.Id}) {_entityManager.GetEntity(netEnt.Id).Prototype?.ID}";
|
||||
var name = $"({netEnt.Id}) {ent.Prototype?.ID}";
|
||||
var color = CalcTextColor(ref netEnt);
|
||||
DrawString(screenHandle, _font, new Vector2(xPos + (TrafficHistorySize + 4), yPos), name, color);
|
||||
DrawTrafficBox(screenHandle, ref netEnt, xPos, yPos);
|
||||
@@ -179,20 +218,19 @@ namespace Robust.Client.GameStates
|
||||
return Color.Green; // Entity in PVS, but not updated recently.
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
protected override void DisposeBehavior()
|
||||
{
|
||||
_gameStateManager.GameStateApplied -= HandleGameStateApplied;
|
||||
|
||||
base.Dispose(disposing);
|
||||
base.DisposeBehavior();
|
||||
}
|
||||
|
||||
private static void DrawString(DrawingHandleScreen handle, Font font, Vector2 pos, string str, Color textColor)
|
||||
{
|
||||
var baseLine = new Vector2(pos.X, font.GetAscent(1) + pos.Y);
|
||||
|
||||
foreach (var chr in str)
|
||||
foreach (var rune in str.EnumerateRunes())
|
||||
{
|
||||
var advance = font.DrawChar(handle, chr, baseLine, 1, textColor);
|
||||
var advance = font.DrawChar(handle, rune, baseLine, 1, textColor);
|
||||
baseLine += new Vector2(advance, 0);
|
||||
}
|
||||
}
|
||||
@@ -225,7 +263,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteError("Invalid argument amount. Expected 2 arguments.");
|
||||
shell.WriteError("Invalid argument amount. Expected 1 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -238,14 +276,14 @@ namespace Robust.Client.GameStates
|
||||
var bValue = iValue > 0;
|
||||
var overlayMan = IoCManager.Resolve<IOverlayManager>();
|
||||
|
||||
if(bValue && !overlayMan.HasOverlay(nameof(NetEntityOverlay)))
|
||||
if(bValue && !overlayMan.HasOverlay(typeof(NetEntityOverlay)))
|
||||
{
|
||||
overlayMan.AddOverlay(new NetEntityOverlay());
|
||||
shell.WriteLine("Enabled network entity report overlay.");
|
||||
}
|
||||
else if(!bValue && overlayMan.HasOverlay(nameof(NetEntityOverlay)))
|
||||
else if(!bValue && overlayMan.HasOverlay(typeof(NetEntityOverlay)))
|
||||
{
|
||||
overlayMan.RemoveOverlay(nameof(NetEntityOverlay));
|
||||
overlayMan.RemoveOverlay(typeof(NetEntityOverlay));
|
||||
shell.WriteLine("Disabled network entity report overlay.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
@@ -34,7 +38,11 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private readonly List<(GameTick Tick, int Payload, int lag, int interp)> _history = new(HistorySize+10);
|
||||
|
||||
public NetGraphOverlay() : base(nameof(NetGraphOverlay))
|
||||
private int _totalHistoryPayload; // sum of all data point sizes in bytes
|
||||
|
||||
public EntityUid WatchEntId { get; set; }
|
||||
|
||||
public NetGraphOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
var cache = IoCManager.Resolve<IResourceCache>();
|
||||
@@ -58,7 +66,73 @@ namespace Robust.Client.GameStates
|
||||
// calc interp info
|
||||
var interpBuff = _gameStateManager.CurrentBufferSize - _gameStateManager.MinBufferSize;
|
||||
|
||||
_totalHistoryPayload += sz;
|
||||
_history.Add((toSeq, sz, lag, interpBuff));
|
||||
|
||||
// not watching an ent
|
||||
if(!WatchEntId.IsValid() || WatchEntId.IsClientSide())
|
||||
return;
|
||||
|
||||
string? entStateString = null;
|
||||
string? entDelString = null;
|
||||
var conShell = IoCManager.Resolve<IConsoleHost>().LocalShell;
|
||||
|
||||
var entStates = args.AppliedState.EntityStates;
|
||||
if (entStates is not null)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var entState in entStates)
|
||||
{
|
||||
if (entState.Uid == WatchEntId)
|
||||
{
|
||||
if(entState.ComponentChanges is not null)
|
||||
{
|
||||
sb.Append($"\n Changes:");
|
||||
foreach (var compChange in entState.ComponentChanges)
|
||||
{
|
||||
var del = compChange.Deleted ? 'D' : 'C';
|
||||
sb.Append($"\n [{del}]{compChange.NetID}:{compChange.ComponentName}");
|
||||
}
|
||||
}
|
||||
|
||||
if (entState.ComponentStates is not null)
|
||||
{
|
||||
sb.Append($"\n States:");
|
||||
foreach (var compState in entState.ComponentStates)
|
||||
{
|
||||
sb.Append($"\n {compState.NetID}:{compState.GetType().Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
entStateString = sb.ToString();
|
||||
}
|
||||
|
||||
var entDeletes = args.AppliedState.EntityDeletions;
|
||||
if (entDeletes is not null)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var entDelete in entDeletes)
|
||||
{
|
||||
if (entDelete == WatchEntId)
|
||||
{
|
||||
entDelString = "\n Deleted";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(entStateString) || !string.IsNullOrWhiteSpace(entDelString))
|
||||
{
|
||||
var fullString = $"watchEnt: from={args.AppliedState.FromSequence}, to={args.AppliedState.ToSequence}, eid={WatchEntId}";
|
||||
if (!string.IsNullOrWhiteSpace(entStateString))
|
||||
fullString += entStateString;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(entDelString))
|
||||
fullString += entDelString;
|
||||
|
||||
conShell.WriteLine(fullString + "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -67,10 +141,16 @@ namespace Robust.Client.GameStates
|
||||
base.FrameUpdate(args);
|
||||
|
||||
var over = _history.Count - HistorySize;
|
||||
if (over > 0)
|
||||
if (over <= 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < over; i++)
|
||||
{
|
||||
_history.RemoveRange(0, over);
|
||||
var point = _history[i];
|
||||
_totalHistoryPayload -= point.Payload;
|
||||
}
|
||||
|
||||
_history.RemoveRange(0, over);
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
|
||||
@@ -80,6 +160,7 @@ namespace Robust.Client.GameStates
|
||||
var leftMargin = 300;
|
||||
var width = HistorySize;
|
||||
var height = 500;
|
||||
var drawSizeThreshold = Math.Min(_totalHistoryPayload / HistorySize, 300);
|
||||
|
||||
// bottom payload line
|
||||
handle.DrawLine(new Vector2(leftMargin, height), new Vector2(leftMargin + width, height), Color.DarkGray.WithAlpha(0.8f));
|
||||
@@ -99,6 +180,12 @@ namespace Robust.Client.GameStates
|
||||
var yoff = height - state.Payload / BytesPerPixel;
|
||||
handle.DrawLine(new Vector2(xOff, height), new Vector2(xOff, yoff), Color.LightGreen.WithAlpha(0.8f));
|
||||
|
||||
// Draw size if above average
|
||||
if (drawSizeThreshold * 1.5 < state.Payload)
|
||||
{
|
||||
DrawString((DrawingHandleScreen) handle, _font, new Vector2(xOff, yoff - _font.GetLineHeight(1)), state.Payload.ToString());
|
||||
}
|
||||
|
||||
// second tick marks
|
||||
if (state.Tick.Value % _gameTiming.TickRate == 0)
|
||||
{
|
||||
@@ -123,6 +210,10 @@ namespace Robust.Client.GameStates
|
||||
handle.DrawLine(new Vector2(xOff, height + LowerGraphOffset), new Vector2(xOff, height + LowerGraphOffset + state.interp * 6), interpColor.WithAlpha(0.8f));
|
||||
}
|
||||
|
||||
// average payload line
|
||||
var avgyoff = height - drawSizeThreshold / BytesPerPixel;
|
||||
handle.DrawLine(new Vector2(leftMargin, avgyoff), new Vector2(leftMargin + width, avgyoff), Color.DarkGray.WithAlpha(0.8f));
|
||||
|
||||
// top payload warning line
|
||||
var warnYoff = height - _warningPayloadSize / BytesPerPixel;
|
||||
handle.DrawLine(new Vector2(leftMargin, warnYoff), new Vector2(leftMargin + width, warnYoff), Color.DarkGray.WithAlpha(0.8f));
|
||||
@@ -142,20 +233,20 @@ namespace Robust.Client.GameStates
|
||||
DrawString((DrawingHandleScreen)handle, _font, new Vector2(leftMargin, height + LowerGraphOffset), $"{_gameStateManager.CurrentBufferSize.ToString()} states");
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
protected override void DisposeBehavior()
|
||||
{
|
||||
_gameStateManager.GameStateApplied -= HandleGameStateApplied;
|
||||
|
||||
base.Dispose(disposing);
|
||||
base.DisposeBehavior();
|
||||
}
|
||||
|
||||
private void DrawString(DrawingHandleScreen handle, Font font, Vector2 pos, string str)
|
||||
{
|
||||
var baseLine = new Vector2(pos.X, font.GetAscent(1) + pos.Y);
|
||||
|
||||
foreach (var chr in str)
|
||||
foreach (var rune in str.EnumerateRunes())
|
||||
{
|
||||
var advance = font.DrawChar(handle, chr, baseLine, 1, Color.White);
|
||||
var advance = font.DrawChar(handle, rune, baseLine, 1, Color.White);
|
||||
baseLine += new Vector2(advance, 0);
|
||||
}
|
||||
}
|
||||
@@ -183,17 +274,48 @@ namespace Robust.Client.GameStates
|
||||
var bValue = iValue > 0;
|
||||
var overlayMan = IoCManager.Resolve<IOverlayManager>();
|
||||
|
||||
if(bValue && !overlayMan.HasOverlay(nameof(NetGraphOverlay)))
|
||||
if(bValue && !overlayMan.HasOverlay(typeof(NetGraphOverlay)))
|
||||
{
|
||||
overlayMan.AddOverlay(new NetGraphOverlay());
|
||||
shell.WriteLine("Enabled network overlay.");
|
||||
}
|
||||
else if(overlayMan.HasOverlay(nameof(NetGraphOverlay)))
|
||||
else if(overlayMan.HasOverlay(typeof(NetGraphOverlay)))
|
||||
{
|
||||
overlayMan.RemoveOverlay(nameof(NetGraphOverlay));
|
||||
overlayMan.RemoveOverlay(typeof(NetGraphOverlay));
|
||||
shell.WriteLine("Disabled network overlay.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class NetWatchEntCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "net_watchent";
|
||||
public string Help => "net_watchent <0|EntityUid>";
|
||||
public string Description => "Dumps all network updates for an EntityId to the console.";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteError("Invalid argument amount. Expected 1 argument.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var eValue))
|
||||
{
|
||||
shell.WriteError("Invalid argument: Needs to be 0 or an entityId.");
|
||||
return;
|
||||
}
|
||||
|
||||
var overlayMan = IoCManager.Resolve<IOverlayManager>();
|
||||
|
||||
if (overlayMan.HasOverlay(typeof(NetGraphOverlay)))
|
||||
{
|
||||
var netOverlay = overlayMan.GetOverlay<NetGraphOverlay>();
|
||||
|
||||
netOverlay.WatchEntId = eValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -5,6 +6,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.GameStates
|
||||
@@ -18,7 +20,7 @@ namespace Robust.Client.GameStates
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
private readonly ShaderInstance _shader;
|
||||
|
||||
public NetInterpOverlay() : base(nameof(NetInterpOverlay))
|
||||
public NetInterpOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_shader = _prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
@@ -29,7 +31,7 @@ namespace Robust.Client.GameStates
|
||||
handle.UseShader(_shader);
|
||||
var worldHandle = (DrawingHandleWorld) handle;
|
||||
var viewport = _eyeManager.GetWorldViewport();
|
||||
foreach (var boundingBox in _componentManager.EntityQuery<IPhysicsComponent>(true))
|
||||
foreach (var boundingBox in _componentManager.EntityQuery<IPhysBody>(true))
|
||||
{
|
||||
// all entities have a TransformComponent
|
||||
var transform = ((IComponent)boundingBox).Owner.Transform;
|
||||
@@ -42,7 +44,7 @@ namespace Robust.Client.GameStates
|
||||
if(transform.LerpDestination == null)
|
||||
continue;
|
||||
|
||||
var aabb = ((IPhysBody)boundingBox).AABB;
|
||||
var aabb = boundingBox.GetWorldAABB();
|
||||
|
||||
// if not on screen, or too small, continue
|
||||
if (!aabb.Translated(transform.WorldPosition).Intersects(viewport) || aabb.IsEmpty())
|
||||
@@ -85,14 +87,14 @@ namespace Robust.Client.GameStates
|
||||
var bValue = iValue > 0;
|
||||
var overlayMan = IoCManager.Resolve<IOverlayManager>();
|
||||
|
||||
if (bValue && !overlayMan.HasOverlay(nameof(NetInterpOverlay)))
|
||||
if (bValue && !overlayMan.HasOverlay<NetInterpOverlay>())
|
||||
{
|
||||
overlayMan.AddOverlay(new NetInterpOverlay());
|
||||
shell.WriteLine("Enabled network interp overlay.");
|
||||
}
|
||||
else if (overlayMan.HasOverlay(nameof(NetInterpOverlay)))
|
||||
else if (overlayMan.HasOverlay<NetInterpOverlay>())
|
||||
{
|
||||
overlayMan.RemoveOverlay(nameof(NetInterpOverlay));
|
||||
overlayMan.RemoveOverlay<NetInterpOverlay>();
|
||||
shell.WriteLine("Disabled network interp overlay.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JetBrains.Annotations;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -36,6 +36,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
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);
|
||||
|
||||
@@ -50,7 +53,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
IsEfxSupported = HasAlDeviceExtension("ALC_EXT_EFX");
|
||||
|
||||
_configurationManager.OnValueChanged(CVars.AudioMasterVolume, SetMasterVolume, true);
|
||||
ConfigurationManager.OnValueChanged(CVars.AudioMasterVolume, SetMasterVolume, true);
|
||||
}
|
||||
|
||||
private void _audioCreateContext()
|
||||
@@ -78,7 +81,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private void _audioOpenDevice()
|
||||
{
|
||||
var preferredDevice = _configurationManager.GetCVar(CVars.AudioDevice);
|
||||
var preferredDevice = ConfigurationManager.GetCVar(CVars.AudioDevice);
|
||||
|
||||
// Open device.
|
||||
if (!string.IsNullOrEmpty(preferredDevice))
|
||||
@@ -182,7 +185,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
public void SetMasterVolume(float newVolume)
|
||||
{
|
||||
AL.Listener(ALListenerf.Gain, newVolume);
|
||||
AL.Listener(ALListenerf.Gain, _baseGain * newVolume);
|
||||
}
|
||||
|
||||
public IClydeAudioSource CreateAudioSource(AudioStream stream)
|
||||
@@ -479,7 +482,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
var (x, y) = position;
|
||||
|
||||
if (!ValidatePosition(x, y))
|
||||
if (!AreFinite(x, y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -500,7 +503,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ValidatePosition(float x, float y)
|
||||
private static bool AreFinite(float x, float y)
|
||||
{
|
||||
if (float.IsFinite(x) && float.IsFinite(y))
|
||||
{
|
||||
@@ -510,6 +513,22 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetVelocity(Vector2 velocity)
|
||||
{
|
||||
_checkDisposed();
|
||||
|
||||
var (x, y) = velocity;
|
||||
|
||||
if (!AreFinite(x, y))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AL.Source(SourceHandle, ALSource3f.Velocity, x, y, 0);
|
||||
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public void SetPitch(float pitch)
|
||||
{
|
||||
_checkDisposed();
|
||||
@@ -664,7 +683,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
|
||||
private void SetOcclusionEfx(float gain, float cutoff)
|
||||
{
|
||||
if (FilterHandle == 0)
|
||||
@@ -691,7 +709,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
var (x, y) = position;
|
||||
|
||||
if (!ValidatePosition(x, y))
|
||||
if (!AreFinite(x, y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -703,7 +721,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ValidatePosition(float x, float y)
|
||||
private static bool AreFinite(float x, float y)
|
||||
{
|
||||
if (float.IsFinite(x) && float.IsFinite(y))
|
||||
{
|
||||
@@ -713,6 +731,22 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetVelocity(Vector2 velocity)
|
||||
{
|
||||
_checkDisposed();
|
||||
|
||||
var (x, y) = velocity;
|
||||
|
||||
if (!AreFinite(x, y))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AL.Source(SourceHandle!.Value, ALSource3f.Velocity, x, y, 0);
|
||||
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public void SetPitch(float pitch)
|
||||
{
|
||||
_checkDisposed();
|
||||
|
||||
@@ -12,7 +12,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
static Clyde()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
|
||||
RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||
RuntimeInformation.ProcessArchitecture == Architecture.X64 &&
|
||||
Environment.GetEnvironmentVariable("ROBUST_INTEGRATED_GPU") != "1")
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Shared.Log;
|
||||
@@ -115,7 +115,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
var prev = cap;
|
||||
var cVarName = $"display.ogl_block_{capName}";
|
||||
var block = _configurationManager.GetCVar<bool>(cVarName);
|
||||
var block = ConfigurationManager.GetCVar<bool>(cVarName);
|
||||
|
||||
if (block)
|
||||
{
|
||||
@@ -146,7 +146,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
foreach (var cvar in cvars)
|
||||
{
|
||||
_configurationManager.RegisterCVar($"display.ogl_block_{cvar}", false);
|
||||
ConfigurationManager.RegisterCVar($"display.ogl_block_{cvar}", false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -208,12 +208,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_setChunkDirty(grid, chunk);
|
||||
}
|
||||
|
||||
private void _updateOnGridCreated(GridId gridId)
|
||||
private void _updateOnGridCreated(MapId mapId, GridId gridId)
|
||||
{
|
||||
_mapChunkData.Add(gridId, new Dictionary<Vector2i, MapChunkData>());
|
||||
}
|
||||
|
||||
private void _updateOnGridRemoved(GridId gridId)
|
||||
private void _updateOnGridRemoved(MapId mapId, GridId gridId)
|
||||
{
|
||||
var data = _mapChunkData[gridId];
|
||||
foreach (var chunkDatum in data.Values)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -7,6 +7,8 @@ using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -68,8 +70,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
RenderOverlays(OverlaySpace.ScreenSpaceBelowWorld);
|
||||
|
||||
_mainViewport.Eye = _eyeManager.CurrentEye;
|
||||
RenderViewport(_mainViewport);
|
||||
|
||||
RenderViewport(_mainViewport); //Worldspace overlays are rendered here.
|
||||
{
|
||||
var handle = _renderHandle.DrawingHandleScreen;
|
||||
var tex = _mainViewport.RenderTarget.Texture;
|
||||
@@ -107,25 +108,67 @@ namespace Robust.Client.Graphics.Clyde
|
||||
list.Add(overlay);
|
||||
}
|
||||
}
|
||||
|
||||
list.Sort(OverlayComparer.Instance);
|
||||
|
||||
foreach (var overlay in list)
|
||||
{
|
||||
overlay.ClydeRender(_renderHandle, space);
|
||||
}
|
||||
|
||||
FlushRenderQueue();
|
||||
list.Sort(OverlayComparer.Instance);
|
||||
foreach (var overlay in list) {
|
||||
if (overlay.RequestScreenTexture) {
|
||||
FlushRenderQueue();
|
||||
UpdateOverlayScreenTexture(space, _mainViewport.RenderTarget);
|
||||
}
|
||||
if (overlay.OverwriteTargetFrameBuffer()) {
|
||||
ClearFramebuffer(default);
|
||||
}
|
||||
overlay.ClydeRender(_renderHandle, space);
|
||||
FlushRenderQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawEntitiesAndWorldOverlay(Viewport viewport, Box2 worldBounds)
|
||||
private ClydeTexture? ScreenBufferTexture;
|
||||
private GLHandle screenBufferHandle;
|
||||
private Vector2 lastFrameSize;
|
||||
/// <summary>
|
||||
/// Sends SCREEN_TEXTURE to all overlays in the given OverlaySpace that request it.
|
||||
/// </summary>
|
||||
private bool UpdateOverlayScreenTexture(OverlaySpace space, RenderTexture texture) {
|
||||
//This currently does NOT consider viewports and just grabs the current screen framebuffer. This will need to be improved upon in the future.
|
||||
List<Overlay> oTargets = new List<Overlay>();
|
||||
foreach (var overlay in _overlayManager.AllOverlays) {
|
||||
if (overlay.RequestScreenTexture && overlay.Space == space) {
|
||||
oTargets.Add(overlay);
|
||||
}
|
||||
}
|
||||
if (oTargets.Count > 0 && ScreenBufferTexture != null) {
|
||||
if (lastFrameSize != _framebufferSize) {
|
||||
GL.BindTexture(TextureTarget.Texture2D, screenBufferHandle.Handle);
|
||||
GL.TexImage2D(TextureTarget.Texture2D, 0, _hasGLSrgb ? PixelInternalFormat.Srgb8Alpha8 : PixelInternalFormat.Rgba8, _framebufferSize.X, _framebufferSize.Y, 0,
|
||||
PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
|
||||
}
|
||||
lastFrameSize = _framebufferSize;
|
||||
CopyRenderTextureToTexture(texture, ScreenBufferTexture);
|
||||
foreach (Overlay overlay in oTargets) {
|
||||
overlay.ScreenTexture = ScreenBufferTexture;
|
||||
}
|
||||
oTargets.Clear();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void DrawEntities(Viewport viewport, Box2 worldBounds)
|
||||
{
|
||||
if (_eyeManager.CurrentMap == MapId.Nullspace || !_mapManager.HasMapEntity(_eyeManager.CurrentMap))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RenderOverlays(OverlaySpace.WorldSpaceBelowEntities);
|
||||
|
||||
var screenSize = viewport.Size;
|
||||
|
||||
// So we could calculate the correct size of the entities based on the contents of their sprite...
|
||||
@@ -183,25 +226,61 @@ namespace Robust.Client.Graphics.Clyde
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
RenderTexture? entityPostRenderTarget = null;
|
||||
Vector2i roundedPos = default;
|
||||
if (entry.sprite.PostShader != null)
|
||||
{
|
||||
_renderHandle.UseRenderTarget(EntityPostRenderTarget);
|
||||
_renderHandle.Clear(new Color());
|
||||
// Calculate viewport so that the entity thinks it's drawing to the same position,
|
||||
// which is necessary for light application,
|
||||
// but it's ACTUALLY drawing into the center of the render target.
|
||||
var spritePos = entry.sprite.Owner.Transform.WorldPosition;
|
||||
var screenPos = _eyeManager.WorldToScreen(spritePos);
|
||||
var (roundedX, roundedY) = roundedPos = (Vector2i) screenPos;
|
||||
var flippedPos = new Vector2i(roundedX, screenSize.Y - roundedY);
|
||||
flippedPos -= EntityPostRenderTarget.Size / 2;
|
||||
_renderHandle.Viewport(Box2i.FromDimensions(-flippedPos, screenSize));
|
||||
// calculate world bounding box
|
||||
var spriteBB = entry.sprite.CalculateBoundingBox();
|
||||
var spriteLB = spriteBB.BottomLeft;
|
||||
var spriteRT = spriteBB.TopRight;
|
||||
|
||||
// finally we can calculate screen bounding in pixels
|
||||
var screenLB = _eyeManager.WorldToScreen(spriteLB);
|
||||
var screenRT = _eyeManager.WorldToScreen(spriteRT);
|
||||
|
||||
// we need to scale RT a for effects like emission or highlight
|
||||
// scale can be passed with PostShader as variable in future
|
||||
var postShadeScale = 1.25f;
|
||||
var screenSpriteSize = (Vector2i)((screenRT - screenLB) * postShadeScale).Rounded();
|
||||
screenSpriteSize.Y = -screenSpriteSize.Y;
|
||||
|
||||
// I'm not 100% sure why it works, but without it post-shader
|
||||
// can be lower or upper by 1px than original sprite depending on sprite rotation or scale
|
||||
// probably some rotation rounding error
|
||||
if (screenSpriteSize.X % 2 != 0)
|
||||
screenSpriteSize.X++;
|
||||
if (screenSpriteSize.Y % 2 != 0)
|
||||
screenSpriteSize.Y++;
|
||||
|
||||
// check that sprite size is valid
|
||||
if (screenSpriteSize.X > 0 && screenSpriteSize.Y > 0)
|
||||
{
|
||||
// create new render texture with correct sprite size
|
||||
entityPostRenderTarget = CreateRenderTarget(screenSpriteSize,
|
||||
new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb, true),
|
||||
name: nameof(entityPostRenderTarget));
|
||||
_renderHandle.UseRenderTarget(entityPostRenderTarget);
|
||||
_renderHandle.Clear(new Color());
|
||||
|
||||
// Calculate viewport so that the entity thinks it's drawing to the same position,
|
||||
// which is necessary for light application,
|
||||
// but it's ACTUALLY drawing into the center of the render target.
|
||||
var spritePos = spriteBB.Center;
|
||||
var screenPos = _eyeManager.WorldToScreen(spritePos);
|
||||
var (roundedX, roundedY) = roundedPos = (Vector2i)screenPos;
|
||||
var flippedPos = new Vector2i(roundedX, screenSize.Y - roundedY);
|
||||
flippedPos -= entityPostRenderTarget.Size / 2;
|
||||
_renderHandle.Viewport(Box2i.FromDimensions(-flippedPos, screenSize));
|
||||
}
|
||||
}
|
||||
|
||||
entry.sprite.Render(_renderHandle.DrawingHandleWorld, entry.worldMatrix, entry.worldRotation);
|
||||
var matrix = entry.worldMatrix;
|
||||
var worldPosition = new Vector2(matrix.R0C2, matrix.R1C2);
|
||||
entry.sprite.Render(_renderHandle.DrawingHandleWorld, in entry.worldRotation, in worldPosition);
|
||||
|
||||
if (entry.sprite.PostShader != null)
|
||||
if (entry.sprite.PostShader != null && entityPostRenderTarget != null)
|
||||
{
|
||||
var oldProj = _currentMatrixProj;
|
||||
var oldView = _currentMatrixView;
|
||||
@@ -214,11 +293,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_renderHandle.SetProjView(proj, view);
|
||||
_renderHandle.SetModelTransform(Matrix3.Identity);
|
||||
|
||||
var rounded = roundedPos - EntityPostRenderTarget.Size / 2;
|
||||
var rounded = roundedPos - entityPostRenderTarget.Size / 2;
|
||||
|
||||
var box = Box2i.FromDimensions(rounded, EntityPostRenderTarget.Size);
|
||||
var box = Box2i.FromDimensions(rounded, entityPostRenderTarget.Size);
|
||||
|
||||
_renderHandle.DrawTextureScreen(EntityPostRenderTarget.Texture,
|
||||
_renderHandle.DrawTextureScreen(entityPostRenderTarget.Texture,
|
||||
box.BottomLeft, box.BottomRight, box.TopLeft, box.TopRight,
|
||||
Color.White, null);
|
||||
|
||||
@@ -231,14 +310,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
_drawingSpriteList.Clear();
|
||||
FlushRenderQueue();
|
||||
|
||||
// Cleanup remainders
|
||||
foreach (var overlay in worldOverlays)
|
||||
{
|
||||
overlay.ClydeRender(_renderHandle, OverlaySpace.WorldSpace);
|
||||
}
|
||||
|
||||
FlushRenderQueue();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
@@ -247,29 +318,44 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
var spriteSystem = _entitySystemManager.GetEntitySystem<RenderingTreeSystem>();
|
||||
|
||||
var tree = spriteSystem.GetSpriteTreeForMap(map);
|
||||
|
||||
tree.QueryAabb(ref list, ((
|
||||
ref RefList<(SpriteComponent sprite, Matrix3 matrix, Angle worldRot, float yWorldPos)> state,
|
||||
in SpriteComponent value) =>
|
||||
foreach (var gridId in _mapManager.FindGridIdsIntersecting(map, worldBounds, true))
|
||||
{
|
||||
if (value.ContainerOccluded || !value.Visible)
|
||||
Box2 gridBounds;
|
||||
|
||||
if (gridId == GridId.Invalid)
|
||||
{
|
||||
return true;
|
||||
gridBounds = worldBounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
gridBounds = worldBounds.Translated(-_mapManager.GetGrid(gridId).WorldPosition);
|
||||
}
|
||||
|
||||
var entity = value.Owner;
|
||||
var transform = entity.Transform;
|
||||
var tree = spriteSystem.GetSpriteTreeForMap(map, gridId);
|
||||
|
||||
ref var entry = ref state.AllocAdd();
|
||||
entry.sprite = value;
|
||||
entry.worldRot = transform.WorldRotation;
|
||||
entry.matrix = transform.WorldMatrix;
|
||||
var worldPos = entry.matrix.Transform(transform.LocalPosition);
|
||||
entry.yWorldPos = worldPos.Y;
|
||||
return true;
|
||||
tree.QueryAabb(ref list, ((
|
||||
ref RefList<(SpriteComponent sprite, Matrix3 matrix, Angle worldRot, float yWorldPos)> state,
|
||||
in SpriteComponent value) =>
|
||||
{
|
||||
// TODO: Probably value in storing this as its own DynamicTree
|
||||
if (value.ContainerOccluded || !value.Visible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}), worldBounds, approx: true);
|
||||
var entity = value.Owner;
|
||||
var transform = entity.Transform;
|
||||
|
||||
ref var entry = ref state.AllocAdd();
|
||||
entry.sprite = value;
|
||||
entry.worldRot = transform.WorldRotation;
|
||||
entry.matrix = transform.WorldMatrix;
|
||||
var worldPos = entry.matrix.Transform(transform.LocalPosition);
|
||||
entry.yWorldPos = worldPos.Y;
|
||||
return true;
|
||||
|
||||
}), gridBounds, approx: true);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSplash(IRenderHandle handle)
|
||||
@@ -332,12 +418,21 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// We will also render worldspace overlays here so we can do them under / above entities as necessary
|
||||
using (DebugGroup("Entities"))
|
||||
{
|
||||
DrawEntitiesAndWorldOverlay(viewport, worldBounds);
|
||||
DrawEntities(viewport, worldBounds);
|
||||
}
|
||||
|
||||
RenderOverlays(OverlaySpace.WorldSpaceBelowFOV);
|
||||
|
||||
if (_lightManager.Enabled && _lightManager.DrawHardFov && eye.DrawFov)
|
||||
{
|
||||
GL.Clear(ClearBufferMask.StencilBufferBit);
|
||||
GL.Enable(EnableCap.StencilTest);
|
||||
GL.StencilOp(OpenToolkit.Graphics.OpenGL4.StencilOp.Keep, OpenToolkit.Graphics.OpenGL4.StencilOp.Keep, OpenToolkit.Graphics.OpenGL4.StencilOp.Replace);
|
||||
GL.StencilFunc(StencilFunction.Always, 1, 0xFF);
|
||||
GL.StencilMask(0xFF);
|
||||
ApplyFovToBuffer(viewport, eye);
|
||||
GL.StencilMask(0x00);
|
||||
GL.Disable(EnableCap.StencilTest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,6 +460,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
viewport.WallBleedIntermediateRenderTarget2.Texture,
|
||||
UIBox2.FromDimensions(Vector2.Zero, ScreenSize), new Color(1, 1, 1, 0.5f));
|
||||
}
|
||||
|
||||
|
||||
RenderOverlays(OverlaySpace.WorldSpace);
|
||||
|
||||
GL.StencilFunc(StencilFunction.Notequal, 1, 0xFF);
|
||||
GL.Disable(EnableCap.DepthTest);
|
||||
RenderOverlays(OverlaySpace.WorldSpaceFOVStencil);
|
||||
GL.Disable(EnableCap.StencilTest);
|
||||
}
|
||||
|
||||
PopRenderStateFull(state);
|
||||
|
||||
@@ -5,6 +5,7 @@ using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using ES20 = OpenToolkit.Graphics.ES20;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
@@ -31,6 +32,27 @@ namespace Robust.Client.Graphics.Clyde
|
||||
CheckGlError();
|
||||
GL.BindTexture(TextureTarget.Texture2D, glHandle.Handle);
|
||||
CheckGlError();
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
}
|
||||
|
||||
private void CopyRenderTextureToTexture(RenderTexture source, ClydeTexture target) {
|
||||
LoadedRenderTarget sourceLoaded = RtToLoaded(source);
|
||||
bool pause = sourceLoaded != _currentBoundRenderTarget;
|
||||
FullStoredRendererState? store = null;
|
||||
if (pause) {
|
||||
store = PushRenderStateFull();
|
||||
BindRenderTargetFull(source);
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
GL.BindTexture(TextureTarget.Texture2D, _loadedTextures[target.TextureId].OpenGLObject.Handle);
|
||||
CheckGlError();
|
||||
GL.CopyTexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, 0, 0, _framebufferSize.X, _framebufferSize.Y);
|
||||
CheckGlError();
|
||||
|
||||
if (pause && store != null) {
|
||||
PopRenderStateFull((FullStoredRendererState)store);
|
||||
}
|
||||
}
|
||||
|
||||
private static long EstPixelSize(PixelInternalFormat format)
|
||||
|
||||
@@ -3,13 +3,14 @@ using System.Collections.Generic;
|
||||
using System.Buffers;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement.ResourceTypes;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.GameObjects.ClientOccluderComponent;
|
||||
using OGLTextureWrapMode = OpenToolkit.Graphics.OpenGL.TextureWrapMode;
|
||||
using TKStencilOp = OpenToolkit.Graphics.OpenGL4.StencilOp;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -195,7 +196,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_lightSoftShaderHandle = LoadShaderHandle("/Shaders/Internal/light-soft.swsl");
|
||||
_lightHardShaderHandle = LoadShaderHandle("/Shaders/Internal/light-hard.swsl");
|
||||
_fovShaderHandle = LoadShaderHandle("/Shaders/Internal/fov.swsl");
|
||||
@@ -361,16 +361,23 @@ namespace Robust.Client.Graphics.Clyde
|
||||
FinalizeDepthDraw();
|
||||
}
|
||||
|
||||
BindRenderTargetImmediate(RtToLoaded(viewport.LightRenderTarget));
|
||||
CheckGlError();
|
||||
GLClearColor(_lightManager.AmbientLightColor);
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||
CheckGlError();
|
||||
GL.Enable(EnableCap.StencilTest);
|
||||
_isStencilling = true;
|
||||
|
||||
var (lightW, lightH) = GetLightMapSize(viewport.Size);
|
||||
GL.Viewport(0, 0, lightW, lightH);
|
||||
CheckGlError();
|
||||
|
||||
BindRenderTargetImmediate(RtToLoaded(viewport.LightRenderTarget));
|
||||
CheckGlError();
|
||||
GLClearColor(_lightManager.AmbientLightColor);
|
||||
GL.ClearStencil(0xFF);
|
||||
GL.StencilMask(0xFF);
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.StencilBufferBit);
|
||||
CheckGlError();
|
||||
|
||||
ApplyLightingFovToBuffer(viewport, eye);
|
||||
|
||||
var lightShader = _loadedShaders[_enableSoftShadows ? _lightSoftShaderHandle : _lightHardShaderHandle].Program;
|
||||
lightShader.Use();
|
||||
|
||||
@@ -382,6 +389,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One);
|
||||
CheckGlError();
|
||||
|
||||
GL.StencilFunc(StencilFunction.Equal, 0xFF, 0xFF);
|
||||
CheckGlError();
|
||||
GL.StencilOp(TKStencilOp.Keep, TKStencilOp.Keep, TKStencilOp.Keep);
|
||||
CheckGlError();
|
||||
|
||||
var lastRange = float.NaN;
|
||||
var lastPower = float.NaN;
|
||||
var lastColor = new Color(float.NaN, float.NaN, float.NaN, float.NaN);
|
||||
@@ -463,11 +475,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
ResetBlendFunc();
|
||||
GL.Disable(EnableCap.StencilTest);
|
||||
_isStencilling = false;
|
||||
|
||||
CheckGlError();
|
||||
|
||||
ApplyLightingFovToBuffer(viewport, eye);
|
||||
|
||||
BlurOntoWalls(viewport, eye);
|
||||
|
||||
MergeWallLayer(viewport);
|
||||
@@ -485,40 +497,55 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GetLightsToRender(MapId map, in Box2 worldBounds)
|
||||
{
|
||||
var renderingTreeSystem = _entitySystemManager.GetEntitySystem<RenderingTreeSystem>();
|
||||
var lightTree = renderingTreeSystem.GetLightTreeForMap(map);
|
||||
|
||||
var state = (this, worldBounds, count: 0);
|
||||
|
||||
lightTree.QueryAabb(ref state, (ref (Clyde clyde, Box2 worldBounds, int count) state, in PointLightComponent light) =>
|
||||
foreach (var gridId in _mapManager.FindGridIdsIntersecting(map, worldBounds, true))
|
||||
{
|
||||
var transform = light.Owner.Transform;
|
||||
Box2 gridBounds;
|
||||
|
||||
if (state.count >= LightsToRenderListSize)
|
||||
if (gridId == GridId.Invalid)
|
||||
{
|
||||
// There are too many lights to fit in the static memory.
|
||||
return false;
|
||||
gridBounds = worldBounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
gridBounds = worldBounds.Translated(-_mapManager.GetGrid(gridId).WorldPosition);
|
||||
}
|
||||
|
||||
if (!light.Enabled || light.ContainerOccluded)
|
||||
var lightTree = renderingTreeSystem.GetLightTreeForMap(map, gridId);
|
||||
|
||||
lightTree.QueryAabb(ref state, (ref (Clyde clyde, Box2 worldBounds, int count) state, in PointLightComponent light) =>
|
||||
{
|
||||
var transform = light.Owner.Transform;
|
||||
|
||||
if (state.count >= LightsToRenderListSize)
|
||||
{
|
||||
// There are too many lights to fit in the static memory.
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Don't insert into trees for these, same as sprites.
|
||||
if (!light.Enabled || light.ContainerOccluded)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var lightPos = transform.WorldMatrix.Transform(light.Offset);
|
||||
|
||||
var circle = new Circle(lightPos, light.Radius);
|
||||
|
||||
// If the light doesn't touch anywhere the camera can see, it doesn't matter.
|
||||
if (!circle.Intersects(state.worldBounds))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
float distanceSquared = (state.worldBounds.Center - lightPos).LengthSquared;
|
||||
state.clyde._lightsToRenderList[state.count++] = (light, lightPos, distanceSquared);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
var lightPos = transform.WorldMatrix.Transform(light.Offset);
|
||||
|
||||
var circle = new Circle(lightPos, light.Radius);
|
||||
|
||||
// If the light doesn't touch anywhere the camera can see, it doesn't matter.
|
||||
if (!circle.Intersects(state.worldBounds))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
float distanceSquared = (state.worldBounds.Center - lightPos).LengthSquared;
|
||||
state.clyde._lightsToRenderList[state.count++] = (light, lightPos, distanceSquared);
|
||||
|
||||
return true;
|
||||
}, worldBounds);
|
||||
}, gridBounds);
|
||||
}
|
||||
|
||||
if (state.count > _maxLightsPerScene)
|
||||
{
|
||||
@@ -690,6 +717,13 @@ namespace Robust.Client.Graphics.Clyde
|
||||
fovShader.SetUniformTextureMaybe(UniIMainTexture, TextureUnit.Texture0);
|
||||
fovShader.SetUniformMaybe("center", eye.Position.Position);
|
||||
|
||||
GL.StencilMask(0xFF);
|
||||
CheckGlError();
|
||||
GL.StencilFunc(StencilFunction.Always, 0, 0);
|
||||
CheckGlError();
|
||||
GL.StencilOp(TKStencilOp.Keep, TKStencilOp.Keep, TKStencilOp.Replace);
|
||||
CheckGlError();
|
||||
|
||||
DrawBlit(viewport, fovShader);
|
||||
|
||||
if (_hasGLSamplerObjects)
|
||||
@@ -775,12 +809,10 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var worldTransform = transform.WorldMatrix;
|
||||
var box = occluder.BoundingBox;
|
||||
|
||||
// So uh, angle 0 = east... Apparently...
|
||||
// We account for that here so I don't go insane.
|
||||
var (tlX, tlY) = worldTransform.Transform(box.BottomLeft);
|
||||
var (trX, trY) = worldTransform.Transform(box.TopLeft);
|
||||
var (brX, brY) = worldTransform.Transform(box.TopRight);
|
||||
var (blX, blY) = worldTransform.Transform(box.BottomRight);
|
||||
var (tlX, tlY) = worldTransform.Transform(box.TopLeft);
|
||||
var (trX, trY) = worldTransform.Transform(box.TopRight);
|
||||
var (brX, brY) = worldTransform.Transform(box.BottomRight);
|
||||
var (blX, blY) = worldTransform.Transform(box.BottomLeft);
|
||||
|
||||
// Faces.
|
||||
var faceN = new Vector4(tlX, tlY, trX, trY);
|
||||
@@ -941,7 +973,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
viewport.WallMaskRenderTarget = CreateRenderTarget(viewport.Size, RenderTargetColorFormat.R8,
|
||||
name: $"{viewport.Name}-{nameof(viewport.WallMaskRenderTarget)}");
|
||||
|
||||
viewport.LightRenderTarget = CreateRenderTarget(lightMapSize, lightMapColorFormat,
|
||||
viewport.LightRenderTarget = CreateRenderTarget(lightMapSize,
|
||||
new RenderTargetFormatParameters(lightMapColorFormat, hasDepthStencil: true),
|
||||
lightMapSampleParameters,
|
||||
$"{viewport.Name}-{nameof(viewport.LightRenderTarget)}");
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -37,28 +37,55 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_clyde.DrawSetProjViewTransform(proj, view);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a sprite to the screen. The coordinate system is left handed.
|
||||
/// Make sure to set <see cref="DrawSetModelTransform"/>
|
||||
/// to set the model matrix if needed.
|
||||
/// </summary>
|
||||
/// <param name="texture">Texture to draw.</param>
|
||||
/// <param name="bl">Bottom left vertex of the quad in object space.</param>
|
||||
/// <param name="br">Bottom right vertex of the quad in object space.</param>
|
||||
/// <param name="tl">Top left vertex of the quad in object space.</param>
|
||||
/// <param name="tr">Top right vertex of the quad in object space.</param>
|
||||
/// <param name="modulate">A color to multiply the texture by when shading.</param
|
||||
/// <param name="subRegion">The four corners of the texture sub region in px.</param>
|
||||
public void DrawTextureScreen(Texture texture, Vector2 bl, Vector2 br, Vector2 tl, Vector2 tr,
|
||||
in Color modulate, in UIBox2? subRegion)
|
||||
{
|
||||
var clydeTexture = ExtractTexture(texture, subRegion, out var csr);
|
||||
var clydeTexture = ExtractTexture(texture, in subRegion, out var csr);
|
||||
|
||||
var (w, h) = clydeTexture.Size;
|
||||
var sr = new Box2(csr.Left / w, (h - csr.Top) / h, csr.Right / w, (h - csr.Bottom) / h);
|
||||
|
||||
_clyde.DrawTexture(clydeTexture.TextureId, bl, br, tl, tr, modulate, sr);
|
||||
_clyde.DrawTexture(clydeTexture.TextureId, bl, br, tl, tr, in modulate, in sr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a sprite to the world. The coordinate system is right handed.
|
||||
/// Make sure to set <see cref="DrawSetModelTransform"/>
|
||||
/// to set the model matrix if needed.
|
||||
/// </summary>
|
||||
/// <param name="texture">Texture to draw.</param>
|
||||
/// <param name="bl">Bottom left vertex of the quad in object space.</param>
|
||||
/// <param name="br">Bottom right vertex of the quad in object space.</param>
|
||||
/// <param name="tl">Top left vertex of the quad in object space.</param>
|
||||
/// <param name="tr">Top right vertex of the quad in object space.</param>
|
||||
/// <param name="modulate">A color to multiply the texture by when shading.</param>
|
||||
/// <param name="subRegion">The four corners of the texture sub region in px.</param>
|
||||
public void DrawTextureWorld(Texture texture, Vector2 bl, Vector2 br, Vector2 tl, Vector2 tr,
|
||||
Color modulate, in UIBox2? subRegion)
|
||||
{
|
||||
var clydeTexture = ExtractTexture(texture, subRegion, out var csr);
|
||||
var clydeTexture = ExtractTexture(texture, in subRegion, out var csr);
|
||||
|
||||
var (w, h) = clydeTexture.Size;
|
||||
var sr = new Box2(csr.Left / w, (h - csr.Bottom) / h, csr.Right / w, (h - csr.Top) / h);
|
||||
|
||||
_clyde.DrawTexture(clydeTexture.TextureId, bl, br, tl, tr, modulate, sr);
|
||||
_clyde.DrawTexture(clydeTexture.TextureId, bl, br, tl, tr, in modulate, in sr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a subRegion (px) into texture coords (0-1) of a given texture (cells of the textureAtlas).
|
||||
/// </summary>
|
||||
private static ClydeTexture ExtractTexture(Texture texture, in UIBox2? subRegion, out UIBox2 sr)
|
||||
{
|
||||
if (texture is AtlasTexture atlas)
|
||||
@@ -383,22 +410,40 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
public override void DrawTextureRectRegion(Texture texture, Box2 rect, UIBox2? subRegion = null,
|
||||
Color? modulate = null)
|
||||
/// <summary>
|
||||
/// Draws a sprite to the world. The coordinate system is right handed.
|
||||
/// Make sure to set <see cref="DrawSetModelTransform"/>
|
||||
/// to set the model matrix if needed.
|
||||
/// </summary>
|
||||
/// <param name="texture">Texture to draw.</param>
|
||||
/// <param name="quad">The four vertices of the quad in object space (or world if the transform is identity.).</param>
|
||||
/// <param name="modulate">A color to multiply the texture by when shading.</param>
|
||||
/// <param name="subRegion">The four corners of the texture sub region in px.</param>
|
||||
public override void DrawTextureRectRegion(Texture texture, Box2 quad,
|
||||
Color? modulate = null, UIBox2? subRegion = null)
|
||||
{
|
||||
var color = (modulate ?? Color.White) * Modulate;
|
||||
|
||||
_renderHandle.DrawTextureWorld(texture, rect.BottomLeft, rect.BottomRight,
|
||||
rect.TopLeft, rect.TopRight, color, subRegion);
|
||||
_renderHandle.DrawTextureWorld(texture, quad.BottomLeft, quad.BottomRight,
|
||||
quad.TopLeft, quad.TopRight, color, in subRegion);
|
||||
}
|
||||
|
||||
public override void DrawTextureRectRegion(Texture texture, in Box2Rotated rect,
|
||||
UIBox2? subRegion = null, Color? modulate = null)
|
||||
/// <summary>
|
||||
/// Draws a sprite to the world. The coordinate system is right handed.
|
||||
/// Make sure to set <see cref="DrawSetModelTransform"/>
|
||||
/// to set the model matrix if needed.
|
||||
/// </summary>
|
||||
/// <param name="texture">Texture to draw.</param>
|
||||
/// <param name="quad">The four vertices of the quad in object space (or world if the transform is identity.).</param>
|
||||
/// <param name="modulate">A color to multiply the texture by when shading.</param>
|
||||
/// <param name="subRegion">The four corners of the texture sub region in px.</param>
|
||||
public override void DrawTextureRectRegion(Texture texture, in Box2Rotated quad,
|
||||
Color? modulate = null, UIBox2? subRegion = null)
|
||||
{
|
||||
var color = (modulate ?? Color.White) * Modulate;
|
||||
|
||||
_renderHandle.DrawTextureWorld(texture, rect.BottomLeft, rect.BottomRight,
|
||||
rect.TopLeft, rect.TopRight, color, subRegion);
|
||||
_renderHandle.DrawTextureWorld(texture, quad.BottomLeft, quad.BottomRight,
|
||||
quad.TopLeft, quad.TopRight, color, in subRegion);
|
||||
}
|
||||
|
||||
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
@@ -280,7 +280,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the render handle, processing and re-pooling all the command lists.
|
||||
/// Flushes the render handle, processing and re-pooling all the command lists.
|
||||
/// </summary>
|
||||
private void FlushRenderQueue()
|
||||
{
|
||||
@@ -371,6 +371,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
program.Use();
|
||||
|
||||
int textureUnitVal = 0;
|
||||
// Assign shader parameters to uniform since they may be dirty.
|
||||
foreach (var (name, value) in instance.Parameters)
|
||||
{
|
||||
@@ -413,6 +414,15 @@ namespace Robust.Client.Graphics.Clyde
|
||||
case Matrix4 matrix4:
|
||||
program.SetUniform(name, matrix4);
|
||||
break;
|
||||
case ClydeTexture clydeTexture:
|
||||
//It's important to start at Texture6 here since DrawCommandBatch uses Texture0 and Texture1 immediately after calling this
|
||||
//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);
|
||||
program.SetUniformTexture(name, cTarget);
|
||||
textureUnitVal++;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Unable to handle shader parameter {name}: {value}");
|
||||
}
|
||||
@@ -477,10 +487,20 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_currentMatrixView = view;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a texture quad to the screen.
|
||||
/// </summary>
|
||||
/// <param name="texture">Texture to draw.</param>
|
||||
/// <param name="bl">Bottom left vertex of the quad in object space.</param>
|
||||
/// <param name="br">Bottom right vertex of the quad in object space.</param>
|
||||
/// <param name="tl">Top left vertex of the quad in object space.</param>
|
||||
/// <param name="tr">Top right vertex of the quad in object space.</param>
|
||||
/// <param name="modulate">A color to multiply the texture by when shading.</param>
|
||||
/// <param name="texCoords">The four corners of the texture coordinates, matching the four vertices.</param>
|
||||
private void DrawTexture(ClydeHandle texture, Vector2 bl, Vector2 br, Vector2 tl, Vector2 tr, in Color modulate,
|
||||
in Box2 sr)
|
||||
in Box2 texCoords)
|
||||
{
|
||||
EnsureBatchState(texture, modulate, true, GetQuadBatchPrimitiveType(), _queuedShader);
|
||||
EnsureBatchState(texture, in modulate, true, GetQuadBatchPrimitiveType(), _queuedShader);
|
||||
|
||||
bl = _currentMatrixModel.Transform(bl);
|
||||
br = _currentMatrixModel.Transform(br);
|
||||
@@ -489,10 +509,10 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
// TODO: split batch if necessary.
|
||||
var vIdx = BatchVertexIndex;
|
||||
BatchVertexData[vIdx + 0] = new Vertex2D(bl, sr.BottomLeft);
|
||||
BatchVertexData[vIdx + 1] = new Vertex2D(br, sr.BottomRight);
|
||||
BatchVertexData[vIdx + 2] = new Vertex2D(tr, sr.TopRight);
|
||||
BatchVertexData[vIdx + 3] = new Vertex2D(tl, sr.TopLeft);
|
||||
BatchVertexData[vIdx + 0] = new Vertex2D(bl, texCoords.BottomLeft);
|
||||
BatchVertexData[vIdx + 1] = new Vertex2D(br, texCoords.BottomRight);
|
||||
BatchVertexData[vIdx + 2] = new Vertex2D(tr, texCoords.TopRight);
|
||||
BatchVertexData[vIdx + 3] = new Vertex2D(tl, texCoords.TopLeft);
|
||||
BatchVertexIndex += 4;
|
||||
QuadBatchIndexWrite(BatchIndexData, ref BatchIndexIndex, (ushort) vIdx);
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Client.ResourceManagement.ResourceTypes;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using StencilOp = Robust.Client.Graphics.StencilOp;
|
||||
@@ -428,7 +428,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private protected override void SetParameterImpl(string name, Texture value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var data = Parent._shaderInstances[Handle];
|
||||
data.Parameters[name] = value;
|
||||
}
|
||||
|
||||
private protected override void SetStencilOpImpl(StencilOp op)
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private readonly ConcurrentQueue<ClydeHandle> _textureDisposeQueue = new();
|
||||
|
||||
public Texture LoadTextureFromPNGStream(Stream stream, string? name = null,
|
||||
public OwnedTexture LoadTextureFromPNGStream(Stream stream, string? name = null,
|
||||
TextureLoadParameters? loadParams = null)
|
||||
{
|
||||
DebugTools.Assert(_mainThread == Thread.CurrentThread);
|
||||
@@ -37,7 +37,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return LoadTextureFromImage(image, name, loadParams);
|
||||
}
|
||||
|
||||
public Texture LoadTextureFromImage<T>(Image<T> image, string? name = null,
|
||||
public OwnedTexture LoadTextureFromImage<T>(Image<T> image, string? name = null,
|
||||
TextureLoadParameters? loadParams = null) where T : unmanaged, IPixel<T>
|
||||
{
|
||||
DebugTools.Assert(_mainThread == Thread.CurrentThread);
|
||||
@@ -56,19 +56,19 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
// Disable sRGB so stuff doesn't get interpreter wrong.
|
||||
actualParams.Srgb = false;
|
||||
var img = ApplyA8Swizzle((Image<A8>) (object) image);
|
||||
using var img = ApplyA8Swizzle((Image<A8>) (object) image);
|
||||
return LoadTextureFromImage(img, name, loadParams);
|
||||
}
|
||||
|
||||
if (pixelType == typeof(L8) && !actualParams.Srgb)
|
||||
{
|
||||
var img = ApplyL8Swizzle((Image<L8>) (object) image);
|
||||
using var img = ApplyL8Swizzle((Image<L8>) (object) image);
|
||||
return LoadTextureFromImage(img, name, loadParams);
|
||||
}
|
||||
}
|
||||
|
||||
// Flip image because OpenGL reads images upside down.
|
||||
var copy = FlipClone(image);
|
||||
using var copy = FlipClone(image);
|
||||
|
||||
var texture = CreateBaseTextureInternal<T>(image.Width, image.Height, actualParams, name);
|
||||
|
||||
@@ -324,11 +324,13 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (typeof(T) == typeof(A8))
|
||||
{
|
||||
SetSubImage(texture, dstTl, ApplyA8Swizzle((Image<A8>) (object) srcImage), srcBox);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof(T) == typeof(L8))
|
||||
{
|
||||
SetSubImage(texture, dstTl, ApplyL8Swizzle((Image<L8>) (object) srcImage), srcBox);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using OpenToolkit;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using OpenToolkit.GraphicsLibraryFramework;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.Utility;
|
||||
@@ -14,6 +15,7 @@ using Robust.Shared;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using static Robust.Client.Utility.LiterallyJustMessageBox;
|
||||
@@ -46,6 +48,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
// Keep delegates around to prevent GC issues.
|
||||
private GLFWCallbacks.ErrorCallback _errorCallback = default!;
|
||||
private GLFWCallbacks.MonitorCallback _monitorCallback = default!;
|
||||
private GLFWCallbacks.CharCallback _charCallback = default!;
|
||||
private GLFWCallbacks.CursorPosCallback _cursorPosCallback = default!;
|
||||
private GLFWCallbacks.KeyCallback _keyCallback = default!;
|
||||
@@ -55,6 +58,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private GLFWCallbacks.WindowSizeCallback _windowSizeCallback = default!;
|
||||
private GLFWCallbacks.WindowContentScaleCallback _windowContentScaleCallback = default!;
|
||||
private GLFWCallbacks.WindowIconifyCallback _windowIconifyCallback = default!;
|
||||
private GLFWCallbacks.WindowFocusCallback _windowFocusCallback = default!;
|
||||
|
||||
private bool _glfwInitialized;
|
||||
|
||||
@@ -62,6 +66,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private Window* _glfwWindow;
|
||||
|
||||
private Vector2i _framebufferSize;
|
||||
private bool _isFocused;
|
||||
private Vector2i _windowSize;
|
||||
private Vector2i _prevWindowSize;
|
||||
private Vector2i _prevWindowPos;
|
||||
@@ -71,9 +76,22 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private Vector2 _lastMousePos;
|
||||
|
||||
// Can't use ClydeHandle because it's 64 bit.
|
||||
private int _nextWindowId = 1;
|
||||
private readonly Dictionary<int, MonitorReg> _monitors = new();
|
||||
|
||||
public event Action<TextEventArgs>? TextEntered;
|
||||
public event Action<MouseMoveEventArgs>? MouseMove;
|
||||
public event Action<KeyEventArgs>? KeyUp;
|
||||
public event Action<KeyEventArgs>? KeyDown;
|
||||
public event Action<MouseWheelEventArgs>? MouseWheel;
|
||||
public event Action<string>? CloseWindow;
|
||||
public event Action? OnWindowScaleChanged;
|
||||
|
||||
// NOTE: in engine we pretend the framebuffer size is the screen size..
|
||||
// For practical reasons like UI rendering.
|
||||
public override Vector2i ScreenSize => _framebufferSize;
|
||||
public override bool IsFocused => _isFocused;
|
||||
public Vector2 DefaultWindowScale => _windowScale;
|
||||
public Vector2 MouseScreenPosition => _lastMousePos;
|
||||
|
||||
@@ -145,15 +163,60 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return false;
|
||||
}
|
||||
|
||||
InitMonitors();
|
||||
InitCursors();
|
||||
|
||||
return InitWindow();
|
||||
}
|
||||
|
||||
private void InitMonitors()
|
||||
{
|
||||
var monitors = GLFW.GetMonitorsRaw(out var count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
SetupMonitor(monitors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupMonitor(Monitor* monitor)
|
||||
{
|
||||
var handle = _nextWindowId++;
|
||||
|
||||
DebugTools.Assert(GLFW.GetMonitorUserPointer(monitor) == null, "GLFW window already has user pointer??");
|
||||
|
||||
var name = GLFW.GetMonitorName(monitor);
|
||||
var videoMode = GLFW.GetVideoMode(monitor);
|
||||
var impl = new ClydeMonitorImpl(handle, name, (videoMode->Width, videoMode->Height), videoMode->RefreshRate);
|
||||
|
||||
GLFW.SetMonitorUserPointer(monitor, (void*) handle);
|
||||
_monitors[handle] = new MonitorReg
|
||||
{
|
||||
Id = handle,
|
||||
Impl = impl,
|
||||
Monitor = monitor
|
||||
};
|
||||
}
|
||||
|
||||
private void DestroyMonitor(Monitor* monitor)
|
||||
{
|
||||
var ptr = GLFW.GetMonitorUserPointer(monitor);
|
||||
|
||||
if (ptr == null)
|
||||
{
|
||||
var name = GLFW.GetMonitorName(monitor);
|
||||
Logger.WarningS("clyde.win", $"Monitor '{name}' had no user pointer set??");
|
||||
return;
|
||||
}
|
||||
|
||||
_monitors.Remove((int) ptr);
|
||||
GLFW.SetMonitorUserPointer(monitor, null);
|
||||
}
|
||||
|
||||
private bool InitWindow()
|
||||
{
|
||||
var width = _configurationManager.GetCVar(CVars.DisplayWidth);
|
||||
var height = _configurationManager.GetCVar(CVars.DisplayHeight);
|
||||
var width = ConfigurationManager.GetCVar(CVars.DisplayWidth);
|
||||
var height = ConfigurationManager.GetCVar(CVars.DisplayHeight);
|
||||
|
||||
Monitor* monitor = null;
|
||||
|
||||
@@ -163,6 +226,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var mode = GLFW.GetVideoMode(monitor);
|
||||
width = mode->Width;
|
||||
height = mode->Height;
|
||||
|
||||
GLFW.WindowHint(WindowHintInt.RefreshRate, mode->RefreshRate);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
@@ -171,13 +236,16 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GLFW.WindowHint(WindowHintString.X11ClassName, "SS14");
|
||||
GLFW.WindowHint(WindowHintString.X11InstanceName, "SS14");
|
||||
|
||||
var renderer = (Renderer) _configurationManager.GetCVar<int>(CVars.DisplayRenderer);
|
||||
var renderer = (Renderer) ConfigurationManager.GetCVar<int>(CVars.DisplayRenderer);
|
||||
|
||||
Span<Renderer> renderers = (renderer == Renderer.Default) ? stackalloc Renderer[] {
|
||||
Renderer.OpenGL33,
|
||||
Renderer.OpenGL31,
|
||||
Renderer.OpenGLES2
|
||||
} : stackalloc Renderer[] {renderer};
|
||||
Span<Renderer> renderers = (renderer == Renderer.Default)
|
||||
? stackalloc Renderer[]
|
||||
{
|
||||
Renderer.OpenGL33,
|
||||
Renderer.OpenGL31,
|
||||
Renderer.OpenGLES2
|
||||
}
|
||||
: stackalloc Renderer[] {renderer};
|
||||
|
||||
foreach (Renderer r in renderers)
|
||||
{
|
||||
@@ -190,6 +258,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_isCore = renderer == Renderer.OpenGL33;
|
||||
break;
|
||||
}
|
||||
|
||||
// Window failed to init due to error.
|
||||
// Try not to treat the error code seriously.
|
||||
var code = GLFW.GetError(out string desc);
|
||||
@@ -203,9 +272,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var code = GLFW.GetError(out string desc);
|
||||
|
||||
var errorContent = "Failed to create the game window. " +
|
||||
"This probably means your GPU is too old to play the game. " +
|
||||
"That or update your graphic drivers\n" +
|
||||
$"The exact error is: [{code}]\n {desc}";
|
||||
"This probably means your GPU is too old to play the game. " +
|
||||
"That or update your graphic drivers\n" +
|
||||
$"The exact error is: [{code}]\n {desc}";
|
||||
|
||||
MessageBoxW(null,
|
||||
errorContent,
|
||||
@@ -231,6 +300,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GLFW.SetMouseButtonCallback(_glfwWindow, _mouseButtonCallback);
|
||||
GLFW.SetWindowContentScaleCallback(_glfwWindow, _windowContentScaleCallback);
|
||||
GLFW.SetWindowIconifyCallback(_glfwWindow, _windowIconifyCallback);
|
||||
GLFW.SetWindowFocusCallback(_glfwWindow, _windowFocusCallback);
|
||||
|
||||
GLFW.MakeContextCurrent(_glfwWindow);
|
||||
|
||||
@@ -297,6 +367,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GLFW.WindowHint(WindowHintOpenGlProfile.OpenGlProfile, OpenGlProfile.Any);
|
||||
GLFW.WindowHint(WindowHintBool.SrgbCapable, false);
|
||||
}
|
||||
|
||||
_glfwWindow = GLFW.CreateWindow(width, height, string.Empty, monitor, null);
|
||||
}
|
||||
}
|
||||
@@ -389,11 +460,30 @@ namespace Robust.Client.Graphics.Clyde
|
||||
Logger.ErrorS("clyde.win.glfw", "GLFW Error: [{0}] {1}", code, description);
|
||||
}
|
||||
|
||||
private void OnGlfwMonitor(Monitor* monitor, ConnectedState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (state == ConnectedState.Connected)
|
||||
{
|
||||
SetupMonitor(monitor);
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyMonitor(monitor);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CatchCallbackException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGlfwChar(Window* window, uint codepoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
_gameController.TextEntered(new TextEventArgs(codepoint));
|
||||
TextEntered?.Invoke(new TextEventArgs(codepoint));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -409,8 +499,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var delta = newPos - _lastMousePos;
|
||||
_lastMousePos = newPos;
|
||||
|
||||
var ev = new MouseMoveEventArgs(delta, newPos);
|
||||
_gameController.MouseMove(ev);
|
||||
MouseMove?.Invoke(new MouseMoveEventArgs(delta, newPos));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -457,11 +546,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
switch (action)
|
||||
{
|
||||
case InputAction.Release:
|
||||
_gameController.KeyUp(ev);
|
||||
KeyUp?.Invoke(ev);
|
||||
break;
|
||||
case InputAction.Press:
|
||||
case InputAction.Repeat:
|
||||
_gameController.KeyDown(ev);
|
||||
KeyDown?.Invoke(ev);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(action), action, null);
|
||||
@@ -473,7 +562,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
try
|
||||
{
|
||||
var ev = new MouseWheelEventArgs(((float) offsetX, (float) offsetY), _lastMousePos);
|
||||
_gameController.MouseWheel(ev);
|
||||
MouseWheel?.Invoke(ev);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -485,7 +574,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
try
|
||||
{
|
||||
_gameController.Shutdown("Window closed");
|
||||
CloseWindow?.Invoke("Window closed");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -529,6 +618,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
try
|
||||
{
|
||||
_windowScale = (xScale, yScale);
|
||||
OnWindowScaleChanged?.Invoke();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -548,9 +638,23 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGlfwWindowFocus(Window* window, bool focused)
|
||||
{
|
||||
try
|
||||
{
|
||||
_isFocused = focused;
|
||||
OnWindowFocused?.Invoke(new WindowFocusedEventArgs(focused));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CatchCallbackException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void StoreCallbacks()
|
||||
{
|
||||
_errorCallback = OnGlfwError;
|
||||
_monitorCallback = OnGlfwMonitor;
|
||||
_charCallback = OnGlfwChar;
|
||||
_cursorPosCallback = OnGlfwCursorPos;
|
||||
_keyCallback = OnGlfwKey;
|
||||
@@ -560,6 +664,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_windowSizeCallback = OnGlfwWindowSize;
|
||||
_windowContentScaleCallback = OnGlfwWindownContentScale;
|
||||
_windowIconifyCallback = OnGlfwWindowIconify;
|
||||
_windowFocusCallback = OnGlfwWindowFocus;
|
||||
}
|
||||
|
||||
public override void SetWindowTitle(string title)
|
||||
@@ -572,6 +677,19 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GLFW.SetWindowTitle(_glfwWindow, title);
|
||||
}
|
||||
|
||||
public void SetWindowMonitor(IClydeMonitor monitor)
|
||||
{
|
||||
var monitorImpl = (ClydeMonitorImpl) monitor;
|
||||
var reg = _monitors[monitorImpl.Id];
|
||||
|
||||
GLFW.SetWindowMonitor(
|
||||
_glfwWindow,
|
||||
reg.Monitor,
|
||||
0, 0,
|
||||
monitorImpl.Size.X, monitorImpl.Size.Y,
|
||||
monitorImpl.RefreshRate);
|
||||
}
|
||||
|
||||
public void RequestWindowAttention()
|
||||
{
|
||||
GLFW.RequestWindowAttention(_glfwWindow);
|
||||
@@ -636,18 +754,40 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
GLFW.GetWindowPos(_glfwWindow, out var x, out var y);
|
||||
_prevWindowPos = (x, y);
|
||||
var monitor = GLFW.GetPrimaryMonitor();
|
||||
var monitor = MonitorForWindow(_glfwWindow);
|
||||
var mode = GLFW.GetVideoMode(monitor);
|
||||
|
||||
GLFW.SetWindowMonitor(_glfwWindow, GLFW.GetPrimaryMonitor(), 0, 0, mode->Width, mode->Height,
|
||||
GLFW.SetWindowMonitor(_glfwWindow, monitor, 0, 0, mode->Width, mode->Height,
|
||||
mode->RefreshRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
GLFW.SetWindowMonitor(_glfwWindow, null, _prevWindowPos.X, _prevWindowPos.Y, _prevWindowSize.X, _prevWindowSize.Y, 0);
|
||||
GLFW.SetWindowMonitor(_glfwWindow, null, _prevWindowPos.X, _prevWindowPos.Y, _prevWindowSize.X,
|
||||
_prevWindowSize.Y, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// glfwGetWindowMonitor only works for fullscreen windows.
|
||||
// Picks the monitor with the top-left corner of the window.
|
||||
private Monitor* MonitorForWindow(Window* window)
|
||||
{
|
||||
GLFW.GetWindowPos(window, out var winPosX, out var winPosY);
|
||||
var monitors = GLFW.GetMonitorsRaw(out var count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var monitor = monitors[i];
|
||||
GLFW.GetMonitorPos(monitor, out var monPosX, out var monPosY);
|
||||
var videoMode = GLFW.GetVideoMode(monitor);
|
||||
|
||||
var box = Box2i.FromDimensions(monPosX, monPosY, videoMode->Width, videoMode->Height);
|
||||
if (box.Contains(winPosX, winPosY))
|
||||
return monitor;
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return GLFW.GetPrimaryMonitor();
|
||||
}
|
||||
|
||||
string IClipboardManager.GetText()
|
||||
{
|
||||
return GLFW.GetClipboardString(_glfwWindow);
|
||||
@@ -658,6 +798,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GLFW.SetClipboardString(_glfwWindow, text);
|
||||
}
|
||||
|
||||
public IEnumerable<IClydeMonitor> EnumerateMonitors()
|
||||
{
|
||||
return _monitors.Values.Select(c => c.Impl);
|
||||
}
|
||||
|
||||
// We can't let exceptions unwind into GLFW, as that can cause the CLR to crash.
|
||||
// And it probably messes up GLFW too.
|
||||
// So all the callbacks are passed to this method.
|
||||
@@ -671,5 +816,28 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
_glfwExceptionList.Add(e);
|
||||
}
|
||||
|
||||
private sealed class MonitorReg
|
||||
{
|
||||
public int Id;
|
||||
public Monitor* Monitor;
|
||||
public ClydeMonitorImpl Impl = default!;
|
||||
}
|
||||
|
||||
private sealed class ClydeMonitorImpl : IClydeMonitor
|
||||
{
|
||||
public ClydeMonitorImpl(int id, string name, Vector2i size, int refreshRate)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
Size = size;
|
||||
RefreshRate = refreshRate;
|
||||
}
|
||||
|
||||
public int Id { get; }
|
||||
public string Name { get; }
|
||||
public Vector2i Size { get; }
|
||||
public int RefreshRate { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
@@ -39,8 +39,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private GLUniformBuffer<ProjViewMatrices> ProjViewUBO = default!;
|
||||
private GLUniformBuffer<UniformConstants> UniformConstantsUBO = default!;
|
||||
|
||||
private RenderTexture EntityPostRenderTarget = default!;
|
||||
|
||||
private GLBuffer BatchVBO = default!;
|
||||
private GLBuffer BatchEBO = default!;
|
||||
private GLHandle BatchVAO;
|
||||
@@ -88,8 +86,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public override bool Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_configurationManager.OnValueChanged(CVars.DisplayOGLCheckErrors, b => _checkGLErrors = b, true);
|
||||
|
||||
ConfigurationManager.OnValueChanged(CVars.DisplayOGLCheckErrors, b => _checkGLErrors = b, true);
|
||||
|
||||
if (!InitWindowing())
|
||||
{
|
||||
@@ -126,9 +124,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
protected override void ReadConfig()
|
||||
{
|
||||
base.ReadConfig();
|
||||
_lightmapDivider = _configurationManager.GetCVar(CVars.DisplayLightMapDivider);
|
||||
_maxLightsPerScene = _configurationManager.GetCVar(CVars.DisplayMaxLightsPerScene);
|
||||
_enableSoftShadows = _configurationManager.GetCVar(CVars.DisplaySoftShadows);
|
||||
_lightmapDivider = ConfigurationManager.GetCVar(CVars.DisplayLightMapDivider);
|
||||
_maxLightsPerScene = ConfigurationManager.GetCVar(CVars.DisplayMaxLightsPerScene);
|
||||
_enableSoftShadows = ConfigurationManager.GetCVar(CVars.DisplaySoftShadows);
|
||||
}
|
||||
|
||||
protected override void ReloadConfig()
|
||||
@@ -152,6 +150,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
public override event Action<WindowResizedEventArgs>? OnWindowResized;
|
||||
|
||||
public override event Action<WindowFocusedEventArgs>? OnWindowFocused;
|
||||
|
||||
public void Screenshot(ScreenshotType type, Action<Image<Rgb24>> callback)
|
||||
{
|
||||
_queuedScreenshots.Add((type, callback));
|
||||
@@ -238,7 +238,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private (int major, int minor)? ParseGLOverrideVersion()
|
||||
{
|
||||
var overrideGLVersion = _configurationManager.GetCVar(CVars.DisplayOGLOverrideVersion);
|
||||
var overrideGLVersion = ConfigurationManager.GetCVar(CVars.DisplayOGLOverrideVersion);
|
||||
if (string.IsNullOrEmpty(overrideGLVersion))
|
||||
{
|
||||
return null;
|
||||
@@ -314,11 +314,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
ProjViewUBO = new GLUniformBuffer<ProjViewMatrices>(this, BindingIndexProjView, nameof(ProjViewUBO));
|
||||
UniformConstantsUBO = new GLUniformBuffer<UniformConstants>(this, BindingIndexUniformConstants, nameof(UniformConstantsUBO));
|
||||
|
||||
EntityPostRenderTarget = CreateRenderTarget(Vector2i.One * 8 * EyeManager.PixelsPerMeter,
|
||||
new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb, true),
|
||||
name: nameof(EntityPostRenderTarget));
|
||||
|
||||
CreateMainViewport();
|
||||
|
||||
screenBufferHandle = new GLHandle(GL.GenTexture());
|
||||
GL.BindTexture(TextureTarget.Texture2D, screenBufferHandle.Handle);
|
||||
ApplySampleParameters(TextureSampleParameters.Default);
|
||||
ScreenBufferTexture = GenTexture(screenBufferHandle, _framebufferSize, true, null, TexturePixelType.Rgba32);
|
||||
}
|
||||
|
||||
private void CreateMainViewport()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Audio;
|
||||
@@ -21,6 +22,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public IRenderWindow MainWindowRenderTarget { get; }
|
||||
public override Vector2i ScreenSize { get; } = (1280, 720);
|
||||
public Vector2 DefaultWindowScale => (1, 1);
|
||||
public override bool IsFocused => true;
|
||||
|
||||
public ShaderInstance InstanceShader(ClydeHandle handle)
|
||||
{
|
||||
@@ -36,6 +38,13 @@ namespace Robust.Client.Graphics.Clyde
|
||||
public IClydeDebugInfo DebugInfo { get; } = new DummyDebugInfo();
|
||||
public IClydeDebugStats DebugStats { get; } = new DummyDebugStats();
|
||||
|
||||
public event Action<TextEventArgs>? TextEntered;
|
||||
public event Action<MouseMoveEventArgs>? MouseMove;
|
||||
public event Action<KeyEventArgs>? KeyUp;
|
||||
public event Action<KeyEventArgs>? KeyDown;
|
||||
public event Action<MouseWheelEventArgs>? MouseWheel;
|
||||
public event Action<string>? CloseWindow;
|
||||
|
||||
public Texture GetStockTexture(ClydeStockTexture stockTexture)
|
||||
{
|
||||
return new DummyTexture((1, 1));
|
||||
@@ -62,6 +71,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetWindowMonitor(IClydeMonitor monitor)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void RequestWindowAttention()
|
||||
{
|
||||
// Nada.
|
||||
@@ -79,6 +93,18 @@ namespace Robust.Client.Graphics.Clyde
|
||||
remove { }
|
||||
}
|
||||
|
||||
public override event Action<WindowFocusedEventArgs> OnWindowFocused
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public event Action OnWindowScaleChanged
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
// Nada.
|
||||
@@ -94,7 +120,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public Texture LoadTextureFromPNGStream(Stream stream, string? name = null,
|
||||
public OwnedTexture LoadTextureFromPNGStream(Stream stream, string? name = null,
|
||||
TextureLoadParameters? loadParams = null)
|
||||
{
|
||||
using (var image = Image.Load<Rgba32>(stream))
|
||||
@@ -103,7 +129,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
public Texture LoadTextureFromImage<T>(Image<T> image, string? name = null,
|
||||
public OwnedTexture LoadTextureFromImage<T>(Image<T> image, string? name = null,
|
||||
TextureLoadParameters? loadParams = null) where T : unmanaged, IPixel<T>
|
||||
{
|
||||
return new DummyTexture((image.Width, image.Height));
|
||||
@@ -149,6 +175,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return new Viewport();
|
||||
}
|
||||
|
||||
public IEnumerable<IClydeMonitor> EnumerateMonitors()
|
||||
{
|
||||
// TODO: Actually return something.
|
||||
yield break;
|
||||
}
|
||||
|
||||
public ClydeHandle LoadShader(ParsedShader shader, string? name = null)
|
||||
{
|
||||
return default;
|
||||
@@ -260,6 +292,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetVelocity(Vector2 velocity)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DummyBufferedAudioSource : DummyAudioSource, IClydeBufferedAudioSource
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
|
||||
@@ -4,7 +4,6 @@ varying highp vec2 Pos;
|
||||
uniform sampler2D lightMap;
|
||||
uniform highp vec4 modulate;
|
||||
|
||||
#line 1000
|
||||
// [SHADER_HEADER_CODE]
|
||||
|
||||
void main()
|
||||
@@ -13,7 +12,6 @@ void main()
|
||||
|
||||
lowp vec4 COLOR;
|
||||
|
||||
#line 10000
|
||||
// [SHADER_CODE]
|
||||
|
||||
lowp vec3 lightSample = texture2D(lightMap, Pos).rgb;
|
||||
|
||||
@@ -89,9 +89,10 @@ uniform highp vec2 TEXTURE_PIXEL_SIZE;
|
||||
// -- srgb emulation --
|
||||
|
||||
#ifdef HAS_SRGB
|
||||
highp vec4 zTexture(highp vec2 uv)
|
||||
|
||||
highp vec4 zTextureSpec(sampler2D tex, highp vec2 uv)
|
||||
{
|
||||
return texture2D(TEXTURE, uv);
|
||||
return texture2D(tex, uv);
|
||||
}
|
||||
|
||||
highp vec4 zAdjustResult(highp vec4 col)
|
||||
@@ -101,9 +102,9 @@ highp vec4 zAdjustResult(highp vec4 col)
|
||||
#else
|
||||
uniform lowp vec2 SRGB_EMU_CONFIG;
|
||||
|
||||
highp vec4 zTexture(highp vec2 uv)
|
||||
highp vec4 zTextureSpec(sampler2D tex, highp vec2 uv)
|
||||
{
|
||||
highp vec4 col = texture2D(TEXTURE, uv);
|
||||
highp vec4 col = texture2D(tex, uv);
|
||||
if (SRGB_EMU_CONFIG.x > 0.5)
|
||||
{
|
||||
return zFromSrgb(col);
|
||||
@@ -121,5 +122,10 @@ highp vec4 zAdjustResult(highp vec4 col)
|
||||
}
|
||||
#endif
|
||||
|
||||
highp vec4 zTexture(highp vec2 uv)
|
||||
{
|
||||
return zTextureSpec(TEXTURE, uv);
|
||||
}
|
||||
|
||||
// -- Utilities End --
|
||||
|
||||
|
||||
@@ -18,22 +18,23 @@ namespace Robust.Client.Graphics
|
||||
/// </summary>
|
||||
internal abstract class ClydeBase
|
||||
{
|
||||
[Dependency] protected readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] protected readonly IGameControllerInternal _gameController = default!;
|
||||
[Dependency] protected readonly IConfigurationManager ConfigurationManager = default!;
|
||||
|
||||
protected WindowMode WindowMode { get; private set; } = WindowMode.Windowed;
|
||||
protected bool VSync { get; private set; } = true;
|
||||
|
||||
public abstract Vector2i ScreenSize { get; }
|
||||
public abstract bool IsFocused { get; }
|
||||
|
||||
public abstract void SetWindowTitle(string title);
|
||||
|
||||
public virtual bool Initialize()
|
||||
{
|
||||
_configurationManager.OnValueChanged(CVars.DisplayVSync, _vSyncChanged, true);
|
||||
_configurationManager.OnValueChanged(CVars.DisplayWindowMode, _windowModeChanged, true);
|
||||
_configurationManager.OnValueChanged(CVars.DisplayLightMapDivider, LightmapDividerChanged, true);
|
||||
_configurationManager.OnValueChanged(CVars.DisplayMaxLightsPerScene, MaxLightsPerSceneChanged, true);
|
||||
_configurationManager.OnValueChanged(CVars.DisplaySoftShadows, SoftShadowsChanged, true);
|
||||
ConfigurationManager.OnValueChanged(CVars.DisplayVSync, _vSyncChanged, true);
|
||||
ConfigurationManager.OnValueChanged(CVars.DisplayWindowMode, _windowModeChanged, true);
|
||||
ConfigurationManager.OnValueChanged(CVars.DisplayLightMapDivider, LightmapDividerChanged, true);
|
||||
ConfigurationManager.OnValueChanged(CVars.DisplayMaxLightsPerScene, MaxLightsPerSceneChanged, true);
|
||||
ConfigurationManager.OnValueChanged(CVars.DisplaySoftShadows, SoftShadowsChanged, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -45,10 +46,12 @@ namespace Robust.Client.Graphics
|
||||
|
||||
public abstract event Action<WindowResizedEventArgs> OnWindowResized;
|
||||
|
||||
public abstract event Action<WindowFocusedEventArgs> OnWindowFocused;
|
||||
|
||||
protected virtual void ReadConfig()
|
||||
{
|
||||
WindowMode = (WindowMode) _configurationManager.GetCVar(CVars.DisplayWindowMode);
|
||||
VSync = _configurationManager.GetCVar(CVars.DisplayVSync);
|
||||
WindowMode = (WindowMode) ConfigurationManager.GetCVar(CVars.DisplayWindowMode);
|
||||
VSync = ConfigurationManager.GetCVar(CVars.DisplayVSync);
|
||||
}
|
||||
|
||||
private void _vSyncChanged(bool newValue)
|
||||
|
||||
@@ -19,17 +19,18 @@ namespace Robust.Client.Graphics
|
||||
Disposed = true;
|
||||
}
|
||||
|
||||
public void SetTransform(Vector2 position, Angle rotation, Vector2 scale)
|
||||
public void SetTransform(in Vector2 position, in Angle rotation, in Vector2 scale)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
var matrix = Matrix3.Identity;
|
||||
(matrix.R0C0, matrix.R1C1) = scale;
|
||||
matrix.Rotate(rotation);
|
||||
matrix.R0C2 += position.X;
|
||||
matrix.R1C2 += position.Y;
|
||||
var matrix = Matrix3.CreateTransform(in position, in rotation, in scale);
|
||||
SetTransform(in matrix);
|
||||
}
|
||||
|
||||
SetTransform(matrix);
|
||||
public void SetTransform(in Vector2 position, in Angle rotation)
|
||||
{
|
||||
var matrix = Matrix3.CreateTransform(in position, in rotation);
|
||||
SetTransform(in matrix);
|
||||
}
|
||||
|
||||
public abstract void SetTransform(in Matrix3 matrix);
|
||||
|
||||
@@ -6,15 +6,63 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
private const int Ppm = EyeManager.PixelsPerMeter;
|
||||
|
||||
/// <summary>
|
||||
/// Draws an untextured colored rectangle 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="rect">The four vertices of the quad in object space (or world if the transform is identity.).</param>
|
||||
/// <param name="color">Color of the rectangle.</param>
|
||||
/// <param name="filled">Is it filled with color, or just the border lines?</param>
|
||||
public abstract void DrawRect(Box2 rect, Color color, bool filled = true);
|
||||
|
||||
/// <summary>
|
||||
/// Draws an untextured colored rectangle 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="rect">The four vertices of the quad in object space (or world if the transform is identity.).
|
||||
/// The rotation of the rectangle is applied before the transform matrix.</param>
|
||||
/// <param name="color">Color of the rectangle.</param>
|
||||
/// <param name="filled">Is it filled with color, or just the border lines?</param>
|
||||
public abstract void DrawRect(in Box2Rotated rect, Color color, bool filled = true);
|
||||
|
||||
public abstract void DrawTextureRectRegion(Texture texture, Box2 rect, UIBox2? subRegion = null,
|
||||
Color? modulate = null);
|
||||
/// <summary>
|
||||
/// Draws a 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="quad">The four vertices of the quad in object space (or world if the transform is identity.).</param>
|
||||
/// <param name="modulate">A color to multiply the texture by when shading.</param>
|
||||
/// <param name="subRegion">The four corners of the texture sub region in px.</param>
|
||||
public abstract void DrawTextureRectRegion(Texture texture, Box2 quad,
|
||||
Color? modulate = null, UIBox2? subRegion = null);
|
||||
|
||||
public abstract void DrawTextureRectRegion(Texture texture, in Box2Rotated rect, UIBox2? subRegion = null,
|
||||
Color? modulate = null);
|
||||
/// <summary>
|
||||
/// Draws a 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="quad">The four vertices of the quad in object space (or world if the transform is identity.).
|
||||
/// The rotation of the rectangle is applied before the transform matrix.</param>
|
||||
/// <param name="modulate">A color to multiply the texture by when shading.</param>
|
||||
/// <param name="subRegion">The four corners of the texture sub region in px.</param>
|
||||
public abstract void DrawTextureRectRegion(Texture texture, in Box2Rotated quad,
|
||||
Color? modulate = null, UIBox2? subRegion = null);
|
||||
|
||||
/// <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="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, Color? modulate = null)
|
||||
{
|
||||
CheckDisposed();
|
||||
@@ -22,18 +70,35 @@ namespace Robust.Client.Graphics
|
||||
DrawTextureRect(texture, Box2.FromDimensions(position, texture.Size / (float) Ppm), modulate);
|
||||
}
|
||||
|
||||
public void DrawTextureRect(Texture texture, Box2 rect, Color? modulate = null)
|
||||
/// <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="quad">The four vertices of the quad in object space (or world if the transform is identity.).</param>
|
||||
/// <param name="modulate">A color to multiply the texture by when shading.</param>
|
||||
public void DrawTextureRect(Texture texture, Box2 quad, Color? modulate = null)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
DrawTextureRectRegion(texture, rect, null, modulate);
|
||||
DrawTextureRectRegion(texture, quad, modulate);
|
||||
}
|
||||
|
||||
public void DrawTextureRect(Texture texture, in Box2Rotated rect, Color? modulate = null)
|
||||
/// <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="quad">The four vertices of the quad in object space (or world if the transform is identity.).
|
||||
/// The rotation of the rectangle is applied before the transform matrix.</param>
|
||||
/// <param name="modulate">A color to multiply the texture by when shading.</param>
|
||||
public void DrawTextureRect(Texture texture, in Box2Rotated quad, Color? modulate = null)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
DrawTextureRectRegion(texture, rect, null, modulate);
|
||||
DrawTextureRectRegion(texture, in quad, modulate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -40,63 +41,48 @@ namespace Robust.Client.Graphics
|
||||
return GetLineHeight(scale) - GetHeight(scale);
|
||||
}
|
||||
|
||||
[Obsolete("Use GetAscent")] public int Ascent => GetAscent(1);
|
||||
[Obsolete("Use GetHeight")] public int Height => GetHeight(1);
|
||||
[Obsolete("Use GetDescent")] public int Descent => GetDescent(1);
|
||||
[Obsolete("Use GetLineHeight")] public int LineHeight => GetLineHeight(1);
|
||||
[Obsolete("Use GetLineSeparation")] public int LineSeparation => GetLineSeparation(1);
|
||||
|
||||
// Yes, I am aware that using char is bad.
|
||||
// At the same time the font system is nowhere close to rendering Unicode so...
|
||||
/// <summary>
|
||||
/// Draw a character at a certain baseline position on screen.
|
||||
/// </summary>
|
||||
/// <param name="handle">The drawing handle to draw to.</param>
|
||||
/// <param name="chr">
|
||||
/// The Unicode code point to draw. Yes I'm aware about UTF-16 being crap,
|
||||
/// do you think this system can draw anything except ASCII?
|
||||
/// </param>
|
||||
/// <param name="rune">The Unicode code point to draw.</param>
|
||||
/// <param name="baseline">The baseline from which to draw the character.</param>
|
||||
/// <param name="scale">DPI scale factor to render the font at.</param>
|
||||
/// <param name="color">The color of the character to draw.</param>
|
||||
/// <param name="fallback">If the character is not available, render "<22>" instead.</param>
|
||||
/// <returns>How much to advance the cursor to draw the next character.</returns>
|
||||
[Obsolete("Use DrawChar with scale support.")]
|
||||
public float DrawChar(DrawingHandleScreen handle, char chr, Vector2 baseline, Color color)
|
||||
{
|
||||
return DrawChar(handle, chr, baseline, 1, color);
|
||||
}
|
||||
public abstract float DrawChar(
|
||||
DrawingHandleScreen handle, Rune rune, Vector2 baseline, float scale,
|
||||
Color color, bool fallback=true);
|
||||
|
||||
public abstract float DrawChar(DrawingHandleScreen handle, char chr, Vector2 baseline, float scale,
|
||||
Color color);
|
||||
[Obsolete("Use Rune variant instead")]
|
||||
public float DrawChar(DrawingHandleScreen handle, char chr, Vector2 baseline, float scale, Color color)
|
||||
{
|
||||
return DrawChar(handle, (Rune) chr, baseline, scale, color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets metrics describing the dimensions and positioning of a single glyph in the font.
|
||||
/// </summary>
|
||||
/// <param name="chr">The character to fetch the glyph metrics for.</param>
|
||||
/// <param name="rune">The unicode codepoint to fetch the glyph metrics for.</param>
|
||||
/// <param name="scale">DPI scale factor to render the font at.</param>
|
||||
/// <param name="fallback">
|
||||
/// If the character is not available, return data for "<22>" instead.
|
||||
/// This can still fail if the font does not define <20> itself.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <c>null</c> if this font does not have a glyph for the specified character,
|
||||
/// otherwise the metrics you asked for.
|
||||
/// </returns>
|
||||
/// <seealso cref="TryGetCharMetrics"/>
|
||||
[Obsolete("Use GetCharMetrics with scale support.")]
|
||||
public CharMetrics? GetCharMetrics(char chr)
|
||||
{
|
||||
return GetCharMetrics(chr, 1);
|
||||
}
|
||||
|
||||
public abstract CharMetrics? GetCharMetrics(char chr, float scale);
|
||||
public abstract CharMetrics? GetCharMetrics(Rune rune, float scale, bool fallback=true);
|
||||
|
||||
/// <summary>
|
||||
/// Try-pattern version of <see cref="GetCharMetrics"/>.
|
||||
/// </summary>
|
||||
[Obsolete("Use TryGetCharMetrics with scale support.")]
|
||||
public bool TryGetCharMetrics(char chr, out CharMetrics metrics)
|
||||
public bool TryGetCharMetrics(Rune rune, float scale, out CharMetrics metrics, bool fallback=true)
|
||||
{
|
||||
return TryGetCharMetrics(chr, 1, out metrics);
|
||||
}
|
||||
|
||||
public bool TryGetCharMetrics(char chr, float scale, out CharMetrics metrics)
|
||||
{
|
||||
var maybe = GetCharMetrics(chr, scale);
|
||||
var maybe = GetCharMetrics(rune, scale);
|
||||
if (maybe.HasValue)
|
||||
{
|
||||
metrics = maybe.Value;
|
||||
@@ -128,15 +114,23 @@ namespace Robust.Client.Graphics
|
||||
public override int GetDescent(float scale) => Handle.GetDescent(scale);
|
||||
public override int GetLineHeight(float scale) => Handle.GetLineHeight(scale);
|
||||
|
||||
public override float DrawChar(DrawingHandleScreen handle, char chr, Vector2 baseline, float scale, Color color)
|
||||
public override float DrawChar(DrawingHandleScreen handle, Rune rune, Vector2 baseline, float scale, Color color, bool fallback=true)
|
||||
{
|
||||
var metrics = Handle.GetCharMetrics(chr, scale);
|
||||
var metrics = Handle.GetCharMetrics(rune, scale);
|
||||
if (!metrics.HasValue)
|
||||
{
|
||||
return 0;
|
||||
if (fallback && !Rune.IsWhiteSpace(rune))
|
||||
{
|
||||
rune = new Rune('<27>');
|
||||
metrics = Handle.GetCharMetrics(rune, scale);
|
||||
if (!metrics.HasValue)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
var texture = Handle.GetCharTexture(chr, scale);
|
||||
var texture = Handle.GetCharTexture(rune, scale);
|
||||
if (texture == null)
|
||||
{
|
||||
return metrics.Value.Advance;
|
||||
@@ -147,9 +141,12 @@ namespace Robust.Client.Graphics
|
||||
return metrics.Value.Advance;
|
||||
}
|
||||
|
||||
public override CharMetrics? GetCharMetrics(char chr, float scale)
|
||||
public override CharMetrics? GetCharMetrics(Rune rune, float scale, bool fallback=true)
|
||||
{
|
||||
return Handle.GetCharMetrics(chr, scale);
|
||||
var metrics = Handle.GetCharMetrics(rune, scale);
|
||||
if (metrics == null && !Rune.IsWhiteSpace(rune) && fallback)
|
||||
return Handle.GetCharMetrics(new Rune('<27>'), scale);
|
||||
return metrics;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,13 +157,13 @@ namespace Robust.Client.Graphics
|
||||
public override int GetDescent(float scale) => default;
|
||||
public override int GetLineHeight(float scale) => default;
|
||||
|
||||
public override float DrawChar(DrawingHandleScreen handle, char chr, Vector2 baseline, float scale, Color color)
|
||||
public override float DrawChar(DrawingHandleScreen handle, Rune rune, Vector2 baseline, float scale, Color color, bool fallback=true)
|
||||
{
|
||||
// Nada, it's a dummy after all.
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override CharMetrics? GetCharMetrics(char chr, float scale)
|
||||
public override CharMetrics? GetCharMetrics(Rune rune, float scale, bool fallback=true)
|
||||
{
|
||||
// Nada, it's a dummy after all.
|
||||
return null;
|
||||
|
||||
@@ -4,9 +4,6 @@ using System.IO;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using SharpFont;
|
||||
@@ -20,18 +17,18 @@ namespace Robust.Client.Graphics
|
||||
private const int SheetWidth = 256;
|
||||
private const int SheetHeight = 256;
|
||||
|
||||
[Dependency] private readonly IConfigurationManager _configuration = default!;
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
private readonly IClyde _clyde;
|
||||
|
||||
private uint BaseFontDPI;
|
||||
private uint _baseFontDpi = 96;
|
||||
|
||||
private readonly Library _library;
|
||||
|
||||
private readonly Dictionary<(FontFaceHandle, int fontSize), FontInstanceHandle> _loadedInstances =
|
||||
new();
|
||||
|
||||
public FontManager()
|
||||
public FontManager(IClyde clyde)
|
||||
{
|
||||
_clyde = clyde;
|
||||
_library = new Library();
|
||||
}
|
||||
|
||||
@@ -42,9 +39,9 @@ namespace Robust.Client.Graphics
|
||||
return handle;
|
||||
}
|
||||
|
||||
void IFontManagerInternal.Initialize()
|
||||
void IFontManagerInternal.SetFontDpi(uint fontDpi)
|
||||
{
|
||||
BaseFontDPI = (uint) _configuration.GetCVar(CVars.DisplayFontDpi);
|
||||
_baseFontDpi = fontDpi;
|
||||
}
|
||||
|
||||
public IFontInstanceHandle MakeInstance(IFontFaceHandle handle, int size)
|
||||
@@ -64,7 +61,7 @@ namespace Robust.Client.Graphics
|
||||
private ScaledFontData _generateScaledDatum(FontInstanceHandle instance, float scale)
|
||||
{
|
||||
var ftFace = instance.FaceHandle.Face;
|
||||
ftFace.SetCharSize(0, instance.Size, 0, (uint) (BaseFontDPI * scale));
|
||||
ftFace.SetCharSize(0, instance.Size, 0, (uint) (_baseFontDpi * scale));
|
||||
|
||||
var ascent = ftFace.Size.Metrics.Ascender.ToInt32();
|
||||
var descent = -ftFace.Size.Metrics.Descender.ToInt32();
|
||||
@@ -83,7 +80,7 @@ namespace Robust.Client.Graphics
|
||||
return;
|
||||
|
||||
var face = instance.FaceHandle.Face;
|
||||
face.SetCharSize(0, instance.Size, 0, (uint) (BaseFontDPI * scale));
|
||||
face.SetCharSize(0, instance.Size, 0, (uint) (_baseFontDpi * scale));
|
||||
face.LoadGlyph(glyph, LoadFlags.Default, LoadTarget.Normal);
|
||||
face.Glyph.RenderGlyph(RenderMode.Normal);
|
||||
|
||||
@@ -189,7 +186,7 @@ namespace Robust.Client.Graphics
|
||||
OwnedTexture GenSheet()
|
||||
{
|
||||
var sheet = _clyde.CreateBlankTexture<A8>((SheetWidth, SheetHeight),
|
||||
$"font-{face.FamilyName}-{instance.Size}-{(uint) (BaseFontDPI * scale)}-sheet{scaled.AtlasTextures.Count}");
|
||||
$"font-{face.FamilyName}-{instance.Size}-{(uint) (_baseFontDpi * scale)}-sheet{scaled.AtlasTextures.Count}");
|
||||
scaled.AtlasTextures.Add(sheet);
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -13,12 +14,15 @@ namespace Robust.Client.Graphics
|
||||
|
||||
Vector2i ScreenSize { get; }
|
||||
|
||||
bool IsFocused { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The default scale ratio for window contents, given to us by the OS.
|
||||
/// </summary>
|
||||
Vector2 DefaultWindowScale { get; }
|
||||
|
||||
void SetWindowTitle(string title);
|
||||
void SetWindowMonitor(IClydeMonitor monitor);
|
||||
|
||||
/// <summary>
|
||||
/// This is the magic method to make the game window ping you in the task bar.
|
||||
@@ -27,10 +31,14 @@ namespace Robust.Client.Graphics
|
||||
|
||||
event Action<WindowResizedEventArgs> OnWindowResized;
|
||||
|
||||
Texture LoadTextureFromPNGStream(Stream stream, string? name = null,
|
||||
event Action<WindowFocusedEventArgs> OnWindowFocused;
|
||||
|
||||
event Action OnWindowScaleChanged;
|
||||
|
||||
OwnedTexture LoadTextureFromPNGStream(Stream stream, string? name = null,
|
||||
TextureLoadParameters? loadParams = null);
|
||||
|
||||
Texture LoadTextureFromImage<T>(Image<T> image, string? name = null,
|
||||
OwnedTexture LoadTextureFromImage<T>(Image<T> image, string? name = null,
|
||||
TextureLoadParameters? loadParams = null) where T : unmanaged, IPixel<T>;
|
||||
|
||||
/// <summary>
|
||||
@@ -100,6 +108,8 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
|
||||
IClydeViewport CreateViewport(Vector2i size, string? name = null);
|
||||
|
||||
IEnumerable<IClydeMonitor> EnumerateMonitors();
|
||||
}
|
||||
|
||||
// TODO: Maybe implement IDisposable for render targets. I got lazy and didn't.
|
||||
|
||||
@@ -20,5 +20,6 @@ namespace Robust.Client.Graphics
|
||||
void SetVolume(float decibels);
|
||||
void SetOcclusion(float blocks);
|
||||
void SetPlaybackPosition(float seconds);
|
||||
void SetVelocity(Vector2 velocity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -16,6 +17,13 @@ namespace Robust.Client.Graphics
|
||||
bool Initialize();
|
||||
void Ready();
|
||||
|
||||
event Action<TextEventArgs> TextEntered;
|
||||
event Action<MouseMoveEventArgs> MouseMove;
|
||||
event Action<KeyEventArgs> KeyUp;
|
||||
event Action<KeyEventArgs> KeyDown;
|
||||
event Action<MouseWheelEventArgs> MouseWheel;
|
||||
event Action<string> CloseWindow;
|
||||
|
||||
ClydeHandle LoadShader(ParsedShader shader, string? name = null);
|
||||
|
||||
void ReloadShader(ClydeHandle handle, ParsedShader newShader);
|
||||
|
||||
18
Robust.Client/Graphics/IClydeMonitor.cs
Normal file
18
Robust.Client/Graphics/IClydeMonitor.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a connected monitor on the user's system.
|
||||
/// </summary>
|
||||
public interface IClydeMonitor
|
||||
{
|
||||
/// <summary>
|
||||
/// This ID is not consistent between startups of the game.
|
||||
/// </summary>
|
||||
int Id { get; }
|
||||
string Name { get; }
|
||||
Vector2i Size { get; }
|
||||
int RefreshRate { get; }
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
IFontFaceHandle Load(Stream stream);
|
||||
IFontInstanceHandle MakeInstance(IFontFaceHandle handle, int size);
|
||||
void Initialize();
|
||||
void SetFontDpi(uint fontDpi);
|
||||
}
|
||||
|
||||
internal interface IFontFaceHandle
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
|
||||
[PublicAPI]
|
||||
public interface IOverlayManager
|
||||
{
|
||||
void AddOverlay(Overlay overlay);
|
||||
void RemoveOverlay(string id);
|
||||
bool HasOverlay(string id);
|
||||
bool AddOverlay(Overlay overlay);
|
||||
|
||||
Overlay GetOverlay(string id);
|
||||
T GetOverlay<T>(string id) where T : Overlay;
|
||||
bool RemoveOverlay(Overlay overlay);
|
||||
bool RemoveOverlay(Type overlayClass);
|
||||
bool RemoveOverlay<T>() where T : Overlay;
|
||||
|
||||
bool TryGetOverlay(string id, [NotNullWhen(true)] out Overlay? overlay);
|
||||
bool TryGetOverlay<T>(string id, [NotNullWhen(true)] out T? overlay) where T : Overlay;
|
||||
bool TryGetOverlay(Type overlayClass, out Overlay? overlay);
|
||||
bool TryGetOverlay<T>(out T? overlay) where T : Overlay;
|
||||
|
||||
Overlay GetOverlay(Type overlayClass);
|
||||
T GetOverlay<T>() where T : Overlay;
|
||||
|
||||
bool HasOverlay(Type overlayClass);
|
||||
bool HasOverlay<T>() where T : Overlay;
|
||||
|
||||
IEnumerable<Overlay> AllOverlays { get; }
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user