Compare commits

..

2 Commits

Author SHA1 Message Date
Pieter-Jan Briers
c6328edefc Update CefGlue 2021-11-02 16:25:44 +01:00
Pieter-Jan Briers
2c4c58edd5 Trying to get res:// and usr:// cookies to wrok 2021-11-02 16:03:08 +01:00
1065 changed files with 18212 additions and 29677 deletions

19
.github/CODEOWNERS vendored
View File

@@ -1,20 +1,7 @@
# Last match in file takes precedence.
# Ping for all PRs
* @Acruid @PJB3005 @Silvertorch5
/Robust.*/Audio/Midi/ @Zumorica
/Robust.Client.NameGenerator @PaulRitter
/Robust.Client.Injectors @PaulRitter
/Robust.Generators @PaulRitter
/Robust.Analyzers @PaulRitter
/Robust.*/GameStates @PaulRitter
/Robust.Shared/Analyzers @PaulRitter
/Robust.*/Serialization @PaulRitter @DrSmugleaf
/Robust.*/Prototypes @PaulRitter
/Robust.Shared/GameObjects/ComponentDependencies @PaulRitter
/Robust.*/Containers @PaulRitter
# Be they Fluent translations or Freemarker templates, I know them both!
*.ftl @RemieRichards
# Ping for all PRs
* @Acruid @PJB3005 @Silvertorch5

View File

@@ -1,33 +0,0 @@
name: Benchmarks
#on:
# push
#schedule:
# - cron: '0 5 * * *'
#push:
# tags:
# - 'v*'
env:
ROBUST_BENCHMARKS_ENABLE_SQL: 1
ROBUST_BENCHMARKS_SQL_ADDRESS: ${{ secrets.BENCHMARKS_WRITE_ADDRESS }}
ROBUST_BENCHMARKS_SQL_PORT: ${{ secrets.BENCHMARKS_WRITE_PORT }}
ROBUST_BENCHMARKS_SQL_USER: ${{ secrets.BENCHMARKS_WRITE_USER }}
ROBUST_BENCHMARKS_SQL_PASSWORD: ${{ secrets.BENCHMARKS_WRITE_PASSWORD }}
ROBUST_BENCHMARKS_SQL_DATABASE: benchmarks
jobs:
benchmark:
name: Run Benchmarks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
- name: Install dependencies
run: dotnet restore
- name: Run benchmark
run: cd Robust.Benchmarks && sudo dotnet run --filter '*' --configuration Release

View File

@@ -1,34 +0,0 @@
name: Build & Publish DocFX
on:
schedule:
- cron: "0 0 * * 0"
jobs:
docfx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
- name: Install dependencies
run: dotnet restore
- name: Build Project
run: dotnet build --no-restore /p:WarningsAsErrors=nullable
- name: Build DocFX
uses: nikeee/docfx-action@v1.0.0
with:
args: Robust.Docfx/docfx.json
- name: Publish Docfx Documentation on GitHub Pages
uses: maxheld83/ghpages@master
env:
BUILD_DIR: Robust.Docfx/_robust-site
GH_PAT: ${{ secrets.GH_PAT }}

View File

@@ -22,12 +22,10 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
dotnet-version: 5.0.100
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore /p:WarningsAsErrors=nullable
- name: Test Engine
run: dotnet test --no-build Robust.UnitTesting/Robust.UnitTesting.csproj -v n

View File

@@ -42,7 +42,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.100
dotnet-version: 5.0.100
- name: Build
run: dotnet build

View File

@@ -23,7 +23,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.100
dotnet-version: 5.0.100
- name: Package client
run: Tools/package_client_build.py -p windows mac linux

View File

@@ -21,7 +21,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.100
dotnet-version: 5.0.100
- name: Disable submodule autoupdate
run: touch BuildChecker/DISABLE_SUBMODULE_AUTOUPDATE
@@ -38,4 +38,4 @@ jobs:
- name: Content.Tests
run: dotnet test --no-build Content.Tests/Content.Tests.csproj -v n
- name: Content.IntegrationTests
run: COMPlus_gcServer=1 dotnet test --no-build Content.IntegrationTests/Content.IntegrationTests.csproj -v n
run: COMPlus_gcServer=1 dotnet test --no-build Content.IntegrationTests/Content.IntegrationTests.csproj -v n

24
.gitignore vendored
View File

@@ -41,6 +41,18 @@ _ReSharper*/
# Resources
*.resources
/Resources/textures/Animations/*.*
/Resources/ResourcePack.zip
/Resources/textures/*_Animations.png
/Resources/textures/*_Decals.png
/Resources/textures/*_Effects.png
/Resources/textures/*_Items.png
/Resources/textures/*_Objects.png
/Resources/textures/*_Tiles.png
/Resources/textures/*_UserInterface.png
/Resources/textures/*.TAI
/Resources/SpriteRenderer/Ogre.log
/Resources/Spriterenderer/output
/Media
/setenv.bat
@@ -66,6 +78,16 @@ project.lock.json
# Created by NUnit.
TestResult.xml
NetSerializerDebug.dll
# We're not gonna ship Mac extlibs with the repo due to size. (11 MB)
Third-Party/extlibs/Mac/
# Or the automatically-fetched Windows natives, for that matter.
Third-Party/extlibs/Windows/
# Actually I'll make this folder because SS14.Shared.Bsdiff isn't third-party is it?
Dependencies/
# Python stuff
__pycache__
.mypy_cache
@@ -76,5 +98,3 @@ MSBuild/Robust.Custom.targets
release/
Robust.Docfx/*-site
Robust.Docfx/api

3
.gitmodules vendored
View File

@@ -13,6 +13,9 @@
[submodule "ManagedHttpListener"]
path = ManagedHttpListener
url = https://github.com/space-wizards/ManagedHttpListener.git
[submodule "Linguini"]
path = Linguini
url = https://github.com/space-wizards/Linguini
[submodule "cefglue"]
path = cefglue
url = https://github.com/space-wizards/cefglue.git

1
Linguini Submodule

Submodule Linguini added at b3c05c2f31

View File

@@ -1,4 +0,0 @@
<Project>
<!-- This file automatically reset by Tools/version.py -->
<PropertyGroup><Version>0.8.86</Version></PropertyGroup>
</Project>

View File

@@ -1,10 +1,9 @@
<Project>
<!-- Engine-specific properties. Content should not use this file. -->
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>10</LangVersion>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>9</LangVersion>
<Nullable>enable</Nullable>
<WarningsAsErrors>nullable</WarningsAsErrors>
</PropertyGroup>
<Import Project="Robust.Engine.Version.props" />
</Project>

View File

@@ -26,7 +26,7 @@
<TargetOS Condition="'$(TargetOS)' == ''">$(ActualOS)</TargetOS>
<Python>python3</Python>
<Python Condition="'$(ActualOS)' == 'Windows'">py -3</Python>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<EnableClientScripting>True</EnableClientScripting>
<!-- Client scripting is disabled on full release builds for security and size reasons. -->
<EnableClientScripting Condition="'$(FullRelease)' == 'True'">False</EnableClientScripting>

View File

@@ -1,122 +0,0 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
<!--
Stuff for using ILLink trimming without self-contained deployments.
This is not something officially supported by the .NET SDK currently, but we can simply run ILLink ourselves.
-->
<!--
A lot of stuff taken from Microsoft.NET.ILLink.targets in the SDK files.
-->
<ItemDefinitionGroup>
<RobustLinkRoots>
<Visible>false</Visible>
</RobustLinkRoots>
<RobustLinkAssemblies>
<Visible>false</Visible>
</RobustLinkAssemblies>
</ItemDefinitionGroup>
<Target Name="RobustILLink"
BeforeTargets="ILLink"
Condition="'$(PublishTrimmed)' != 'true' And
'$(RobustILLink)' == 'true'"
DependsOnTargets="_ComputeAssembliesToPostprocessOnPublish">
<ComputeManagedAssemblies Assemblies="@(ResolvedFileToPublish)">
<Output TaskParameter="ManagedAssemblies" ItemName="_ResolvedFileToPublishFiltered" />
</ComputeManagedAssemblies>
<JoinItems Left="@(_ResolvedFileToPublishFiltered)" LeftKey="FileName" LeftMetadata="*"
Right="@(RobustLinkRoots)"
ItemSpecToUse="Left">
<Output TaskParameter="JoinResult" ItemName="_RobustLinkRootsJoined" />
</JoinItems>
<JoinItems Left="@(_ResolvedFileToPublishFiltered)" LeftKey="FileName" LeftMetadata="*"
Right="@(RobustLinkAssemblies)"
ItemSpecToUse="Left">
<Output TaskParameter="JoinResult" ItemName="_RobustLinkAssembliesJoined" />
</JoinItems>
<PropertyGroup>
<TrimMode Condition=" '$(TrimMode)' == '' ">link</TrimMode>
<TrimmerDefaultAction Condition=" '$(TrimmerDefaultAction)' == '' ">copy</TrimmerDefaultAction>
<_ExtraTrimmerArgs>--skip-unresolved true $(_ExtraTrimmerArgs)</_ExtraTrimmerArgs>
<ILLinkTreatWarningsAsErrors Condition=" '$(ILLinkTreatWarningsAsErrors)' == '' ">$(TreatWarningsAsErrors)</ILLinkTreatWarningsAsErrors>
<TrimmerSingleWarn Condition=" '$(TrimmerSingleWarn)' == '' ">true</TrimmerSingleWarn>
</PropertyGroup>
<ItemGroup>
<RobustAssemblyToLink Include="@(_RobustLinkRootsJoined)">
<TrimMode>Copy</TrimMode>
</RobustAssemblyToLink>
<RobustAssemblyToLink Include="@(_RobustLinkAssembliesJoined)">
<TrimMode>Link</TrimMode>
</RobustAssemblyToLink>
</ItemGroup>
<ItemGroup>
<!-- The linker implicitly picks up PDBs next to input assemblies. We will filter these out of the publish set. -->
<__PDBToLink Include="@(ResolvedFileToPublish)" Exclude="@(RobustAssemblyToLink->'%(RelativeDir)%(Filename).pdb')" />
<_PDBToLink Include="@(ResolvedFileToPublish)" Exclude="@(__PDBToLink)" />
</ItemGroup>
<ItemGroup>
<_LinkedResolvedFileToPublishCandidate Include="@(RobustAssemblyToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" />
<_LinkedResolvedFileToPublishCandidate Include="@(_PDBToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" />
</ItemGroup>
<!--<Message Text="@(ResolvedFileToPublish)" Importance="high" />-->
<ItemGroup>
<_TrimmerFeatureSettings Include="@(RuntimeHostConfigurationOption)"
Condition="'%(RuntimeHostConfigurationOption.Trim)' == 'true'" />
</ItemGroup>
<Delete Files="@(_LinkedResolvedFileToPublishCandidate)" />
<ILLink AssemblyPaths="@(RobustAssemblyToLink)"
ReferenceAssemblyPaths="@(ReferencePath)"
RootAssemblyNames="@(RobustLinkRoots)"
TrimMode="Skip"
DefaultAction="$(TrimmerDefaultAction)"
RemoveSymbols="false"
FeatureSettings="@(_TrimmerFeatureSettings)"
CustomData="@(_TrimmerCustomData)"
BeforeFieldInit="$(_TrimmerBeforeFieldInit)"
OverrideRemoval="$(_TrimmerOverrideRemoval)"
UnreachableBodies="$(_TrimmerUnreachableBodies)"
UnusedInterfaces="$(_TrimmerUnusedInterfaces)"
IPConstProp="$(_TrimmerIPConstProp)"
Sealer="$(_TrimmerSealer)"
Warn="$(ILLinkWarningLevel)"
NoWarn="$(NoWarn)"
TreatWarningsAsErrors="$(ILLinkTreatWarningsAsErrors)"
WarningsAsErrors="$(WarningsAsErrors)"
WarningsNotAsErrors="$(WarningsNotAsErrors)"
SingleWarn="$(TrimmerSingleWarn)"
CustomSteps="@(_TrimmerCustomSteps)"
RootDescriptorFiles="@(TrimmerRootDescriptor)"
OutputDirectory="$(IntermediateLinkDir)"
DumpDependencies="$(_TrimmerDumpDependencies)"
ExtraArgs="$(_ExtraTrimmerArgs)"
ToolExe="$(_DotNetHostFileName)"
ToolPath="$(_DotNetHostDirectory)"
ContinueOnError="ErrorAndContinue">
<Output TaskParameter="ExitCode" PropertyName="_ILLinkExitCode" />
</ILLink>
<Touch Files="$(_LinkSemaphore)" AlwaysCreate="true" Condition=" '$(_ILLinkExitCode)' == '0' " />
<ItemGroup>
<_LinkedResolvedFileToPublish Include="@(_LinkedResolvedFileToPublishCandidate)" Condition="Exists('%(Identity)')" />
<ResolvedFileToPublish Remove="@(RobustAssemblyToLink)" />
<ResolvedFileToPublish Remove="@(_PDBToLink)" />
<ResolvedFileToPublish Include="@(_LinkedResolvedFileToPublish)" />
</ItemGroup>
</Target>
</Project>

View File

@@ -18,18 +18,20 @@ namespace OpenToolkit.GraphicsLibraryFramework
NativeLibrary.SetDllImportResolver(typeof(GLFWNative).Assembly, (name, assembly, path) =>
{
// Please keep in sync with what Robust.Shared/DllMapHelper.cs does.
if (name != "glfw3.dll")
{
return IntPtr.Zero;
}
string rName = null;
if (OperatingSystem.IsLinux()) rName = "libglfw.so.3";
else if (OperatingSystem.IsMacOS()) rName = "libglfw.3.dylib";
if (OperatingSystem.IsLinux())
{
return NativeLibrary.Load("libglfw.so.3", assembly, path);
}
if ((rName != null) && NativeLibrary.TryLoad(rName, assembly, path, out var handle))
return handle;
if (OperatingSystem.IsMacOS())
{
return NativeLibrary.Load("libglfw.3.dylib", assembly, path);
}
return IntPtr.Zero;
});

View File

@@ -10,7 +10,7 @@ Use the [content repo](https://github.com/space-wizards/space-station-14) for ac
## Documentation/Wiki
The [wiki](https://docs.spacestation14.io/) has documentation on SS14s content, engine, game design and more. We also have lots of resources for new contributors to the project.
The [HackMD Wiki](https://hackmd.io/@ss14/docs/wiki) has documentation on SS14s content, engine, game design and more. We also have lots of resources for new contributors to the project.
## Contributing

8
Resources/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
# .import files are made by Godot because the assets are exposed if using symlinks.
# IF you need to persist a .import file because of something used Godot-side (GUI-side),
# you can do a negation with !.
*.import
# Negation would be like this:
#!/Textures/UserInterface/1pxwhite.png.import
!/Scenes/SS14Window/closewindow.png.import
/I_MADE_THE_SYMLINK

View File

@@ -1 +0,0 @@
defaultwindow-placeholder-title = Exemplary Window Title Here

View File

@@ -0,0 +1 @@
ss14window-placeholder-title = Exemplary Window Title Here

View File

@@ -7,8 +7,3 @@
- type: shader
id: shaded
kind: canvas
- type: shader
id: bgra
kind: source
path: "/Shaders/Internal/bgra.swsl"

View File

@@ -1,6 +0,0 @@
// Swaps B and R channel so you can render BGRA stuff without swizzling at upload time.
// Currently used by CEF.
void fragment() {
COLOR = zTexture(UV).bgra;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,2 @@
sample:
filter: true

View File

@@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="113.67364mm"
height="56.37999mm"
viewBox="0 0 113.67364 56.37999"
version="1.1"
id="svg3223"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
sodipodi:docname="logo.svg"
inkscape:export-filename="/home/pj/Projects/space-station-14/RobustToolbox/Resources/Textures/Logo/logo.png"
inkscape:export-xdpi="67.034012"
inkscape:export-ydpi="67.034012">
<defs
id="defs3217" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="159.25688"
inkscape:cy="149.8376"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:window-width="1920"
inkscape:window-height="1043"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata3220">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-56.556041,-109.30405)">
<g
transform="translate(-3.7799155,-23.482217)"
id="g2559">
<path
inkscape:connector-curvature="0"
id="path148"
style="fill:#e23229;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 143.44393,145.03077 0.1484,-0.70241 5.14257,-0.35974 -6.23062,29.75753 -0.69199,0.0483 6.08255,-29.05476 z m 1.18682,31.16724 7.31835,-35.07438 -10.43302,0.72923 -1.28514,6.12249 4.45021,-0.31125 -6.03235,28.95253 z" />
<path
sodipodi:nodetypes="cccccccccccccccccccccc"
inkscape:connector-curvature="0"
id="path152"
style="fill:#e23229;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 169.25392,142.53363 -3.53672,16.8925 4.79124,-0.32874 -0.15681,0.75402 -4.79234,0.32881 -2.53549,12.11072 -0.75405,0.052 2.53724,-12.11108 -11.37332,0.78724 z m -18.98495,21.327 11.37324,-0.78724 -2.5247,12.11146 5.98312,-0.41839 2.52513,-12.10406 4.82658,-0.33116 1.26721,-6.07903 -4.82564,0.33109 3.52398,-16.89101 -4.56014,0.33565 -16.70168,19.60839 z" />
<path
inkscape:connector-curvature="0"
id="path156"
style="fill:#fefefe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 88.591903,151.83158 -2.595368,0.18103 0.741705,-3.62685 2.595079,-0.182 z m -4.581719,-6.72375 -3.99857,19.05572 3.468889,-0.24195 1.774487,-8.38766 6.064403,-0.42299 2.224789,-10.66981 z" />
<path
inkscape:connector-curvature="0"
id="path160"
style="fill:#fefefe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 100.85029,147.82177 2.83371,-0.19837 -0.74166,3.60045 -2.83371,0.19836 z m -6.726869,15.77696 3.469312,-0.24198 1.774487,-8.38695 2.8337,-0.19837 -1.77453,8.38695 3.46938,-0.24296 3.99895,-19.05608 -9.771974,0.68329 z" />
<path
inkscape:connector-curvature="0"
id="path164"
style="fill:#fefefe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 108.80467,162.68768 9.74554,-0.68118 1.377,-6.50502 -3.4693,0.24296 -0.63527,2.9832 -2.80718,0.19654 2.51548,-12.01379 2.80731,-0.19754 -0.50239,2.4195 3.46902,-0.24294 1.24513,-5.93998 -9.74589,0.68119 z" />
<path
inkscape:connector-curvature="0"
id="path168"
style="fill:#fefefe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 135.66767,145.61788 0.7409,-3.5211 -8.95132,0.6259 -3.99824,19.0564 8.95097,-0.62626 0.74082,-3.52077 -5.4816,0.383 1.03272,-4.89224 3.78675,-0.26474 0.74189,-3.52113 -3.78675,0.26474 0.74081,-3.6011 z" />
<path
inkscape:connector-curvature="0"
id="path172"
style="fill:#d5d5d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 71.070134,172.95255 -4.734664,0.33077 0.57447,-2.73456 2.139294,-0.14973 -0.376241,1.80924 2.595442,-0.18103 0.930868,-4.44332 -7.330114,0.5118 -1.66358,8.00131 4.734672,-0.33077 -0.773014,3.62025 -2.119829,0.1484 0.475696,-2.23174 -2.614982,0.18237 -1.030327,4.86616 7.330104,-0.5118 z" />
<path
inkscape:connector-curvature="0"
id="path176"
style="fill:#d5d5d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 74.794828,169.99754 2.496095,-0.17422 -2.436556,11.62085 2.595151,-0.182 2.436903,-11.62086 2.496023,-0.17421 0.554678,-2.63441 -7.587516,0.53064 z" />
<path
inkscape:connector-curvature="0"
id="path180"
style="fill:#d5d5d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 87.573322,171.75893 0.554606,-2.69318 2.119754,-0.1484 -0.554978,2.69354 z m 2.832629,8.59803 2.991888,-14.25529 -7.310379,0.51154 -2.99153,14.25493 2.595227,-0.18201 1.327293,-6.27378 2.11968,-0.14839 -1.327288,6.27414 z" />
<path
inkscape:connector-curvature="0"
id="path184"
style="fill:#d5d5d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 98.977684,179.78056 2.436926,-11.62086 2.49608,-0.1752 0.55493,-2.63374 -7.587518,0.53065 -0.554634,2.63373 2.495947,-0.17421 -2.436599,11.6212 z" />
<path
inkscape:connector-curvature="0"
id="path188"
style="fill:#d5d5d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 104.62426,179.38562 2.5951,-0.18101 2.9915,-14.25528 -2.59522,0.182 z" />
<path
inkscape:connector-curvature="0"
id="path192"
style="fill:#d5d5d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 115.73198,167.18138 2.11975,-0.14839 -1.88236,8.98749 -2.11949,0.14838 z m -5.03203,11.80203 7.31001,-0.51042 2.99153,-14.25494 -7.31028,0.51044 z" />
<path
inkscape:connector-curvature="0"
id="path196"
style="fill:#d5d5d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 124.08538,178.07141 1.36653,-6.47511 1.18856,6.29603 2.25853,-0.15792 2.99192,-14.25493 -2.59522,0.18201 -1.36734,6.57353 -1.18854,-6.39514 -2.25854,0.15791 -2.99084,14.25493 z" />
<path
inkscape:connector-curvature="0"
id="path200"
style="fill:#fefefe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 66.677649,158.65784 -1.377018,6.50538 9.798398,-0.68471 2.489415,-11.87958 -6.329111,0.44214 0.767747,-3.65465 2.860076,-0.20018 -0.503204,2.41917 3.469299,-0.24297 1.244526,-5.93996 -4.328229,0.30289 -8.728772,0.56991 2.909827,1.489 -1.87565,9.01912 6.329037,-0.44213 -1.032406,4.83973 -2.833645,0.19738 0.635302,-2.98354 z" />
<path
inkscape:connector-curvature="0"
id="path204"
style="fill:#e23229;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 70.519326,139.92277 -0.277457,1.36553 103.473271,-7.11317 0.29445,-1.38886 z" />
<path
inkscape:connector-curvature="0"
id="path208"
style="fill:#e23229;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34773216"
d="m 60.335956,189.16626 103.473274,-7.11356 0.29444,-1.38885 -103.489968,7.13649 z" />
<path
sodipodi:nodetypes="cccccccc"
inkscape:connector-curvature="0"
id="path1772"
d="m 167.92741,145.26275 -12.87687,14.89523 9.91366,-0.68019 z m -3.743,7.27758 -1.02896,4.93612 -3.51544,0.2412 z"
style="fill:#e23229;fill-opacity:1;stroke:none;stroke-width:0.2626844px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 535 B

View File

@@ -1,14 +1,10 @@
using Microsoft.CodeAnalysis;
namespace Robust.Analyzers;
public static class Diagnostics
namespace Robust.Generators
{
public const string IdExplicitInterface = "RA0000";
public const string IdSerializable = "RA0001";
public const string IdFriend = "RA0002";
public const string IdExplicitVirtual = "RA0003";
public static SuppressionDescriptor MeansImplicitAssignment =>
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
public static class Diagnostics
{
public static SuppressionDescriptor MeansImplicitAssignment =>
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
}
}

View File

@@ -24,14 +24,14 @@ namespace Robust.Analyzers
SyntaxKind.OverrideKeyword
};
[SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new(
Diagnostics.IdExplicitInterface,
"No explicit interface specified",
"No explicit interface specified",
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "Make sure to specify the interface in your method-declaration.");
public const string DiagnosticId = "RA0000";
private const string Title = "No explicit interface specified";
private const string MessageFormat = "No explicit interface specified";
private const string Description = "Make sure to specify the interface in your method-declaration.";
private const string Category = "Usage";
[SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
private const string RequiresExplicitImplementationAttributeMetadataName =
"Robust.Shared.Analyzers.RequiresExplicitImplementationAttribute";

View File

@@ -1,176 +0,0 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class ExplicitVirtualAnalyzer : DiagnosticAnalyzer
{
internal const string Attribute = "Robust.Shared.Analyzers.VirtualAttribute";
[SuppressMessage("ReSharper", "RS2008")]
private static readonly DiagnosticDescriptor Rule = new(
Diagnostics.IdExplicitVirtual,
"Class must be explicitly marked as [Virtual], abstract, static or sealed",
"Class must be explicitly marked as [Virtual], abstract, static or sealed",
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "Class must be explicitly marked as [Virtual], abstract, static or sealed.");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration);
}
private static bool HasAttribute(INamedTypeSymbol namedTypeSymbol, INamedTypeSymbol attrSymbol)
{
return namedTypeSymbol.GetAttributes()
.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol));
}
private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
var attrSymbol = context.Compilation.GetTypeByMetadataName(Attribute);
var classDecl = (ClassDeclarationSyntax)context.Node;
var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDecl);
if (classSymbol == null)
return;
if (classSymbol.IsSealed || classSymbol.IsAbstract || classSymbol.IsStatic)
return;
if (HasAttribute(classSymbol, attrSymbol))
return;
var diag = Diagnostic.Create(Rule, classDecl.Keyword.GetLocation());
context.ReportDiagnostic(diag);
}
}
// Doesn't work as I'd hoped: Roslyn doesn't provide an API for global usings and I can't get batch changes to work.
/*
[ExportCodeFixProvider(LanguageNames.CSharp)]
public sealed class ExplicitVirtualCodeFixProvider : CodeFixProvider
{
private const string TitleSealed = "Annotate class as sealed.";
private const string TitleVirtual = "Annotate class as [Virtual].";
private const string TitleAbstract = "Annotate class as abstract.";
private const string TitleStatic = "Annotate class as static.";
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
foreach (var diagnostic in context.Diagnostics)
{
var span = diagnostic.Location.SourceSpan;
var classDecl = root.FindToken(span.Start).Parent.AncestorsAndSelf().OfType<ClassDeclarationSyntax>()
.First();
context.RegisterCodeFix(
CodeAction.Create(
TitleVirtual,
c => FixVirtualAsync(context.Document, classDecl, c),
TitleVirtual),
diagnostic);
context.RegisterCodeFix(
CodeAction.Create(
TitleStatic,
c => FixStaticAsync(context.Document, classDecl, c),
TitleStatic),
diagnostic);
context.RegisterCodeFix(
CodeAction.Create(
TitleSealed,
c => FixSealedAsync(context.Document, classDecl, c),
TitleSealed),
diagnostic);
context.RegisterCodeFix(
CodeAction.Create(
TitleAbstract,
c => FixAbstractAsync(context.Document, classDecl, c),
TitleAbstract),
diagnostic);
}
}
private async Task<Document> FixVirtualAsync(
Document document,
ClassDeclarationSyntax classDecl,
CancellationToken cancellationToken)
{
var ns = "Robust.Shared.Analyzers";
var attrib = SyntaxFactory.Attribute(SyntaxFactory.ParseName("Virtual"));
var newClassDecl = classDecl.AddAttributeLists(
SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(new[] { attrib })));
var root = (CompilationUnitSyntax)await document.GetSyntaxRootAsync(cancellationToken);
root = root.ReplaceNode(classDecl, newClassDecl);
var options = await document.GetOptionsAsync(cancellationToken);
if (root.Usings.All(u => u.Name.ToString() != ns))
{
root = root.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(ns)));
}
return document.WithSyntaxRoot(root);
}
private async Task<Document> FixStaticAsync(
Document document,
ClassDeclarationSyntax classDecl,
CancellationToken cancellationToken)
{
var newClassDecl = classDecl.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword));
var root = (CompilationUnitSyntax)await document.GetSyntaxRootAsync(cancellationToken);
root = root.ReplaceNode(classDecl, newClassDecl);
return document.WithSyntaxRoot(root);
}
private async Task<Document> FixAbstractAsync(
Document document,
ClassDeclarationSyntax classDecl,
CancellationToken cancellationToken)
{
var newClassDecl = classDecl.AddModifiers(SyntaxFactory.Token(SyntaxKind.AbstractKeyword));
var root = (CompilationUnitSyntax)await document.GetSyntaxRootAsync(cancellationToken);
root = root.ReplaceNode(classDecl, newClassDecl);
return document.WithSyntaxRoot(root);
}
private async Task<Document> FixSealedAsync(
Document document,
ClassDeclarationSyntax classDecl,
CancellationToken cancellationToken)
{
var newClassDecl = classDecl.AddModifiers(SyntaxFactory.Token(SyntaxKind.SealedKeyword));
var root = (CompilationUnitSyntax)await document.GetSyntaxRootAsync(cancellationToken);
root = root.ReplaceNode(classDecl, newClassDecl);
return document.WithSyntaxRoot(root);
}
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(Diagnostics.IdExplicitVirtual);
}
*/

View File

@@ -14,15 +14,15 @@ namespace Robust.Analyzers
{
const string FriendAttribute = "Robust.Shared.Analyzers.FriendAttribute";
public const string DiagnosticId = "RA0002";
private const string Title = "Tried to access friend-only member";
private const string MessageFormat = "Tried to access member \"{0}\" in class \"{1}\" which can only be accessed by friend classes";
private const string Description = "Make sure to specify the accessing class in the friends attribute.";
private const string Category = "Usage";
[SuppressMessage("ReSharper", "RS2008")]
private static readonly DiagnosticDescriptor Rule = new (
Diagnostics.IdFriend,
"Tried to access friend-only member",
"Tried to access member \"{0}\" in class \"{1}\" which can only be accessed by friend classes",
"Usage",
DiagnosticSeverity.Error,
true,
"Make sure to specify the accessing class in the friends attribute.");
private static readonly DiagnosticDescriptor Rule = new (DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, true, Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

View File

@@ -1,7 +1,9 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Generators;
namespace Robust.Analyzers
{

View File

@@ -2,13 +2,13 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10</LangVersion>
<LangVersion>9</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.0.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="3.8.0" />
</ItemGroup>
</Project>

View File

@@ -17,21 +17,19 @@ namespace Robust.Analyzers
public class SerializableAnalyzer : DiagnosticAnalyzer
{
// Metadata of the analyzer
public const string DiagnosticId = "RA0001";
// You could use LocalizedString but it's a little more complicated for this sample
private const string Title = "Class not marked as (Net)Serializable";
private const string MessageFormat = "Class not marked as (Net)Serializable";
private const string Description = "The class should be marked as (Net)Serializable.";
private const string Category = "Usage";
private const string RequiresSerializableAttributeMetadataName = "Robust.Shared.Analyzers.RequiresSerializableAttribute";
private const string SerializableAttributeMetadataName = "System.SerializableAttribute";
private const string NetSerializableAttributeMetadataName = "Robust.Shared.Serialization.NetSerializableAttribute";
[SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new(
Diagnostics.IdSerializable,
"Class not marked as (Net)Serializable",
"Class not marked as (Net)Serializable",
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "The class should be marked as (Net)Serializable.");
[SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
@@ -141,8 +139,7 @@ namespace Robust.Analyzers
return document.WithSyntaxRoot(root);
}
public sealed override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(Diagnostics.IdSerializable);
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(SerializableAnalyzer.DiagnosticId);
public override FixAllProvider GetFixAllProvider()
{

View File

@@ -1,53 +0,0 @@
using System.Collections.Generic;
using System.Globalization;
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Filters;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Order;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Validators;
using Robust.Benchmarks.Exporters;
namespace Robust.Benchmarks.Configs;
public sealed class DefaultSQLConfig : IConfig
{
public static readonly IConfig Instance = new DefaultSQLConfig();
private DefaultSQLConfig(){}
public IEnumerable<IExporter> GetExporters()
{
yield return SQLExporter.Default;
}
public IEnumerable<IColumnProvider> GetColumnProviders() => DefaultConfig.Instance.GetColumnProviders();
public IEnumerable<ILogger> GetLoggers() => DefaultConfig.Instance.GetLoggers();
public IEnumerable<IDiagnoser> GetDiagnosers() => DefaultConfig.Instance.GetDiagnosers();
public IEnumerable<IAnalyser> GetAnalysers() => DefaultConfig.Instance.GetAnalysers();
public IEnumerable<Job> GetJobs() => DefaultConfig.Instance.GetJobs();
public IEnumerable<IValidator> GetValidators() => DefaultConfig.Instance.GetValidators();
public IEnumerable<HardwareCounter> GetHardwareCounters() => DefaultConfig.Instance.GetHardwareCounters();
public IEnumerable<IFilter> GetFilters() => DefaultConfig.Instance.GetFilters();
public IEnumerable<BenchmarkLogicalGroupRule> GetLogicalGroupRules() => DefaultConfig.Instance.GetLogicalGroupRules();
public IOrderer Orderer => DefaultConfig.Instance.Orderer!;
public SummaryStyle SummaryStyle => DefaultConfig.Instance.SummaryStyle;
public ConfigUnionRule UnionRule => DefaultConfig.Instance.UnionRule;
public string ArtifactsPath => DefaultConfig.Instance.ArtifactsPath;
public CultureInfo CultureInfo => DefaultConfig.Instance.CultureInfo!;
public ConfigOptions Options => DefaultConfig.Instance.Options;
}

View File

@@ -1,54 +0,0 @@
using BenchmarkDotNet.Attributes;
using JetBrains.Annotations;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.UnitTesting.Server;
namespace Robust.Benchmarks.EntityManager;
[Virtual]
public class AddRemoveComponentBenchmark
{
private ISimulation _simulation = default!;
private IEntityManager _entityManager = default!;
[UsedImplicitly]
[Params(1, 10, 100, 1000)]
public int N;
[GlobalSetup]
public void GlobalSetup()
{
_simulation = RobustServerSimulation
.NewSimulation()
.RegisterComponents(f => f.RegisterClass<A>())
.InitializeInstance();
_entityManager = _simulation.Resolve<IEntityManager>();
var coords = new MapCoordinates(0, 0, new MapId(1));
_simulation.AddMap(coords.MapId);
for (var i = 0; i < N; i++)
{
_entityManager.SpawnEntity(null, coords);
}
}
[Benchmark]
public void AddRemoveComponent()
{
for (var i = 2; i <= N+1; i++)
{
var uid = new EntityUid(i);
_entityManager.AddComponent<A>(uid);
_entityManager.RemoveComponent<A>(uid);
}
}
[ComponentProtoName("A")]
public sealed class A : Component
{
}
}

View File

@@ -1,61 +0,0 @@
using System;
using BenchmarkDotNet.Attributes;
using JetBrains.Annotations;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.UnitTesting.Server;
namespace Robust.Benchmarks.EntityManager;
[Virtual]
public class GetComponentBenchmark
{
private ISimulation _simulation = default!;
private IEntityManager _entityManager = default!;
[UsedImplicitly]
[Params(1, 10, 100, 1000)]
public int N;
public A[] Comps = default!;
[GlobalSetup]
public void GlobalSetup()
{
_simulation = RobustServerSimulation
.NewSimulation()
.RegisterComponents(f => f.RegisterClass<A>())
.InitializeInstance();
_entityManager = _simulation.Resolve<IEntityManager>();
Comps = new A[N+2];
var coords = new MapCoordinates(0, 0, new MapId(1));
_simulation.AddMap(coords.MapId);
for (var i = 0; i < N; i++)
{
var uid = _entityManager.SpawnEntity(null, coords);
_entityManager.AddComponent<A>(uid);
}
}
[Benchmark]
public A[] GetComponent()
{
for (var i = 2; i <= N+1; i++)
{
Comps[i] = _entityManager.GetComponent<A>(new EntityUid(i));
}
// Return something so the JIT doesn't optimize out all the GetComponent calls.
return Comps;
}
[ComponentProtoName("A")]
public sealed class A : Component
{
}
}

View File

@@ -1,62 +0,0 @@
using BenchmarkDotNet.Attributes;
using JetBrains.Annotations;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.UnitTesting.Server;
namespace Robust.Benchmarks.EntityManager;
[Virtual]
public class SpawnDeleteEntityBenchmark
{
private ISimulation _simulation = default!;
private IEntityManager _entityManager = default!;
private MapCoordinates _mapCoords = MapCoordinates.Nullspace;
private EntityCoordinates _entCoords = EntityCoordinates.Invalid;
[UsedImplicitly]
[Params(1, 10, 100, 1000)]
public int N;
[GlobalSetup]
public void GlobalSetup()
{
_simulation = RobustServerSimulation
.NewSimulation()
.RegisterComponents(f => f.RegisterClass<A>())
.InitializeInstance();
_entityManager = _simulation.Resolve<IEntityManager>();
_mapCoords = new MapCoordinates(0, 0, new MapId(1));
var uid = _simulation.AddMap(_mapCoords.MapId);
_entCoords = new EntityCoordinates(uid, 0, 0);
}
[Benchmark(Baseline = true)]
public void SpawnDeleteEntityMapCoords()
{
for (var i = 0; i < N; i++)
{
var uid = _entityManager.SpawnEntity(null, _mapCoords);
_entityManager.DeleteEntity(uid);
}
}
[Benchmark]
public void SpawnDeleteEntityEntCoords()
{
for (var i = 0; i < N; i++)
{
var uid = _entityManager.SpawnEntity(null, _entCoords);
_entityManager.DeleteEntity(uid);
}
}
[ComponentProtoName("A")]
public sealed class A : Component
{
}
}

View File

@@ -1,143 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Mathematics;
using BenchmarkDotNet.Reports;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Npgsql;
namespace Robust.Benchmarks.Exporters;
public sealed class SQLExporter : IExporter
{
public static readonly IExporter Default = new SQLExporter();
private SQLExporter(){}
public void ExportToLog(Summary summary, ILogger logger)
{
Export(summary, logger);
}
public IEnumerable<string> ExportToFiles(Summary summary, ILogger consoleLogger)
{
Export(summary, consoleLogger);
return Array.Empty<string>();
}
private bool TryGetEnvironmentVariable(string name, ILogger logger, [NotNullWhen(true)] out string? value)
{
value = Environment.GetEnvironmentVariable(name);
if (value == null)
logger.WriteError($"ROBUST_BENCHMARKS_ENABLE_SQL is set, but {name} is missing.");
return value != null;
}
private void Export(Summary summary, ILogger logger)
{
if (!TryGetEnvironmentVariable("ROBUST_BENCHMARKS_SQL_ADDRESS", logger, out var address) ||
!TryGetEnvironmentVariable("ROBUST_BENCHMARKS_SQL_PORT", logger, out var rawPort) ||
!TryGetEnvironmentVariable("ROBUST_BENCHMARKS_SQL_USER", logger, out var user) ||
!TryGetEnvironmentVariable("ROBUST_BENCHMARKS_SQL_PASSWORD", logger, out var password) ||
!TryGetEnvironmentVariable("ROBUST_BENCHMARKS_SQL_DATABASE", logger, out var db) ||
!TryGetEnvironmentVariable("GITHUB_SHA", logger, out var gitHash))
return;
if (!int.TryParse(rawPort, out var port))
{
logger.WriteError("Failed parsing ROBUST_BENCHMARKS_SQL_PORT to int.");
return;
}
var builder = new DbContextOptionsBuilder<BenchmarkContext>();
var connectionString = new NpgsqlConnectionStringBuilder
{
Host = address,
Port = port,
Database = db,
Username = user,
Password = password
}.ConnectionString;
builder.UseNpgsql(connectionString);
using var ctx = new BenchmarkContext(builder.Options);
try
{
ctx.Database.Migrate();
ctx.BenchmarkRuns.Add(BenchmarkRun.FromSummary(summary, gitHash));
ctx.SaveChanges();
}
finally
{
ctx.Dispose();
}
}
public string Name => "sql";
}
public sealed class DesignTimeContextFactoryPostgres : IDesignTimeDbContextFactory<BenchmarkContext>
{
public BenchmarkContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<BenchmarkContext>();
optionsBuilder.UseNpgsql("Server=localhost");
return new BenchmarkContext(optionsBuilder.Options);
}
}
public class BenchmarkContext : DbContext
{
public DbSet<BenchmarkRun> BenchmarkRuns { get; set; } = default!;
public BenchmarkContext() { }
public BenchmarkContext(DbContextOptions<BenchmarkContext> options) : base(options) { }
}
public class BenchmarkRun
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public ulong Id { get; set; }
public string GitHash { get; set; } = string.Empty;
[Column(TypeName = "timestamptz")]
public DateTime RunDate { get; set; }
public string Name { get; set; } = string.Empty;
[Column(TypeName = "jsonb")]
public BenchmarkRunReport[] Reports { get; set; } = Array.Empty<BenchmarkRunReport>();
public static BenchmarkRun FromSummary(Summary summary, string gitHash)
{
return new BenchmarkRun
{
Reports = summary.Reports.Select(r => new BenchmarkRunReport
{
Parameters = r.BenchmarkCase.Parameters.Items.Select(p => new BenchmarkRunParameter
{
Name = p.Name,
Value = p.Value
}).ToArray(),
Statistics = r.ResultStatistics
}).ToArray(),
Name = summary.BenchmarksCases.First().FolderInfo,
RunDate = DateTime.UtcNow,
GitHash = gitHash
};
}
}
public class BenchmarkRunReport
{
public BenchmarkRunParameter[] Parameters { get; set; } = Array.Empty<BenchmarkRunParameter>();
public Statistics Statistics { get; set; } = default!;
}
public class BenchmarkRunParameter
{
public string Name { get; set; } = string.Empty;
public object Value { get; set; } = default!;
}

View File

@@ -1,55 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Robust.Benchmarks.Exporters;
#nullable disable
namespace Robust.Benchmarks.Migrations
{
[DbContext(typeof(BenchmarkContext))]
[Migration("20220328231938_InitialCreate")]
partial class InitialCreate
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Robust.Benchmarks.Exporters.BenchmarkRun", b =>
{
b.Property<decimal>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("numeric(20,0)");
b.Property<string>("GitHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<BenchmarkRunReport[]>("Reports")
.IsRequired()
.HasColumnType("jsonb");
b.Property<DateTime>("RunDate")
.HasColumnType("Date");
b.HasKey("Id");
b.ToTable("BenchmarkRuns");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -1,35 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Robust.Benchmarks.Exporters;
#nullable disable
namespace Robust.Benchmarks.Migrations
{
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "BenchmarkRuns",
columns: table => new
{
Id = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
GitHash = table.Column<string>(type: "text", nullable: false),
RunDate = table.Column<DateTime>(type: "Date", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Reports = table.Column<BenchmarkRunReport[]>(type: "jsonb", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_BenchmarkRuns", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "BenchmarkRuns");
}
}
}

View File

@@ -1,53 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Robust.Benchmarks.Exporters;
#nullable disable
namespace Robust.Benchmarks.Migrations
{
[DbContext(typeof(BenchmarkContext))]
partial class BenchmarkContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Robust.Benchmarks.Exporters.BenchmarkRun", b =>
{
b.Property<decimal>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("numeric(20,0)");
b.Property<string>("GitHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<BenchmarkRunReport[]>("Reports")
.IsRequired()
.HasColumnType("jsonb");
b.Property<DateTime>("RunDate")
.HasColumnType("Date");
b.HasKey("Id");
b.ToTable("BenchmarkRuns");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -1,17 +1,12 @@
using BenchmarkDotNet.Attributes;
using Robust.Shared.Analyzers;
namespace Robust.Benchmarks.NumericsHelpers
{
[Virtual]
public class AddBenchmark
{
[Params(32, 128)]
public int N { get; set; }
[Params(1,2)]
public int T { get; set; }
private float[] _inputA = default!;
private float[] _inputB = default!;
private float[] _output = default!;

View File

@@ -1,26 +1,14 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
using System;
using Robust.Benchmarks.Configs;
using Robust.Benchmarks.Exporters;
using BenchmarkDotNet.Running;
namespace Robust.Benchmarks
{
internal static class Program
internal class Program
{
// --allCategories=ctg1,ctg2
// --anyCategories=ctg1,ctg2
public static void Main(string[] args)
{
#if DEBUG
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("\nWARNING: YOU ARE RUNNING A DEBUG BUILD, USE A RELEASE BUILD FOR AN ACCURATE BENCHMARK");
Console.WriteLine("THE DEBUG BUILD IS ONLY GOOD FOR FIXING A CRASHING BENCHMARK\n");
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig());
#else
var config = Environment.GetEnvironmentVariable("ROBUST_BENCHMARKS_ENABLE_SQL") != null ? DefaultSQLConfig.Instance : null;
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config);
#endif
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
}
}
}

View File

@@ -10,15 +10,9 @@
<ItemGroup>
<ProjectReference Include="..\Robust.Server\Robust.Server.csproj" />
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
<ProjectReference Include="..\Robust.UnitTesting\Robust.UnitTesting.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.0-rc.2" />
</ItemGroup>
<Import Project="..\MSBuild\Robust.Engine.targets" />
</Project>

View File

@@ -9,7 +9,7 @@ using Robust.Shared.Serialization.TypeSerializers.Interfaces;
namespace Robust.Benchmarks.Serialization
{
public sealed class BenchmarkIntSerializer : ITypeSerializer<int, ValueDataNode>
public class BenchmarkIntSerializer : ITypeSerializer<int, ValueDataNode>
{
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
IDependencyCollection dependencies, ISerializationContext? context = null)

View File

@@ -2,7 +2,6 @@
using System.Linq;
using BenchmarkDotNet.Attributes;
using Robust.Benchmarks.Serialization.Definitions;
using Robust.Shared.Analyzers;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Markdown;
using Robust.Shared.Serialization.Markdown.Mapping;
@@ -14,7 +13,6 @@ using YamlDotNet.RepresentationModel;
namespace Robust.Benchmarks.Serialization.Copy
{
[MemoryDiagnoser]
[Virtual]
public class SerializationCopyBenchmark : SerializationBenchmark
{
public SerializationCopyBenchmark()

View File

@@ -3,7 +3,7 @@ using Robust.Shared.Serialization;
namespace Robust.Benchmarks.Serialization.Definitions
{
public sealed class BenchmarkFlags
public class BenchmarkFlags
{
public const int Zero = 1 << 0;
public const int ThirtyOne = 1 << 31;

View File

@@ -1,10 +1,8 @@
using Robust.Shared.Analyzers;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Robust.Benchmarks.Serialization.Definitions
{
[DataDefinition]
[Virtual]
public class DataDefinitionWithString
{
[DataField("string")]

View File

@@ -11,7 +11,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
/// Taken from content.
/// </summary>
[Prototype("seed")]
public sealed class SeedDataDefinition : IPrototype
public class SeedDataDefinition : IPrototype
{
public const string Prototype = @"
- type: seed

View File

@@ -1,11 +1,9 @@
using BenchmarkDotNet.Attributes;
using Robust.Shared.Analyzers;
using Robust.Shared.Serialization.Manager;
namespace Robust.Benchmarks.Serialization.Initialize
{
[MemoryDiagnoser]
[Virtual]
public class SerializationInitializeBenchmark : SerializationBenchmark
{
[IterationCleanup]

View File

@@ -1,7 +1,6 @@
using System.IO;
using BenchmarkDotNet.Attributes;
using Robust.Benchmarks.Serialization.Definitions;
using Robust.Shared.Analyzers;
using Robust.Shared.Serialization.Manager.Result;
using Robust.Shared.Serialization.Markdown;
using Robust.Shared.Serialization.Markdown.Mapping;
@@ -13,7 +12,6 @@ using YamlDotNet.RepresentationModel;
namespace Robust.Benchmarks.Serialization.Read
{
[MemoryDiagnoser]
[Virtual]
public class SerializationReadBenchmark : SerializationBenchmark
{
public SerializationReadBenchmark()

View File

@@ -1,6 +1,5 @@
using BenchmarkDotNet.Attributes;
using Robust.Benchmarks.Serialization.Definitions;
using Robust.Shared.Analyzers;
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.Markdown.Sequence;
using Robust.Shared.Serialization.Markdown.Value;
@@ -8,7 +7,6 @@ using Robust.Shared.Serialization.Markdown.Value;
namespace Robust.Benchmarks.Serialization
{
[MemoryDiagnoser]
[Virtual]
public class SerializationArrayBenchmark : SerializationBenchmark
{
public SerializationArrayBenchmark()

View File

@@ -2,7 +2,6 @@
using System.IO;
using BenchmarkDotNet.Attributes;
using Robust.Benchmarks.Serialization.Definitions;
using Robust.Shared.Analyzers;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Markdown;
using Robust.Shared.Serialization.Markdown.Mapping;
@@ -14,7 +13,6 @@ using YamlDotNet.RepresentationModel;
namespace Robust.Benchmarks.Serialization.Write
{
[MemoryDiagnoser]
[Virtual]
public class SerializationWriteBenchmark : SerializationBenchmark
{
public SerializationWriteBenchmark()

View File

@@ -1,11 +0,0 @@
#!/usr/bin/env pwsh
param([String]$name)
if ($name -eq "")
{
Write-Error "must specify migration name"
exit
}
dotnet ef migrations add --context BenchmarkContext -o Migrations $name

View File

@@ -1,8 +0,0 @@
#!/usr/bin/env bash
if [ -z "$1" ] ; then
echo "Must specify migration name"
exit 1
fi
dotnet ef migrations add --context BenchmarkContext -o Migrations "$1"

View File

@@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Framework" Version="17.0.0" />
<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>

View File

@@ -5,8 +5,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>

View File

@@ -301,7 +301,7 @@ namespace {nameSpace}
foreach (var candidateClass in receiver.CandidateClasses)
{
var model = compilation.GetSemanticModel(candidateClass.SyntaxTree);
var typeSymbol = model.GetDeclaredSymbol(candidateClass);
var typeSymbol = (INamedTypeSymbol) model.GetDeclaredSymbol(candidateClass);
var relevantAttribute = typeSymbol.GetAttributes().FirstOrDefault(attr =>
attr.AttributeClass != null &&
attr.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default));

View File

@@ -1,8 +1,8 @@
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
namespace Robust.Client.WebView
{
internal sealed class CefBeforeBrowseContext : IBeforeBrowseContext
public sealed class BeforeBrowseContext
{
internal readonly CefRequest CefRequest;
@@ -14,7 +14,7 @@ namespace Robust.Client.WebView.Cef
public bool IsCancelled { get; private set; }
internal CefBeforeBrowseContext(
internal BeforeBrowseContext(
bool isRedirect,
bool userGesture,
CefRequest cefRequest)

View File

@@ -1,88 +0,0 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using Robust.Shared.ContentPack;
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
{
public static class Program
{
// This was supposed to be the main entry for the subprocess program... It doesn't work.
public static int Main(string[] args)
{
// This is a workaround for this to work on UNIX.
var argv = args;
if (CefRuntime.Platform != CefRuntimePlatform.Windows)
{
argv = new string[args.Length + 1];
Array.Copy(args, 0, argv, 1, args.Length);
argv[0] = "-";
}
/*
if (OperatingSystem.IsLinux())
{
// Chromium tries to load libEGL.so and libGLESv2.so relative to the process executable on Linux.
// (Compared to Windows where it is relative to Chromium's *module*)
// There is a TODO "is this correct?" in the Chromium code for this.
// Great.
//CopyDllToExecutableDir("libEGL.so");
//CopyDllToExecutableDir("libGLESv2.so");
// System.Threading.Thread.Sleep(200000);
}
*/
var mainArgs = new CefMainArgs(argv);
// This will block executing until the subprocess is shut down.
var code = CefRuntime.ExecuteProcess(mainArgs, null, IntPtr.Zero);
if (code != 0)
{
System.Console.WriteLine($"CEF Subprocess exited unsuccessfully with exit code {code}! Arguments: {string.Join(' ', argv)}");
}
return code;
}
/* private static void CopyDllToExecutableDir(string dllName)
{
var executableDir = PathHelpers.GetExecutableDirectory();
var targetPath = Path.Combine(executableDir, dllName);
if (File.Exists(targetPath))
return;
// Find source file.
string? srcFile = null;
foreach (var searchDir in WebViewManagerCef.NativeDllSearchDirectories())
{
var searchPath = Path.Combine(searchDir, dllName);
if (File.Exists(searchPath))
{
srcFile = searchPath;
break;
}
}
if (srcFile == null)
return;
for (var i = 0; i < 5; i++)
{
try
{
if (File.Exists(targetPath))
return;
File.Copy(srcFile, targetPath);
return;
}
catch
{
// Catching race condition lock errors and stuff I guess.
}
}
} */
}
}

View File

@@ -1,584 +0,0 @@
using System;
using System.Collections.Generic;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.UserInterface;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using SixLabors.ImageSharp.PixelFormats;
using Xilium.CefGlue;
using static Robust.Client.WebView.Cef.CefKeyCodes.ChromiumKeyboardCode;
using static Robust.Client.Input.Keyboard;
namespace Robust.Client.WebView.Cef
{
internal partial class WebViewManagerCef
{
public IWebViewControlImpl MakeControlImpl(WebViewControl owner)
{
var shader = _prototypeManager.Index<ShaderPrototype>("bgra");
var shaderInstance = shader.Instance();
var impl = new ControlImpl(owner, shaderInstance);
_dependencyCollection.InjectDependencies(impl);
return impl;
}
private sealed class ControlImpl : IWebViewControlImpl
{
private static readonly Dictionary<Key, CefKeyCodes.ChromiumKeyboardCode> KeyMap = new()
{
[Key.A] = VKEY_A,
[Key.B] = VKEY_B,
[Key.C] = VKEY_C,
[Key.D] = VKEY_D,
[Key.E] = VKEY_E,
[Key.F] = VKEY_F,
[Key.G] = VKEY_G,
[Key.H] = VKEY_H,
[Key.I] = VKEY_I,
[Key.J] = VKEY_J,
[Key.K] = VKEY_K,
[Key.L] = VKEY_L,
[Key.M] = VKEY_M,
[Key.N] = VKEY_N,
[Key.O] = VKEY_O,
[Key.P] = VKEY_P,
[Key.Q] = VKEY_Q,
[Key.R] = VKEY_R,
[Key.S] = VKEY_S,
[Key.T] = VKEY_T,
[Key.U] = VKEY_U,
[Key.V] = VKEY_V,
[Key.W] = VKEY_W,
[Key.X] = VKEY_X,
[Key.Y] = VKEY_Y,
[Key.Z] = VKEY_Z,
[Key.Num0] = VKEY_0,
[Key.Num1] = VKEY_1,
[Key.Num2] = VKEY_2,
[Key.Num3] = VKEY_3,
[Key.Num4] = VKEY_4,
[Key.Num5] = VKEY_5,
[Key.Num6] = VKEY_6,
[Key.Num7] = VKEY_7,
[Key.Num8] = VKEY_8,
[Key.Num9] = VKEY_9,
[Key.NumpadNum0] = VKEY_NUMPAD0,
[Key.NumpadNum1] = VKEY_NUMPAD1,
[Key.NumpadNum2] = VKEY_NUMPAD2,
[Key.NumpadNum3] = VKEY_NUMPAD3,
[Key.NumpadNum4] = VKEY_NUMPAD4,
[Key.NumpadNum5] = VKEY_NUMPAD5,
[Key.NumpadNum6] = VKEY_NUMPAD6,
[Key.NumpadNum7] = VKEY_NUMPAD7,
[Key.NumpadNum8] = VKEY_NUMPAD8,
[Key.NumpadNum9] = VKEY_NUMPAD9,
[Key.Escape] = VKEY_ESCAPE,
[Key.Control] = VKEY_CONTROL,
[Key.Shift] = VKEY_SHIFT,
[Key.Alt] = VKEY_MENU,
[Key.LSystem] = VKEY_LWIN,
[Key.RSystem] = VKEY_RWIN,
[Key.LBracket] = VKEY_OEM_4,
[Key.RBracket] = VKEY_OEM_6,
[Key.SemiColon] = VKEY_OEM_1,
[Key.Comma] = VKEY_OEM_COMMA,
[Key.Period] = VKEY_OEM_PERIOD,
[Key.Apostrophe] = VKEY_OEM_7,
[Key.Slash] = VKEY_OEM_2,
[Key.BackSlash] = VKEY_OEM_5,
[Key.Tilde] = VKEY_OEM_3,
[Key.Equal] = VKEY_OEM_PLUS,
[Key.Space] = VKEY_SPACE,
[Key.Return] = VKEY_RETURN,
[Key.BackSpace] = VKEY_BACK,
[Key.Tab] = VKEY_TAB,
[Key.PageUp] = VKEY_PRIOR,
[Key.PageDown] = VKEY_NEXT,
[Key.End] = VKEY_END,
[Key.Home] = VKEY_HOME,
[Key.Insert] = VKEY_INSERT,
[Key.Delete] = VKEY_DELETE,
[Key.Minus] = VKEY_OEM_MINUS,
[Key.NumpadAdd] = VKEY_ADD,
[Key.NumpadSubtract] = VKEY_SUBTRACT,
[Key.NumpadDivide] = VKEY_DIVIDE,
[Key.NumpadMultiply] = VKEY_MULTIPLY,
[Key.NumpadDecimal] = VKEY_DECIMAL,
[Key.Left] = VKEY_LEFT,
[Key.Right] = VKEY_RIGHT,
[Key.Up] = VKEY_UP,
[Key.Down] = VKEY_DOWN,
[Key.F1] = VKEY_F1,
[Key.F2] = VKEY_F2,
[Key.F3] = VKEY_F3,
[Key.F4] = VKEY_F4,
[Key.F5] = VKEY_F5,
[Key.F6] = VKEY_F6,
[Key.F7] = VKEY_F7,
[Key.F8] = VKEY_F8,
[Key.F9] = VKEY_F9,
[Key.F10] = VKEY_F10,
[Key.F11] = VKEY_F11,
[Key.F12] = VKEY_F12,
[Key.F13] = VKEY_F13,
[Key.F14] = VKEY_F14,
[Key.F15] = VKEY_F15,
[Key.Pause] = VKEY_PAUSE,
};
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IInputManager _inputMgr = default!;
public readonly WebViewControl Owner;
private readonly ShaderInstance _shaderInstance;
public ControlImpl(WebViewControl owner, ShaderInstance shaderInstance)
{
Owner = owner;
_shaderInstance = shaderInstance;
}
private const int ScrollSpeed = 50;
private readonly RobustRequestHandler _requestHandler = new(Logger.GetSawmill("root"));
private LiveData? _data;
private string _startUrl = "about:blank";
public string Url
{
get => _data == null ? _startUrl : _data.Browser.GetMainFrame().Url;
set
{
if (_data == null)
_startUrl = value;
else
_data.Browser.GetMainFrame().LoadUrl(value);
}
}
public bool IsLoading => _data?.Browser.IsLoading ?? false;
public void EnteredTree()
{
DebugTools.AssertNull(_data);
// A funny render handler that will allow us to render to the control.
var renderer = new ControlRenderHandler(this);
// A funny web cef client. This can actually be shared by multiple browsers, but I'm not sure how the
// rendering would work in that case? TODO CEF: Investigate a way to share the web client?
var client = new RobustCefClient(renderer, _requestHandler, new RobustLoadHandler());
var info = CefWindowInfo.Create();
// FUNFACT: If you DO NOT set these below and set info.Width/info.Height instead, you get an external window
// Good to know, huh? Setup is the same, except you can pass a dummy render handler to the CEF client.
info.SetAsWindowless(IntPtr.Zero, false); // TODO CEF: Pass parent handle?
info.WindowlessRenderingEnabled = true;
var settings = new CefBrowserSettings()
{
WindowlessFrameRate = 60
};
// Create the web browser! And by default, we go to about:blank.
var browser = CefBrowserHost.CreateBrowserSync(info, client, settings, _startUrl);
var texture = _clyde.CreateBlankTexture<Rgba32>(Vector2i.One);
_data = new LiveData(texture, client, browser, renderer);
}
public void ExitedTree()
{
DebugTools.AssertNotNull(_data);
_data!.Texture.Dispose();
_data.Browser.GetHost().CloseBrowser(true);
_data = null;
}
public void MouseMove(GUIMouseMoveEventArgs args)
{
if (_data == null)
return;
// Logger.Debug();
var modifiers = CalcMouseModifiers();
var mouseEvent = new CefMouseEvent(
(int)args.RelativePosition.X, (int)args.RelativePosition.Y,
modifiers);
_data.Browser.GetHost().SendMouseMoveEvent(mouseEvent, false);
}
public void MouseExited()
{
if (_data == null)
return;
var modifiers = CalcMouseModifiers();
_data.Browser.GetHost().SendMouseMoveEvent(new CefMouseEvent(0, 0, modifiers), true);
}
public void MouseWheel(GUIMouseWheelEventArgs args)
{
if (_data == null)
return;
var modifiers = CalcMouseModifiers();
var mouseEvent = new CefMouseEvent(
(int)args.RelativePosition.X, (int)args.RelativePosition.Y,
modifiers);
_data.Browser.GetHost().SendMouseWheelEvent(
mouseEvent,
(int)args.Delta.X * ScrollSpeed,
(int)args.Delta.Y * ScrollSpeed);
}
public bool RawKeyEvent(in GuiRawKeyEvent guiRawEvent)
{
if (_data == null)
return false;
var host = _data.Browser.GetHost();
if (guiRawEvent.Key is Key.MouseLeft or Key.MouseMiddle or Key.MouseRight)
{
var key = guiRawEvent.Key switch
{
Key.MouseLeft => CefMouseButtonType.Left,
Key.MouseMiddle => CefMouseButtonType.Middle,
Key.MouseRight => CefMouseButtonType.Right,
_ => default // not possible
};
var mouseEvent = new CefMouseEvent(
guiRawEvent.MouseRelative.X, guiRawEvent.MouseRelative.Y,
CefEventFlags.None);
// Logger.Debug($"MOUSE: {guiRawEvent.Action} {guiRawEvent.Key} {guiRawEvent.ScanCode} {key}");
// TODO: double click support?
host.SendMouseClickEvent(mouseEvent, key, guiRawEvent.Action == RawKeyAction.Up, 1);
}
else
{
// TODO: Handle left/right modifier keys??
if (!KeyMap.TryGetValue(guiRawEvent.Key, out var vkKey))
vkKey = default;
// Logger.Debug($"{guiRawEvent.Action} {guiRawEvent.Key} {guiRawEvent.ScanCode} {vkKey}");
var lParam = 0;
lParam |= (guiRawEvent.ScanCode & 0xFF) << 16;
if (guiRawEvent.Action != RawKeyAction.Down)
lParam |= 1 << 30;
if (guiRawEvent.Action == RawKeyAction.Up)
lParam |= 1 << 31;
var modifiers = CalcModifiers(guiRawEvent.Key);
host.SendKeyEvent(new CefKeyEvent
{
// Repeats are sent as key downs, I guess?
EventType = guiRawEvent.Action == RawKeyAction.Up
? CefKeyEventType.KeyUp
: CefKeyEventType.RawKeyDown,
NativeKeyCode = lParam,
// NativeKeyCode = guiRawEvent.ScanCode,
WindowsKeyCode = (int)vkKey,
IsSystemKey = false, // TODO
Modifiers = modifiers
});
if (guiRawEvent.Action != RawKeyAction.Up && guiRawEvent.Key == Key.Return)
{
host.SendKeyEvent(new CefKeyEvent
{
EventType = CefKeyEventType.Char,
WindowsKeyCode = '\r',
NativeKeyCode = lParam,
Modifiers = modifiers
});
}
}
return true;
}
private CefEventFlags CalcModifiers(Key key)
{
CefEventFlags modifiers = default;
if (_inputMgr.IsKeyDown(Key.Control))
modifiers |= CefEventFlags.ControlDown;
if (_inputMgr.IsKeyDown(Key.Alt))
modifiers |= CefEventFlags.AltDown;
if (_inputMgr.IsKeyDown(Key.Shift))
modifiers |= CefEventFlags.ShiftDown;
if (_inputMgr.IsKeyDown(Key.Shift))
modifiers |= CefEventFlags.ShiftDown;
return modifiers;
}
private CefEventFlags CalcMouseModifiers()
{
CefEventFlags modifiers = default;
if (_inputMgr.IsKeyDown(Key.Control))
modifiers |= CefEventFlags.ControlDown;
if (_inputMgr.IsKeyDown(Key.Alt))
modifiers |= CefEventFlags.AltDown;
if (_inputMgr.IsKeyDown(Key.Shift))
modifiers |= CefEventFlags.ShiftDown;
if (_inputMgr.IsKeyDown(Key.Shift))
modifiers |= CefEventFlags.ShiftDown;
if (_inputMgr.IsKeyDown(Key.MouseLeft))
modifiers |= CefEventFlags.LeftMouseButton;
if (_inputMgr.IsKeyDown(Key.MouseMiddle))
modifiers |= CefEventFlags.MiddleMouseButton;
if (_inputMgr.IsKeyDown(Key.MouseRight))
modifiers |= CefEventFlags.RightMouseButton;
return modifiers;
}
public void TextEntered(GUITextEventArgs args)
{
if (_data == null)
return;
var host = _data.Browser.GetHost();
Span<char> buf = stackalloc char[2];
var written = args.AsRune.EncodeToUtf16(buf);
for (var i = 0; i < written; i++)
{
host.SendKeyEvent(new CefKeyEvent
{
EventType = CefKeyEventType.Char,
WindowsKeyCode = buf[i],
Character = buf[i],
UnmodifiedCharacter = buf[i]
});
}
}
public void Resized()
{
if (_data == null)
return;
_data.Browser.GetHost().NotifyMoveOrResizeStarted();
_data.Browser.GetHost().WasResized();
_data.Texture.Dispose();
_data.Texture = _clyde.CreateBlankTexture<Rgba32>((Owner.PixelWidth, Owner.PixelHeight));
}
public void Draw(DrawingHandleScreen handle)
{
if (_data == null)
return;
var bufImg = _data.Renderer.Buffer.Buffer;
_data.Texture.SetSubImage(
Vector2i.Zero,
bufImg,
new UIBox2i(
0, 0,
Math.Min(Owner.PixelWidth, bufImg.Width),
Math.Min(Owner.PixelHeight, bufImg.Height)));
handle.UseShader(_shaderInstance);
handle.DrawTexture(_data.Texture, Vector2.Zero);
}
public void StopLoad()
{
if (_data == null)
throw new InvalidOperationException();
_data.Browser.StopLoad();
}
public void Reload()
{
if (_data == null)
throw new InvalidOperationException();
_data.Browser.Reload();
}
public bool GoBack()
{
if (_data == null)
throw new InvalidOperationException();
if (!_data.Browser.CanGoBack)
return false;
_data.Browser.GoBack();
return true;
}
public bool GoForward()
{
if (_data == null)
throw new InvalidOperationException();
if (!_data.Browser.CanGoForward)
return false;
_data.Browser.GoForward();
return true;
}
public void ExecuteJavaScript(string code)
{
if (_data == null)
throw new InvalidOperationException();
// TODO: this should not run until the browser is done loading seriously does this even work?
_data.Browser.GetMainFrame().ExecuteJavaScript(code, string.Empty, 1);
}
public void AddResourceRequestHandler(Action<IRequestHandlerContext> handler)
{
_requestHandler.AddResourceRequestHandler(handler);
}
public void RemoveResourceRequestHandler(Action<IRequestHandlerContext> handler)
{
_requestHandler.RemoveResourceRequestHandler(handler);
}
public void AddBeforeBrowseHandler(Action<IBeforeBrowseContext> handler)
{
_requestHandler.AddBeforeBrowseHandler(handler);
}
public void RemoveBeforeBrowseHandler(Action<IBeforeBrowseContext> handler)
{
_requestHandler.RemoveBeforeBrowseHandler(handler);
}
private sealed class LiveData
{
public OwnedTexture Texture;
public readonly RobustCefClient Client;
public readonly CefBrowser Browser;
public readonly ControlRenderHandler Renderer;
public LiveData(
OwnedTexture texture,
RobustCefClient client,
CefBrowser browser,
ControlRenderHandler renderer)
{
Texture = texture;
Client = client;
Browser = browser;
Renderer = renderer;
}
}
}
private sealed class ControlRenderHandler : CefRenderHandler
{
public ImageBuffer Buffer { get; }
private ControlImpl _control;
internal ControlRenderHandler(ControlImpl control)
{
Buffer = new ImageBuffer();
_control = control;
}
protected override CefAccessibilityHandler? GetAccessibilityHandler() => null;
protected override void GetViewRect(CefBrowser browser, out CefRectangle rect)
{
if (_control.Owner.Disposed)
{
rect = new CefRectangle();
return;
}
// TODO CEF: Do we need to pass real screen coords? Cause what we do already works...
//var screenCoords = _control.ScreenCoordinates;
//rect = new CefRectangle((int) screenCoords.X, (int) screenCoords.Y, (int)Math.Max(_control.Size.X, 1), (int)Math.Max(_control.Size.Y, 1));
// We do the max between size and 1 because it will LITERALLY CRASH WITHOUT AN ERROR otherwise.
rect = new CefRectangle(
0, 0,
(int)Math.Max(_control.Owner.Size.X, 1), (int)Math.Max(_control.Owner.Size.Y, 1));
}
protected override bool GetScreenInfo(CefBrowser browser, CefScreenInfo screenInfo)
{
if (_control.Owner.Disposed)
return false;
screenInfo.DeviceScaleFactor = _control.Owner.UIScale;
return true;
}
protected override void OnPopupSize(CefBrowser browser, CefRectangle rect)
{
if (_control.Owner.Disposed)
return;
}
protected override void OnPaint(CefBrowser browser, CefPaintElementType type, CefRectangle[] dirtyRects,
IntPtr buffer, int width, int height)
{
if (_control.Owner.Disposed)
return;
foreach (var dirtyRect in dirtyRects)
{
Buffer.UpdateBuffer(width, height, buffer, dirtyRect);
}
}
protected override void OnAcceleratedPaint(CefBrowser browser, CefPaintElementType type,
CefRectangle[] dirtyRects, IntPtr sharedHandle)
{
// Unused, but we're forced to implement it so.. NOOP.
}
protected override void OnScrollOffsetChanged(CefBrowser browser, double x, double y)
{
if (_control.Owner.Disposed)
return;
}
protected override void OnImeCompositionRangeChanged(CefBrowser browser, CefRange selectedRange,
CefRectangle[] characterBounds)
{
if (_control.Owner.Disposed)
return;
}
}
}
}

View File

@@ -1,106 +0,0 @@
using System;
using System.IO;
using System.Reflection;
using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Prototypes;
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
{
internal partial class WebViewManagerCef : IWebViewManagerImpl
{
private static readonly string BasePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location!)!;
private CefApp _app = default!;
[Dependency] private readonly IDependencyCollection _dependencyCollection = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public void Initialize()
{
IoCManager.Instance!.InjectDependencies(this, oneOff: true);
string subProcessName;
if (OperatingSystem.IsWindows())
subProcessName = "Robust.Client.WebView.exe";
else if (OperatingSystem.IsLinux())
subProcessName = "Robust.Client.WebView";
else
throw new NotSupportedException("Unsupported platform for CEF!");
var subProcessPath = Path.Combine(BasePath, subProcessName);
var cefResourcesPath = LocateCefResources();
// System.Console.WriteLine(AppContext.GetData("NATIVE_DLL_SEARCH_DIRECTORIES"));
if (cefResourcesPath == null)
throw new InvalidOperationException("Unable to locate cef_resources directory!");
var settings = new CefSettings()
{
WindowlessRenderingEnabled = true, // So we can render to our UI controls.
ExternalMessagePump = false, // Unsure, honestly. TODO CEF: Research this?
NoSandbox = true, // Not disabling the sandbox crashes CEF.
BrowserSubprocessPath = subProcessPath,
LocalesDirPath = Path.Combine(cefResourcesPath, "locales"),
ResourcesDirPath = cefResourcesPath,
RemoteDebuggingPort = 9222,
};
Logger.Info($"CEF Version: {CefRuntime.ChromeVersion}");
_app = new RobustCefApp();
// We pass no main arguments...
CefRuntime.Initialize(new CefMainArgs(null), settings, _app, IntPtr.Zero);
// TODO CEF: After this point, debugging breaks. No, literally. My client crashes but ONLY with the debugger.
// I have tried using the DEBUG and RELEASE versions of libcef.so, stripped or non-stripped...
// And nothing seemed to work. Odd.
}
private static string? LocateCefResources()
{
if (ProbeDir(BasePath, out var path))
return path;
foreach (var searchDir in NativeDllSearchDirectories())
{
if (ProbeDir(searchDir, out path))
return path;
}
return null;
static bool ProbeDir(string dir, out string path)
{
path = Path.Combine(dir, "cef_resources");
return Directory.Exists(path);
}
}
internal static string[] NativeDllSearchDirectories()
{
var sepChar = OperatingSystem.IsWindows() ? ';' : ':';
var searchDirectories = ((string)AppContext.GetData("NATIVE_DLL_SEARCH_DIRECTORIES")!)
.Split(sepChar, StringSplitOptions.RemoveEmptyEntries);
return searchDirectories;
}
public void Update()
{
// Calling this makes CEF do its work, without using its own update loop.
CefRuntime.DoMessageLoopWork();
}
public void Shutdown()
{
CefRuntime.Shutdown();
}
}
}

View File

@@ -1,7 +1,7 @@

using System.Diagnostics.CodeAnalysis;
namespace Robust.Client.WebView.Cef
namespace Robust.Client.WebView
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "UnusedMember.Global")]

View File

@@ -6,9 +6,9 @@ using Robust.Shared.Log;
using Robust.Shared.ViewVariables;
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
namespace Robust.Client.WebView
{
internal partial class WebViewManagerCef
internal partial class CefManager
{
[Dependency] private readonly IClydeInternal _clyde = default!;
@@ -41,11 +41,11 @@ namespace Robust.Client.WebView.Cef
private sealed class WebViewWindowImpl : IWebViewWindow
{
private readonly WebViewManagerCef _manager;
private readonly CefManager _manager;
internal CefBrowser Browser = default!;
internal RobustRequestHandler RequestHandler = default!;
public Action<CefRequestHandlerContext>? OnResourceRequest { get; set; }
public Action<RequestHandlerContext>? OnResourceRequest { get; set; }
[ViewVariables(VVAccess.ReadWrite)]
public string Url
@@ -72,7 +72,7 @@ namespace Robust.Client.WebView.Cef
}
}
public WebViewWindowImpl(WebViewManagerCef manager)
public WebViewWindowImpl(CefManager manager)
{
_manager = manager;
}
@@ -115,12 +115,12 @@ namespace Robust.Client.WebView.Cef
Browser.GetMainFrame().ExecuteJavaScript(code, string.Empty, 1);
}
public void AddResourceRequestHandler(Action<IRequestHandlerContext> handler)
public void AddResourceRequestHandler(Action<RequestHandlerContext> handler)
{
RequestHandler.AddResourceRequestHandler(handler);
}
public void RemoveResourceRequestHandler(Action<IRequestHandlerContext> handler)
public void RemoveResourceRequestHandler(Action<RequestHandlerContext> handler)
{
RequestHandler.RemoveResourceRequestHandler(handler);
}

View File

@@ -0,0 +1,137 @@
using System;
using System.IO;
using System.Net;
using Robust.Client.WebView;
using Robust.Client.WebViewHook;
using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Utility;
// The library we're using right now.
// TODO CEF: Do we want to use something else? We will need to ship it ourselves if so.
using Xilium.CefGlue;
[assembly: WebViewManagerImpl(typeof(CefManager))]
namespace Robust.Client.WebView
{
internal partial class CefManager : IWebViewManagerHook, IWebViewManager
{
[Dependency] private readonly IResourceManager _resourceManager = default!;
private CefApp _app = default!;
private bool _initialized = false;
public void Initialize()
{
IoCManager.RegisterInstance<IWebViewManager>(this);
IoCManager.RegisterInstance<CefManager>(this);
DebugTools.Assert(!_initialized);
string subProcessName;
if (OperatingSystem.IsWindows())
subProcessName = "Robust.Client.WebView.exe";
else if (OperatingSystem.IsLinux())
subProcessName = "Robust.Client.WebView";
else
throw new NotSupportedException("Unsupported platform for CEF!");
var subProcessPath = PathHelpers.ExecutableRelativeFile(subProcessName);
var settings = new CefSettings()
{
WindowlessRenderingEnabled = true, // So we can render to our UI controls.
ExternalMessagePump = false, // Unsure, honestly. TODO CEF: Research this?
NoSandbox = true, // Not disabling the sandbox crashes CEF.
BrowserSubprocessPath = subProcessPath,
LocalesDirPath = Path.Combine(PathHelpers.GetExecutableDirectory(), "locales"),
ResourcesDirPath = PathHelpers.GetExecutableDirectory(),
RemoteDebuggingPort = 9222,
CookieableSchemesList = "usr,res"
};
Logger.Info($"CEF Version: {CefRuntime.ChromeVersion}");
// --------------------------- README --------------------------------------------------
// By the way! You're gonna need the CEF binaries in your client's bin folder.
// More specifically, version cef_binary_95.7.14+g9f72f35+chromium-95.0.4638.69
// Windows: https://cef-builds.spotifycdn.com/cef_binary_94.4.1%2Bg4b61a8c%2Bchromium-94.0.4606.54_windows64_minimal.tar.bz2
// Linux: https://cef-builds.spotifycdn.com/cef_binary_94.4.1%2Bg4b61a8c%2Bchromium-94.0.4606.54_linux64_minimal.tar.bz2
// Here's how to get it to work:
// 1. Copy all the contents of "Release" to the bin/Content.Client folder.
// 2. Copy all the contents of "Resources" to the bin/Content.Client folder.
// -------------------------------------------------------------------------------------
_app = new RobustCefApp();
// We pass no main arguments...
CefRuntime.Initialize(new CefMainArgs(null), settings, _app, IntPtr.Zero);
// TODO CEF: After this point, debugging breaks. No, literally. My client crashes but ONLY with the debugger.
// I have tried using the DEBUG and RELEASE versions of libcef.so, stripped or non-stripped...
// And nothing seemed to work. Odd.
CefRuntime.RegisterSchemeHandlerFactory("res", "", new ResourceSchemeFactoryHandler(_resourceManager));
_initialized = true;
}
private sealed class ResourceSchemeFactoryHandler : CefSchemeHandlerFactory
{
private readonly IResourceManager _resourceManager;
public ResourceSchemeFactoryHandler(IResourceManager resourceManager)
{
_resourceManager = resourceManager;
}
protected override CefResourceHandler Create(CefBrowser browser, CefFrame frame, string schemeName, CefRequest request)
{
var uri = new Uri(request.Url);
// _sawmill.Debug($"HANDLING: {obj.Url}, {uri.AbsolutePath}");
if (_resourceManager.TryContentFileRead(uri.AbsolutePath, out var stream))
{
var mime = "text/plain";
if (uri.AbsolutePath.EndsWith(".png"))
mime = "image/png";
else if (uri.AbsolutePath.EndsWith(".html"))
mime = "text/html";
return new RequestResultStream(stream, mime, HttpStatusCode.OK).MakeHandler();
}
else
{
return new RequestResultStream(_resourceManager.ContentFileRead("/404.txt"), "text/plain", HttpStatusCode.NotFound).MakeHandler();
// obj.DoRespondStream(_res.ContentFileRead("/404.txt"), "text/plain");
}
}
}
public void CheckInitialized()
{
if (!_initialized)
throw new InvalidOperationException("CefManager has not been initialized!");
}
public void Update()
{
DebugTools.Assert(_initialized);
// Calling this makes CEF do its work, without using its own update loop.
CefRuntime.DoMessageLoopWork();
}
public void Shutdown()
{
DebugTools.Assert(_initialized);
CefRuntime.Shutdown();
}
}
}

View File

@@ -1,127 +0,0 @@
using System;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
namespace Robust.Client.WebView.Headless
{
internal sealed class WebViewManagerHeadless : IWebViewManagerImpl
{
public IWebViewWindow CreateBrowserWindow(BrowserWindowCreateParameters createParams)
{
return new WebViewWindowDummy();
}
public IWebViewControlImpl MakeControlImpl(WebViewControl owner)
{
return new WebViewControlImplDummy();
}
public void Initialize()
{
// Nop
}
public void Update()
{
// Nop
}
public void Shutdown()
{
// Nop
}
private abstract class DummyBase : IWebViewControl
{
public string Url { get; set; } = "about:blank";
public bool IsLoading => true;
public void StopLoad()
{
}
public void Reload()
{
}
public bool GoBack()
{
return false;
}
public bool GoForward()
{
return false;
}
public void ExecuteJavaScript(string code)
{
}
public void AddResourceRequestHandler(Action<IRequestHandlerContext> handler)
{
}
public void RemoveResourceRequestHandler(Action<IRequestHandlerContext> handler)
{
}
}
private sealed class WebViewControlImplDummy : DummyBase, IWebViewControlImpl
{
public void EnteredTree()
{
}
public void ExitedTree()
{
}
public void MouseMove(GUIMouseMoveEventArgs args)
{
}
public void MouseExited()
{
}
public void MouseWheel(GUIMouseWheelEventArgs args)
{
}
public bool RawKeyEvent(in GuiRawKeyEvent guiRawEvent)
{
return false;
}
public void TextEntered(GUITextEventArgs args)
{
}
public void Resized()
{
}
public void Draw(DrawingHandleScreen handle)
{
}
public void AddBeforeBrowseHandler(Action<IBeforeBrowseContext> handler)
{
}
public void RemoveBeforeBrowseHandler(Action<IBeforeBrowseContext> handler)
{
}
}
private sealed class WebViewWindowDummy : DummyBase, IWebViewWindow
{
public void Dispose()
{
}
public bool Closed => false;
}
}
}

View File

@@ -1,12 +0,0 @@
namespace Robust.Client.WebView
{
public interface IBeforeBrowseContext
{
string Url { get; }
string Method { get; }
bool IsRedirect { get; }
bool UserGesture { get; }
bool IsCancelled { get; }
void DoCancel();
}
}

View File

@@ -1,18 +0,0 @@
using System.IO;
using System.Net;
namespace Robust.Client.WebView
{
public interface IRequestHandlerContext
{
bool IsNavigation { get; }
bool IsDownload { get; }
string RequestInitiator { get; }
string Url { get; }
string Method { get; }
bool IsHandled { get; }
bool IsCancelled { get; }
void DoCancel();
void DoRespondStream(Stream stream, string contentType, HttpStatusCode code = HttpStatusCode.OK);
}
}

View File

@@ -1,5 +1,4 @@
using System;
using Robust.Client.WebView.Cef;
namespace Robust.Client.WebView
{
@@ -43,7 +42,7 @@ namespace Robust.Client.WebView
/// <param name="code">JavaScript code.</param>
void ExecuteJavaScript(string code);
void AddResourceRequestHandler(Action<IRequestHandlerContext> handler);
void RemoveResourceRequestHandler(Action<IRequestHandlerContext> handler);
void AddResourceRequestHandler(Action<RequestHandlerContext> handler);
void RemoveResourceRequestHandler(Action<RequestHandlerContext> handler);
}
}

View File

@@ -1,24 +0,0 @@
using System;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
namespace Robust.Client.WebView
{
/// <summary>
/// Internal swappable implementation of <see cref="WebViewControl"/>.
/// </summary>
internal interface IWebViewControlImpl : IWebViewControl
{
void EnteredTree();
void ExitedTree();
void MouseMove(GUIMouseMoveEventArgs args);
void MouseExited();
void MouseWheel(GUIMouseWheelEventArgs args);
bool RawKeyEvent(in GuiRawKeyEvent guiRawEvent);
void TextEntered(GUITextEventArgs args);
void Resized();
void Draw(DrawingHandleScreen handle);
void AddBeforeBrowseHandler(Action<IBeforeBrowseContext> handler);
void RemoveBeforeBrowseHandler(Action<IBeforeBrowseContext> handler);
}
}

View File

@@ -1,14 +0,0 @@
using Robust.Client.WebViewHook;
namespace Robust.Client.WebView
{
/// <summary>
/// Internal implementation of WebViewManager that is switched out by <see cref="IWebViewManagerHook"/>.
/// </summary>
internal interface IWebViewManagerImpl : IWebViewManagerInternal
{
void Initialize();
void Update();
void Shutdown();
}
}

View File

@@ -1,7 +0,0 @@
namespace Robust.Client.WebView
{
internal interface IWebViewManagerInternal : IWebViewManager
{
IWebViewControlImpl MakeControlImpl(WebViewControl owner);
}
}

View File

@@ -1,24 +1,30 @@
using System;
using Robust.Client.UserInterface;
using Robust.Client.Utility;
using Robust.Shared.Maths;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
namespace Robust.Client.WebView
{
internal sealed class ImageBuffer
{
public Image<Rgba32> Buffer { get; private set; } = new(1, 1);
private readonly Control _control;
public ImageBuffer(Control control)
{
_control = control;
}
public Image<Bgra32> Buffer { get; private set; } = new(1, 1);
public unsafe void UpdateBuffer(int width, int height, IntPtr buffer, CefRectangle dirtyRect)
{
if (width != Buffer.Width || height != Buffer.Height)
UpdateSize(width, height);
// NOTE: Image data from CEF is actually BGRA32, not RGBA32.
// OpenGL ES does not allow uploading BGRA data, so we pretend it's RGBA32 and use a shader to swizzle it.
var span = new ReadOnlySpan<Rgba32>((void*) buffer, width * height);
var span = new ReadOnlySpan<Bgra32>((void*) buffer, width * height);
ImageSharpExt.Blit(
span,
@@ -30,7 +36,7 @@ namespace Robust.Client.WebView.Cef
private void UpdateSize(int width, int height)
{
Buffer = new Image<Rgba32>(width, height);
Buffer = new Image<Bgra32>(width, height);
}
}
}

View File

@@ -0,0 +1,33 @@
using System;
using Xilium.CefGlue;
namespace Robust.Client.WebView
{
public static class Program
{
// This was supposed to be the main entry for the subprocess program... It doesn't work.
public static int Main(string[] args)
{
// This is a workaround for this to work on UNIX.
var argv = args;
if (CefRuntime.Platform != CefRuntimePlatform.Windows)
{
argv = new string[args.Length + 1];
Array.Copy(args, 0, argv, 1, args.Length);
argv[0] = "-";
}
var mainArgs = new CefMainArgs(argv);
// This will block executing until the subprocess is shut down.
var code = CefRuntime.ExecuteProcess(mainArgs, new RobustCefApp(), IntPtr.Zero);
if (code != 0)
{
System.Console.WriteLine($"CEF Subprocess exited unsuccessfully with exit code {code}! Arguments: {string.Join(' ', argv)}");
}
return code;
}
}
}

View File

@@ -3,9 +3,9 @@ using System.IO;
using System.Net;
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
namespace Robust.Client.WebView
{
internal sealed class CefRequestHandlerContext : IRequestHandlerContext
public sealed class RequestHandlerContext
{
internal readonly CefRequest CefRequest;
@@ -22,7 +22,7 @@ namespace Robust.Client.WebView.Cef
internal IRequestResult? Result { get; private set; }
internal CefRequestHandlerContext(
internal RequestHandlerContext(
bool isNavigation,
bool isDownload,
string requestInitiator,

View File

@@ -3,7 +3,7 @@ using System.IO;
using System.Net;
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
namespace Robust.Client.WebView
{
internal interface IRequestResult
{

View File

@@ -3,7 +3,8 @@
<Import Project="..\MSBuild\Robust.Engine.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputType>WinExe</OutputType>
</PropertyGroup>
@@ -14,7 +15,7 @@
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" />
<PackageReference Include="Robust.Natives.Cef" Version="95.7.14" />
<PackageReference Include="System.Drawing.Common" Version="5.0.2" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,9 +1,11 @@
using System;
using System.Diagnostics;
using Robust.Client.Utility;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
namespace Robust.Client.WebView
{
internal class RobustCefApp : CefApp
{
@@ -20,16 +22,20 @@ namespace Robust.Client.WebView.Cef
return _renderProcessHandler;
}
protected override unsafe void OnRegisterCustomSchemes(CefSchemeRegistrar registrar)
{
base.OnRegisterCustomSchemes(registrar);
// Win32.MessageBoxW(null, "A", $"{Process.GetCurrentProcess().Id}", 0);
registrar.AddCustomScheme("usr", CefSchemeOptions.None);
registrar.AddCustomScheme("src", CefSchemeOptions.None);
}
protected override void OnBeforeCommandLineProcessing(string processType, CefCommandLine commandLine)
{
// Disable zygote on Linux.
commandLine.AppendSwitch("--no-zygote");
// Work around https://bitbucket.org/chromiumembedded/cef/issues/3213/ozone-egl-initialization-does-not-have
// Desktop GL force makes Chromium not try to load its own ANGLE/Swiftshader so load paths aren't problematic.
if (OperatingSystem.IsLinux())
commandLine.AppendSwitch("--use-gl", "desktop");
// commandLine.AppendSwitch("--single-process");
//commandLine.AppendSwitch("--disable-gpu");

View File

@@ -1,6 +1,6 @@
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
namespace Robust.Client.WebView
{
// Simple CEF client.
internal class RobustCefClient : CefClient

View File

@@ -1,6 +1,6 @@
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
namespace Robust.Client.WebView
{
public sealed class RobustLoadHandler : CefLoadHandler
{

View File

@@ -3,20 +3,20 @@ using System.Collections.Generic;
using Robust.Shared.Log;
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
namespace Robust.Client.WebView
{
internal sealed class RobustRequestHandler : CefRequestHandler
{
private readonly ISawmill _sawmill;
private readonly List<Action<IRequestHandlerContext>> _resourceRequestHandlers = new();
private readonly List<Action<IBeforeBrowseContext>> _beforeBrowseHandlers = new();
private readonly List<Action<RequestHandlerContext>> _resourceRequestHandlers = new();
private readonly List<Action<BeforeBrowseContext>> _beforeBrowseHandlers = new();
public RobustRequestHandler(ISawmill sawmill)
{
_sawmill = sawmill;
}
public void AddResourceRequestHandler(Action<IRequestHandlerContext> handler)
public void AddResourceRequestHandler(Action<RequestHandlerContext> handler)
{
lock (_resourceRequestHandlers)
{
@@ -24,7 +24,7 @@ namespace Robust.Client.WebView.Cef
}
}
public void RemoveResourceRequestHandler(Action<IRequestHandlerContext> handler)
public void RemoveResourceRequestHandler(Action<RequestHandlerContext> handler)
{
lock (_resourceRequestHandlers)
{
@@ -32,7 +32,7 @@ namespace Robust.Client.WebView.Cef
}
}
public void AddBeforeBrowseHandler(Action<IBeforeBrowseContext> handler)
public void AddBeforeBrowseHandler(Action<BeforeBrowseContext> handler)
{
lock (_beforeBrowseHandlers)
{
@@ -40,7 +40,7 @@ namespace Robust.Client.WebView.Cef
}
}
public void RemoveBeforeBrowseHandler(Action<IBeforeBrowseContext> handler)
public void RemoveBeforeBrowseHandler(Action<BeforeBrowseContext> handler)
{
lock (_beforeBrowseHandlers)
{
@@ -60,8 +60,9 @@ namespace Robust.Client.WebView.Cef
lock (_resourceRequestHandlers)
{
_sawmill.Debug($"HANDLING REQUEST: {request.Url}");
return null;
var context = new CefRequestHandlerContext(isNavigation, isDownload, requestInitiator, request);
var context = new RequestHandlerContext(isNavigation, isDownload, requestInitiator, request);
foreach (var handler in _resourceRequestHandlers)
{
@@ -85,7 +86,7 @@ namespace Robust.Client.WebView.Cef
{
lock (_beforeBrowseHandlers)
{
var context = new CefBeforeBrowseContext(isRedirect, userGesture, request);
var context = new BeforeBrowseContext(isRedirect, userGesture, request);
foreach (var handler in _beforeBrowseHandlers)
{
@@ -124,5 +125,18 @@ namespace Robust.Client.WebView.Cef
return _handler;
}
}
private sealed class CookieHandler : CefCookieAccessFilter
{
protected override bool CanSendCookie(CefBrowser browser, CefFrame frame, CefRequest request, CefCookie cookie)
{
return true;
}
protected override bool CanSaveCookie(CefBrowser browser, CefFrame frame, CefRequest request, CefResponse response, CefCookie cookie)
{
return true;
}
}
}
}

View File

@@ -1,9 +1,18 @@
using System;
using System.Collections.Generic;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.UserInterface;
using Robust.Client.WebView.Cef;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
using SixLabors.ImageSharp.PixelFormats;
using Xilium.CefGlue;
using static Robust.Client.WebView.CefKeyCodes;
using static Robust.Client.WebView.CefKeyCodes.ChromiumKeyboardCode;
using static Robust.Client.Input.Keyboard;
namespace Robust.Client.WebView
{
@@ -12,18 +21,132 @@ namespace Robust.Client.WebView
/// </summary>
public sealed class WebViewControl : Control, IWebViewControl, IRawInputControl
{
[Dependency] private readonly IWebViewManagerInternal _webViewManager = default!;
private const int ScrollSpeed = 50;
private readonly IWebViewControlImpl _controlImpl;
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IInputManager _inputMgr = default!;
[Dependency] private readonly CefManager _cef = default!;
private RobustRequestHandler _requestHandler = new RobustRequestHandler(Logger.GetSawmill("root"));
private LiveData? _data;
private string _startUrl = "about:blank";
[ViewVariables(VVAccess.ReadWrite)]
public string Url
{
get => _controlImpl.Url;
set => _controlImpl.Url = value;
get => _data == null ? _startUrl : _data.Browser.GetMainFrame().Url;
set
{
if (_data == null)
_startUrl = value;
else
_data.Browser.GetMainFrame().LoadUrl(value);
}
}
[ViewVariables] public bool IsLoading => _controlImpl.IsLoading;
[ViewVariables] public bool IsLoading => _data?.Browser.IsLoading ?? false;
private readonly Dictionary<Key, ChromiumKeyboardCode> _keyMap = new()
{
[Key.A] = VKEY_A,
[Key.B] = VKEY_B,
[Key.C] = VKEY_C,
[Key.D] = VKEY_D,
[Key.E] = VKEY_E,
[Key.F] = VKEY_F,
[Key.G] = VKEY_G,
[Key.H] = VKEY_H,
[Key.I] = VKEY_I,
[Key.J] = VKEY_J,
[Key.K] = VKEY_K,
[Key.L] = VKEY_L,
[Key.M] = VKEY_M,
[Key.N] = VKEY_N,
[Key.O] = VKEY_O,
[Key.P] = VKEY_P,
[Key.Q] = VKEY_Q,
[Key.R] = VKEY_R,
[Key.S] = VKEY_S,
[Key.T] = VKEY_T,
[Key.U] = VKEY_U,
[Key.V] = VKEY_V,
[Key.W] = VKEY_W,
[Key.X] = VKEY_X,
[Key.Y] = VKEY_Y,
[Key.Z] = VKEY_Z,
[Key.Num0] = VKEY_0,
[Key.Num1] = VKEY_1,
[Key.Num2] = VKEY_2,
[Key.Num3] = VKEY_3,
[Key.Num4] = VKEY_4,
[Key.Num5] = VKEY_5,
[Key.Num6] = VKEY_6,
[Key.Num7] = VKEY_7,
[Key.Num8] = VKEY_8,
[Key.Num9] = VKEY_9,
[Key.NumpadNum0] = VKEY_NUMPAD0,
[Key.NumpadNum1] = VKEY_NUMPAD1,
[Key.NumpadNum2] = VKEY_NUMPAD2,
[Key.NumpadNum3] = VKEY_NUMPAD3,
[Key.NumpadNum4] = VKEY_NUMPAD4,
[Key.NumpadNum5] = VKEY_NUMPAD5,
[Key.NumpadNum6] = VKEY_NUMPAD6,
[Key.NumpadNum7] = VKEY_NUMPAD7,
[Key.NumpadNum8] = VKEY_NUMPAD8,
[Key.NumpadNum9] = VKEY_NUMPAD9,
[Key.Escape] = VKEY_ESCAPE,
[Key.Control] = VKEY_CONTROL,
[Key.Shift] = VKEY_SHIFT,
[Key.Alt] = VKEY_MENU,
[Key.LSystem] = VKEY_LWIN,
[Key.RSystem] = VKEY_RWIN,
[Key.LBracket] = VKEY_OEM_4,
[Key.RBracket] = VKEY_OEM_6,
[Key.SemiColon] = VKEY_OEM_1,
[Key.Comma] = VKEY_OEM_COMMA,
[Key.Period] = VKEY_OEM_PERIOD,
[Key.Apostrophe] = VKEY_OEM_7,
[Key.Slash] = VKEY_OEM_2,
[Key.BackSlash] = VKEY_OEM_5,
[Key.Tilde] = VKEY_OEM_3,
[Key.Equal] = VKEY_OEM_PLUS,
[Key.Space] = VKEY_SPACE,
[Key.Return] = VKEY_RETURN,
[Key.BackSpace] = VKEY_BACK,
[Key.Tab] = VKEY_TAB,
[Key.PageUp] = VKEY_PRIOR,
[Key.PageDown] = VKEY_NEXT,
[Key.End] = VKEY_END,
[Key.Home] = VKEY_HOME,
[Key.Insert] = VKEY_INSERT,
[Key.Delete] = VKEY_DELETE,
[Key.Minus] = VKEY_OEM_MINUS,
[Key.NumpadAdd] = VKEY_ADD,
[Key.NumpadSubtract] = VKEY_SUBTRACT,
[Key.NumpadDivide] = VKEY_DIVIDE,
[Key.NumpadMultiply] = VKEY_MULTIPLY,
[Key.NumpadDecimal] = VKEY_DECIMAL,
[Key.Left] = VKEY_LEFT,
[Key.Right] = VKEY_RIGHT,
[Key.Up] = VKEY_UP,
[Key.Down] = VKEY_DOWN,
[Key.F1] = VKEY_F1,
[Key.F2] = VKEY_F2,
[Key.F3] = VKEY_F3,
[Key.F4] = VKEY_F4,
[Key.F5] = VKEY_F5,
[Key.F6] = VKEY_F6,
[Key.F7] = VKEY_F7,
[Key.F8] = VKEY_F8,
[Key.F9] = VKEY_F9,
[Key.F10] = VKEY_F10,
[Key.F11] = VKEY_F11,
[Key.F12] = VKEY_F12,
[Key.F13] = VKEY_F13,
[Key.F14] = VKEY_F14,
[Key.F15] = VKEY_F15,
[Key.Pause] = VKEY_PAUSE,
};
public WebViewControl()
{
@@ -32,114 +155,441 @@ namespace Robust.Client.WebView
MouseFilter = MouseFilterMode.Stop;
IoCManager.InjectDependencies(this);
_controlImpl = _webViewManager.MakeControlImpl(this);
}
protected override void EnteredTree()
{
base.EnteredTree();
_controlImpl.EnteredTree();
_cef.CheckInitialized();
DebugTools.AssertNull(_data);
// A funny render handler that will allow us to render to the control.
var renderer = new ControlRenderHandler(this);
// A funny web cef client. This can actually be shared by multiple browsers, but I'm not sure how the
// rendering would work in that case? TODO CEF: Investigate a way to share the web client?
var client = new RobustCefClient(renderer, _requestHandler, new RobustLoadHandler());
var info = CefWindowInfo.Create();
// FUNFACT: If you DO NOT set these below and set info.Width/info.Height instead, you get an external window
// Good to know, huh? Setup is the same, except you can pass a dummy render handler to the CEF client.
info.SetAsWindowless(IntPtr.Zero, false); // TODO CEF: Pass parent handle?
info.WindowlessRenderingEnabled = true;
var settings = new CefBrowserSettings()
{
WindowlessFrameRate = 60
};
// Create the web browser! And by default, we go to about:blank.
var browser = CefBrowserHost.CreateBrowserSync(info, client, settings, _startUrl);
var texture = _clyde.CreateBlankTexture<Bgra32>(Vector2i.One);
_data = new LiveData(texture, client, browser, renderer);
}
protected override void ExitedTree()
{
base.ExitedTree();
_controlImpl.ExitedTree();
DebugTools.AssertNotNull(_data);
_data!.Texture.Dispose();
_data.Browser.GetHost().CloseBrowser(true);
_data = null;
}
protected internal override void MouseMove(GUIMouseMoveEventArgs args)
{
base.MouseMove(args);
_controlImpl.MouseMove(args);
if (_data == null)
return;
// Logger.Debug();
var modifiers = CalcMouseModifiers();
var mouseEvent = new CefMouseEvent(
(int) args.RelativePosition.X, (int) args.RelativePosition.Y,
modifiers);
_data.Browser.GetHost().SendMouseMoveEvent(mouseEvent, false);
}
protected internal override void MouseExited()
{
base.MouseExited();
_controlImpl.MouseExited();
if (_data == null)
return;
var modifiers = CalcMouseModifiers();
_data.Browser.GetHost().SendMouseMoveEvent(new CefMouseEvent(0, 0, modifiers), true);
}
protected internal override void MouseWheel(GUIMouseWheelEventArgs args)
{
base.MouseWheel(args);
_controlImpl.MouseWheel(args);
if (_data == null)
return;
var modifiers = CalcMouseModifiers();
var mouseEvent = new CefMouseEvent(
(int) args.RelativePosition.X, (int) args.RelativePosition.Y,
modifiers);
_data.Browser.GetHost().SendMouseWheelEvent(
mouseEvent,
(int) args.Delta.X * ScrollSpeed,
(int) args.Delta.Y * ScrollSpeed);
}
bool IRawInputControl.RawKeyEvent(in GuiRawKeyEvent guiRawEvent)
{
return _controlImpl.RawKeyEvent(guiRawEvent);
if (_data == null)
return false;
var host = _data.Browser.GetHost();
if (guiRawEvent.Key is Key.MouseLeft or Key.MouseMiddle or Key.MouseRight)
{
var key = guiRawEvent.Key switch
{
Key.MouseLeft => CefMouseButtonType.Left,
Key.MouseMiddle => CefMouseButtonType.Middle,
Key.MouseRight => CefMouseButtonType.Right,
_ => default // not possible
};
var mouseEvent = new CefMouseEvent(
guiRawEvent.MouseRelative.X, guiRawEvent.MouseRelative.Y,
CefEventFlags.None);
// Logger.Debug($"MOUSE: {guiRawEvent.Action} {guiRawEvent.Key} {guiRawEvent.ScanCode} {key}");
// TODO: double click support?
host.SendMouseClickEvent(mouseEvent, key, guiRawEvent.Action == RawKeyAction.Up, 1);
}
else
{
// TODO: Handle left/right modifier keys??
if (!_keyMap.TryGetValue(guiRawEvent.Key, out var vkKey))
vkKey = default;
// Logger.Debug($"{guiRawEvent.Action} {guiRawEvent.Key} {guiRawEvent.ScanCode} {vkKey}");
var lParam = 0;
lParam |= (guiRawEvent.ScanCode & 0xFF) << 16;
if (guiRawEvent.Action != RawKeyAction.Down)
lParam |= 1 << 30;
if (guiRawEvent.Action == RawKeyAction.Up)
lParam |= 1 << 31;
var modifiers = CalcModifiers(guiRawEvent.Key);
host.SendKeyEvent(new CefKeyEvent
{
// Repeats are sent as key downs, I guess?
EventType = guiRawEvent.Action == RawKeyAction.Up
? CefKeyEventType.KeyUp
: CefKeyEventType.RawKeyDown,
NativeKeyCode = lParam,
// NativeKeyCode = guiRawEvent.ScanCode,
WindowsKeyCode = (int) vkKey,
IsSystemKey = false, // TODO
Modifiers = modifiers
});
if (guiRawEvent.Action != RawKeyAction.Up && guiRawEvent.Key == Key.Return)
{
host.SendKeyEvent(new CefKeyEvent
{
EventType = CefKeyEventType.Char,
WindowsKeyCode = '\r',
NativeKeyCode = lParam,
Modifiers = modifiers
});
}
}
return true;
}
private CefEventFlags CalcModifiers(Key key)
{
CefEventFlags modifiers = default;
if (_inputMgr.IsKeyDown(Key.Control))
modifiers |= CefEventFlags.ControlDown;
if (_inputMgr.IsKeyDown(Key.Alt))
modifiers |= CefEventFlags.AltDown;
if (_inputMgr.IsKeyDown(Key.Shift))
modifiers |= CefEventFlags.ShiftDown;
if (_inputMgr.IsKeyDown(Key.Shift))
modifiers |= CefEventFlags.ShiftDown;
return modifiers;
}
private CefEventFlags CalcMouseModifiers()
{
CefEventFlags modifiers = default;
if (_inputMgr.IsKeyDown(Key.Control))
modifiers |= CefEventFlags.ControlDown;
if (_inputMgr.IsKeyDown(Key.Alt))
modifiers |= CefEventFlags.AltDown;
if (_inputMgr.IsKeyDown(Key.Shift))
modifiers |= CefEventFlags.ShiftDown;
if (_inputMgr.IsKeyDown(Key.Shift))
modifiers |= CefEventFlags.ShiftDown;
if (_inputMgr.IsKeyDown(Key.MouseLeft))
modifiers |= CefEventFlags.LeftMouseButton;
if (_inputMgr.IsKeyDown(Key.MouseMiddle))
modifiers |= CefEventFlags.MiddleMouseButton;
if (_inputMgr.IsKeyDown(Key.MouseRight))
modifiers |= CefEventFlags.RightMouseButton;
return modifiers;
}
protected internal override void TextEntered(GUITextEventArgs args)
{
base.TextEntered(args);
_controlImpl.TextEntered(args);
if (_data == null)
return;
var host = _data.Browser.GetHost();
Span<char> buf = stackalloc char[2];
var written = args.AsRune.EncodeToUtf16(buf);
for (var i = 0; i < written; i++)
{
host.SendKeyEvent(new CefKeyEvent
{
EventType = CefKeyEventType.Char,
WindowsKeyCode = buf[i],
Character = buf[i],
UnmodifiedCharacter = buf[i]
});
}
}
protected override void Resized()
{
base.Resized();
_controlImpl.Resized();
if (_data == null)
return;
_data.Browser.GetHost().NotifyMoveOrResizeStarted();
_data.Browser.GetHost().WasResized();
_data.Texture.Dispose();
_data.Texture = _clyde.CreateBlankTexture<Bgra32>((PixelWidth, PixelHeight));
}
protected internal override void Draw(DrawingHandleScreen handle)
{
base.Draw(handle);
_controlImpl.Draw(handle);
if (_data == null)
return;
var bufImg = _data.Renderer.Buffer.Buffer;
_data.Texture.SetSubImage(
Vector2i.Zero,
bufImg,
new UIBox2i(
0, 0,
Math.Min(PixelWidth, bufImg.Width),
Math.Min(PixelHeight, bufImg.Height)));
handle.DrawTexture(_data.Texture, Vector2.Zero);
}
public void StopLoad()
{
_controlImpl.StopLoad();
if (_data == null)
throw new InvalidOperationException();
_data.Browser.StopLoad();
}
public void Reload()
{
_controlImpl.Reload();
if (_data == null)
throw new InvalidOperationException();
_data.Browser.Reload();
}
public bool GoBack()
{
return _controlImpl.GoBack();
if (_data == null)
throw new InvalidOperationException();
if (!_data.Browser.CanGoBack)
return false;
_data.Browser.GoBack();
return true;
}
public bool GoForward()
{
return _controlImpl.GoForward();
if (_data == null)
throw new InvalidOperationException();
if (!_data.Browser.CanGoForward)
return false;
_data.Browser.GoForward();
return true;
}
public void ExecuteJavaScript(string code)
{
_controlImpl.ExecuteJavaScript(code);
if (_data == null)
throw new InvalidOperationException();
// TODO: this should not run until the browser is done loading seriously does this even work?
_data.Browser.GetMainFrame().ExecuteJavaScript(code, string.Empty, 1);
}
public void AddResourceRequestHandler(Action<IRequestHandlerContext> handler)
public void AddResourceRequestHandler(Action<RequestHandlerContext> handler)
{
_controlImpl.AddResourceRequestHandler(handler);
_requestHandler.AddResourceRequestHandler(handler);
}
public void RemoveResourceRequestHandler(Action<IRequestHandlerContext> handler)
public void RemoveResourceRequestHandler(Action<RequestHandlerContext> handler)
{
_controlImpl.RemoveResourceRequestHandler(handler);
_requestHandler.RemoveResourceRequestHandler(handler);
}
public void AddBeforeBrowseHandler(Action<IBeforeBrowseContext> handler)
public void AddBeforeBrowseHandler(Action<BeforeBrowseContext> handler)
{
_controlImpl.AddBeforeBrowseHandler(handler);
_requestHandler.AddBeforeBrowseHandler(handler);
}
public void RemoveBeforeBrowseHandler(Action<IBeforeBrowseContext> handler)
public void RemoveBeforeBrowseHandler(Action<BeforeBrowseContext> handler)
{
_controlImpl.RemoveBeforeBrowseHandler(handler);
_requestHandler.RemoveBeforeBrowseHandler(handler);
}
private sealed class LiveData
{
public OwnedTexture Texture;
public readonly RobustCefClient Client;
public readonly CefBrowser Browser;
public readonly ControlRenderHandler Renderer;
public LiveData(
OwnedTexture texture,
RobustCefClient client,
CefBrowser browser,
ControlRenderHandler renderer)
{
Texture = texture;
Client = client;
Browser = browser;
Renderer = renderer;
}
}
}
internal class ControlRenderHandler : CefRenderHandler
{
public ImageBuffer Buffer { get; }
private Control _control;
internal ControlRenderHandler(Control control)
{
Buffer = new ImageBuffer(control);
_control = control;
}
protected override CefAccessibilityHandler? GetAccessibilityHandler() => null;
protected override void GetViewRect(CefBrowser browser, out CefRectangle rect)
{
if (_control.Disposed)
{
rect = new CefRectangle();
return;
}
// TODO CEF: Do we need to pass real screen coords? Cause what we do already works...
//var screenCoords = _control.ScreenCoordinates;
//rect = new CefRectangle((int) screenCoords.X, (int) screenCoords.Y, (int)Math.Max(_control.Size.X, 1), (int)Math.Max(_control.Size.Y, 1));
// We do the max between size and 1 because it will LITERALLY CRASH WITHOUT AN ERROR otherwise.
rect = new CefRectangle(0, 0, (int) Math.Max(_control.Size.X, 1), (int) Math.Max(_control.Size.Y, 1));
}
protected override bool GetScreenInfo(CefBrowser browser, CefScreenInfo screenInfo)
{
if (_control.Disposed)
return false;
// TODO CEF: Get actual scale factor?
screenInfo.DeviceScaleFactor = 1.0f;
return true;
}
protected override void OnPopupSize(CefBrowser browser, CefRectangle rect)
{
if (_control.Disposed)
return;
}
protected override void OnPaint(CefBrowser browser, CefPaintElementType type, CefRectangle[] dirtyRects,
IntPtr buffer, int width, int height)
{
if (_control.Disposed)
return;
foreach (var dirtyRect in dirtyRects)
{
Buffer.UpdateBuffer(width, height, buffer, dirtyRect);
}
}
protected override void OnAcceleratedPaint(CefBrowser browser, CefPaintElementType type,
CefRectangle[] dirtyRects, IntPtr sharedHandle)
{
// Unused, but we're forced to implement it so.. NOOP.
}
protected override void OnScrollOffsetChanged(CefBrowser browser, double x, double y)
{
if (_control.Disposed)
return;
}
protected override void OnImeCompositionRangeChanged(CefBrowser browser, CefRange selectedRange,
CefRectangle[] characterBounds)
{
if (_control.Disposed)
return;
}
}
}

View File

@@ -1,59 +0,0 @@
using Robust.Client.WebView;
using Robust.Client.WebView.Cef;
using Robust.Client.WebView.Headless;
using Robust.Client.WebViewHook;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
[assembly: WebViewManagerImpl(typeof(WebViewManager))]
namespace Robust.Client.WebView
{
internal sealed class WebViewManager : IWebViewManagerInternal, IWebViewManagerHook
{
private IWebViewManagerImpl? _impl;
public void Initialize(GameController.DisplayMode mode)
{
DebugTools.Assert(_impl == null, "WebViewManager has already been initialized!");
IoCManager.RegisterInstance<IWebViewManager>(this);
IoCManager.RegisterInstance<IWebViewManagerInternal>(this);
if (mode == GameController.DisplayMode.Headless)
_impl = new WebViewManagerHeadless();
else
_impl = new WebViewManagerCef();
_impl.Initialize();
}
public void Update()
{
DebugTools.Assert(_impl != null, "WebViewManager has not yet been initialized!");
_impl!.Update();
}
public void Shutdown()
{
DebugTools.Assert(_impl != null, "WebViewManager has not yet been initialized!");
_impl!.Shutdown();
}
public IWebViewWindow CreateBrowserWindow(BrowserWindowCreateParameters createParams)
{
DebugTools.Assert(_impl != null, "WebViewManager has not yet been initialized!");
return _impl!.CreateBrowserWindow(createParams);
}
public IWebViewControlImpl MakeControlImpl(WebViewControl owner)
{
DebugTools.Assert(_impl != null, "WebViewManager has not yet been initialized!");
return _impl!.MakeControlImpl(owner);
}
}
}

View File

@@ -2,7 +2,6 @@ using System;
using JetBrains.Annotations;
using Robust.Shared.Animations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Robust.Client.Animations
{
@@ -19,8 +18,8 @@ namespace Robust.Client.Animations
throw new InvalidOperationException("Must set parameters to non-null values.");
}
var entity = (EntityUid) context;
var component = IoCManager.Resolve<IEntityManager>().GetComponent(entity, ComponentType);
var entity = (IEntity) context;
var component = entity.GetComponent(ComponentType);
if (component is IAnimationProperties properties)
{

View File

@@ -3,7 +3,7 @@ using Robust.Shared.Animations;
namespace Robust.Client.Animations
{
public sealed class AnimationTrackControlProperty : AnimationTrackProperty
public class AnimationTrackControlProperty : AnimationTrackProperty
{
public string? Property { get; set; }

View File

@@ -25,7 +25,7 @@ namespace Robust.Client.Animations
public override (int KeyFrameIndex, float FramePlayingTime)
AdvancePlayback(object context, int prevKeyFrameIndex, float prevPlayingTime, float frameTime)
{
var entity = (EntityUid) context;
var entity = (IEntity) context;
var playingTime = prevPlayingTime + frameTime;
var keyFrameIndex = prevKeyFrameIndex;

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
namespace Robust.Client.Animations
@@ -39,8 +38,8 @@ namespace Robust.Client.Animations
{
DebugTools.AssertNotNull(LayerKey);
var entity = (EntityUid) context;
var sprite = IoCManager.Resolve<IEntityManager>().GetComponent<ISpriteComponent>(entity);
var entity = (IEntity) context;
var sprite = entity.GetComponent<ISpriteComponent>();
var playingTime = prevPlayingTime + frameTime;
var keyFrameIndex = prevKeyFrameIndex;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
@@ -58,7 +58,7 @@ namespace Robust.Client.Audio.Midi
void Shutdown();
}
internal sealed class MidiManager : IMidiManager
internal class MidiManager : IMidiManager
{
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
@@ -293,50 +293,60 @@ namespace Robust.Client.Audio.Midi
}
MapCoordinates? mapPos = null;
var trackingEntity = renderer.TrackingEntity != null && !_entityManager.Deleted(renderer.TrackingEntity);
if (trackingEntity)
{
renderer.TrackingCoordinates = _entityManager.GetComponent<TransformComponent>(renderer.TrackingEntity!.Value).Coordinates;
}
if (renderer.TrackingCoordinates != null)
{
mapPos = renderer.TrackingCoordinates.Value.ToMap(_entityManager);
}
else if (renderer.TrackingEntity != null)
{
mapPos = renderer.TrackingEntity.Transform.MapPosition;
}
if (mapPos != null && mapPos.Value.MapId == _eyeManager.CurrentMap)
if (mapPos != null)
{
var pos = mapPos.Value;
var sourceRelative = _eyeManager.CurrentEye.Position.Position - pos.Position;
var occlusion = 0f;
if (sourceRelative.Length > 0)
if (pos.MapId != _eyeManager.CurrentMap)
{
occlusion = _broadPhaseSystem.IntersectRayPenetration(
pos.MapId,
new CollisionRay(
pos.Position,
sourceRelative.Normalized,
OcclusionCollisionMask),
sourceRelative.Length,
renderer.TrackingEntity);
renderer.Source.SetVolume(-10000000);
}
else
{
var sourceRelative = _eyeManager.CurrentEye.Position.Position - pos.Position;
var occlusion = 0f;
if (sourceRelative.Length > 0)
{
occlusion = _broadPhaseSystem.IntersectRayPenetration(
pos.MapId,
new CollisionRay(
pos.Position,
sourceRelative.Normalized,
OcclusionCollisionMask),
sourceRelative.Length,
renderer.TrackingEntity);
}
renderer.Source.SetOcclusion(occlusion);
}
renderer.Source.SetOcclusion(occlusion);
if (!renderer.Source.SetPosition(pos.Position))
if (renderer.Source.SetPosition(pos.Position))
{
return;
continue;
}
if (trackingEntity)
if (renderer.TrackingEntity != null)
{
renderer.Source.SetVelocity(renderer.TrackingEntity!.Value.GlobalLinearVelocity());
renderer.Source.SetVelocity(renderer.TrackingEntity.GlobalLinearVelocity());
}
}
else
{
renderer.Source.SetOcclusion(float.MaxValue);
if (float.IsNaN(pos.Position.X) || float.IsNaN(pos.Position.Y))
{
// just duck out instead of move to NaN
renderer.Source.SetOcclusion(float.MaxValue);
continue;
}
_midiSawmill?.Warning("Interrupting positional audio, can't set position.");
renderer.Source.StopPlaying();
}
}
@@ -392,7 +402,7 @@ namespace Robust.Client.Audio.Midi
/// <summary>
/// This class is used to load soundfonts.
/// </summary>
private sealed class ResourceLoaderCallbacks : SoundFontLoaderCallbacks
private class ResourceLoaderCallbacks : SoundFontLoaderCallbacks
{
private readonly Dictionary<int, Stream> _openStreams = new();
private int _nextStreamId = 1;

View File

@@ -150,7 +150,7 @@ namespace Robust.Client.Audio.Midi
/// The entity whose position will be used for positional audio.
/// This is only used if <see cref="Mono"/> is set to True.
/// </summary>
EntityUid? TrackingEntity { get; set; }
IEntity? TrackingEntity { get; set; }
/// <summary>
/// The position that will be used for positional audio.
@@ -180,7 +180,7 @@ namespace Robust.Client.Audio.Midi
internal void InternalDispose();
}
internal sealed class MidiRenderer : IMidiRenderer
internal class MidiRenderer : IMidiRenderer
{
[Dependency] private readonly IClydeAudio _clydeAudio = default!;
[Dependency] private readonly ITaskManager _taskManager = default!;
@@ -297,7 +297,7 @@ namespace Robust.Client.Audio.Midi
set
{
lock (_playerStateLock)
_player?.SetLoop(value ? -1 : 0);
_player?.SetLoop(value ? -1 : 1);
_loopMidi = value;
}
}
@@ -306,7 +306,7 @@ namespace Robust.Client.Audio.Midi
public bool VolumeBoost { get; set; }
[ViewVariables(VVAccess.ReadWrite)]
public EntityUid? TrackingEntity { get; set; } = null;
public IEntity? TrackingEntity { get; set; } = null;
[ViewVariables(VVAccess.ReadWrite)]
public EntityCoordinates? TrackingCoordinates { get; set; } = null;
@@ -619,7 +619,6 @@ namespace Robust.Client.Audio.Midi
var seqEv = (SequencerEvent) midiEvent;
seqEv.Dest = _debugEvents ? _debugRegister : _synthRegister;
_sequencer.SendAt(seqEv, time, absolute);
seqEv.Dispose();
}
public void Dispose()

View File

@@ -20,12 +20,13 @@ using Robust.Shared.Utility;
namespace Robust.Client
{
/// <inheritdoc />
public sealed class BaseClient : IBaseClient
public class BaseClient : IBaseClient
{
[Dependency] private readonly IClientNetManager _net = default!;
[Dependency] private readonly IPlayerManager _playMan = default!;
[Dependency] private readonly INetConfigurationManager _configManager = default!;
[Dependency] private readonly IClientEntityManager _entityManager = default!;
[Dependency] private readonly IEntityLookup _entityLookup = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IDiscordRichPresence _discord = default!;
[Dependency] private readonly IGameTiming _timing = default!;
@@ -212,6 +213,7 @@ namespace Robust.Client
{
_entityManager.Startup();
_mapManager.Startup();
_entityLookup.Startup();
_timing.ResetSimTime();
_timing.Paused = false;
@@ -222,6 +224,7 @@ namespace Robust.Client
IoCManager.Resolve<INetConfigurationManager>().FlushMessages();
_gameStates.Reset();
_playMan.Shutdown();
_entityLookup.Shutdown();
_entityManager.Shutdown();
_mapManager.Shutdown();
_discord.ClearPresence();
@@ -288,7 +291,7 @@ namespace Robust.Client
/// <summary>
/// Event arguments for when something changed with the player.
/// </summary>
public sealed class PlayerEventArgs : EventArgs
public class PlayerEventArgs : EventArgs
{
/// <summary>
/// The session that triggered the event.
@@ -307,7 +310,7 @@ namespace Robust.Client
/// <summary>
/// Event arguments for when the RunLevel has changed in the BaseClient.
/// </summary>
public sealed class RunLevelChangedEventArgs : EventArgs
public class RunLevelChangedEventArgs : EventArgs
{
/// <summary>
/// RunLevel that the BaseClient switched from.
@@ -332,7 +335,7 @@ namespace Robust.Client
/// <summary>
/// Info about the server and player that is sent to the client while connecting.
/// </summary>
public sealed class ServerInfo
public class ServerInfo
{
public ServerInfo(string serverName)
{

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