mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee330d0ae9 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -13,6 +13,3 @@
|
||||
[submodule "ManagedHttpListener"]
|
||||
path = ManagedHttpListener
|
||||
url = https://github.com/space-wizards/ManagedHttpListener.git
|
||||
[submodule "Linguini"]
|
||||
path = Linguini
|
||||
url = https://github.com/space-wizards/Linguini
|
||||
|
||||
1
Linguini
1
Linguini
Submodule Linguini deleted from 62b0e75b91
@@ -23,48 +23,6 @@
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
- name: Box2D
|
||||
license:
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Erin Catto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
- name: Bullet Physics SDK
|
||||
license:
|
||||
The files in this repository are licensed under the zlib license, except for the files under 'Extras' and examples/ThirdPartyLibs.
|
||||
|
||||
Bullet Continuous Collision Detection and Physics Library
|
||||
http://bulletphysics.org
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it freely,
|
||||
subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||
If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
- name: Castle Core
|
||||
license: |
|
||||
Copyright 2004-2016 Castle Project - http://www.castleproject.org/
|
||||
@@ -359,43 +317,6 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
- name: Farseer Physics Engine
|
||||
license:
|
||||
Microsoft Permissive License (Ms-PL)
|
||||
|
||||
This license governs use of the accompanying software.
|
||||
If you use the software, you accept this license.
|
||||
If you do not accept the license, do not use the software.
|
||||
|
||||
1. Definitions
|
||||
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
|
||||
A "contribution" is the original software, or any additions or changes to the software.
|
||||
A "contributor" is any person that distributes its contribution under this license.
|
||||
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
|
||||
|
||||
2. Grant of Rights
|
||||
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
|
||||
each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution,
|
||||
prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
|
||||
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
|
||||
each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to
|
||||
make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or
|
||||
derivative works of the contribution in the software.
|
||||
|
||||
3. Conditions and Limitations
|
||||
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
|
||||
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software,
|
||||
your patent license from such contributor to the software ends automatically.
|
||||
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark,
|
||||
and attribution notices that are present in the software.
|
||||
(D) If you distribute any portion of the software in source code form, you may do so only under this license by
|
||||
including a complete copy of this license with your distribution. If you distribute any portion of the software in
|
||||
compiled or object code form, you may only do so under a license that complies with this license.
|
||||
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties,
|
||||
guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change.
|
||||
To the extent permitted under your local laws, the contributors exclude the implied warranties of
|
||||
merchantability, fitness for a particular purpose and non-infringement.
|
||||
|
||||
- name: Mono.Cecil
|
||||
license: |
|
||||
Copyright (c) 2008 - 2015 Jb Evain
|
||||
|
||||
@@ -6,8 +6,6 @@ using System.Threading;
|
||||
using NFluidsynth;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -63,7 +61,6 @@ namespace Robust.Client.Audio.Midi
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfgMan = default!;
|
||||
|
||||
private SharedBroadPhaseSystem _broadPhaseSystem = default!;
|
||||
|
||||
@@ -97,7 +94,7 @@ namespace Robust.Client.Audio.Midi
|
||||
if (MathHelper.CloseTo(_volume, value))
|
||||
return;
|
||||
|
||||
_cfgMan.SetCVar(CVars.MidiVolume, value);
|
||||
_volume = value;
|
||||
_volumeDirty = true;
|
||||
}
|
||||
}
|
||||
@@ -134,12 +131,6 @@ namespace Robust.Client.Audio.Midi
|
||||
{
|
||||
if (FluidsynthInitialized || _failedInitialize) return;
|
||||
|
||||
_cfgMan.OnValueChanged(CVars.MidiVolume, value =>
|
||||
{
|
||||
_volume = value;
|
||||
_volumeDirty = true;
|
||||
}, true);
|
||||
|
||||
_midiSawmill = Logger.GetSawmill("midi");
|
||||
_sawmill = Logger.GetSawmill("midi.fluidsynth");
|
||||
_loggerDelegate = LoggerDelegate;
|
||||
|
||||
@@ -7,7 +7,6 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using MidiEvent = NFluidsynth.MidiEvent;
|
||||
@@ -204,9 +203,7 @@ namespace Robust.Client.Audio.Midi
|
||||
private const int SampleRate = 44100;
|
||||
private const int Buffers = SampleRate / 2205;
|
||||
private readonly object _playerStateLock = new();
|
||||
private bool _debugEvents = false;
|
||||
private SequencerClientId _synthRegister;
|
||||
private SequencerClientId _debugRegister;
|
||||
public IClydeBufferedAudioSource Source { get; set; }
|
||||
IClydeBufferedAudioSource IMidiRenderer.Source => Source;
|
||||
|
||||
@@ -316,7 +313,6 @@ namespace Robust.Client.Audio.Midi
|
||||
_soundFontLoader = soundFontLoader;
|
||||
_synth = new Synth(_settings);
|
||||
_sequencer = new Sequencer(false);
|
||||
_debugRegister = _sequencer.RegisterClient("honk", DumpSequencerEvent);
|
||||
_synthRegister = _sequencer.RegisterFluidsynth(_synth);
|
||||
|
||||
_synth.AddSoundFontLoader(soundFontLoader);
|
||||
@@ -326,27 +322,6 @@ namespace Robust.Client.Audio.Midi
|
||||
Source.StartPlaying();
|
||||
}
|
||||
|
||||
private void DumpSequencerEvent(uint time, SequencerEvent @event)
|
||||
{
|
||||
// ReSharper disable once UseStringInterpolation
|
||||
_midiSawmill.Debug(string.Format(
|
||||
"{0:D8}: {1} chan:{2:D2} key:{3:D5} bank:{4:D2} ctrl:{5:D5} dur:{6:D5} pitch:{7:D5} prog:{8:D3} val:{9:D5} vel:{10:D5}",
|
||||
time,
|
||||
@event.Type.ToString().PadLeft(22),
|
||||
@event.Channel,
|
||||
@event.Key,
|
||||
@event.Bank,
|
||||
@event.Control,
|
||||
@event.Duration,
|
||||
@event.Pitch,
|
||||
@event.Program,
|
||||
@event.Value,
|
||||
@event.Velocity));
|
||||
|
||||
@event.Dest = _synthRegister;
|
||||
_sequencer.SendNow(@event);
|
||||
}
|
||||
|
||||
public bool OpenInput()
|
||||
{
|
||||
if (Disposed)
|
||||
@@ -453,6 +428,9 @@ namespace Robust.Client.Audio.Midi
|
||||
{
|
||||
if (Disposed) return;
|
||||
|
||||
// SSE needs this.
|
||||
DebugTools.Assert(length % 4 == 0, "Sample length must be multiple of 4");
|
||||
|
||||
var buffersProcessed = Source.GetNumberOfBuffersProcessed();
|
||||
if(buffersProcessed == Buffers) _midiSawmill.Warning("MIDI buffer overflow!");
|
||||
if (buffersProcessed == 0) return;
|
||||
@@ -467,16 +445,36 @@ namespace Robust.Client.Audio.Midi
|
||||
Source.GetBuffersProcessed(buffers);
|
||||
|
||||
lock (_playerStateLock)
|
||||
{
|
||||
// _sequencer.Process(10);
|
||||
_synth?.WriteSampleFloat(length * buffers.Length, audio, 0, Mono ? 1 : 2,
|
||||
audio, Mono ? length * buffers.Length : 1, Mono ? 1 : 2);
|
||||
}
|
||||
|
||||
if (Mono) // Turn audio to mono
|
||||
{
|
||||
var l = length * buffers.Length;
|
||||
|
||||
NumericsHelpers.Add(audio[..l], audio[l..]);
|
||||
if (Sse.IsSupported)
|
||||
{
|
||||
fixed (float* ptr = audio)
|
||||
{
|
||||
for (var j = 0; j < l; j += 4)
|
||||
{
|
||||
var k = j + l;
|
||||
|
||||
var jV = Sse.LoadVector128(ptr + j);
|
||||
var kV = Sse.LoadVector128(ptr + k);
|
||||
|
||||
Sse.Store(j + ptr, Sse.Add(jV, kV));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var j = 0; j < l; j++)
|
||||
{
|
||||
var k = j + l;
|
||||
audio[j] = ((audio[k] + audio[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < buffers.Length; i++)
|
||||
@@ -534,16 +532,16 @@ namespace Robust.Client.Audio.Midi
|
||||
lock(_playerStateLock)
|
||||
switch (midiEvent.Type)
|
||||
{
|
||||
// Note Off - 0x80
|
||||
case 128:
|
||||
_synth.NoteOff(midiEvent.Channel, midiEvent.Key);
|
||||
break;
|
||||
|
||||
// Note On 0x90
|
||||
// Note On 0x80
|
||||
case 144:
|
||||
_synth.NoteOn(midiEvent.Channel, midiEvent.Key, midiEvent.Velocity);
|
||||
break;
|
||||
|
||||
// Note Off - 0x90
|
||||
case 128:
|
||||
_synth.NoteOff(midiEvent.Channel, midiEvent.Key);
|
||||
break;
|
||||
|
||||
// After Touch - 0xA
|
||||
case 160:
|
||||
_synth.KeyPressure(midiEvent.Channel, midiEvent.Key, midiEvent.Value);
|
||||
@@ -578,12 +576,6 @@ namespace Robust.Client.Audio.Midi
|
||||
case 81:
|
||||
// System Messages - 0xF0
|
||||
case 240:
|
||||
switch (midiEvent.Control)
|
||||
{
|
||||
case 11:
|
||||
_synth.AllNotesOff(midiEvent.Channel);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
@@ -605,7 +597,7 @@ namespace Robust.Client.Audio.Midi
|
||||
if (Disposed) return;
|
||||
|
||||
var seqEv = (SequencerEvent) midiEvent;
|
||||
seqEv.Dest = _debugEvents ? _debugRegister : _synthRegister;
|
||||
seqEv.Dest = _synthRegister;
|
||||
_sequencer.SendAt(seqEv, time, absolute);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ namespace Robust.Client
|
||||
public bool Launcher { get; }
|
||||
public string? Username { get; }
|
||||
public IReadOnlyCollection<(string key, string value)> CVars { get; }
|
||||
public IReadOnlyCollection<(string key, string value)> LogLevels { get; }
|
||||
|
||||
// Manual parser because C# has no good command line parsing libraries. Also dependencies bad.
|
||||
// Also I don't like spending 100ms parsing command line args. Do you?
|
||||
@@ -32,7 +31,6 @@ namespace Robust.Client
|
||||
var launcher = false;
|
||||
string? username = null;
|
||||
var cvars = new List<(string, string)>();
|
||||
var logLevels = new List<(string, string)>();
|
||||
var mountOptions = new MountOptions();
|
||||
|
||||
using var enumerator = args.GetEnumerator();
|
||||
@@ -126,26 +124,6 @@ namespace Robust.Client
|
||||
|
||||
mountOptions.DirMounts.Add(enumerator.Current);
|
||||
}
|
||||
else if (arg == "--loglevel")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing loglevel sawmill.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var loglevel = enumerator.Current;
|
||||
DebugTools.AssertNotNull(loglevel);
|
||||
var pos = loglevel.IndexOf('=');
|
||||
|
||||
if (pos == -1)
|
||||
{
|
||||
C.WriteLine("Expected = in loglevel.");
|
||||
return false;
|
||||
}
|
||||
|
||||
logLevels.Add((loglevel[..pos], loglevel[(pos + 1)..]));
|
||||
}
|
||||
else if (arg == "--help")
|
||||
{
|
||||
PrintHelp();
|
||||
@@ -164,7 +142,6 @@ namespace Robust.Client
|
||||
launcher,
|
||||
username,
|
||||
cvars,
|
||||
logLevels,
|
||||
connectAddress,
|
||||
ss14Address,
|
||||
mountOptions);
|
||||
@@ -185,7 +162,6 @@ Options:
|
||||
--launcher Run in launcher mode (no main menu, auto connect).
|
||||
--username Override username.
|
||||
--cvar Specifies an additional cvar overriding the config file. Syntax is <key>=<value>
|
||||
--loglevel Specifies an additional sawmill log level overriding the default values. Syntax is <key>=<value>
|
||||
--mount-dir Resource directory to mount.
|
||||
--mount-zip Resource zip to mount.
|
||||
--help Display this help text and exit.
|
||||
@@ -199,7 +175,6 @@ Options:
|
||||
bool launcher,
|
||||
string? username,
|
||||
IReadOnlyCollection<(string key, string value)> cVars,
|
||||
IReadOnlyCollection<(string key, string value)> logLevels,
|
||||
string connectAddress, string? ss14Address,
|
||||
MountOptions mountOptions)
|
||||
{
|
||||
@@ -209,7 +184,6 @@ Options:
|
||||
Launcher = launcher;
|
||||
Username = username;
|
||||
CVars = cVars;
|
||||
LogLevels = logLevels;
|
||||
ConnectAddress = connectAddress;
|
||||
Ss14Address = ss14Address;
|
||||
MountOptions = mountOptions;
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Robust.Client.Console
|
||||
Message = message;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc cref="IClientConsoleHost" />
|
||||
internal class ClientConsoleHost : ConsoleHost, IClientConsoleHost
|
||||
{
|
||||
@@ -45,9 +45,9 @@ namespace Robust.Client.Console
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
NetManager.RegisterNetMessage<MsgConCmdReg>(HandleConCmdReg);
|
||||
NetManager.RegisterNetMessage<MsgConCmdAck>(HandleConCmdAck);
|
||||
NetManager.RegisterNetMessage<MsgConCmd>(ProcessCommand);
|
||||
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME, HandleConCmdReg);
|
||||
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME, HandleConCmdAck);
|
||||
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
|
||||
|
||||
Reset();
|
||||
LogManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));
|
||||
|
||||
@@ -17,11 +17,11 @@ namespace Robust.Client.Console
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<MsgScriptStop>();
|
||||
_netManager.RegisterNetMessage<MsgScriptEval>();
|
||||
_netManager.RegisterNetMessage<MsgScriptStart>();
|
||||
_netManager.RegisterNetMessage<MsgScriptResponse>(ReceiveScriptResponse);
|
||||
_netManager.RegisterNetMessage<MsgScriptStartAck>(ReceiveScriptStartAckResponse);
|
||||
_netManager.RegisterNetMessage<MsgScriptStop>(MsgScriptStop.NAME);
|
||||
_netManager.RegisterNetMessage<MsgScriptEval>(MsgScriptEval.NAME);
|
||||
_netManager.RegisterNetMessage<MsgScriptStart>(MsgScriptStart.NAME);
|
||||
_netManager.RegisterNetMessage<MsgScriptResponse>(MsgScriptResponse.NAME, ReceiveScriptResponse);
|
||||
_netManager.RegisterNetMessage<MsgScriptStartAck>(MsgScriptStartAck.NAME, ReceiveScriptStartAckResponse);
|
||||
}
|
||||
|
||||
private void ReceiveScriptStartAckResponse(MsgScriptStartAck message)
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Robust.Client.Debugging
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityLookup _lookup = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
if (value && !_overlayManager.HasOverlay<EntityPositionOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new EntityPositionOverlay(_lookup, _eyeManager));
|
||||
_overlayManager.AddOverlay(new EntityPositionOverlay(_entityManager, _eyeManager));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -171,7 +171,7 @@ namespace Robust.Client.Debugging
|
||||
// all entities have a TransformComponent
|
||||
var transform = physBody.Owner.Transform;
|
||||
|
||||
var worldBox = physBody.GetWorldAABB();
|
||||
var worldBox = physBody.GetWorldAABB(_mapManager);
|
||||
if (worldBox.IsEmpty()) continue;
|
||||
|
||||
foreach (var fixture in physBody.Fixtures)
|
||||
@@ -270,14 +270,14 @@ namespace Robust.Client.Debugging
|
||||
|
||||
private sealed class EntityPositionOverlay : Overlay
|
||||
{
|
||||
private readonly IEntityLookup _lookup;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly IEyeManager _eyeManager;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public EntityPositionOverlay(IEntityLookup lookup, IEyeManager eyeManager)
|
||||
public EntityPositionOverlay(IEntityManager entityManager, IEyeManager eyeManager)
|
||||
{
|
||||
_lookup = lookup;
|
||||
_entityManager = entityManager;
|
||||
_eyeManager = eyeManager;
|
||||
}
|
||||
|
||||
@@ -286,17 +286,18 @@ namespace Robust.Client.Debugging
|
||||
const float stubLength = 0.25f;
|
||||
|
||||
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
|
||||
var viewport = _eyeManager.GetWorldViewport();
|
||||
|
||||
foreach (var entity in _lookup.GetEntitiesIntersecting(_eyeManager.CurrentMap, viewport))
|
||||
foreach (var entity in _entityManager.GetEntities())
|
||||
{
|
||||
var transform = entity.Transform;
|
||||
if (transform.MapID != _eyeManager.CurrentMap ||
|
||||
!_eyeManager.GetWorldViewport().Contains(transform.WorldPosition))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var center = transform.WorldPosition;
|
||||
var worldRotation = transform.WorldRotation;
|
||||
|
||||
var xLine = worldRotation.RotateVec(Vector2.UnitX);
|
||||
var yLine = worldRotation.RotateVec(Vector2.UnitY);
|
||||
var xLine = transform.WorldRotation.RotateVec(Vector2.UnitX);
|
||||
var yLine = transform.WorldRotation.RotateVec(Vector2.UnitY);
|
||||
|
||||
worldHandle.DrawLine(center, center + xLine * stubLength, Color.Red);
|
||||
worldHandle.DrawLine(center, center + yLine * stubLength, Color.Green);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Robust.Client.Debugging
|
||||
{
|
||||
@@ -54,7 +54,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_net.RegisterNetMessage<MsgRay>(HandleDrawRay);
|
||||
_net.RegisterNetMessage<MsgRay>(MsgRay.NAME, HandleDrawRay);
|
||||
}
|
||||
|
||||
private void HandleDrawRay(MsgRay msg)
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace Robust.Client
|
||||
// Disable load context usage on content start.
|
||||
// This prevents Content.Client being loaded twice and things like csi blowing up because of it.
|
||||
_modLoader.SetUseLoadContext(!ContentStart);
|
||||
_modLoader.SetEnableSandboxing(Options.Sandboxing);
|
||||
_modLoader.SetEnableSandboxing(false && Options.Sandboxing);
|
||||
|
||||
if (!_modLoader.TryLoadModulesFrom(new ResourcePath("/Assemblies/"), Options.ContentModulePrefix))
|
||||
{
|
||||
@@ -202,28 +202,6 @@ namespace Robust.Client
|
||||
|
||||
SetupLogging(_logManager, logHandlerFactory ?? (() => new ConsoleLogHandler()));
|
||||
|
||||
if (_commandLineArgs != null)
|
||||
{
|
||||
foreach (var (sawmill, level) in _commandLineArgs.LogLevels)
|
||||
{
|
||||
LogLevel? logLevel;
|
||||
if (level == "null")
|
||||
logLevel = null;
|
||||
else
|
||||
{
|
||||
if (!Enum.TryParse<LogLevel>(level, out var result))
|
||||
{
|
||||
System.Console.WriteLine($"LogLevel {level} does not exist!");
|
||||
continue;
|
||||
}
|
||||
logLevel = result;
|
||||
}
|
||||
_logManager.GetSawmill(sawmill).Level = logLevel;
|
||||
}
|
||||
}
|
||||
|
||||
ProgramShared.PrintRuntimeInfo(_logManager.RootSawmill);
|
||||
|
||||
// Figure out user data directory.
|
||||
var userDataDir = GetUserDataDir();
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Prometheus;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -17,7 +21,6 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
public sealed class ClientEntityManager : EntityManager, IClientEntityManagerInternal
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IClientNetManager _networkManager = default!;
|
||||
[Dependency] private readonly IClientGameStateManager _gameStateManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
@@ -64,7 +67,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <inheritdoc />
|
||||
public void SetupNetworking()
|
||||
{
|
||||
_networkManager.RegisterNetMessage<MsgEntity>(HandleEntityNetworkMessage);
|
||||
_networkManager.RegisterNetMessage<MsgEntity>(MsgEntity.NAME, HandleEntityNetworkMessage);
|
||||
}
|
||||
|
||||
public override void TickUpdate(float frameTime, Histogram? histogram)
|
||||
@@ -145,11 +148,7 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
|
||||
case EntityMessageType.SystemMessage:
|
||||
var msg = message.SystemMessage;
|
||||
var sessionType = typeof(EntitySessionMessage<>).MakeGenericType(msg.GetType());
|
||||
var sessionMsg = Activator.CreateInstance(sessionType, new EntitySessionEventArgs(_playerManager.LocalPlayer!.Session), msg)!;
|
||||
ReceivedSystemMessage?.Invoke(this, msg);
|
||||
ReceivedSystemMessage?.Invoke(this, sessionMsg);
|
||||
ReceivedSystemMessage?.Invoke(this, message.SystemMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
@@ -44,29 +44,11 @@ namespace Robust.Client.GameObjects
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set
|
||||
{
|
||||
if (_enabled == value) return;
|
||||
|
||||
_enabled = value;
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PointLightUpdateEvent());
|
||||
}
|
||||
set => _enabled = value;
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool ContainerOccluded
|
||||
{
|
||||
get => _containerOccluded;
|
||||
set
|
||||
{
|
||||
if (_containerOccluded == value) return;
|
||||
|
||||
_containerOccluded = value;
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PointLightUpdateEvent());
|
||||
}
|
||||
}
|
||||
|
||||
private bool _containerOccluded;
|
||||
public bool ContainerOccluded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the light mask should automatically rotate with the entity. (like a flashlight)
|
||||
@@ -135,13 +117,30 @@ namespace Robust.Client.GameObjects
|
||||
public bool VisibleNested
|
||||
{
|
||||
get => _visibleNested;
|
||||
set => _visibleNested = value;
|
||||
set
|
||||
{
|
||||
if (_visibleNested == value) return;
|
||||
_visibleNested = value;
|
||||
if (value)
|
||||
{
|
||||
if (Owner.Transform.Parent == null) return;
|
||||
|
||||
_lightOnParent = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_lightOnParent) return;
|
||||
|
||||
_lightOnParent = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DataField("radius")]
|
||||
private float _radius = 5f;
|
||||
[DataField("nestedvisible")]
|
||||
private bool _visibleNested = true;
|
||||
private bool _lightOnParent;
|
||||
[DataField("color")]
|
||||
private Color _color = Color.White;
|
||||
[DataField("offset")]
|
||||
@@ -207,6 +206,35 @@ namespace Robust.Client.GameObjects
|
||||
UpdateMask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
|
||||
if (message is ParentChangedMessage msg)
|
||||
{
|
||||
HandleTransformParentChanged(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleTransformParentChanged(ParentChangedMessage obj)
|
||||
{
|
||||
// TODO: this does not work for things nested multiply layers deep.
|
||||
if (!VisibleNested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj.NewParent != null && obj.NewParent.IsValid())
|
||||
{
|
||||
_lightOnParent = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lightOnParent = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
@@ -242,9 +270,4 @@ namespace Robust.Client.GameObjects
|
||||
PointLightComponent = pointLightComponent;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PointLightUpdateEvent : EntityEventArgs
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,12 +42,7 @@ namespace Robust.Client.GameObjects
|
||||
public override bool Visible
|
||||
{
|
||||
get => _visible;
|
||||
set
|
||||
{
|
||||
if (_visible == value) return;
|
||||
_visible = value;
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new SpriteUpdateEvent());
|
||||
}
|
||||
set => _visible = value;
|
||||
}
|
||||
|
||||
[DataField("drawdepth", customTypeSerializer: typeof(ConstantSerializer<DrawDepthTag>))]
|
||||
@@ -157,7 +152,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null) return;
|
||||
if(value == null) return;
|
||||
|
||||
Layers.Clear();
|
||||
foreach (var layerDatum in value)
|
||||
@@ -167,12 +162,11 @@ namespace Robust.Client.GameObjects
|
||||
if (!string.IsNullOrWhiteSpace(layerDatum.RsiPath))
|
||||
{
|
||||
var path = TextureRoot / layerDatum.RsiPath;
|
||||
|
||||
if (IoCManager.Resolve<IResourceCache>().TryGetResource(path, out RSIResource? resource))
|
||||
try
|
||||
{
|
||||
layer.RSI = resource.RSI;
|
||||
layer.RSI = IoCManager.Resolve<IResourceCache>().GetResource<RSIResource>(path).RSI;
|
||||
}
|
||||
else
|
||||
catch
|
||||
{
|
||||
Logger.ErrorS(LogCategory, "Unable to load layer RSI '{0}'.", path);
|
||||
}
|
||||
@@ -314,24 +308,13 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
[DataField("sprite", readOnly: true)] private string? rsi;
|
||||
[DataField("layers", readOnly: true)] private List<PrototypeLayerData> layerDatums = new();
|
||||
[DataField("layers", readOnly: true)] private List<PrototypeLayerData> layerDatums = new ();
|
||||
|
||||
[DataField("state", readOnly: true)] private string? state;
|
||||
[DataField("texture", readOnly: true)] private string? texture;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool ContainerOccluded
|
||||
{
|
||||
get => _containerOccluded;
|
||||
set
|
||||
{
|
||||
if (_containerOccluded == value) return;
|
||||
_containerOccluded = value;
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new SpriteUpdateEvent());
|
||||
}
|
||||
}
|
||||
|
||||
private bool _containerOccluded;
|
||||
public bool ContainerOccluded { get; set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool TreeUpdateQueued { get; set; }
|
||||
@@ -365,13 +348,13 @@ namespace Robust.Client.GameObjects
|
||||
if (!string.IsNullOrWhiteSpace(rsi))
|
||||
{
|
||||
var rsiPath = TextureRoot / rsi;
|
||||
if(IoCManager.Resolve<IResourceCache>().TryGetResource(rsiPath, out RSIResource? resource))
|
||||
try
|
||||
{
|
||||
BaseRSI = resource.RSI;
|
||||
BaseRSI = IoCManager.Resolve<IResourceCache>().GetResource<RSIResource>(rsiPath).RSI;
|
||||
}
|
||||
else
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorS(LogCategory, "Unable to load RSI '{0}'. Trace:\n{1}", rsiPath);
|
||||
Logger.ErrorS(SpriteComponent.LogCategory, "Unable to load RSI '{0}'. Trace:\n{1}", rsiPath, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -499,7 +482,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public int AddBlankLayer(int? newIndex = null)
|
||||
{
|
||||
var layer = new Layer(this) { Visible = false };
|
||||
var layer = new Layer(this) {Visible = false};
|
||||
return AddLayer(layer, newIndex);
|
||||
}
|
||||
|
||||
@@ -528,13 +511,13 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public int AddLayer(Texture? texture, int? newIndex = null)
|
||||
{
|
||||
var layer = new Layer(this) { Texture = texture };
|
||||
var layer = new Layer(this) {Texture = texture};
|
||||
return AddLayer(layer, newIndex);
|
||||
}
|
||||
|
||||
public int AddLayer(RSI.StateId stateId, int? newIndex = null)
|
||||
{
|
||||
var layer = new Layer(this) { State = stateId };
|
||||
var layer = new Layer(this) {State = stateId};
|
||||
if (BaseRSI != null && BaseRSI.TryGetState(stateId, out var state))
|
||||
{
|
||||
layer.AnimationTimeLeft = state.GetDelay(0);
|
||||
@@ -580,7 +563,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public int AddLayer(RSI.StateId stateId, RSI? rsi, int? newIndex = null)
|
||||
{
|
||||
var layer = new Layer(this) { State = stateId, RSI = rsi };
|
||||
var layer = new Layer(this) {State = stateId, RSI = rsi};
|
||||
if (rsi != null && rsi.TryGetState(stateId, out var state))
|
||||
{
|
||||
layer.AnimationTimeLeft = state.GetDelay(0);
|
||||
@@ -1305,8 +1288,8 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
var layerColor = color * layer.Color;
|
||||
|
||||
var position = -(Vector2)texture.Size / (2f * EyeManager.PixelsPerMeter) + layer.Offset;
|
||||
var textureSize = texture.Size / (float)EyeManager.PixelsPerMeter;
|
||||
var position = -(Vector2) texture.Size / (2f * EyeManager.PixelsPerMeter) + layer.Offset;
|
||||
var textureSize = texture.Size / (float) EyeManager.PixelsPerMeter;
|
||||
var quad = Box2.FromDimensions(position, textureSize);
|
||||
|
||||
// TODO: Implement layer-specific rotation and scale.
|
||||
@@ -1326,7 +1309,7 @@ namespace Robust.Client.GameObjects
|
||||
public static Angle CalcRectWorldAngle(Angle worldAngle, int numDirections)
|
||||
{
|
||||
var theta = worldAngle.Theta;
|
||||
var segSize = (MathF.PI * 2) / (numDirections * 2);
|
||||
var segSize = (MathF.PI*2) / (numDirections * 2);
|
||||
var segments = (int)(theta / segSize);
|
||||
var odd = segments % 2;
|
||||
var result = theta - (segments * segSize) - (odd * segSize);
|
||||
@@ -1376,6 +1359,18 @@ namespace Robust.Client.GameObjects
|
||||
return texture;
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
var map = Owner.Transform.MapID;
|
||||
if (map != MapId.Nullspace)
|
||||
{
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local,
|
||||
new RenderTreeRemoveSpriteEvent(this, map));
|
||||
}
|
||||
}
|
||||
|
||||
public void FrameUpdate(float delta)
|
||||
{
|
||||
foreach (var t in Layers)
|
||||
@@ -1426,7 +1421,7 @@ namespace Robust.Client.GameObjects
|
||||
if (curState == null)
|
||||
return;
|
||||
|
||||
var thestate = (SpriteComponentState)curState;
|
||||
var thestate = (SpriteComponentState) curState;
|
||||
|
||||
Visible = thestate.Visible;
|
||||
DrawDepth = thestate.DrawDepth;
|
||||
@@ -1996,13 +1991,13 @@ namespace Robust.Client.GameObjects
|
||||
switch (layerProp)
|
||||
{
|
||||
case "texture":
|
||||
LayerSetTexture(index, (string)value);
|
||||
LayerSetTexture(index, (string) value);
|
||||
return;
|
||||
case "state":
|
||||
LayerSetState(index, (string)value);
|
||||
LayerSetState(index, (string) value);
|
||||
return;
|
||||
case "color":
|
||||
LayerSetColor(index, (Color)value);
|
||||
LayerSetColor(index, (Color) value);
|
||||
return;
|
||||
default:
|
||||
throw new ArgumentException($"Unknown layer property '{layerProp}'");
|
||||
@@ -2047,7 +2042,7 @@ namespace Robust.Client.GameObjects
|
||||
yield break;
|
||||
}
|
||||
|
||||
var dummy = new DummyIconEntity { Prototype = prototype };
|
||||
var dummy = new DummyIconEntity {Prototype = prototype};
|
||||
var spriteComponent = dummy.AddComponent<SpriteComponent>();
|
||||
|
||||
if (prototype.Components.TryGetValue("Appearance", out _))
|
||||
@@ -2091,7 +2086,7 @@ namespace Robust.Client.GameObjects
|
||||
return GetFallbackState(resourceCache);
|
||||
}
|
||||
|
||||
var dummy = new DummyIconEntity { Prototype = prototype };
|
||||
var dummy = new DummyIconEntity {Prototype = prototype};
|
||||
var spriteComponent = dummy.AddComponent<SpriteComponent>();
|
||||
dummy.Delete();
|
||||
|
||||
@@ -2128,7 +2123,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
var typeFactory = IoCManager.Resolve<IDynamicTypeFactoryInternal>();
|
||||
var serializationManager = IoCManager.Resolve<ISerializationManager>();
|
||||
var comp = (T)typeFactory.CreateInstanceUnchecked(typeof(T));
|
||||
var comp = (T) typeFactory.CreateInstanceUnchecked(typeof(T));
|
||||
_components[typeof(T)] = comp;
|
||||
comp.Owner = this;
|
||||
|
||||
@@ -2162,7 +2157,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public T GetComponent<T>()
|
||||
{
|
||||
return (T)_components[typeof(T)];
|
||||
return (T) _components[typeof(T)];
|
||||
}
|
||||
|
||||
public IComponent GetComponent(Type type)
|
||||
@@ -2174,7 +2169,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
component = null;
|
||||
if (!_components.TryGetValue(typeof(T), out var value)) return false;
|
||||
component = (T)value;
|
||||
component = (T) value;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2230,9 +2225,4 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal sealed class SpriteUpdateEvent : EntityEventArgs
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace Robust.Client.GameObjects
|
||||
if (!source.SetPosition(entity.Transform.WorldPosition))
|
||||
{
|
||||
source.Dispose();
|
||||
Logger.Warning($"Can't play positional audio \"{stream.Name}\", can't set position.");
|
||||
Logger.Warning("Can't play positional audio, can't set position.");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ namespace Robust.Client.GameObjects
|
||||
if (!source.SetPosition(coordinates.ToMapPos(EntityManager)))
|
||||
{
|
||||
source.Dispose();
|
||||
Logger.Warning("Can't play positional audio \"{stream.Name}\", can't set position.");
|
||||
Logger.Warning("Can't play positional audio, can't set position.");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
#if DEBUG
|
||||
using System;
|
||||
using System.Text;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
internal sealed class DebugGridTileLookupSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set
|
||||
{
|
||||
if (_enabled == value) return;
|
||||
_enabled = value;
|
||||
|
||||
if (_enabled)
|
||||
{
|
||||
_label.Visible = true;
|
||||
LastTile = default;
|
||||
}
|
||||
else
|
||||
{
|
||||
_label.Text = null;
|
||||
_label.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _enabled;
|
||||
|
||||
private (GridId Grid, Vector2i Indices) LastTile;
|
||||
|
||||
// Label and shit that follows cursor
|
||||
private Label _label = new()
|
||||
{
|
||||
Visible = false,
|
||||
};
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeNetworkEvent<SendGridTileLookupMessage>(HandleSentEntities);
|
||||
IoCManager.Resolve<IUserInterfaceManager>().StateRoot.AddChild(_label);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
UnsubscribeNetworkEvent<SendGridTileLookupMessage>();
|
||||
IoCManager.Resolve<IUserInterfaceManager>().StateRoot.RemoveChild(_label);
|
||||
}
|
||||
|
||||
private void RequestEntities(GridId gridId, Vector2i indices)
|
||||
{
|
||||
if (gridId == GridId.Invalid) return;
|
||||
RaiseNetworkEvent(new RequestGridTileLookupMessage(gridId, indices));
|
||||
}
|
||||
|
||||
private void HandleSentEntities(SendGridTileLookupMessage message)
|
||||
{
|
||||
if (!Enabled) return;
|
||||
var text = new StringBuilder();
|
||||
text.AppendLine($"GridId: {LastTile.Grid}, Tile: {LastTile.Indices}");
|
||||
|
||||
for (var i = 0; i < message.Entities.Count; i++)
|
||||
{
|
||||
var uid = message.Entities[i];
|
||||
|
||||
if (!EntityManager.TryGetEntity(uid, out var entity)) continue;
|
||||
|
||||
text.AppendLine(entity.ToString());
|
||||
}
|
||||
|
||||
_label.Text = text.ToString();
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
if (!Enabled) return;
|
||||
|
||||
var mousePos = _inputManager.MouseScreenPosition;
|
||||
var worldPos = _eyeManager.ScreenToMap(mousePos);
|
||||
|
||||
GridId gridId;
|
||||
Vector2i tile;
|
||||
|
||||
if (_mapManager.TryFindGridAt(worldPos, out var grid))
|
||||
{
|
||||
gridId = grid.Index;
|
||||
tile = grid.WorldToTile(worldPos.Position);
|
||||
}
|
||||
else
|
||||
{
|
||||
gridId = GridId.Invalid;
|
||||
tile = new Vector2i((int) MathF.Floor(worldPos.Position.X), (int) MathF.Floor(worldPos.Position.Y));
|
||||
}
|
||||
|
||||
LayoutContainer.SetPosition(_label, mousePos.Position);
|
||||
|
||||
if ((gridId, tile).Equals(LastTile)) return;
|
||||
|
||||
_label.Text = null;
|
||||
LastTile = (gridId, tile);
|
||||
RequestEntities(gridId, tile);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class RequestTileEntities : IConsoleCommand
|
||||
{
|
||||
public string Command => "tilelookup";
|
||||
public string Description => "Used for debugging GridTileLookupSystem";
|
||||
public string Help => $"{Command}";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<DebugGridTileLookupSystem>().Enabled ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -173,24 +173,4 @@ namespace Robust.Client.GameObjects
|
||||
AttachedEntity = attachedEntity;
|
||||
}
|
||||
}
|
||||
|
||||
public class PlayerAttachedEvent : EntityEventArgs
|
||||
{
|
||||
public PlayerAttachedEvent(IEntity entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
public IEntity Entity { get; }
|
||||
}
|
||||
|
||||
public class PlayerDetachedEvent : EntityEventArgs
|
||||
{
|
||||
public PlayerDetachedEvent(IEntity entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
public IEntity Entity { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,6 @@ namespace Robust.Client.GameObjects
|
||||
private readonly List<SpriteComponent> _spriteQueue = new();
|
||||
private readonly List<PointLightComponent> _lightQueue = new();
|
||||
|
||||
private HashSet<EntityUid> _checkedChildren = new();
|
||||
|
||||
internal DynamicTree<SpriteComponent> GetSpriteTreeForMap(MapId map, GridId grid)
|
||||
{
|
||||
return _gridTrees[map][grid].SpriteTree;
|
||||
@@ -52,58 +50,16 @@ namespace Robust.Client.GameObjects
|
||||
_mapManager.OnGridCreated += MapManagerOnGridCreated;
|
||||
_mapManager.OnGridRemoved += MapManagerOnGridRemoved;
|
||||
|
||||
// Due to how recursion works, this must be done.
|
||||
SubscribeLocalEvent<MoveEvent>(AnythingMoved);
|
||||
|
||||
SubscribeLocalEvent<SpriteComponent, EntMapIdChangedMessage>(SpriteMapChanged);
|
||||
SubscribeLocalEvent<SpriteComponent, MoveEvent>(SpriteMoved);
|
||||
SubscribeLocalEvent<SpriteComponent, EntParentChangedMessage>(SpriteParentChanged);
|
||||
SubscribeLocalEvent<SpriteComponent, ComponentRemove>(RemoveSprite);
|
||||
SubscribeLocalEvent<SpriteComponent, SpriteUpdateEvent>(HandleSpriteUpdate);
|
||||
SubscribeLocalEvent<SpriteComponent, RenderTreeRemoveSpriteEvent>(RemoveSprite);
|
||||
|
||||
SubscribeLocalEvent<PointLightComponent, EntMapIdChangedMessage>(LightMapChanged);
|
||||
SubscribeLocalEvent<PointLightComponent, MoveEvent>(LightMoved);
|
||||
SubscribeLocalEvent<PointLightComponent, EntParentChangedMessage>(LightParentChanged);
|
||||
SubscribeLocalEvent<PointLightComponent, PointLightRadiusChangedEvent>(PointLightRadiusChanged);
|
||||
SubscribeLocalEvent<PointLightComponent, RenderTreeRemoveLightEvent>(RemoveLight);
|
||||
SubscribeLocalEvent<PointLightComponent, PointLightUpdateEvent>(HandleLightUpdate);
|
||||
}
|
||||
|
||||
private void HandleLightUpdate(EntityUid uid, PointLightComponent component, PointLightUpdateEvent args)
|
||||
{
|
||||
if (component.TreeUpdateQueued) return;
|
||||
QueueLightUpdate(component);
|
||||
}
|
||||
|
||||
private void HandleSpriteUpdate(EntityUid uid, SpriteComponent component, SpriteUpdateEvent args)
|
||||
{
|
||||
if (component.TreeUpdateQueued) return;
|
||||
QueueSpriteUpdate(component);
|
||||
}
|
||||
|
||||
private void AnythingMoved(MoveEvent args)
|
||||
{
|
||||
AnythingMovedSubHandler(args.Sender.Transform);
|
||||
}
|
||||
|
||||
private void AnythingMovedSubHandler(ITransformComponent sender)
|
||||
{
|
||||
// To avoid doing redundant updates (and we don't need to update a grid's children ever)
|
||||
if (!_checkedChildren.Add(sender.Owner.Uid) ||
|
||||
sender.Owner.HasComponent<MapGridComponent>() ||
|
||||
sender.Owner.HasComponent<MapComponent>()) return;
|
||||
|
||||
// This recursive search is needed, as MoveEvent is defined to not care about indirect events like children.
|
||||
// WHATEVER YOU DO, DON'T REPLACE THIS WITH SPAMMING EVENTS UNLESS YOU HAVE A GUARANTEE IT WON'T LAG THE GC.
|
||||
// (Struct-based events ok though)
|
||||
if (sender.Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
QueueSpriteUpdate(sprite);
|
||||
|
||||
if (sender.Owner.TryGetComponent(out PointLightComponent? light))
|
||||
QueueLightUpdate(light);
|
||||
|
||||
foreach (ITransformComponent child in sender.Children)
|
||||
{
|
||||
AnythingMovedSubHandler(child);
|
||||
}
|
||||
}
|
||||
|
||||
// For the RemoveX methods
|
||||
@@ -117,12 +73,17 @@ namespace Robust.Client.GameObjects
|
||||
QueueSpriteUpdate(component);
|
||||
}
|
||||
|
||||
private void SpriteMoved(EntityUid uid, SpriteComponent component, MoveEvent args)
|
||||
{
|
||||
QueueSpriteUpdate(component);
|
||||
}
|
||||
|
||||
private void SpriteParentChanged(EntityUid uid, SpriteComponent component, EntParentChangedMessage args)
|
||||
{
|
||||
QueueSpriteUpdate(component);
|
||||
}
|
||||
|
||||
private void RemoveSprite(EntityUid uid, SpriteComponent component, ComponentRemove args)
|
||||
private void RemoveSprite(EntityUid uid, SpriteComponent component, RenderTreeRemoveSpriteEvent args)
|
||||
{
|
||||
ClearSprite(component);
|
||||
}
|
||||
@@ -147,6 +108,22 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
component.TreeUpdateQueued = true;
|
||||
_spriteQueue.Add(component);
|
||||
|
||||
foreach (var child in component.Owner.Transform.Children)
|
||||
{
|
||||
QueueSpriteUpdate(child.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
private void QueueSpriteUpdate(IEntity entity)
|
||||
{
|
||||
if (!entity.TryGetComponent(out SpriteComponent? spriteComponent)) return;
|
||||
QueueSpriteUpdate(spriteComponent);
|
||||
|
||||
foreach (var child in entity.Transform.Children)
|
||||
{
|
||||
QueueSpriteUpdate(child.Owner);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -156,6 +133,11 @@ namespace Robust.Client.GameObjects
|
||||
QueueLightUpdate(component);
|
||||
}
|
||||
|
||||
private void LightMoved(EntityUid uid, PointLightComponent component, MoveEvent args)
|
||||
{
|
||||
QueueLightUpdate(component);
|
||||
}
|
||||
|
||||
private void LightParentChanged(EntityUid uid, PointLightComponent component, EntParentChangedMessage args)
|
||||
{
|
||||
QueueLightUpdate(component);
|
||||
@@ -191,6 +173,22 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
component.TreeUpdateQueued = true;
|
||||
_lightQueue.Add(component);
|
||||
|
||||
foreach (var child in component.Owner.Transform.Children)
|
||||
{
|
||||
QueueLightUpdate(child.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
private void QueueLightUpdate(IEntity entity)
|
||||
{
|
||||
if (!entity.TryGetComponent(out PointLightComponent? lightComponent)) return;
|
||||
QueueLightUpdate(lightComponent);
|
||||
|
||||
foreach (var child in entity.Transform.Children)
|
||||
{
|
||||
QueueLightUpdate(child.Owner);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -265,19 +263,10 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
_checkedChildren.Clear();
|
||||
|
||||
foreach (var sprite in _spriteQueue)
|
||||
{
|
||||
sprite.TreeUpdateQueued = false;
|
||||
var mapId = sprite.Owner.Transform.MapID;
|
||||
|
||||
if (!sprite.Visible || sprite.ContainerOccluded)
|
||||
{
|
||||
ClearSprite(sprite);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're on a new map then clear the old one.
|
||||
if (sprite.IntersectingMapId != mapId)
|
||||
{
|
||||
@@ -313,19 +302,14 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
sprite.IntersectingGrids.Add(gridId);
|
||||
}
|
||||
|
||||
sprite.TreeUpdateQueued = false;
|
||||
}
|
||||
|
||||
foreach (var light in _lightQueue)
|
||||
{
|
||||
light.TreeUpdateQueued = false;
|
||||
var mapId = light.Owner.Transform.MapID;
|
||||
|
||||
if (!light.Enabled || light.ContainerOccluded)
|
||||
{
|
||||
ClearLight(light);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're on a new map then clear the old one.
|
||||
if (light.IntersectingMapId != mapId)
|
||||
{
|
||||
@@ -341,7 +325,7 @@ namespace Robust.Client.GameObjects
|
||||
var intersectingGrids = _mapManager.FindGridIdsIntersecting(mapId, aabb, true).ToList();
|
||||
|
||||
// Remove from old
|
||||
foreach (var gridId in light.IntersectingGrids)
|
||||
foreach (var gridId in intersectingGrids)
|
||||
{
|
||||
if (intersectingGrids.Contains(gridId)) continue;
|
||||
mapTree[gridId].LightTree.Remove(light);
|
||||
@@ -360,6 +344,8 @@ namespace Robust.Client.GameObjects
|
||||
mapTree[gridId].LightTree.AddOrUpdate(light, translated);
|
||||
light.IntersectingGrids.Add(gridId);
|
||||
}
|
||||
|
||||
light.TreeUpdateQueued = false;
|
||||
}
|
||||
|
||||
_spriteQueue.Clear();
|
||||
@@ -394,6 +380,18 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
internal class RenderTreeRemoveSpriteEvent : EntityEventArgs
|
||||
{
|
||||
public RenderTreeRemoveSpriteEvent(SpriteComponent sprite, MapId map)
|
||||
{
|
||||
Sprite = sprite;
|
||||
Map = map;
|
||||
}
|
||||
|
||||
public SpriteComponent Sprite { get; }
|
||||
public MapId Map { get; }
|
||||
}
|
||||
|
||||
internal class RenderTreeRemoveLightEvent : EntityEventArgs
|
||||
{
|
||||
public RenderTreeRemoveLightEvent(PointLightComponent light, MapId map)
|
||||
|
||||
17
Robust.Client/GameObjects/PlayerMessages.cs
Normal file
17
Robust.Client/GameObjects/PlayerMessages.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
public class PlayerAttachedMsg : ComponentMessage
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
public class PlayerDetachedMsg : ComponentMessage
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
// ReSharper disable once RedundantUsingDirective
|
||||
// Used in EXCEPTION_TOLERANCE preprocessor
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@@ -7,18 +5,19 @@ using System.Linq;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Map;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -81,8 +80,8 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
_processor = new GameStateProcessor(_timing);
|
||||
|
||||
_network.RegisterNetMessage<MsgState>(HandleStateMessage);
|
||||
_network.RegisterNetMessage<MsgStateAck>();
|
||||
_network.RegisterNetMessage<MsgState>(MsgState.NAME, HandleStateMessage);
|
||||
_network.RegisterNetMessage<MsgStateAck>(MsgStateAck.NAME);
|
||||
_client.RunLevelChanged += RunLevelChanged;
|
||||
|
||||
_config.OnValueChanged(CVars.NetInterp, b => _processor.Interpolation = b, true);
|
||||
@@ -319,8 +318,6 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private void ResetPredictedEntities(GameTick curTick)
|
||||
{
|
||||
var bus = (EntityEventBus) _entities.EventBus;
|
||||
|
||||
foreach (var entity in _entities.GetEntities())
|
||||
{
|
||||
// TODO: 99% there's an off-by-one here.
|
||||
@@ -349,7 +346,6 @@ namespace Robust.Client.GameStates
|
||||
|
||||
Logger.DebugS(CVars.NetPredict.Name, $" And also its component {comp.Name}");
|
||||
// TODO: Handle interpolation.
|
||||
bus.RaiseComponentEvent(comp, new ComponentHandleState(compState, null));
|
||||
comp.HandleComponentState(compState, null);
|
||||
}
|
||||
}
|
||||
@@ -397,7 +393,7 @@ namespace Robust.Client.GameStates
|
||||
private List<EntityUid> ApplyGameState(GameState curState, GameState? nextState)
|
||||
{
|
||||
_config.TickProcessMessages();
|
||||
_mapManager.ApplyGameStatePre(curState.MapData, curState.EntityStates);
|
||||
_mapManager.ApplyGameStatePre(curState.MapData);
|
||||
var createdEntities = ApplyEntityStates(curState.EntityStates, curState.EntityDeletions,
|
||||
nextState?.EntityStates);
|
||||
_players.ApplyPlayerStates(curState.PlayerStates);
|
||||
@@ -458,14 +454,12 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
var bus = (EntityEventBus) _entities.EventBus;
|
||||
|
||||
// Make sure this is done after all entities have been instantiated.
|
||||
foreach (var kvStates in toApply)
|
||||
{
|
||||
var ent = kvStates.Key;
|
||||
var entity = (Entity) ent;
|
||||
HandleEntityState(entity.EntityManager.ComponentManager, entity, bus, kvStates.Value.Item1,
|
||||
HandleEntityState(entity.EntityManager.ComponentManager, entity, kvStates.Value.Item1,
|
||||
kvStates.Value.Item2);
|
||||
}
|
||||
|
||||
@@ -532,7 +526,7 @@ namespace Robust.Client.GameStates
|
||||
return created;
|
||||
}
|
||||
|
||||
private void HandleEntityState(IComponentManager compMan, IEntity entity, EntityEventBus bus, EntityState? curState,
|
||||
private void HandleEntityState(IComponentManager compMan, IEntity entity, EntityState? curState,
|
||||
EntityState? nextState)
|
||||
{
|
||||
var compStateWork = new Dictionary<uint, (ComponentState? curState, ComponentState? nextState)>();
|
||||
@@ -590,7 +584,6 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
try
|
||||
{
|
||||
bus.RaiseComponentEvent(component, new ComponentHandleState(cur, next));
|
||||
component.HandleComponentState(cur, next);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -44,12 +44,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
internal bool IsEfxSupported;
|
||||
|
||||
private ISawmill _openALSawmill = default!;
|
||||
|
||||
private void _initializeAudio()
|
||||
{
|
||||
_openALSawmill = Logger.GetSawmill("clyde.oal");
|
||||
|
||||
_audioOpenDevice();
|
||||
|
||||
// Create OpenAL context.
|
||||
@@ -78,9 +74,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_alContextExtensions.Add(extension);
|
||||
}
|
||||
|
||||
_openALSawmill.Debug("OpenAL Vendor: {0}", AL.Get(ALGetString.Vendor));
|
||||
_openALSawmill.Debug("OpenAL Renderer: {0}", AL.Get(ALGetString.Renderer));
|
||||
_openALSawmill.Debug("OpenAL Version: {0}", AL.Get(ALGetString.Version));
|
||||
Logger.DebugS("clyde.oal", "OpenAL Vendor: {0}", AL.Get(ALGetString.Vendor));
|
||||
Logger.DebugS("clyde.oal", "OpenAL Renderer: {0}", AL.Get(ALGetString.Renderer));
|
||||
Logger.DebugS("clyde.oal", "OpenAL Version: {0}", AL.Get(ALGetString.Version));
|
||||
}
|
||||
|
||||
private void _audioOpenDevice()
|
||||
@@ -93,7 +89,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_openALDevice = ALC.OpenDevice(preferredDevice);
|
||||
if (_openALDevice == IntPtr.Zero)
|
||||
{
|
||||
_openALSawmill.Warning("Unable to open preferred audio device '{0}': {1}. Falling back default.",
|
||||
Logger.WarningS("clyde.oal", "Unable to open preferred audio device '{0}': {1}. Falling back default.",
|
||||
preferredDevice, ALC.GetError(ALDevice.Null));
|
||||
|
||||
_openALDevice = ALC.OpenDevice(null);
|
||||
@@ -157,7 +153,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Clear out finalized audio sources.
|
||||
while (_sourceDisposeQueue.TryDequeue(out var handles))
|
||||
{
|
||||
_openALSawmill.Debug("Cleaning out source {0} which finalized in another thread.", handles.sourceHandle);
|
||||
Logger.DebugS("clyde.oal", "Cleaning out source {0} which finalized in another thread.", handles.sourceHandle);
|
||||
if (IsEfxSupported) RemoveEfx(handles);
|
||||
AL.DeleteSource(handles.sourceHandle);
|
||||
_checkAlError();
|
||||
@@ -167,7 +163,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Clear out finalized buffered audio sources.
|
||||
while (_bufferedSourceDisposeQueue.TryDequeue(out var handles))
|
||||
{
|
||||
_openALSawmill.Debug("Cleaning out buffered source {0} which finalized in another thread.", handles.sourceHandle);
|
||||
Logger.DebugS("clyde.oal", "Cleaning out buffered source {0} which finalized in another thread.", handles.sourceHandle);
|
||||
if (IsEfxSupported) RemoveEfx(handles);
|
||||
AL.DeleteSource(handles.sourceHandle);
|
||||
_checkAlError();
|
||||
@@ -187,6 +183,32 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (handles.filterHandle != 0) EFX.DeleteFilter(handles.filterHandle);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioRaw(short[] samples, int channels, int sampleRate)
|
||||
{
|
||||
var buffer = AL.GenBuffer();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (short* ptr = samples)
|
||||
{
|
||||
AL.BufferData(
|
||||
buffer,
|
||||
channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16,
|
||||
(IntPtr) ptr,
|
||||
samples.Length * 2,
|
||||
sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
_checkAlError();
|
||||
|
||||
var handle = new ClydeHandle(_audioSampleBuffers.Count);
|
||||
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
|
||||
// ReSharper disable once PossibleLossOfFraction
|
||||
var length = TimeSpan.FromSeconds(samples.Length / channels / (double) sampleRate);
|
||||
return new AudioStream(handle, length, channels);
|
||||
}
|
||||
|
||||
public void SetMasterVolume(float newVolume)
|
||||
{
|
||||
AL.Listener(ALListenerf.Gain, _baseGain * newVolume);
|
||||
@@ -215,24 +237,24 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return audioSource;
|
||||
}
|
||||
|
||||
private void _checkAlcError(ALDevice device,
|
||||
private static void _checkAlcError(ALDevice device,
|
||||
[CallerMemberName] string callerMember = "",
|
||||
[CallerLineNumber] int callerLineNumber = -1)
|
||||
{
|
||||
var error = ALC.GetError(device);
|
||||
if (error != AlcError.NoError)
|
||||
{
|
||||
_openALSawmill.Error("[{0}:{1}] ALC error: {2}", callerMember, callerLineNumber, error);
|
||||
Logger.ErrorS("clyde.oal", "[{0}:{1}] ALC error: {2}", callerMember, callerLineNumber, error);
|
||||
}
|
||||
}
|
||||
|
||||
private void _checkAlError([CallerMemberName] string callerMember = "",
|
||||
private static void _checkAlError([CallerMemberName] string callerMember = "",
|
||||
[CallerLineNumber] int callerLineNumber = -1)
|
||||
{
|
||||
var error = AL.GetError();
|
||||
if (error != ALError.NoError)
|
||||
{
|
||||
_openALSawmill.Error("[{0}:{1}] AL error: {2}", callerMember, callerLineNumber, error);
|
||||
Logger.ErrorS("clyde.oal", "[{0}:{1}] AL error: {2}", callerMember, callerLineNumber, error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,35 +356,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return new AudioStream(handle, length, wav.NumChannels, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
|
||||
{
|
||||
var fmt = channels switch
|
||||
{
|
||||
1 => ALFormat.Mono16,
|
||||
2 => ALFormat.Stereo16,
|
||||
_ => throw new ArgumentOutOfRangeException(
|
||||
nameof(channels), "Only stereo and mono is currently supported")
|
||||
};
|
||||
|
||||
var buffer = AL.GenBuffer();
|
||||
_checkAlError();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (short* ptr = samples)
|
||||
{
|
||||
AL.BufferData(buffer, fmt, (IntPtr) ptr, samples.Length * sizeof(short), sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
_checkAlError();
|
||||
|
||||
var handle = new ClydeHandle(_audioSampleBuffers.Count);
|
||||
var length = TimeSpan.FromSeconds((double) samples.Length / channels / sampleRate);
|
||||
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
|
||||
return new AudioStream(handle, length, channels, name);
|
||||
}
|
||||
|
||||
private sealed class LoadedAudioSample
|
||||
{
|
||||
public readonly int BufferHandle;
|
||||
@@ -414,14 +407,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.SourcePlay(SourceHandle);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public void StopPlaying()
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.SourceStop(SourceHandle);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public bool IsPlaying
|
||||
@@ -440,14 +433,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.GetSource(SourceHandle, ALSourceb.Looping, out var ret);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
return ret;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.Source(SourceHandle, ALSourceb.Looping, value);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,7 +448,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.Source(SourceHandle, ALSourceb.SourceRelative, true);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public void SetVolume(float decibels)
|
||||
@@ -469,10 +462,10 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
_gain = MathF.Pow(10, decibels / 10);
|
||||
AL.Source(SourceHandle, ALSourcef.Gain, _gain * priorOcclusion);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public void SetVolumeDirect(float scale)
|
||||
public void SetVolumeDirect(float decibels)
|
||||
{
|
||||
_checkDisposed();
|
||||
var priorOcclusion = 1f;
|
||||
@@ -481,9 +474,10 @@ namespace Robust.Client.Graphics.Clyde
|
||||
AL.GetSource(SourceHandle, ALSourcef.Gain, out var priorGain);
|
||||
priorOcclusion = priorGain / _gain;
|
||||
}
|
||||
_gain = scale;
|
||||
|
||||
_gain = decibels;
|
||||
AL.Source(SourceHandle, ALSourcef.Gain, _gain * priorOcclusion);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public void SetOcclusion(float blocks)
|
||||
@@ -500,7 +494,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
gain *= gain * gain;
|
||||
AL.Source(SourceHandle, ALSourcef.Gain, _gain * gain);
|
||||
}
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
private void SetOcclusionEfx(float gain, float cutoff)
|
||||
@@ -520,7 +514,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.Source(SourceHandle, ALSourcef.SecOffset, seconds);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public bool SetPosition(Vector2 position)
|
||||
@@ -546,7 +540,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
#endif
|
||||
|
||||
AL.Source(SourceHandle, ALSource3f.Position, x, y, 0);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -573,14 +567,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
AL.Source(SourceHandle, ALSource3f.Velocity, x, y, 0);
|
||||
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public void SetPitch(float pitch)
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.Source(SourceHandle, ALSourcef.Pitch, pitch);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
~AudioSource()
|
||||
@@ -606,7 +600,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (FilterHandle != 0) EFX.DeleteFilter(FilterHandle);
|
||||
AL.DeleteSource(SourceHandle);
|
||||
_master._audioSources.Remove(SourceHandle);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
SourceHandle = -1;
|
||||
@@ -656,7 +650,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_checkDisposed();
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
AL.SourcePlay(stackalloc int[] {SourceHandle!.Value});
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public void StopPlaying()
|
||||
@@ -664,7 +658,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_checkDisposed();
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
AL.SourceStop(SourceHandle!.Value);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public bool IsPlaying
|
||||
@@ -690,7 +684,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_mono = false;
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
AL.Source(SourceHandle!.Value, ALSourceb.SourceRelative, true);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public void SetLooping()
|
||||
@@ -709,21 +703,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
_gain = MathF.Pow(10, decibels / 10);
|
||||
AL.Source(SourceHandle!.Value, ALSourcef.Gain, _gain * priorOcclusion);
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public void SetVolumeDirect(float scale)
|
||||
{
|
||||
_checkDisposed();
|
||||
var priorOcclusion = 1f;
|
||||
if (!IsEfxSupported)
|
||||
{
|
||||
AL.GetSource(SourceHandle!.Value, ALSourcef.Gain, out var priorGain);
|
||||
priorOcclusion = priorGain / _gain;
|
||||
}
|
||||
_gain = scale;
|
||||
AL.Source(SourceHandle!.Value, ALSourcef.Gain, _gain * priorOcclusion);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public void SetOcclusion(float blocks)
|
||||
@@ -741,7 +721,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
AL.Source(SourceHandle!.Value, ALSourcef.Gain, gain * _gain);
|
||||
}
|
||||
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
private void SetOcclusionEfx(float gain, float cutoff)
|
||||
@@ -761,7 +741,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_checkDisposed();
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
AL.Source(SourceHandle!.Value, ALSourcef.SecOffset, seconds);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public bool SetPosition(Vector2 position)
|
||||
@@ -778,7 +758,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_mono = true;
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
AL.Source(SourceHandle!.Value, ALSource3f.Position, x, y, 0);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -805,7 +785,22 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
AL.Source(SourceHandle!.Value, ALSource3f.Velocity, x, y, 0);
|
||||
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public void SetVolumeDirect(float masterVolumeDecay)
|
||||
{
|
||||
_checkDisposed();
|
||||
var priorOcclusion = 1f;
|
||||
if (!IsEfxSupported)
|
||||
{
|
||||
AL.GetSource(SourceHandle!.Value, ALSourcef.Gain, out var priorGain);
|
||||
priorOcclusion = priorGain / _gain;
|
||||
}
|
||||
|
||||
_gain = masterVolumeDecay;
|
||||
AL.Source(SourceHandle!.Value, ALSourcef.Gain, _gain * priorOcclusion);
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
public void SetPitch(float pitch)
|
||||
@@ -813,7 +808,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_checkDisposed();
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
AL.Source(SourceHandle!.Value, ALSourcef.Pitch, pitch);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
~BufferedAudioSource()
|
||||
@@ -844,7 +839,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
AL.DeleteSource(SourceHandle.Value);
|
||||
AL.DeleteBuffers(BufferHandles);
|
||||
_master._bufferedAudioSources.Remove(SourceHandle.Value);
|
||||
_master._checkAlError();
|
||||
_checkAlError();
|
||||
}
|
||||
|
||||
SourceHandle = null;
|
||||
|
||||
@@ -387,6 +387,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
ref RefList<(SpriteComponent sprite, Matrix3 matrix, Angle worldRot, float yWorldPos)> state,
|
||||
in SpriteComponent value) =>
|
||||
{
|
||||
// TODO: Probably value in storing this as its own DynamicTree
|
||||
if (value.ContainerOccluded || !value.Visible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var entity = value.Owner;
|
||||
var transform = entity.Transform;
|
||||
|
||||
|
||||
@@ -149,16 +149,16 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// FOV FBO.
|
||||
_fovRenderTarget = CreateRenderTarget((FovMapSize, 2),
|
||||
new RenderTargetFormatParameters(_hasGLFloatFramebuffers ? RenderTargetColorFormat.RG32F : RenderTargetColorFormat.Rgba8, true),
|
||||
new TextureSampleParameters { WrapMode = TextureWrapMode.Repeat },
|
||||
new TextureSampleParameters {WrapMode = TextureWrapMode.Repeat},
|
||||
nameof(_fovRenderTarget));
|
||||
|
||||
if (_hasGLSamplerObjects)
|
||||
{
|
||||
_fovFilterSampler = new GLHandle(GL.GenSampler());
|
||||
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureMagFilter, (int)All.Linear);
|
||||
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureMinFilter, (int)All.Linear);
|
||||
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureWrapS, (int)All.Repeat);
|
||||
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureWrapT, (int)All.Repeat);
|
||||
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureMagFilter, (int) All.Linear);
|
||||
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureMinFilter, (int) All.Linear);
|
||||
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureWrapS, (int) All.Repeat);
|
||||
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureWrapT, (int) All.Repeat);
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
@@ -180,17 +180,20 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_fovCalculationProgram = _compileProgram(depthVert, depthFrag, attribLocations, "Shadow Depth Program");
|
||||
|
||||
var debugShader = _resourceCache.GetResource<ShaderSourceResource>("/Shaders/Internal/depth-debug.swsl");
|
||||
_fovDebugShaderInstance = (ClydeShaderInstance)InstanceShader(debugShader.ClydeHandle);
|
||||
_fovDebugShaderInstance = (ClydeShaderInstance) InstanceShader(debugShader.ClydeHandle);
|
||||
|
||||
ClydeHandle LoadShaderHandle(string path)
|
||||
{
|
||||
if (_resourceCache.TryGetResource(path, out ShaderSourceResource? resource))
|
||||
try
|
||||
{
|
||||
return resource.ClydeHandle;
|
||||
var shaderSource = _resourceCache.GetResource<ShaderSourceResource>(path);
|
||||
return shaderSource.ClydeHandle;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning($"Can't load shader {path}\n{ex.GetType().Name}: {ex.Message}");
|
||||
return default;
|
||||
}
|
||||
|
||||
Logger.Warning($"Can't load shader {path}\n");
|
||||
return default;
|
||||
}
|
||||
|
||||
_lightSoftShaderHandle = LoadShaderHandle("/Shaders/Internal/light-soft.swsl");
|
||||
@@ -211,7 +214,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
// Calculate maximum distance for the projection based on screen size.
|
||||
var screenSizeCut = viewport.Size / EyeManager.PixelsPerMeter;
|
||||
var maxDist = (float)Math.Max(screenSizeCut.X, screenSizeCut.Y);
|
||||
var maxDist = (float) Math.Max(screenSizeCut.X, screenSizeCut.Y);
|
||||
|
||||
// FOV is rendered twice.
|
||||
// Once with back face culling like regular lighting.
|
||||
@@ -521,6 +524,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Don't insert into trees for these, same as sprites.
|
||||
if (!light.Enabled || light.ContainerOccluded)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var lightPos = transform.WorldMatrix.Transform(light.Offset);
|
||||
|
||||
var circle = new Circle(lightPos, light.Radius);
|
||||
@@ -581,7 +590,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
SetupGlobalUniformsImmediate(shader, viewport.LightRenderTarget.Texture);
|
||||
|
||||
shader.SetUniformMaybe("size", (Vector2)viewport.WallBleedIntermediateRenderTarget1.Size);
|
||||
shader.SetUniformMaybe("size", (Vector2) viewport.WallBleedIntermediateRenderTarget1.Size);
|
||||
shader.SetUniformTextureMaybe(UniIMainTexture, TextureUnit.Texture0);
|
||||
|
||||
var size = viewport.WallBleedIntermediateRenderTarget1.Size;
|
||||
@@ -800,7 +809,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
occluderTree.QueryAabb((in OccluderComponent sOccluder) =>
|
||||
{
|
||||
var occluder = (ClientOccluderComponent)sOccluder;
|
||||
var occluder = (ClientOccluderComponent) sOccluder;
|
||||
var transform = occluder.Owner.Transform;
|
||||
if (!occluder.Enabled)
|
||||
{
|
||||
@@ -884,11 +893,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// DddD
|
||||
// HHhh
|
||||
// deflection
|
||||
arrayVIBuffer[avi++] = (byte)((((vi + 1) & 2) != 0) ? 0 : 255);
|
||||
arrayVIBuffer[avi++] = (byte) ((((vi + 1) & 2) != 0) ? 0 : 255);
|
||||
// height
|
||||
arrayVIBuffer[avi++] = (byte)(((vi & 2) != 0) ? 0 : 255);
|
||||
arrayVIBuffer[avi++] = (byte) (((vi & 2) != 0) ? 0 : 255);
|
||||
}
|
||||
QuadBatchIndexWrite(indexBuffer, ref ii, (ushort)aiBase);
|
||||
QuadBatchIndexWrite(indexBuffer, ref ii, (ushort) aiBase);
|
||||
}
|
||||
|
||||
// North face (TL/TR)
|
||||
@@ -922,7 +931,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
arrayMaskBuffer[ami + 3] = new Vector2(blX, blY);
|
||||
|
||||
// Generate mask indices.
|
||||
QuadBatchIndexWrite(indexMaskBuffer, ref imi, (ushort)ami);
|
||||
QuadBatchIndexWrite(indexMaskBuffer, ref imi, (ushort) ami);
|
||||
|
||||
ami += 4;
|
||||
|
||||
@@ -964,7 +973,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var lightMapSize = GetLightMapSize(viewport.Size);
|
||||
var lightMapSizeQuart = GetLightMapSize(viewport.Size, true);
|
||||
var lightMapColorFormat = _hasGLFloatFramebuffers ? RenderTargetColorFormat.R11FG11FB10F : RenderTargetColorFormat.Rgba8;
|
||||
var lightMapSampleParameters = new TextureSampleParameters { Filter = true };
|
||||
var lightMapSampleParameters = new TextureSampleParameters {Filter = true};
|
||||
|
||||
viewport.LightRenderTarget?.Dispose();
|
||||
viewport.WallMaskRenderTarget?.Dispose();
|
||||
@@ -1001,14 +1010,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private Vector2i GetLightMapSize(Vector2i screenSize, bool furtherDivide = false)
|
||||
{
|
||||
var divider = (float)_lightmapDivider;
|
||||
var divider = (float) _lightmapDivider;
|
||||
if (furtherDivide)
|
||||
{
|
||||
divider *= 2;
|
||||
}
|
||||
|
||||
var w = (int)Math.Ceiling(screenSize.X / divider);
|
||||
var h = (int)Math.Ceiling(screenSize.Y / divider);
|
||||
var w = (int) Math.Ceiling(screenSize.X / divider);
|
||||
var h = (int) Math.Ceiling(screenSize.Y / divider);
|
||||
|
||||
return (w, h);
|
||||
}
|
||||
@@ -1034,7 +1043,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Shadow FBO.
|
||||
_shadowRenderTarget = CreateRenderTarget((ShadowMapSize, _maxLightsPerScene),
|
||||
new RenderTargetFormatParameters(_hasGLFloatFramebuffers ? RenderTargetColorFormat.RG32F : RenderTargetColorFormat.Rgba8, true),
|
||||
new TextureSampleParameters { WrapMode = TextureWrapMode.Repeat, Filter = true },
|
||||
new TextureSampleParameters {WrapMode = TextureWrapMode.Repeat, Filter = true},
|
||||
nameof(_shadowRenderTarget));
|
||||
}
|
||||
|
||||
|
||||
@@ -248,10 +248,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return new(default, default, 1, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
|
||||
public AudioStream LoadAudioRaw(short[] samples, int channels, int sampleRate)
|
||||
{
|
||||
// TODO: Might wanna actually load this so the length gets reported correctly.
|
||||
return new(default, default, channels, name);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IClydeAudioSource CreateAudioSource(AudioStream stream)
|
||||
@@ -329,11 +328,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetVolumeDirect(float scale)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetOcclusion(float blocks)
|
||||
{
|
||||
// Nada.
|
||||
@@ -348,6 +342,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetVolumeDirect(float masterVolumeDecay)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DummyBufferedAudioSource : DummyAudioSource, IClydeBufferedAudioSource
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using Robust.Client.Audio;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
@@ -9,7 +8,7 @@ namespace Robust.Client.Graphics
|
||||
// AUDIO SYSTEM DOWN BELOW.
|
||||
AudioStream LoadAudioOggVorbis(Stream stream, string? name = null);
|
||||
AudioStream LoadAudioWav(Stream stream, string? name = null);
|
||||
AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null);
|
||||
AudioStream LoadAudioRaw(short[] samples, int channels, int sampleRate);
|
||||
|
||||
void SetMasterVolume(float newVolume);
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ namespace Robust.Client.Graphics
|
||||
void SetPitch(float pitch);
|
||||
void SetGlobal();
|
||||
void SetVolume(float decibels);
|
||||
void SetVolumeDirect(float decibels);
|
||||
void SetOcclusion(float blocks);
|
||||
void SetPlaybackPosition(float seconds);
|
||||
void SetVelocity(Vector2 velocity);
|
||||
void SetVolumeDirect(float masterVolumeDecay);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
internal interface IRenderHandle
|
||||
public interface IRenderHandle
|
||||
{
|
||||
DrawingHandleScreen DrawingHandleScreen { get; }
|
||||
DrawingHandleWorld DrawingHandleWorld { get; }
|
||||
|
||||
@@ -1,111 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Map
|
||||
{
|
||||
internal class ClientMapManager : MapManager, IClientMapManager
|
||||
{
|
||||
public void ApplyGameStatePre(GameStateMapData? data, EntityState[]? entityStates)
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
|
||||
public void ApplyGameStatePre(GameStateMapData? data)
|
||||
{
|
||||
// There was no map data this tick, so nothing to do.
|
||||
if(data == null)
|
||||
return;
|
||||
|
||||
var createdGrids = data.CreatedGrids != null
|
||||
? new Dictionary<GridId, GameStateMapData.GridCreationDatum>(data.CreatedGrids)
|
||||
: null;
|
||||
|
||||
// First we need to figure out all the NEW MAPS.
|
||||
if(data.CreatedMaps != null)
|
||||
{
|
||||
DebugTools.Assert(entityStates is not null, "Received new maps, but no entity state.");
|
||||
|
||||
foreach (var mapId in data.CreatedMaps)
|
||||
{
|
||||
// map already exists from a previous state.
|
||||
if (_maps.Contains(mapId))
|
||||
continue;
|
||||
|
||||
EntityUid mapEuid = default;
|
||||
|
||||
//get shared euid of map comp entity
|
||||
foreach (var entityState in entityStates!)
|
||||
{
|
||||
if(entityState.ComponentStates is null)
|
||||
continue;
|
||||
|
||||
foreach (var compState in entityState.ComponentStates)
|
||||
{
|
||||
if (compState is not MapComponentState mapCompState || mapCompState.MapId != mapId)
|
||||
continue;
|
||||
|
||||
mapEuid = entityState.Uid;
|
||||
goto BreakMapEntSearch;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
BreakMapEntSearch:
|
||||
|
||||
DebugTools.Assert(mapEuid != default, $"Could not find corresponding entity state for new map {mapId}.");
|
||||
|
||||
CreateMap(mapId, mapEuid);
|
||||
CreateMap(mapId);
|
||||
}
|
||||
}
|
||||
|
||||
// Then make all the grids.
|
||||
if(data.CreatedGrids != null)
|
||||
{
|
||||
DebugTools.Assert(data.GridData is not null, "Received new grids, but GridData was null.");
|
||||
var gridData = data.GridData != null
|
||||
? new Dictionary<GridId, GameStateMapData.GridDatum>(data.GridData)
|
||||
: null;
|
||||
|
||||
DebugTools.AssertNotNull(createdGrids);
|
||||
|
||||
foreach (var (gridId, creationDatum) in data.CreatedGrids)
|
||||
{
|
||||
if (_grids.ContainsKey(gridId))
|
||||
{
|
||||
continue;
|
||||
|
||||
EntityUid gridEuid = default;
|
||||
|
||||
//get shared euid of map comp entity
|
||||
foreach (var entityState in entityStates!)
|
||||
{
|
||||
if(entityState.ComponentStates is null)
|
||||
continue;
|
||||
|
||||
foreach (var compState in entityState.ComponentStates)
|
||||
{
|
||||
if (compState is not MapGridComponentState gridCompState || gridCompState.GridIndex != gridId)
|
||||
continue;
|
||||
|
||||
gridEuid = entityState.Uid;
|
||||
goto BreakGridEntSearch;
|
||||
}
|
||||
}
|
||||
BreakGridEntSearch:
|
||||
|
||||
DebugTools.Assert(gridEuid != default, $"Could not find corresponding entity state for new grid {gridId}.");
|
||||
|
||||
MapId gridMapId = default;
|
||||
foreach (var kvData in data.GridData!)
|
||||
{
|
||||
if (kvData.Key != gridId)
|
||||
continue;
|
||||
|
||||
gridMapId = kvData.Value.Coordinates.MapId;
|
||||
break;
|
||||
}
|
||||
|
||||
DebugTools.Assert(gridMapId != default, $"Could not find corresponding gridData for new grid {gridId}.");
|
||||
|
||||
CreateGrid(gridMapId, gridId, creationDatum.ChunkSize, gridEuid);
|
||||
CreateGrid(gridData![gridId].Coordinates.MapId, gridId, creationDatum.ChunkSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Process all grid updates.
|
||||
if(data.GridData != null)
|
||||
{
|
||||
SuppressOnTileChanged = true;
|
||||
// Ok good all the grids and maps exist now.
|
||||
foreach (var (gridId, gridDatum) in data.GridData)
|
||||
{
|
||||
|
||||
var grid = _grids[gridId];
|
||||
if (grid.ParentMapId != gridDatum.Coordinates.MapId)
|
||||
{
|
||||
@@ -151,9 +112,99 @@ namespace Robust.Client.Map
|
||||
|
||||
public void ApplyGameStatePost(GameStateMapData? data)
|
||||
{
|
||||
DebugTools.Assert(_netManager.IsClient, "Only the client should call this.");
|
||||
|
||||
if(data == null) // if there is no data, there is nothing to do!
|
||||
return;
|
||||
|
||||
// maps created on the client in pre-state are linked to client entities
|
||||
// resolve new maps with their shared component that the server just gave us
|
||||
// and delete the client entities
|
||||
if (data.CreatedMaps != null)
|
||||
{
|
||||
foreach (var mapId in data.CreatedMaps)
|
||||
{
|
||||
// CreateMap should have set this
|
||||
DebugTools.Assert(_mapEntities.ContainsKey(mapId));
|
||||
|
||||
// this was already linked in a previous state.
|
||||
if(!_mapEntities[mapId].IsClientSide())
|
||||
continue;
|
||||
|
||||
// get the existing client entity for the map.
|
||||
var cEntity = EntityManager.GetEntity(_mapEntities[mapId]);
|
||||
|
||||
// locate the entity that represents this map that was just sent to us
|
||||
IEntity? sharedMapEntity = null;
|
||||
var mapComps = EntityManager.ComponentManager.EntityQuery<IMapComponent>(true);
|
||||
foreach (var mapComp in mapComps)
|
||||
{
|
||||
if (!mapComp.Owner.Uid.IsClientSide() && mapComp.WorldMap == mapId)
|
||||
{
|
||||
sharedMapEntity = mapComp.Owner;
|
||||
_mapEntities[mapId] = mapComp.Owner.Uid;
|
||||
Logger.DebugS("map", $"Map {mapId} pivoted bound entity from {cEntity.Uid} to {mapComp.Owner.Uid}.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// verify shared entity was found (the server sent us one)
|
||||
DebugTools.AssertNotNull(sharedMapEntity);
|
||||
DebugTools.Assert(!_mapEntities[mapId].IsClientSide());
|
||||
|
||||
// Transfer client child grids made in GameStatePre to the shared component
|
||||
// so they are not deleted
|
||||
foreach (var childGridTrans in cEntity.Transform.Children.ToList())
|
||||
{
|
||||
childGridTrans.AttachParent(sharedMapEntity!);
|
||||
}
|
||||
|
||||
// remove client entity
|
||||
var cGridComp = cEntity.GetComponent<IMapComponent>();
|
||||
cGridComp.ClearMapId();
|
||||
cEntity.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// grids created on the client in pre-state are linked to client entities
|
||||
// resolve new grids with their shared component that the server just gave us
|
||||
// and delete the client entities
|
||||
if (data.CreatedGrids != null)
|
||||
{
|
||||
foreach (var kvNewGrid in data.CreatedGrids)
|
||||
{
|
||||
var grid = _grids[kvNewGrid.Key];
|
||||
|
||||
// this was already linked in a previous state.
|
||||
if(!grid.GridEntityId.IsClientSide())
|
||||
continue;
|
||||
|
||||
// remove the existing client entity.
|
||||
var cEntity = EntityManager.GetEntity(grid.GridEntityId);
|
||||
var cGridComp = cEntity.GetComponent<IMapGridComponent>();
|
||||
|
||||
// prevents us from deleting the grid when deleting the grid entity
|
||||
if(cEntity.Uid.IsClientSide())
|
||||
cGridComp.ClearGridId();
|
||||
|
||||
cEntity.Delete(); // normal entities are already parented to the shared comp, client comp has no children
|
||||
|
||||
var gridComps = EntityManager.ComponentManager.EntityQuery<IMapGridComponent>(true);
|
||||
foreach (var gridComp in gridComps)
|
||||
{
|
||||
if (gridComp.GridIndex == kvNewGrid.Key)
|
||||
{
|
||||
grid.GridEntityId = gridComp.Owner.Uid;
|
||||
Logger.DebugS("map", $"Grid {grid.Index} pivoted bound entity from {cEntity.Uid} to {grid.GridEntityId}.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DebugTools.Assert(!grid.GridEntityId.IsClientSide());
|
||||
}
|
||||
}
|
||||
|
||||
if(data.DeletedGrids != null)
|
||||
{
|
||||
foreach (var grid in data.DeletedGrids)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
@@ -8,7 +7,7 @@ namespace Robust.Client.Map
|
||||
{
|
||||
// Two methods here, so that new grids etc can be made BEFORE entities get states applied,
|
||||
// but old ones can be deleted after.
|
||||
void ApplyGameStatePre(GameStateMapData? data, EntityState[]? entityStates);
|
||||
void ApplyGameStatePre(GameStateMapData? data);
|
||||
void ApplyGameStatePost(GameStateMapData? data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -25,6 +25,7 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
public partial class PlacementManager : IPlacementManager, IDisposable
|
||||
{
|
||||
[Dependency] public readonly IPhysicsManager PhysicsManager = default!;
|
||||
[Dependency] private readonly IClientNetManager NetworkManager = default!;
|
||||
[Dependency] public readonly IPlayerManager PlayerManager = default!;
|
||||
[Dependency] public readonly IResourceCache ResourceCache = default!;
|
||||
@@ -155,7 +156,7 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
_drawingShader = _prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
|
||||
NetworkManager.RegisterNetMessage<MsgPlacement>(HandlePlacementMessage);
|
||||
NetworkManager.RegisterNetMessage<MsgPlacement>(MsgPlacement.NAME, HandlePlacementMessage);
|
||||
|
||||
_modeDictionary.Clear();
|
||||
foreach (var type in ReflectionManager.GetAllChildren<PlacementMode>())
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Network;
|
||||
@@ -70,10 +71,10 @@ namespace Robust.Client.Player
|
||||
eye.Current = true;
|
||||
|
||||
EntityAttached?.Invoke(new EntityAttachedEventArgs(entity));
|
||||
entity.SendMessage(null, new PlayerAttachedMsg());
|
||||
|
||||
// notify ECS Systems
|
||||
ControlledEntity.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PlayerAttachSysMessage(ControlledEntity));
|
||||
ControlledEntity.EntityManager.EventBus.RaiseLocalEvent(ControlledEntity.Uid, new PlayerAttachedEvent(ControlledEntity));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -82,13 +83,13 @@ namespace Robust.Client.Player
|
||||
public void DetachEntity()
|
||||
{
|
||||
var previous = ControlledEntity;
|
||||
if (previous is {Initialized: true, Deleted: false})
|
||||
if (previous != null && previous.Initialized && !previous.Deleted)
|
||||
{
|
||||
previous.GetComponent<EyeComponent>().Current = false;
|
||||
previous.SendMessage(null, new PlayerDetachedMsg());
|
||||
|
||||
// notify ECS Systems
|
||||
previous.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PlayerAttachSysMessage(null));
|
||||
previous.EntityManager.EventBus.RaiseLocalEvent(previous.Uid, new PlayerDetachedEvent(previous));
|
||||
}
|
||||
|
||||
ControlledEntity = null;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
@@ -81,8 +82,8 @@ namespace Robust.Client.Player
|
||||
{
|
||||
_client.RunLevelChanged += OnRunLevelChanged;
|
||||
|
||||
_network.RegisterNetMessage<MsgPlayerListReq>();
|
||||
_network.RegisterNetMessage<MsgPlayerList>(HandlePlayerList);
|
||||
_network.RegisterNetMessage<MsgPlayerListReq>(MsgPlayerListReq.NAME);
|
||||
_network.RegisterNetMessage<MsgPlayerList>(MsgPlayerList.NAME, HandlePlayerList);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Robust.Client.Prototypes
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_netManager.RegisterNetMessage<MsgReloadPrototypes>(accept: NetMessageAccept.Server);
|
||||
_netManager.RegisterNetMessage<MsgReloadPrototypes>(MsgReloadPrototypes.NAME, accept: NetMessageAccept.Server);
|
||||
|
||||
_clyde.OnWindowFocused += WindowFocusedChanged;
|
||||
|
||||
|
||||
@@ -463,7 +463,7 @@ namespace Robust.Client.UserInterface
|
||||
{
|
||||
}
|
||||
|
||||
internal virtual void DrawInternal(IRenderHandle renderHandle)
|
||||
public virtual void DrawInternal(IRenderHandle renderHandle)
|
||||
{
|
||||
Draw(renderHandle.DrawingHandleScreen);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -41,14 +41,14 @@ namespace Robust.Client.UserInterface.Controls
|
||||
return (32, 32) * Scale;
|
||||
}
|
||||
|
||||
internal override void DrawInternal(IRenderHandle renderHandle)
|
||||
public override void DrawInternal(IRenderHandle renderHandle)
|
||||
{
|
||||
if (Sprite == null || Sprite.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
renderHandle.DrawEntity(Sprite.Owner, GlobalPixelPosition + PixelSize / 2, Scale * UIScale, OverrideDirection);
|
||||
renderHandle.DrawEntity(Sprite.Owner, GlobalPixelPosition + PixelSize / 2, Scale, OverrideDirection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -165,8 +165,6 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
|
||||
protected internal override void MouseExited()
|
||||
{
|
||||
base.MouseExited();
|
||||
|
||||
if (Resizable && CurrentDrag == DragMode.None)
|
||||
{
|
||||
DefaultCursorShape = CursorShape.Arrow;
|
||||
|
||||
@@ -36,8 +36,7 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
return;
|
||||
}
|
||||
|
||||
var functionsText = string.Join("\n", _inputManager.DownKeyFunctions);
|
||||
_label.Text = $"Context: {_inputManager.Contexts.ActiveContext.Name}\n{functionsText}";
|
||||
_label.Text = string.Join("\n", _inputManager.DownKeyFunctions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.ViewVariables.Editors.VVPropEditorNumeric;
|
||||
using NumberType = Robust.Client.ViewVariables.Editors.VVPropEditorNumeric.NumberType;
|
||||
|
||||
namespace Robust.Client.ViewVariables
|
||||
{
|
||||
@@ -43,13 +43,17 @@ namespace Robust.Client.ViewVariables
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesOpenSession>(_netMessageOpenSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesRemoteData>(_netMessageRemoteData);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesCloseSession>(_netMessageCloseSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesDenySession>(_netMessageDenySession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesModifyRemote>();
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqSession>();
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqData>();
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesOpenSession>(MsgViewVariablesOpenSession.NAME,
|
||||
_netMessageOpenSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesRemoteData>(MsgViewVariablesRemoteData.NAME,
|
||||
_netMessageRemoteData);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesCloseSession>(MsgViewVariablesCloseSession.NAME,
|
||||
_netMessageCloseSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesDenySession>(MsgViewVariablesDenySession.NAME,
|
||||
_netMessageDenySession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesModifyRemote>(MsgViewVariablesModifyRemote.NAME);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqSession>(MsgViewVariablesReqSession.NAME);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqData>(MsgViewVariablesReqData.NAME);
|
||||
}
|
||||
|
||||
public VVPropEditor PropertyFor(Type? type)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime;
|
||||
@@ -217,28 +217,6 @@ namespace Robust.Server
|
||||
_log.RootSawmill.AddHandler(_logHandler!);
|
||||
}
|
||||
|
||||
if (_commandLineArgs != null)
|
||||
{
|
||||
foreach (var (sawmill, level) in _commandLineArgs.LogLevels)
|
||||
{
|
||||
LogLevel? logLevel;
|
||||
if (level == "null")
|
||||
logLevel = null;
|
||||
else
|
||||
{
|
||||
if (!Enum.TryParse<LogLevel>(level, out var result))
|
||||
{
|
||||
System.Console.WriteLine($"LogLevel {level} does not exist!");
|
||||
continue;
|
||||
}
|
||||
logLevel = result;
|
||||
}
|
||||
_log.GetSawmill(sawmill).Level = logLevel;
|
||||
}
|
||||
}
|
||||
|
||||
ProgramShared.PrintRuntimeInfo(_log.RootSawmill);
|
||||
|
||||
SelfLog.Enable(s => { System.Console.WriteLine("SERILOG ERROR: {0}", s); });
|
||||
|
||||
if (!SetupLoki())
|
||||
@@ -352,7 +330,6 @@ namespace Robust.Server
|
||||
|
||||
_watchdogApi.Initialize();
|
||||
|
||||
AddFinalStringsToSerializer();
|
||||
_stringSerializer.LockStrings();
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && _config.GetCVar(CVars.SysWinTickPeriod) >= 0)
|
||||
@@ -365,29 +342,6 @@ namespace Robust.Server
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AddFinalStringsToSerializer()
|
||||
{
|
||||
var factory = IoCManager.Resolve<IComponentFactory>();
|
||||
foreach (var regType in factory.AllRegisteredTypes)
|
||||
{
|
||||
var reg = factory.GetRegistration(regType);
|
||||
_stringSerializer.AddString(reg.Name);
|
||||
}
|
||||
|
||||
using var extraMappedStrings = typeof(BaseServer).Assembly
|
||||
.GetManifestResourceStream("Robust.Server.ExtraMappedSerializerStrings.txt");
|
||||
|
||||
if (extraMappedStrings != null)
|
||||
{
|
||||
using var sr = new StreamReader(extraMappedStrings);
|
||||
string? line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
_stringSerializer.AddString(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool SetupLoki()
|
||||
{
|
||||
var enabled = _config.GetCVar(CVars.LokiEnabled);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -12,7 +12,6 @@ namespace Robust.Server
|
||||
public string? ConfigFile { get; }
|
||||
public string? DataDir { get; }
|
||||
public IReadOnlyCollection<(string key, string value)> CVars { get; }
|
||||
public IReadOnlyCollection<(string key, string value)> LogLevels { get; }
|
||||
|
||||
// Manual parser because C# has no good command line parsing libraries. Also dependencies bad.
|
||||
// Also I don't like spending 100ms parsing command line args. Do you?
|
||||
@@ -22,7 +21,6 @@ namespace Robust.Server
|
||||
string? configFile = null;
|
||||
string? dataDir = null;
|
||||
var cvars = new List<(string, string)>();
|
||||
var logLevels = new List<(string, string)>();
|
||||
var mountOptions = new MountOptions();
|
||||
|
||||
using var enumerator = args.GetEnumerator();
|
||||
@@ -95,33 +93,13 @@ namespace Robust.Server
|
||||
|
||||
mountOptions.DirMounts.Add(enumerator.Current);
|
||||
}
|
||||
else if (arg == "--loglevel")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing loglevel sawmill.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var loglevel = enumerator.Current;
|
||||
DebugTools.AssertNotNull(loglevel);
|
||||
var pos = loglevel.IndexOf('=');
|
||||
|
||||
if (pos == -1)
|
||||
{
|
||||
C.WriteLine("Expected = in loglevel.");
|
||||
return false;
|
||||
}
|
||||
|
||||
logLevels.Add((loglevel[..pos], loglevel[(pos + 1)..]));
|
||||
}
|
||||
else
|
||||
{
|
||||
C.WriteLine("Unknown argument: {0}", arg);
|
||||
}
|
||||
}
|
||||
|
||||
parsed = new CommandLineArgs(configFile, dataDir, cvars, logLevels, mountOptions);
|
||||
parsed = new CommandLineArgs(configFile, dataDir, cvars, mountOptions);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -132,19 +110,17 @@ Options:
|
||||
--config-file Path to the config file to read from.
|
||||
--data-dir Path to the data directory to read/write from/to.
|
||||
--cvar Specifies an additional cvar overriding the config file. Syntax is <key>=<value>
|
||||
--loglevel Specifies an additional sawmill log level overriding the default values. Syntax is <key>=<value>
|
||||
--mount-dir Resource directory to mount.
|
||||
--mount-zip Resource zip to mount.
|
||||
--help Display this help text and exit.
|
||||
");
|
||||
}
|
||||
|
||||
private CommandLineArgs(string? configFile, string? dataDir, IReadOnlyCollection<(string, string)> cVars, IReadOnlyCollection<(string, string)> logLevels, MountOptions mountOptions)
|
||||
private CommandLineArgs(string? configFile, string? dataDir, IReadOnlyCollection<(string, string)> cVars, MountOptions mountOptions)
|
||||
{
|
||||
ConfigFile = configFile;
|
||||
DataDir = dataDir;
|
||||
CVars = cVars;
|
||||
LogLevels = logLevels;
|
||||
MountOptions = mountOptions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,14 +62,15 @@ namespace Robust.Server.Console
|
||||
var sudoShell = new SudoShell(this, localShell, shell);
|
||||
ExecuteInShell(sudoShell, argStr.Substring("sudo ".Length));
|
||||
});
|
||||
|
||||
|
||||
LoadConsoleCommands();
|
||||
|
||||
// setup networking with clients
|
||||
NetManager.RegisterNetMessage<MsgConCmd>(ProcessCommand);
|
||||
NetManager.RegisterNetMessage<MsgConCmdAck>();
|
||||
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
|
||||
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME);
|
||||
|
||||
NetManager.RegisterNetMessage<MsgConCmdReg>(message => HandleRegistrationRequest(message.MsgChannel));
|
||||
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME,
|
||||
message => HandleRegistrationRequest(message.MsgChannel));
|
||||
}
|
||||
|
||||
private void ExecuteInShell(IConsoleShell shell, string command)
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Robust.Server.Debugging
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_net.RegisterNetMessage<MsgRay>();
|
||||
_net.RegisterNetMessage<MsgRay>(MsgRay.NAME);
|
||||
// TODO _physics.DebugDrawRay += data => PhysicsOnDebugDrawRay(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
fixture-0
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -8,7 +9,69 @@ namespace Robust.Server.GameObjects
|
||||
{
|
||||
public override string Name => "Actor";
|
||||
|
||||
[ViewVariables]
|
||||
public IPlayerSession PlayerSession { get; internal set; } = default!;
|
||||
[ViewVariables] public IPlayerSession PlayerSession { get; internal set; } = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
// Warning: careful here, Detach removes this component, make sure this is after the base shutdown
|
||||
// to prevent infinite recursion
|
||||
// ReSharper disable once ConstantConditionalAccessQualifier
|
||||
PlayerSession?.DetachFromEntity();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on an entity whenever a player attaches to this entity.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
public class PlayerAttachedMsg : ComponentMessage
|
||||
{
|
||||
public IPlayerSession NewPlayer { get; }
|
||||
|
||||
public PlayerAttachedMsg(IPlayerSession newPlayer)
|
||||
{
|
||||
NewPlayer = newPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on an entity whenever a player detaches from this entity.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
public class PlayerDetachedMsg : ComponentMessage
|
||||
{
|
||||
public IPlayerSession OldPlayer { get; }
|
||||
|
||||
public PlayerDetachedMsg(IPlayerSession oldPlayer)
|
||||
{
|
||||
OldPlayer = oldPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
public class PlayerAttachSystemMessage : EntityEventArgs
|
||||
{
|
||||
public PlayerAttachSystemMessage(IEntity entity, IPlayerSession newPlayer)
|
||||
{
|
||||
Entity = entity;
|
||||
NewPlayer = newPlayer;
|
||||
}
|
||||
|
||||
public IEntity Entity { get; }
|
||||
public IPlayerSession NewPlayer { get; }
|
||||
}
|
||||
|
||||
public class PlayerDetachedSystemMessage : EntityEventArgs
|
||||
{
|
||||
public PlayerDetachedSystemMessage(IEntity entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
public IEntity Entity { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// System that handles players being attached/detached from entities.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class ActorSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AttachPlayerEvent>(OnActorPlayerAttach);
|
||||
SubscribeLocalEvent<ActorComponent, DetachPlayerEvent>(OnActorPlayerDetach);
|
||||
SubscribeLocalEvent<ActorComponent, ComponentShutdown>(OnActorShutdown);
|
||||
}
|
||||
|
||||
private void OnActorPlayerAttach(AttachPlayerEvent args)
|
||||
{
|
||||
args.Result = Attach(args.Entity, args.Player, args.Force, out var forceKicked);
|
||||
args.ForceKicked = forceKicked;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a player session to an entity, optionally kicking any sessions already attached to it.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to attach the player to</param>
|
||||
/// <param name="player">The player to attach to the entity</param>
|
||||
/// <param name="force">Whether to kick any existing players from the entity</param>
|
||||
/// <returns>Whether the attach succeeded, or not.</returns>
|
||||
public bool Attach(IEntity entity, IPlayerSession player, bool force = false)
|
||||
{
|
||||
return Attach(entity, player, false, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a player session to an entity, optionally kicking any sessions already attached to it.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to attach the player to</param>
|
||||
/// <param name="player">The player to attach to the entity</param>
|
||||
/// <param name="force">Whether to kick any existing players from the entity</param>
|
||||
/// <param name="forceKicked">The player that was forcefully kicked, or null.</param>
|
||||
/// <returns>Whether the attach succeeded, or not.</returns>
|
||||
public bool Attach(IEntity entity, IPlayerSession player, bool force, out IPlayerSession? forceKicked)
|
||||
{
|
||||
// Null by default.
|
||||
forceKicked = null;
|
||||
|
||||
// Cannot attach to a deleted entity.
|
||||
if (entity.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var uid = entity.Uid;
|
||||
|
||||
// Check if there was a player attached to the entity already...
|
||||
if (ComponentManager.TryGetComponent(uid, out ActorComponent actor))
|
||||
{
|
||||
// If we're not forcing the attach, this fails.
|
||||
if (!force)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the event's force-kicked session before detaching it.
|
||||
forceKicked = actor.PlayerSession;
|
||||
|
||||
// This detach cannot fail, as a player is attached to this entity.
|
||||
// It's important to note that detaching the player removes the component.
|
||||
RaiseLocalEvent(uid, new DetachPlayerEvent());
|
||||
}
|
||||
|
||||
// We add the actor component.
|
||||
actor = ComponentManager.AddComponent<ActorComponent>(entity);
|
||||
actor.PlayerSession = player;
|
||||
player.SetAttachedEntity(entity);
|
||||
|
||||
// The player is fully attached now, raise an event!
|
||||
RaiseLocalEvent(uid, new PlayerAttachedEvent(entity, player, forceKicked));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not gonna make this method call Detach as all we have to do is remove a component...
|
||||
private void OnActorPlayerDetach(EntityUid uid, ActorComponent component, DetachPlayerEvent args)
|
||||
{
|
||||
// Removing the component will call shutdown, and our subscription will handle the rest of the detach logic.
|
||||
ComponentManager.RemoveComponent<ActorComponent>(uid);
|
||||
args.Result = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detaches an attached session from the entity, if any.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity player sessions will be detached from.</param>
|
||||
/// <returns>Whether any player session was detached.</returns>
|
||||
public bool Detach(IEntity entity)
|
||||
{
|
||||
if (!entity.HasComponent<ActorComponent>())
|
||||
return false;
|
||||
|
||||
// Removing the component will call shutdown, and our subscription will handle the rest of the detach logic.
|
||||
entity.RemoveComponent<ActorComponent>();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detaches this player from its attached entity, if any.
|
||||
/// </summary>
|
||||
/// <param name="player">The player session that will be detached from any attached entities.</param>
|
||||
/// <returns>Whether the player is now detached from any entities.
|
||||
/// This returns true if the player wasn't attached to any entity.</returns>
|
||||
public bool Detach(IPlayerSession player)
|
||||
{
|
||||
var entity = player.AttachedEntity;
|
||||
return entity == null || Detach(entity);
|
||||
}
|
||||
|
||||
private void OnActorShutdown(EntityUid uid, ActorComponent component, ComponentShutdown args)
|
||||
{
|
||||
component.PlayerSession.SetAttachedEntity(null);
|
||||
|
||||
var entity = EntityManager.GetEntity(uid);
|
||||
|
||||
// The player is fully detached now that the component has shut down.
|
||||
RaiseLocalEvent(uid, new PlayerDetachedEvent(entity, component.PlayerSession));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise this broadcast event to attach a player to an entity, optionally detaching the player attached to it.
|
||||
/// </summary>
|
||||
public sealed class AttachPlayerEvent : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Player to attach to the entity.
|
||||
/// Input parameter.
|
||||
/// </summary>
|
||||
public IPlayerSession Player { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Entity to attach the player to.
|
||||
/// Input parameter.
|
||||
/// </summary>
|
||||
public IEntity Entity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to force-attach the player,
|
||||
/// detaching any players attached to it if any.
|
||||
/// Input parameter.
|
||||
/// </summary>
|
||||
public bool Force { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If the attach was forced and there was a player attached to the entity before, this will be it.
|
||||
/// Output parameter.
|
||||
/// </summary>
|
||||
public IPlayerSession? ForceKicked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the player was attached correctly.
|
||||
/// False if not forcing and the entity already had a player attached to it.
|
||||
/// Output parameter.
|
||||
/// </summary>
|
||||
public bool Result { get; set; } = false;
|
||||
|
||||
public AttachPlayerEvent(IEntity entity, IPlayerSession player, bool force = false)
|
||||
{
|
||||
Entity = entity;
|
||||
Player = player;
|
||||
Force = force;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise this directed event to detach a player from an entity.
|
||||
/// </summary>
|
||||
public sealed class DetachPlayerEvent : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the player was detached correctly.
|
||||
/// Fails if no player was attached to the entity.
|
||||
/// Output parameter.
|
||||
/// </summary>
|
||||
public bool Result { get; set; } = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for when a player has been attached to an entity.
|
||||
/// </summary>
|
||||
public sealed class PlayerAttachedEvent : EntityEventArgs
|
||||
{
|
||||
public IEntity Entity { get; }
|
||||
public IPlayerSession Player { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The player session that was forcefully kicked from the entity, if any.
|
||||
/// </summary>
|
||||
public IPlayerSession? Kicked { get; }
|
||||
|
||||
public PlayerAttachedEvent(IEntity entity, IPlayerSession player, IPlayerSession? kicked = null)
|
||||
{
|
||||
Entity = entity;
|
||||
Player = player;
|
||||
Kicked = kicked;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for when a player has been detached from an entity.
|
||||
/// </summary>
|
||||
public sealed class PlayerDetachedEvent : EntityEventArgs
|
||||
{
|
||||
public IEntity Entity { get; }
|
||||
public IPlayerSession Player { get; }
|
||||
|
||||
public PlayerDetachedEvent(IEntity entity, IPlayerSession player)
|
||||
{
|
||||
Entity = entity;
|
||||
Player = player;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -8,7 +7,7 @@ namespace Robust.Server.GameObjects
|
||||
internal sealed class GridTileLookupNode
|
||||
{
|
||||
internal GridTileLookupChunk ParentChunk { get; }
|
||||
|
||||
|
||||
internal Vector2i Indices { get; }
|
||||
|
||||
internal IEnumerable<IEntity> Entities
|
||||
@@ -18,18 +17,7 @@ namespace Robust.Server.GameObjects
|
||||
foreach (var entity in _entities)
|
||||
{
|
||||
if (!entity.Deleted)
|
||||
{
|
||||
yield return entity;
|
||||
}
|
||||
|
||||
if (!entity.TryGetComponent(out ContainerManagerComponent? containerManager)) continue;
|
||||
foreach (var container in containerManager.GetAllContainers())
|
||||
{
|
||||
foreach (var child in container.ContainedEntities)
|
||||
{
|
||||
yield return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +29,7 @@ namespace Robust.Server.GameObjects
|
||||
ParentChunk = parentChunk;
|
||||
Indices = indices;
|
||||
}
|
||||
|
||||
|
||||
internal void AddEntity(IEntity entity)
|
||||
{
|
||||
_entities.Add(entity);
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
@@ -17,7 +16,6 @@ namespace Robust.Server.GameObjects
|
||||
[UsedImplicitly]
|
||||
public sealed class GridTileLookupSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEntityLookup _lookup = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
private readonly Dictionary<GridId, Dictionary<Vector2i, GridTileLookupChunk>> _graph =
|
||||
@@ -200,21 +198,17 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
private Box2 GetEntityBox(IEntity entity)
|
||||
{
|
||||
var aabb = _lookup.GetWorldAabbFromEntity(entity);
|
||||
|
||||
// Need to clip the aabb as anything with an edge intersecting another tile might be picked up, such as walls.
|
||||
return aabb.Scale(0.98f);
|
||||
if (entity.TryGetComponent(out IPhysBody? physics))
|
||||
return new Box2(physics.GetWorldAABB().BottomLeft + 0.01f, physics.GetWorldAABB().TopRight - 0.01f);
|
||||
|
||||
// Don't want to accidentally get neighboring tiles unless we're near an edge
|
||||
return Box2.CenteredAround(entity.Transform.Coordinates.ToMapPos(EntityManager), Vector2.One / 2);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
#if DEBUG
|
||||
SubscribeNetworkEvent<RequestGridTileLookupMessage>(HandleRequest);
|
||||
#endif
|
||||
SubscribeLocalEvent<MoveEvent>(HandleEntityMove);
|
||||
SubscribeLocalEvent<EntInsertedIntoContainerMessage>(HandleContainerInsert);
|
||||
SubscribeLocalEvent<EntRemovedFromContainerMessage>(HandleContainerRemove);
|
||||
SubscribeLocalEvent<EntityInitializedMessage>(HandleEntityInitialized);
|
||||
SubscribeLocalEvent<EntityDeletedMessage>(HandleEntityDeleted);
|
||||
_mapManager.OnGridCreated += HandleGridCreated;
|
||||
@@ -222,16 +216,6 @@ namespace Robust.Server.GameObjects
|
||||
_mapManager.TileChanged += HandleTileChanged;
|
||||
}
|
||||
|
||||
private void HandleContainerRemove(EntRemovedFromContainerMessage ev)
|
||||
{
|
||||
HandleEntityAdd(ev.Entity);
|
||||
}
|
||||
|
||||
private void HandleContainerInsert(EntInsertedIntoContainerMessage ev)
|
||||
{
|
||||
HandleEntityRemove(ev.Entity);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
@@ -241,21 +225,8 @@ namespace Robust.Server.GameObjects
|
||||
_mapManager.TileChanged -= HandleTileChanged;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private void HandleRequest(RequestGridTileLookupMessage message, EntitySessionEventArgs args)
|
||||
{
|
||||
var entities = GetEntitiesIntersecting(message.GridId, message.Indices).Select(e => e.Uid).ToList();
|
||||
RaiseNetworkEvent(new SendGridTileLookupMessage(message.GridId, message.Indices, entities), args.SenderSession.ConnectedClient);
|
||||
}
|
||||
#endif
|
||||
|
||||
private void HandleEntityInitialized(EntityInitializedMessage message)
|
||||
{
|
||||
if (message.Entity.IsInContainer())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HandleEntityAdd(message.Entity);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -107,7 +108,7 @@ namespace Robust.Server.GameObjects
|
||||
/// <inheritdoc />
|
||||
public void SetupNetworking()
|
||||
{
|
||||
_networkManager.RegisterNetMessage<MsgEntity>(HandleEntityNetworkMessage);
|
||||
_networkManager.RegisterNetMessage<MsgEntity>(MsgEntity.NAME, HandleEntityNetworkMessage);
|
||||
|
||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
|
||||
@@ -232,7 +233,6 @@ namespace Robust.Server.GameObjects
|
||||
var msg = message.SystemMessage;
|
||||
var sessionType = typeof(EntitySessionMessage<>).MakeGenericType(msg.GetType());
|
||||
var sessionMsg = Activator.CreateInstance(sessionType, new EntitySessionEventArgs(player), msg)!;
|
||||
ReceivedSystemMessage?.Invoke(this, msg);
|
||||
ReceivedSystemMessage?.Invoke(this, sessionMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
@@ -62,8 +63,8 @@ namespace Robust.Server.GameStates
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
_networkManager.RegisterNetMessage<MsgState>();
|
||||
_networkManager.RegisterNetMessage<MsgStateAck>(HandleStateAck);
|
||||
_networkManager.RegisterNetMessage<MsgState>(MsgState.NAME);
|
||||
_networkManager.RegisterNetMessage<MsgStateAck>(MsgStateAck.NAME, HandleStateAck);
|
||||
|
||||
_networkManager.Connected += HandleClientConnected;
|
||||
_networkManager.Disconnect += HandleClientDisconnect;
|
||||
@@ -210,10 +211,7 @@ namespace Robust.Server.GameStates
|
||||
_logger.Log(LogLevel.Error, e, string.Empty);
|
||||
}
|
||||
|
||||
var msg = _networkManager.CreateNetMessage<MsgState>();
|
||||
msg.MsgChannel = session.ConnectedClient;
|
||||
|
||||
return (msg, null);
|
||||
return (new MsgState(session.ConnectedClient), null);
|
||||
}
|
||||
|
||||
var mailBag = _playerManager.GetAllPlayers()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
@@ -34,7 +34,7 @@ namespace Robust.Server.Placement
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_networkManager.RegisterNetMessage<MsgPlacement>(HandleNetMessage);
|
||||
_networkManager.RegisterNetMessage<MsgPlacement>(MsgPlacement.NAME, HandleNetMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -20,8 +20,8 @@ namespace Robust.Server.Player
|
||||
/// NOTE: The content pack almost certainly has an alternative for this.
|
||||
/// Do not call this directly for most content code.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to attach to.</param>
|
||||
void AttachToEntity(IEntity? entity);
|
||||
/// <param name="a">The entity to attach to.</param>
|
||||
void AttachToEntity(IEntity? a);
|
||||
|
||||
/// <summary>
|
||||
/// Detaches this player from an entity.
|
||||
@@ -36,12 +36,5 @@ namespace Robust.Server.Player
|
||||
/// Persistent data for this player.
|
||||
/// </summary>
|
||||
IPlayerData Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal method to set <see cref="ICommonSession.AttachedEntity"/> and update the player's status.
|
||||
/// Do NOT use this unless you know what you're doing, you probably want <see cref="AttachToEntity"/>
|
||||
/// and <see cref="DetachFromEntity"/> instead.
|
||||
/// </summary>
|
||||
internal void SetAttachedEntity(IEntity? entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,8 +107,8 @@ namespace Robust.Server.Player
|
||||
|
||||
MaxPlayers = maxPlayers;
|
||||
|
||||
_network.RegisterNetMessage<MsgPlayerListReq>(HandlePlayerListReq);
|
||||
_network.RegisterNetMessage<MsgPlayerList>();
|
||||
_network.RegisterNetMessage<MsgPlayerListReq>(MsgPlayerListReq.NAME, HandlePlayerListReq);
|
||||
_network.RegisterNetMessage<MsgPlayerList>(MsgPlayerList.NAME);
|
||||
|
||||
_network.Connecting += OnConnecting;
|
||||
_network.Connected += NewSession;
|
||||
|
||||
@@ -6,7 +6,6 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Logger = Robust.Shared.Log.Logger;
|
||||
|
||||
namespace Robust.Server.Player
|
||||
{
|
||||
@@ -37,7 +36,7 @@ namespace Robust.Server.Player
|
||||
|
||||
[ViewVariables] public INetChannel ConnectedClient { get; }
|
||||
|
||||
[ViewVariables] public IEntity? AttachedEntity { get; set; }
|
||||
[ViewVariables] public IEntity? AttachedEntity { get; private set; }
|
||||
|
||||
[ViewVariables] public EntityUid? AttachedEntityUid => AttachedEntity?.Uid;
|
||||
|
||||
@@ -110,17 +109,21 @@ namespace Robust.Server.Player
|
||||
public event EventHandler<SessionStatusEventArgs>? PlayerStatusChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AttachToEntity(IEntity? entity)
|
||||
public void AttachToEntity(IEntity? a)
|
||||
{
|
||||
DetachFromEntity();
|
||||
|
||||
if (entity == null)
|
||||
return;
|
||||
|
||||
if (!EntitySystem.Get<ActorSystem>().Attach(entity, this))
|
||||
if (a == null)
|
||||
{
|
||||
Logger.Warning($"Couldn't attach player \"{this}\" to entity \"{entity}\"! Did it have a player already attached to it?");
|
||||
return;
|
||||
}
|
||||
|
||||
var actorComponent = a.AddComponent<ActorComponent>();
|
||||
actorComponent.PlayerSession = this;
|
||||
AttachedEntity = a;
|
||||
a.SendMessage(actorComponent, new PlayerAttachedMsg(this));
|
||||
a.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PlayerAttachSystemMessage(a, this));
|
||||
UpdatePlayerState();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -129,21 +132,22 @@ namespace Robust.Server.Player
|
||||
if (AttachedEntity == null)
|
||||
return;
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
if (AttachedEntity.Deleted)
|
||||
{
|
||||
Logger.Warning($"Player \"{this}\" was attached to an entity that was deleted. THIS SHOULD NEVER HAPPEN, BUT DOES.");
|
||||
// We can't contact ActorSystem because trying to fire an entity event would crash.
|
||||
// Work around it.
|
||||
throw new InvalidOperationException("Tried to detach player, but my entity does not exist!");
|
||||
}
|
||||
|
||||
if (AttachedEntity.TryGetComponent<ActorComponent>(out var actor))
|
||||
{
|
||||
AttachedEntity.SendMessage(actor, new PlayerDetachedMsg(this));
|
||||
AttachedEntity.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PlayerDetachedSystemMessage(AttachedEntity));
|
||||
AttachedEntity.RemoveComponent<ActorComponent>();
|
||||
AttachedEntity = null;
|
||||
UpdatePlayerState();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!EntitySystem.Get<ActorSystem>().Detach(AttachedEntity))
|
||||
else
|
||||
{
|
||||
Logger.Warning($"Couldn't detach player \"{this}\" to entity \"{AttachedEntity}\"! Is it missing an ActorComponent?");
|
||||
throw new InvalidOperationException("Tried to detach player, but entity does not have ActorComponent!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,13 +190,6 @@ namespace Robust.Server.Player
|
||||
|
||||
public LoginType AuthType => ConnectedClient.AuthType;
|
||||
|
||||
/// <inheritdoc />
|
||||
void IPlayerSession.SetAttachedEntity(IEntity? entity)
|
||||
{
|
||||
AttachedEntity = entity;
|
||||
UpdatePlayerState();
|
||||
}
|
||||
|
||||
private void UpdatePlayerState()
|
||||
{
|
||||
PlayerState.Status = Status;
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Robust.Server.Prototypes
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_netManager.RegisterNetMessage<MsgReloadPrototypes>(HandleReloadPrototypes, NetMessageAccept.Server);
|
||||
_netManager.RegisterNetMessage<MsgReloadPrototypes>(MsgReloadPrototypes.NAME, HandleReloadPrototypes, NetMessageAccept.Server);
|
||||
}
|
||||
|
||||
private void HandleReloadPrototypes(MsgReloadPrototypes msg)
|
||||
|
||||
@@ -29,9 +29,6 @@
|
||||
<Content Include="server_config.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<EmbeddedResource Include="ExtraMappedSerializerStrings.txt">
|
||||
<LogicalName>Robust.Server.ExtraMappedSerializerStrings.txt</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<Import Project="..\MSBuild\Robust.Engine.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -36,11 +36,11 @@ namespace Robust.Server.Scripting
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<MsgScriptStop>(ReceiveScriptEnd);
|
||||
_netManager.RegisterNetMessage<MsgScriptEval>(ReceiveScriptEval);
|
||||
_netManager.RegisterNetMessage<MsgScriptStart>(ReceiveScriptStart);
|
||||
_netManager.RegisterNetMessage<MsgScriptResponse>();
|
||||
_netManager.RegisterNetMessage<MsgScriptStartAck>();
|
||||
_netManager.RegisterNetMessage<MsgScriptStop>(MsgScriptStop.NAME, ReceiveScriptEnd);
|
||||
_netManager.RegisterNetMessage<MsgScriptEval>(MsgScriptEval.NAME, ReceiveScriptEval);
|
||||
_netManager.RegisterNetMessage<MsgScriptStart>(MsgScriptStart.NAME, ReceiveScriptStart);
|
||||
_netManager.RegisterNetMessage<MsgScriptResponse>(MsgScriptResponse.NAME);
|
||||
_netManager.RegisterNetMessage<MsgScriptStartAck>(MsgScriptStartAck.NAME);
|
||||
|
||||
_playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Shared.Network.Messages.MsgViewVariablesDenySession;
|
||||
using DenyReason = Robust.Shared.Network.Messages.MsgViewVariablesDenySession.DenyReason;
|
||||
|
||||
namespace Robust.Server.ViewVariables
|
||||
{
|
||||
@@ -34,13 +34,16 @@ namespace Robust.Server.ViewVariables
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqSession>(_msgReqSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqData>(_msgReqData);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesModifyRemote>(_msgModifyRemote);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesCloseSession>(_msgCloseSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesDenySession>();
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesOpenSession>();
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesRemoteData>();
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqSession>(MsgViewVariablesReqSession.NAME,
|
||||
_msgReqSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqData>(MsgViewVariablesReqData.NAME, _msgReqData);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesModifyRemote>(MsgViewVariablesModifyRemote.NAME,
|
||||
_msgModifyRemote);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesCloseSession>(MsgViewVariablesCloseSession.NAME,
|
||||
_msgCloseSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesDenySession>(MsgViewVariablesDenySession.NAME);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesOpenSession>(MsgViewVariablesOpenSession.NAME);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesRemoteData>(MsgViewVariablesRemoteData.NAME);
|
||||
}
|
||||
|
||||
private void _msgCloseSession(MsgViewVariablesCloseSession message)
|
||||
|
||||
45
Robust.Shared.Maths/FloatMath.cs
Normal file
45
Robust.Shared.Maths/FloatMath.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Robust.Shared.Maths
|
||||
{
|
||||
[Obsolete("Use MathHelper instead.")]
|
||||
public static class FloatMath
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float Clamp01(float val)
|
||||
{
|
||||
return MathHelper.Clamp01(val);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Clamp<T>(T val, T min, T max) where T : IComparable<T>
|
||||
{
|
||||
return MathHelper.Clamp(val, min, max);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float Clamp(float val, float min, float max)
|
||||
{
|
||||
return MathHelper.Clamp(val, min, max);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double Clamp(double val, double min, double max)
|
||||
{
|
||||
return MathHelper.Clamp(val, min, max);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool CloseTo(float a, float b, double tolerance = .00001)
|
||||
{
|
||||
return MathHelper.CloseTo(a, b, tolerance);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float Lerp(float a, float b, float blend)
|
||||
{
|
||||
return MathHelper.Lerp(a, b, blend);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,12 +463,5 @@ namespace Robust.Shared
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> DebugTargetFps =
|
||||
CVarDef.Create("debug.target_fps", 60, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
/*
|
||||
* MIDI
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<float> MidiVolume =
|
||||
CVarDef.Create("midi.volume", 0f, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Robust.Shared.Configuration
|
||||
_netManager.Disconnect += PeerDisconnected;
|
||||
}
|
||||
|
||||
_netManager.RegisterNetMessage<MsgConVars>(HandleNetVarMessage);
|
||||
_netManager.RegisterNetMessage<MsgConVars>(MsgConVars.NAME, HandleNetVarMessage);
|
||||
}
|
||||
|
||||
private void PeerConnected(object? sender, NetChannelArgs e)
|
||||
|
||||
@@ -145,8 +145,9 @@ namespace Robust.Shared.Containers
|
||||
{
|
||||
DebugTools.Assert(!Deleted);
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new EntInsertedIntoContainerMessage(toinsert, this));
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new EntInsertedIntoContainerMessage(toinsert, this));
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(toinsert));
|
||||
Manager.Owner.SendMessage(Manager, new ContainerContentsModifiedMessage(this, toinsert, false));
|
||||
Manager.Dirty();
|
||||
}
|
||||
|
||||
@@ -161,8 +162,9 @@ namespace Robust.Shared.Containers
|
||||
DebugTools.AssertNotNull(toremove);
|
||||
DebugTools.Assert(toremove.IsValid());
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new EntRemovedFromContainerMessage(toremove, this));
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new EntRemovedFromContainerMessage(toremove, this));
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(toremove));
|
||||
Manager.Owner.SendMessage(Manager, new ContainerContentsModifiedMessage(this, toremove, true));
|
||||
Manager.Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
40
Robust.Shared/Containers/ContainerContentsModifiedMessage.cs
Normal file
40
Robust.Shared/Containers/ContainerContentsModifiedMessage.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Shared.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// The contents of this container have been changed.
|
||||
/// </summary>
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
public class ContainerContentsModifiedMessage : ComponentMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Container whose contents were modified.
|
||||
/// </summary>
|
||||
public IContainer Container { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Entity that was added or removed from the container.
|
||||
/// </summary>
|
||||
public IEntity Entity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, the entity was removed. If false, it was added to the container.
|
||||
/// </summary>
|
||||
public bool Removed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="ContainerContentsModifiedMessage"/>.
|
||||
/// </summary>
|
||||
/// <param name="container">Container whose contents were modified.</param>
|
||||
/// <param name="entity">Entity that was added or removed in the container.</param>
|
||||
/// <param name="removed">If true, the entity was removed. If false, it was added to the container.</param>
|
||||
public ContainerContentsModifiedMessage(IContainer container, IEntity entity, bool removed)
|
||||
{
|
||||
Container = container;
|
||||
Entity = entity;
|
||||
Removed = removed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,27 +197,6 @@ namespace Robust.Shared.Containers
|
||||
return userContainer == otherContainer;
|
||||
}
|
||||
|
||||
public static bool IsInSameOrParentContainer(this IEntity user, IEntity other)
|
||||
{
|
||||
DebugTools.AssertNotNull(user);
|
||||
DebugTools.AssertNotNull(other);
|
||||
|
||||
var isUserContained = TryGetContainer(user, out var userContainer);
|
||||
var isOtherContained = TryGetContainer(other, out var otherContainer);
|
||||
|
||||
// Both entities are not in a container
|
||||
if (!isUserContained && !isOtherContained) return true;
|
||||
|
||||
// One contains the other
|
||||
if (userContainer?.Owner == other || otherContainer?.Owner == user) return true;
|
||||
|
||||
// Both entities are in different contained states
|
||||
if (isUserContained != isOtherContained) return false;
|
||||
|
||||
// Both entities are in the same container
|
||||
return userContainer == otherContainer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shortcut method to make creation of containers easier.
|
||||
/// Creates a new container on the entity and gives it back to you.
|
||||
|
||||
@@ -106,12 +106,6 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
var asmName = reader.GetString(reader.GetAssemblyDefinition().Name);
|
||||
|
||||
if (peReader.PEHeaders.CorHeader?.ManagedNativeHeaderDirectory is {Size: not 0})
|
||||
{
|
||||
_sawmill.Error($"Assembly {asmName} contains native code.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (VerifyIL)
|
||||
{
|
||||
if (!DoVerifyIL(asmName, resolver, peReader, reader))
|
||||
@@ -410,8 +404,7 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsTypeAccessAllowed(SandboxConfig sandboxConfig, MTypeReferenced type,
|
||||
[NotNullWhen(true)] out TypeConfig? cfg)
|
||||
private bool IsTypeAccessAllowed(SandboxConfig sandboxConfig, MTypeReferenced type, [NotNullWhen(true)] out TypeConfig? cfg)
|
||||
{
|
||||
if (type.Namespace == null)
|
||||
{
|
||||
|
||||
@@ -106,16 +106,8 @@ namespace Robust.Shared.ContentPack
|
||||
Logger.DebugS("res.mod", $"Verified assemblies in {checkerSw.ElapsedMilliseconds}ms");
|
||||
}
|
||||
|
||||
var nodes = TopologicalSort.FromBeforeAfter(
|
||||
files,
|
||||
kv => kv.Key,
|
||||
kv => kv.Value.Path,
|
||||
_ => Array.Empty<string>(),
|
||||
kv => kv.Value.references,
|
||||
allowMissing: true); // missing refs would be non-content assemblies so allow that.
|
||||
|
||||
// Actually load them in the order they depend on each other.
|
||||
foreach (var path in TopologicalSort.Sort(nodes))
|
||||
foreach (var path in TopologicalSortModules(files))
|
||||
{
|
||||
Logger.DebugS("res.mod", $"Loading module: '{path}'");
|
||||
try
|
||||
@@ -144,6 +136,38 @@ namespace Robust.Shared.ContentPack
|
||||
return true;
|
||||
}
|
||||
|
||||
private static IEnumerable<ResourcePath> TopologicalSortModules(
|
||||
IEnumerable<KeyValuePair<string, (ResourcePath Path, string[] references)>> modules)
|
||||
{
|
||||
var elems = modules.ToDictionary(
|
||||
node => node.Key,
|
||||
node => (node.Value.Path, refs: new HashSet<string>(node.Value.references)));
|
||||
|
||||
// Remove assembly references we aren't sorting for.
|
||||
foreach (var (_, set) in elems.Values)
|
||||
{
|
||||
set.RemoveWhere(r => !elems.ContainsKey(r));
|
||||
}
|
||||
|
||||
while (elems.Count > 0)
|
||||
{
|
||||
var elem = elems.FirstOrNull(x => x.Value.refs.Count == 0);
|
||||
if (elem == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Found circular dependency in assembly dependency graph");
|
||||
}
|
||||
|
||||
elems.Remove(elem.Value.Key);
|
||||
foreach (var sElem in elems)
|
||||
{
|
||||
sElem.Value.refs.Remove(elem.Value.Key);
|
||||
}
|
||||
|
||||
yield return elem.Value.Value.Path;
|
||||
}
|
||||
}
|
||||
|
||||
private static (string[] refs, string name) GetAssemblyReferenceData(Stream stream)
|
||||
{
|
||||
using var reader = new PEReader(stream);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
@@ -94,21 +93,5 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
return entity.AddComponent<T>();
|
||||
}
|
||||
|
||||
public static IComponent SetAndDirtyIfChanged<TValue>(
|
||||
this IComponent comp,
|
||||
ref TValue backingField,
|
||||
TValue value)
|
||||
{
|
||||
if (EqualityComparer<TValue>.Default.Equals(backingField, value))
|
||||
{
|
||||
return comp;
|
||||
}
|
||||
|
||||
backingField = value;
|
||||
comp.Dirty();
|
||||
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@ namespace Robust.Shared.GameObjects
|
||||
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
|
||||
#endif
|
||||
|
||||
public IComponentFactory ComponentFactory => _componentFactory;
|
||||
|
||||
private const int TypeCapacity = 32;
|
||||
private const int ComponentCollectionCapacity = 1024;
|
||||
private const int EntityCapacity = 1024;
|
||||
|
||||
45
Robust.Shared/GameObjects/ComponentMessages/Messages.cs
Normal file
45
Robust.Shared/GameObjects/ComponentMessages/Messages.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
public class RelayMovementEntityMessage : ComponentMessage
|
||||
{
|
||||
[PublicAPI]
|
||||
public readonly IEntity Entity;
|
||||
|
||||
public RelayMovementEntityMessage(IEntity entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The entity transform parent has been changed.
|
||||
/// </summary>
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
public class ParentChangedMessage : ComponentMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// The new parent of the transform.
|
||||
/// </summary>
|
||||
public IEntity? NewParent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The old parent of the transform.
|
||||
/// </summary>
|
||||
public IEntity? OldParent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="ParentChangedMessage"/>.
|
||||
/// </summary>
|
||||
/// <param name="newParent">The new parent of the transform.</param>
|
||||
/// <param name="oldParent">The old parent of the transform.</param>
|
||||
public ParentChangedMessage(IEntity? newParent, IEntity? oldParent)
|
||||
{
|
||||
NewParent = newParent;
|
||||
OldParent = oldParent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,12 @@ namespace Robust.Shared.GameObjects
|
||||
void CollideWith(Fixture ourFixture, Fixture otherFixture, in Manifold manifold);
|
||||
}
|
||||
|
||||
public interface ICollideSpecial
|
||||
{
|
||||
[Obsolete("Use PreventCollideEvent instead")]
|
||||
bool PreventCollide(IPhysBody collidedwith);
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum BodyStatus: byte
|
||||
{
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
@@ -62,9 +61,8 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
/// <summary>
|
||||
/// Store the body's index within the island so we can lookup its data.
|
||||
/// Key is Island's ID and value is our index.
|
||||
/// </summary>
|
||||
public Dictionary<int, int> IslandIndex { get; set; } = new();
|
||||
public int IslandIndex { get; set; }
|
||||
|
||||
// TODO: Actually implement after the initial pr dummy
|
||||
/// <summary>
|
||||
@@ -165,11 +163,11 @@ namespace Robust.Shared.GameObjects
|
||||
if (value)
|
||||
{
|
||||
_sleepTime = 0.0f;
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PhysicsWakeMessage(this));
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PhysicsWakeMessage(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PhysicsSleepMessage(this));
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PhysicsSleepMessage(this));
|
||||
ResetDynamics();
|
||||
_sleepTime = 0.0f;
|
||||
}
|
||||
@@ -263,7 +261,10 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
fixture.Body = this;
|
||||
fixture.ComputeProperties();
|
||||
fixture.ID = GetFixtureName(fixture);
|
||||
if (string.IsNullOrEmpty(fixture.Name))
|
||||
{
|
||||
fixture.Name = GetFixtureName(fixture);
|
||||
}
|
||||
}
|
||||
|
||||
ResetMassData();
|
||||
@@ -284,7 +285,7 @@ namespace Robust.Shared.GameObjects
|
||||
joints.Add(je.Joint);
|
||||
}
|
||||
|
||||
return new PhysicsComponentState(_canCollide, _sleepingAllowed, _fixedRotation, _bodyStatus, _fixtures, joints, LinearVelocity, AngularVelocity, BodyType);
|
||||
return new PhysicsComponentState(_canCollide, _sleepingAllowed, _fixedRotation, _bodyStatus, _fixtures, joints, _mass, LinearVelocity, AngularVelocity, BodyType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -303,76 +304,60 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// We will pray that this deferred joint is handled properly.
|
||||
|
||||
// TODO: Crude as FUCK diffing here as well, fine for now.
|
||||
/*
|
||||
* -- Joints --
|
||||
*/
|
||||
|
||||
// TODO: Iterating like this is inefficient and bloated as fuck but on the other hand the linked-list is very convenient
|
||||
// for bodies with a large number of fixtures / joints.
|
||||
// Probably store them in Dictionaries but still store the linked-list stuff on the fixture / joint itself.
|
||||
var existingJoints = Joints.ToList();
|
||||
var toAddJoints = new List<Joint>();
|
||||
var toRemoveJoints = new List<Joint>();
|
||||
var existingJoints = new List<Joint>();
|
||||
|
||||
foreach (var newJoint in newState.Joints)
|
||||
for (var je = JointEdges; je != null; je = je.Next)
|
||||
{
|
||||
var jointFound = false;
|
||||
existingJoints.Add(je.Joint);
|
||||
}
|
||||
|
||||
foreach (var joint in existingJoints)
|
||||
var jointsDiff = true;
|
||||
|
||||
if (existingJoints.Count == newState.Joints.Count)
|
||||
{
|
||||
var anyDiff = false;
|
||||
for (var i = 0; i < existingJoints.Count; i++)
|
||||
{
|
||||
if (joint.ID.Equals(newJoint.ID))
|
||||
{
|
||||
if (!newJoint.Equals(joint) && TrySetupNetworkedJoint(newJoint))
|
||||
{
|
||||
toAddJoints.Add(newJoint);
|
||||
toRemoveJoints.Add(joint);
|
||||
}
|
||||
var existing = existingJoints[i];
|
||||
var newJoint = newState.Joints[i];
|
||||
|
||||
jointFound = true;
|
||||
if (!existing.Equals(newJoint))
|
||||
{
|
||||
anyDiff = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!jointFound && TrySetupNetworkedJoint(newJoint))
|
||||
if (!anyDiff)
|
||||
{
|
||||
toAddJoints.Add(newJoint);
|
||||
jointsDiff = false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var joint in existingJoints)
|
||||
if (jointsDiff)
|
||||
{
|
||||
var jointFound = false;
|
||||
ClearJoints();
|
||||
|
||||
foreach (var newJoint in newState.Joints)
|
||||
foreach (var joint in newState.Joints)
|
||||
{
|
||||
if (joint.ID.Equals(newJoint.ID))
|
||||
{
|
||||
jointFound = true;
|
||||
break;
|
||||
}
|
||||
joint.EdgeA = new JointEdge();
|
||||
joint.EdgeB = new JointEdge();
|
||||
// Defer joints given it relies on 2 bodies.
|
||||
AddJoint(joint);
|
||||
}
|
||||
|
||||
if (jointFound) continue;
|
||||
|
||||
toRemoveJoints.Add(joint);
|
||||
}
|
||||
|
||||
foreach (var joint in toRemoveJoints)
|
||||
{
|
||||
RemoveJoint(joint);
|
||||
}
|
||||
|
||||
foreach (var joint in toAddJoints)
|
||||
{
|
||||
AddJoint(joint);
|
||||
}
|
||||
|
||||
/*
|
||||
* -- Fixtures --
|
||||
*/
|
||||
|
||||
var toAddFixtures = new List<Fixture>();
|
||||
var toRemoveFixtures = new List<Fixture>();
|
||||
var toAdd = new List<Fixture>();
|
||||
var toRemove = new List<Fixture>();
|
||||
var computeProperties = false;
|
||||
|
||||
// Given a bunch of data isn't serialized need to sort of re-initialise it
|
||||
@@ -392,12 +377,12 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
foreach (var existing in _fixtures)
|
||||
{
|
||||
if (!fixture.ID.Equals(existing.ID)) continue;
|
||||
if (!fixture.Name.Equals(existing.Name)) continue;
|
||||
|
||||
if (!fixture.Equals(existing))
|
||||
{
|
||||
toAddFixtures.Add(fixture);
|
||||
toRemoveFixtures.Add(existing);
|
||||
toAdd.Add(fixture);
|
||||
toRemove.Add(existing);
|
||||
}
|
||||
|
||||
found = true;
|
||||
@@ -406,7 +391,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
if (!found)
|
||||
{
|
||||
toAddFixtures.Add(fixture);
|
||||
toAdd.Add(fixture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,7 +402,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
foreach (var fixture in newFixtures)
|
||||
{
|
||||
if (fixture.ID.Equals(existing.ID))
|
||||
if (fixture.Name.Equals(existing.Name))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
@@ -426,18 +411,18 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
if (!found)
|
||||
{
|
||||
toRemoveFixtures.Add(existing);
|
||||
toRemove.Add(existing);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var fixture in toRemoveFixtures)
|
||||
foreach (var fixture in toRemove)
|
||||
{
|
||||
computeProperties = true;
|
||||
RemoveFixture(fixture);
|
||||
}
|
||||
|
||||
// TODO: We also still need event listeners for shapes (Probably need C# events)
|
||||
foreach (var fixture in toAddFixtures)
|
||||
foreach (var fixture in toAdd)
|
||||
{
|
||||
computeProperties = true;
|
||||
AddFixture(fixture);
|
||||
@@ -461,29 +446,6 @@ namespace Robust.Shared.GameObjects
|
||||
Predict = false;
|
||||
}
|
||||
|
||||
private bool TrySetupNetworkedJoint(Joint joint)
|
||||
{
|
||||
// This can fail if we've already deleted the entity or remove physics from it I think?
|
||||
|
||||
if (!Owner.EntityManager.TryGetEntity(joint.BodyAUid, out var entityA) ||
|
||||
!entityA.TryGetComponent(out PhysicsComponent? bodyA))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Owner.EntityManager.TryGetEntity(joint.BodyBUid, out var entityB) ||
|
||||
!entityB.TryGetComponent(out PhysicsComponent? bodyB))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
joint.BodyA = bodyA;
|
||||
joint.BodyB = bodyB;
|
||||
joint.EdgeA = new JointEdge();
|
||||
joint.EdgeB = new JointEdge();
|
||||
return true;
|
||||
}
|
||||
|
||||
public Fixture? GetFixture(string name)
|
||||
{
|
||||
// Sooo I'd rather have fixtures as a list in serialization but there's not really an easy way to have it as a
|
||||
@@ -493,7 +455,7 @@ namespace Robust.Shared.GameObjects
|
||||
// If we really need it then you just deserialize onto a dummy field that then just never gets used again.
|
||||
foreach (var fixture in _fixtures)
|
||||
{
|
||||
if (fixture.ID.Equals(name))
|
||||
if (fixture.Name.Equals(name))
|
||||
{
|
||||
return fixture;
|
||||
}
|
||||
@@ -515,23 +477,35 @@ namespace Robust.Shared.GameObjects
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public Box2 GetWorldAABB(Vector2? worldPos = null, Angle? worldRot = null)
|
||||
public Box2 GetWorldAABB(IMapManager? mapManager = null)
|
||||
{
|
||||
worldPos ??= Owner.Transform.WorldPosition;
|
||||
worldRot ??= Owner.Transform.WorldRotation;
|
||||
|
||||
var worldPosValue = worldPos.Value;
|
||||
var worldRotValue = worldRot.Value;
|
||||
|
||||
var bounds = new Box2(worldPosValue, worldPosValue);
|
||||
mapManager ??= IoCManager.Resolve<IMapManager>();
|
||||
var bounds = new Box2();
|
||||
|
||||
foreach (var fixture in _fixtures)
|
||||
{
|
||||
var boundy = fixture.Shape.CalculateLocalBounds(worldRotValue);
|
||||
bounds = bounds.Union(boundy.Translated(worldPosValue));
|
||||
foreach (var (gridId, proxies) in fixture.Proxies)
|
||||
{
|
||||
Vector2 offset;
|
||||
|
||||
if (gridId == GridId.Invalid)
|
||||
{
|
||||
offset = Vector2.Zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = mapManager.GetGrid(gridId).WorldPosition;
|
||||
}
|
||||
|
||||
foreach (var proxy in proxies)
|
||||
{
|
||||
var shapeBounds = proxy.AABB.Translated(offset);
|
||||
bounds = bounds.IsEmpty() ? shapeBounds : bounds.Union(shapeBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bounds;
|
||||
return bounds.IsEmpty() ? Box2.UnitCentered.Translated(Owner.Transform.WorldPosition) : bounds;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -572,6 +546,7 @@ namespace Robust.Shared.GameObjects
|
||||
return;
|
||||
|
||||
_canCollide = value;
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new CollisionChangeMessage(this, Owner.Uid, _canCollide));
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PhysicsUpdateMessage(this));
|
||||
Dirty();
|
||||
@@ -993,46 +968,12 @@ namespace Robust.Shared.GameObjects
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new FixtureUpdateMessage(this, fixture));
|
||||
}
|
||||
|
||||
private string GetFixtureName(Fixture fixture)
|
||||
internal string GetFixtureName(Fixture fixture)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(fixture.ID)) return fixture.ID;
|
||||
|
||||
// For any fixtures that aren't named in the code we will assign one.
|
||||
return $"fixture-{_fixtures.IndexOf(fixture)}";
|
||||
}
|
||||
|
||||
private string GetJointName(Joint joint)
|
||||
{
|
||||
var id = joint.ID;
|
||||
|
||||
if (!string.IsNullOrEmpty(id)) return id;
|
||||
|
||||
var jointCount = Joints.Count();
|
||||
|
||||
for (var i = 0; i < jointCount + 1; i++)
|
||||
{
|
||||
id = $"joint-{i}";
|
||||
if (GetJoint(id) != null) continue;
|
||||
return id;
|
||||
}
|
||||
|
||||
Logger.WarningS("physics", $"Unable to get a joint ID; using its hashcode instead.");
|
||||
return joint.GetHashCode().ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a joint with the specified ID.
|
||||
/// </summary>
|
||||
public Joint? GetJoint(string id)
|
||||
{
|
||||
foreach (var joint in Joints)
|
||||
{
|
||||
if (joint.ID == id) return joint;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal Transform GetTransform()
|
||||
{
|
||||
return new(Owner.Transform.WorldPosition, (float) Owner.Transform.WorldRotation.Theta);
|
||||
@@ -1180,10 +1121,15 @@ namespace Robust.Shared.GameObjects
|
||||
else
|
||||
{
|
||||
// TODO: Probably a bad idea but ehh future sloth's problem; namely that we have to duplicate code between here and CanCollide.
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new CollisionChangeMessage(this, Owner.Uid, _canCollide));
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PhysicsUpdateMessage(this));
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new CollisionChangeMessage(this, Owner.Uid, _canCollide));
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PhysicsUpdateMessage(this));
|
||||
}
|
||||
}
|
||||
|
||||
if (EntitySystem.Get<SharedPhysicsSystem>().Maps.TryGetValue(Owner.Transform.MapID, out var map))
|
||||
{
|
||||
PhysicsMap = map;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearJoints()
|
||||
@@ -1235,23 +1181,12 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public void AddJoint(Joint joint)
|
||||
{
|
||||
var id = GetJointName(joint);
|
||||
|
||||
foreach (var existing in Joints)
|
||||
{
|
||||
// This can happen if a server created joint is sent and applied before the client can create it locally elsewhere
|
||||
if (existing.ID.Equals(id)) return;
|
||||
}
|
||||
|
||||
PhysicsMap.AddJoint(joint);
|
||||
joint.ID = id;
|
||||
Logger.DebugS("physics", $"Added joint id: {joint.ID} type: {joint.GetType().Name} to {Owner}");
|
||||
}
|
||||
|
||||
public void RemoveJoint(Joint joint)
|
||||
{
|
||||
PhysicsMap.RemoveJoint(joint);
|
||||
Logger.DebugS("physics", $"Removed joint id: {joint.ID} type: {joint.GetType().Name} from {Owner}");
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
@@ -1359,10 +1294,17 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
if (preventCollideMessage.Cancelled) return false;
|
||||
|
||||
preventCollideMessage = new PreventCollideEvent(other, this);
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(other.Owner.Uid, preventCollideMessage);
|
||||
#pragma warning disable 618
|
||||
foreach (var comp in Owner.GetAllComponents<ICollideSpecial>())
|
||||
{
|
||||
if (comp.PreventCollide(other)) return false;
|
||||
}
|
||||
|
||||
if (preventCollideMessage.Cancelled) return false;
|
||||
foreach (var comp in other.Owner.GetAllComponents<ICollideSpecial>())
|
||||
{
|
||||
if (comp.PreventCollide(this)) return false;
|
||||
}
|
||||
#pragma warning restore 618
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@ namespace Robust.Shared.GameObjects
|
||||
public readonly List<Fixture> Fixtures;
|
||||
public readonly List<Joint> Joints;
|
||||
|
||||
/// <summary>
|
||||
/// Current mass of the entity, stored in grams.
|
||||
/// </summary>
|
||||
public readonly int Mass;
|
||||
public readonly Vector2 LinearVelocity;
|
||||
public readonly float AngularVelocity;
|
||||
public readonly BodyType BodyType;
|
||||
@@ -31,6 +35,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="status"></param>
|
||||
/// <param name="fixtures"></param>
|
||||
/// <param name="joints"></param>
|
||||
/// <param name="mass">Current Mass of the entity.</param>
|
||||
/// <param name="linearVelocity">Current linear velocity of the entity in meters per second.</param>
|
||||
/// <param name="angularVelocity">Current angular velocity of the entity in radians per sec.</param>
|
||||
/// <param name="bodyType"></param>
|
||||
@@ -41,6 +46,7 @@ namespace Robust.Shared.GameObjects
|
||||
BodyStatus status,
|
||||
List<Fixture> fixtures,
|
||||
List<Joint> joints,
|
||||
float mass,
|
||||
Vector2 linearVelocity,
|
||||
float angularVelocity,
|
||||
BodyType bodyType)
|
||||
@@ -55,6 +61,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
LinearVelocity = linearVelocity;
|
||||
AngularVelocity = angularVelocity;
|
||||
Mass = (int) Math.Round(mass * 1000); // rounds kg to nearest gram
|
||||
BodyType = bodyType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -33,7 +32,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
internal void RaiseStateChange()
|
||||
private void RaiseStateChange()
|
||||
{
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new CollisionWakeStateMessage(), false);
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// Set _nextRotation to null to break any active lerps if this is a client side prediction.
|
||||
_nextRotation = null;
|
||||
_localRotation = value;
|
||||
SetRotation(value);
|
||||
Dirty();
|
||||
|
||||
if (!DeferUpdates)
|
||||
@@ -258,46 +258,17 @@ namespace Robust.Shared.GameObjects
|
||||
var valid = _parent.IsValid();
|
||||
return new EntityCoordinates(valid ? _parent : Owner.Uid, valid ? LocalPosition : Vector2.Zero);
|
||||
}
|
||||
// NOTE: This setter must be callable from before initialize (inheriting from AttachParent's note)
|
||||
set
|
||||
{
|
||||
var oldPosition = Coordinates;
|
||||
_localPosition = value.Position;
|
||||
|
||||
var changedParent = false;
|
||||
|
||||
if (value.EntityId != _parent)
|
||||
{
|
||||
changedParent = true;
|
||||
var newParentEnt = Owner.EntityManager.GetEntity(value.EntityId);
|
||||
var newParent = newParentEnt.Transform;
|
||||
|
||||
DebugTools.Assert(newParent != this,
|
||||
$"Can't parent a {nameof(ITransformComponent)} to itself.");
|
||||
|
||||
// That's already our parent, don't bother attaching again.
|
||||
|
||||
var oldParent = Parent;
|
||||
var oldConcrete = (TransformComponent?) oldParent;
|
||||
var uid = Owner.Uid;
|
||||
oldConcrete?._children.Remove(uid);
|
||||
var newConcrete = (TransformComponent) newParent;
|
||||
newConcrete._children.Add(uid);
|
||||
|
||||
// offset position from world to parent
|
||||
_parent = newParentEnt.Uid;
|
||||
ChangeMapId(newConcrete.MapID);
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new EntParentChangedMessage(Owner, oldParent?.Owner));
|
||||
|
||||
GridID = GetGridIndex();
|
||||
var newEntity = Owner.EntityManager.GetEntity(value.EntityId);
|
||||
AttachParent(newEntity);
|
||||
}
|
||||
|
||||
// These conditions roughly emulate the effects of the code before I changed things,
|
||||
// in regards to when to rebuild matrices.
|
||||
// This may not in fact be the right thing.
|
||||
if (changedParent || !DeferUpdates)
|
||||
RebuildMatrices();
|
||||
Dirty();
|
||||
|
||||
if (!DeferUpdates)
|
||||
@@ -305,6 +276,7 @@ namespace Robust.Shared.GameObjects
|
||||
//TODO: This is a hack, look into WHY we can't call GridPosition before the comp is Running
|
||||
if (Running)
|
||||
{
|
||||
RebuildMatrices();
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new MoveEvent(Owner, oldPosition, Coordinates));
|
||||
}
|
||||
}
|
||||
@@ -332,7 +304,7 @@ namespace Robust.Shared.GameObjects
|
||||
_nextPosition = null;
|
||||
|
||||
var oldGridPos = Coordinates;
|
||||
_localPosition = value;
|
||||
SetPosition(value);
|
||||
Dirty();
|
||||
|
||||
if (!DeferUpdates)
|
||||
@@ -567,8 +539,13 @@ namespace Robust.Shared.GameObjects
|
||||
var uid = Owner.Uid;
|
||||
oldConcrete._children.Remove(uid);
|
||||
|
||||
var oldParentOwner = oldParent?.Owner;
|
||||
|
||||
var entMessage = new EntParentChangedMessage(Owner, oldParentOwner);
|
||||
var compMessage = new ParentChangedMessage(null, oldParentOwner);
|
||||
_parent = EntityUid.Invalid;
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new EntParentChangedMessage(Owner, oldParent?.Owner));
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, entMessage);
|
||||
Owner.SendMessage(this, compMessage);
|
||||
var oldMapId = MapID;
|
||||
MapID = MapId.Nullspace;
|
||||
|
||||
@@ -579,22 +556,49 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets another entity as the parent entity, maintaining world position.
|
||||
/// Sets another entity as the parent entity.
|
||||
/// </summary>
|
||||
/// <param name="newParent"></param>
|
||||
public virtual void AttachParent(ITransformComponent newParent)
|
||||
{
|
||||
//NOTE: This function must be callable from before initialize
|
||||
|
||||
// don't attach to something we're already attached to
|
||||
// nothing to attach to.
|
||||
if (ParentUid == newParent.Owner.Uid)
|
||||
return;
|
||||
|
||||
DebugTools.Assert(newParent != this,
|
||||
$"Can't parent a {nameof(ITransformComponent)} to itself.");
|
||||
|
||||
// offset position from world to parent, and set
|
||||
Coordinates = new EntityCoordinates(newParent.Owner.Uid, newParent.InvWorldMatrix.Transform(WorldPosition));
|
||||
// That's already our parent, don't bother attaching again.
|
||||
var newParentEnt = newParent.Owner;
|
||||
if (newParentEnt.Uid == _parent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var oldParent = Parent;
|
||||
var oldConcrete = (TransformComponent?) oldParent;
|
||||
var uid = Owner.Uid;
|
||||
oldConcrete?._children.Remove(uid);
|
||||
var newConcrete = (TransformComponent) newParent;
|
||||
newConcrete._children.Add(uid);
|
||||
|
||||
var oldParentOwner = oldParent?.Owner;
|
||||
var entMessage = new EntParentChangedMessage(Owner, oldParentOwner);
|
||||
var compMessage = new ParentChangedMessage(newParentEnt, oldParentOwner);
|
||||
|
||||
// offset position from world to parent
|
||||
SetPosition(newParent.InvWorldMatrix.Transform(WorldPosition));
|
||||
_parent = newParentEnt.Uid;
|
||||
ChangeMapId(newConcrete.MapID);
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, entMessage);
|
||||
Owner.SendMessage(this, compMessage);
|
||||
|
||||
RebuildMatrices();
|
||||
Dirty();
|
||||
GridID = GetGridIndex();
|
||||
}
|
||||
|
||||
internal void ChangeMapId(MapId newMapId)
|
||||
@@ -709,14 +713,14 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
if (LocalRotation != newState.Rotation)
|
||||
{
|
||||
_localRotation = newState.Rotation;
|
||||
SetRotation(newState.Rotation);
|
||||
rebuildMatrices = true;
|
||||
}
|
||||
|
||||
if (!_localPosition.EqualsApprox(newState.LocalPosition, 0.0001))
|
||||
{
|
||||
var oldPos = Coordinates;
|
||||
_localPosition = newState.LocalPosition;
|
||||
SetPosition(newState.LocalPosition);
|
||||
|
||||
var ev = new MoveEvent(Owner, oldPos, Coordinates);
|
||||
EntitySystem.Get<SharedTransformSystem>().DeferMoveEvent(ev);
|
||||
@@ -751,6 +755,17 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SetPosition(Vector2 position)
|
||||
{
|
||||
// DebugTools.Assert(!float.IsNaN(position.X) && !float.IsNaN(position.Y));
|
||||
_localPosition = position;
|
||||
}
|
||||
|
||||
protected virtual void SetRotation(Angle rotation)
|
||||
{
|
||||
_localRotation = rotation;
|
||||
}
|
||||
|
||||
public Matrix3 GetLocalMatrix()
|
||||
{
|
||||
return _localMatrix;
|
||||
|
||||
@@ -24,15 +24,6 @@ namespace Robust.Shared.GameObjects
|
||||
void SubscribeEvent<T>(EventSource source, IEntityEventSubscriber subscriber,
|
||||
EntityEventHandler<T> eventHandler) where T : notnull;
|
||||
|
||||
void SubscribeEvent<T>(
|
||||
EventSource source,
|
||||
IEntityEventSubscriber subscriber,
|
||||
EntityEventHandler<T> eventHandler,
|
||||
Type orderType,
|
||||
Type[]? before=null,
|
||||
Type[]? after=null)
|
||||
where T : notnull;
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes all event handlers of a given type.
|
||||
/// </summary>
|
||||
@@ -146,7 +137,7 @@ namespace Robust.Shared.GameObjects
|
||||
return;
|
||||
|
||||
// UnsubscribeEvent modifies _inverseEventSubscriptions, requires val to be cached
|
||||
foreach (var (type, (source, originalHandler, handler, _)) in val.ToList())
|
||||
foreach (var (type, (source, originalHandler, handler)) in val.ToList())
|
||||
{
|
||||
UnsubscribeEvent(source, type, originalHandler, handler, subscriber);
|
||||
}
|
||||
@@ -163,36 +154,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SubscribeEvent<T>(
|
||||
EventSource source,
|
||||
IEntityEventSubscriber subscriber,
|
||||
EntityEventHandler<T> eventHandler)
|
||||
where T : notnull
|
||||
{
|
||||
SubscribeEventCommon(source, subscriber, eventHandler, null);
|
||||
}
|
||||
|
||||
public void SubscribeEvent<T>(
|
||||
EventSource source,
|
||||
IEntityEventSubscriber subscriber,
|
||||
EntityEventHandler<T> eventHandler,
|
||||
Type orderType,
|
||||
Type[]? before=null,
|
||||
Type[]? after=null)
|
||||
where T : notnull
|
||||
{
|
||||
var order = new OrderingData(orderType, before, after);
|
||||
|
||||
SubscribeEventCommon(source, subscriber, eventHandler, order);
|
||||
HandleOrderRegistration(typeof(T), order);
|
||||
}
|
||||
|
||||
private void SubscribeEventCommon<T>(
|
||||
EventSource source,
|
||||
IEntityEventSubscriber subscriber,
|
||||
EntityEventHandler<T> eventHandler,
|
||||
OrderingData? order)
|
||||
where T : notnull
|
||||
public void SubscribeEvent<T>(EventSource source, IEntityEventSubscriber subscriber, EntityEventHandler<T> eventHandler) where T : notnull
|
||||
{
|
||||
if (source == EventSource.None)
|
||||
throw new ArgumentOutOfRangeException(nameof(source));
|
||||
@@ -204,7 +166,7 @@ namespace Robust.Shared.GameObjects
|
||||
throw new ArgumentNullException(nameof(subscriber));
|
||||
|
||||
var eventType = typeof(T);
|
||||
var subscriptionTuple = new Registration(source, eventHandler, ev => eventHandler((T) ev), eventHandler, order);
|
||||
var subscriptionTuple = new Registration(source, eventHandler, ev => eventHandler((T) ev), eventHandler);
|
||||
if (!_eventSubscriptions.TryGetValue(eventType, out var subscriptions))
|
||||
_eventSubscriptions.Add(eventType, new List<Registration> {subscriptionTuple});
|
||||
else if (!subscriptions.Any(p => p.Mask == source && p.Original == (Delegate) eventHandler))
|
||||
@@ -222,6 +184,7 @@ namespace Robust.Shared.GameObjects
|
||||
inverseSubscription
|
||||
);
|
||||
}
|
||||
|
||||
else if (!inverseSubscription.ContainsKey(eventType))
|
||||
{
|
||||
inverseSubscription.Add(eventType, subscriptionTuple);
|
||||
@@ -327,13 +290,15 @@ namespace Robust.Shared.GameObjects
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
_awaitingMessages.Add(type, (source, reg, tcs));
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
private void UnsubscribeEvent(EventSource source, Type eventType, Delegate originalHandler, EventHandler handler, IEntityEventSubscriber subscriber)
|
||||
{
|
||||
var tuple = new Registration(source, originalHandler, handler, originalHandler, null);
|
||||
var tuple = new Registration(source, originalHandler, handler, originalHandler);
|
||||
if (_eventSubscriptions.TryGetValue(eventType, out var subscriptions) && subscriptions.Contains(tuple))
|
||||
subscriptions.Remove(tuple);
|
||||
|
||||
@@ -345,11 +310,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
var eventType = eventArgs.GetType();
|
||||
|
||||
if (_orderedEvents.Contains(eventType))
|
||||
{
|
||||
ProcessSingleEventOrdered(source, eventArgs, eventType);
|
||||
}
|
||||
else if (_eventSubscriptions.TryGetValue(eventType, out var subs))
|
||||
if (_eventSubscriptions.TryGetValue(eventType, out var subs))
|
||||
{
|
||||
foreach (var handler in subs)
|
||||
{
|
||||
@@ -358,20 +319,25 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
ProcessAwaitingMessages(source, eventArgs, eventType);
|
||||
if (_awaitingMessages.TryGetValue(eventType, out var awaiting))
|
||||
{
|
||||
var (mask, _, tcs) = awaiting;
|
||||
|
||||
if ((source & mask) != 0)
|
||||
{
|
||||
tcs.TrySetResult(eventArgs);
|
||||
_awaitingMessages.Remove(eventType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessSingleEvent<T>(EventSource source, T eventArgs) where T : notnull
|
||||
{
|
||||
var eventType = typeof(T);
|
||||
|
||||
if (_orderedEvents.Contains(eventType))
|
||||
if (_eventSubscriptions.TryGetValue(eventType, out var subs))
|
||||
{
|
||||
ProcessSingleEventOrdered(source, eventArgs, eventType);
|
||||
}
|
||||
else if (_eventSubscriptions.TryGetValue(eventType, out var subs))
|
||||
{
|
||||
foreach (var (mask, originalHandler, _, _) in subs)
|
||||
foreach (var (mask, originalHandler, _) in subs)
|
||||
{
|
||||
if ((mask & source) != 0)
|
||||
{
|
||||
@@ -381,13 +347,6 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
ProcessAwaitingMessages(source, eventArgs, eventType);
|
||||
}
|
||||
|
||||
// Generic here so we can avoid boxing alloc unless actually awaiting.
|
||||
private void ProcessAwaitingMessages<T>(EventSource source, T eventArgs, Type eventType)
|
||||
where T : notnull
|
||||
{
|
||||
if (_awaitingMessages.TryGetValue(eventType, out var awaiting))
|
||||
{
|
||||
var (mask1, _, tcs) = awaiting;
|
||||
@@ -407,20 +366,13 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public readonly Delegate Original;
|
||||
public readonly EventHandler Handler;
|
||||
public readonly OrderingData? Ordering;
|
||||
|
||||
public Registration(
|
||||
EventSource mask,
|
||||
Delegate original,
|
||||
EventHandler handler,
|
||||
object equalityToken,
|
||||
OrderingData? ordering)
|
||||
public Registration(EventSource mask, Delegate original, EventHandler handler, object equalityToken)
|
||||
{
|
||||
Mask = mask;
|
||||
Original = original;
|
||||
Handler = handler;
|
||||
EqualityToken = equalityToken;
|
||||
Ordering = ordering;
|
||||
}
|
||||
|
||||
public bool Equals(Registration other)
|
||||
@@ -451,16 +403,11 @@ namespace Robust.Shared.GameObjects
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public void Deconstruct(
|
||||
out EventSource mask,
|
||||
out Delegate originalHandler,
|
||||
out EventHandler handler,
|
||||
out OrderingData? order)
|
||||
public void Deconstruct(out EventSource mask, out Delegate originalHandler, out EventHandler handler)
|
||||
{
|
||||
mask = Mask;
|
||||
originalHandler = Original;
|
||||
handler = Handler;
|
||||
order = Ordering;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,6 @@ namespace Robust.Shared.GameObjects
|
||||
where TComp : IComponent
|
||||
where TEvent : EntityEventArgs;
|
||||
|
||||
void SubscribeLocalEvent<TComp, TEvent>(
|
||||
ComponentEventHandler<TComp, TEvent> handler,
|
||||
Type orderType, Type[]? before=null, Type[]? after=null)
|
||||
where TComp : IComponent
|
||||
where TEvent : EntityEventArgs;
|
||||
|
||||
[Obsolete("Use the overload without the handler argument.")]
|
||||
void UnsubscribeLocalEvent<TComp, TEvent>(ComponentEventHandler<TComp, TEvent> handler)
|
||||
where TComp : IComponent
|
||||
@@ -67,12 +61,6 @@ namespace Robust.Shared.GameObjects
|
||||
public void RaiseLocalEvent<TEvent>(EntityUid uid, TEvent args, bool broadcast = true)
|
||||
where TEvent : EntityEventArgs
|
||||
{
|
||||
if (_orderedEvents.Contains(typeof(TEvent)))
|
||||
{
|
||||
RaiseLocalOrdered(uid, args, broadcast);
|
||||
return;
|
||||
}
|
||||
|
||||
_eventTables.Dispatch(uid, typeof(TEvent), args);
|
||||
|
||||
// we also broadcast it so the call site does not have to.
|
||||
@@ -88,24 +76,7 @@ namespace Robust.Shared.GameObjects
|
||||
void EventHandler(EntityUid uid, IComponent comp, EntityEventArgs args)
|
||||
=> handler(uid, (TComp) comp, (TEvent) args);
|
||||
|
||||
_eventTables.Subscribe(typeof(TComp), typeof(TEvent), EventHandler, null);
|
||||
}
|
||||
|
||||
public void SubscribeLocalEvent<TComp, TEvent>(
|
||||
ComponentEventHandler<TComp, TEvent> handler,
|
||||
Type orderType,
|
||||
Type[]? before=null,
|
||||
Type[]? after=null)
|
||||
where TComp : IComponent
|
||||
where TEvent : EntityEventArgs
|
||||
{
|
||||
void EventHandler(EntityUid uid, IComponent comp, EntityEventArgs args)
|
||||
=> handler(uid, (TComp) comp, (TEvent) args);
|
||||
|
||||
var orderData = new OrderingData(orderType, before, after);
|
||||
|
||||
_eventTables.Subscribe(typeof(TComp), typeof(TEvent), EventHandler, orderData);
|
||||
HandleOrderRegistration(typeof(TEvent), orderData);
|
||||
_eventTables.Subscribe(typeof(TComp), typeof(TEvent), EventHandler);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -128,13 +99,12 @@ namespace Robust.Shared.GameObjects
|
||||
private class EventTables : IDisposable
|
||||
{
|
||||
private IEntityManager _entMan;
|
||||
private IComponentFactory _comFac;
|
||||
|
||||
// eUid -> EventType -> { CompType1, ... CompTypeN }
|
||||
private Dictionary<EntityUid, Dictionary<Type, HashSet<Type>>> _eventTables;
|
||||
|
||||
// EventType -> CompType -> Handler
|
||||
private Dictionary<Type, Dictionary<Type, (DirectedEventHandler handler, OrderingData? ordering)>> _subscriptions;
|
||||
private Dictionary<Type, Dictionary<Type, DirectedEventHandler>> _subscriptions;
|
||||
|
||||
// prevents shitcode, get your subscriptions figured out before you start spawning entities
|
||||
private bool _subscriptionLock;
|
||||
@@ -142,7 +112,6 @@ namespace Robust.Shared.GameObjects
|
||||
public EventTables(IEntityManager entMan)
|
||||
{
|
||||
_entMan = entMan;
|
||||
_comFac = entMan.ComponentManager.ComponentFactory;
|
||||
|
||||
_entMan.EntityAdded += OnEntityAdded;
|
||||
_entMan.EntityDeleted += OnEntityDeleted;
|
||||
@@ -177,21 +146,25 @@ namespace Robust.Shared.GameObjects
|
||||
RemoveComponent(e.OwnerUid, e.Component.GetType());
|
||||
}
|
||||
|
||||
public void Subscribe(Type compType, Type eventType, DirectedEventHandler handler, OrderingData? order)
|
||||
public void Subscribe(Type compType, Type eventType, DirectedEventHandler handler)
|
||||
{
|
||||
if (_subscriptionLock)
|
||||
throw new InvalidOperationException("Subscription locked.");
|
||||
|
||||
if (!_subscriptions.TryGetValue(compType, out var compSubs))
|
||||
{
|
||||
compSubs = new Dictionary<Type, (DirectedEventHandler, OrderingData?)>();
|
||||
compSubs = new Dictionary<Type, DirectedEventHandler>();
|
||||
_subscriptions.Add(compType, compSubs);
|
||||
|
||||
compSubs.Add(eventType, handler);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (compSubs.ContainsKey(eventType))
|
||||
throw new InvalidOperationException($"Duplicate Subscriptions for comp={compType.Name}, event={eventType.Name}");
|
||||
|
||||
if (compSubs.ContainsKey(eventType))
|
||||
throw new InvalidOperationException($"Duplicate Subscriptions for comp={compType.Name}, event={eventType.Name}");
|
||||
|
||||
compSubs.Add(eventType, (handler, order));
|
||||
compSubs.Add(eventType, handler);
|
||||
}
|
||||
}
|
||||
|
||||
public void Unsubscribe(Type compType, Type eventType)
|
||||
@@ -221,21 +194,18 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
var eventTable = _eventTables[euid];
|
||||
|
||||
foreach (var type in GetReferences(compType))
|
||||
if (!_subscriptions.TryGetValue(compType, out var compSubs))
|
||||
return;
|
||||
|
||||
foreach (var kvSub in compSubs)
|
||||
{
|
||||
if (!_subscriptions.TryGetValue(type, out var compSubs))
|
||||
continue;
|
||||
|
||||
foreach (var kvSub in compSubs)
|
||||
if(!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
|
||||
{
|
||||
if(!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
|
||||
{
|
||||
subscribedComps = new HashSet<Type>();
|
||||
eventTable.Add(kvSub.Key, subscribedComps);
|
||||
}
|
||||
|
||||
subscribedComps.Add(type);
|
||||
subscribedComps = new HashSet<Type>();
|
||||
eventTable.Add(kvSub.Key, subscribedComps);
|
||||
}
|
||||
|
||||
subscribedComps.Add(compType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,18 +213,15 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
var eventTable = _eventTables[euid];
|
||||
|
||||
foreach (var type in GetReferences(compType))
|
||||
if (!_subscriptions.TryGetValue(compType, out var compSubs))
|
||||
return;
|
||||
|
||||
foreach (var kvSub in compSubs)
|
||||
{
|
||||
if (!_subscriptions.TryGetValue(type, out var compSubs))
|
||||
continue;
|
||||
if (!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
|
||||
return;
|
||||
|
||||
foreach (var kvSub in compSubs)
|
||||
{
|
||||
if (!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
|
||||
return;
|
||||
|
||||
subscribedComps.Remove(type);
|
||||
}
|
||||
subscribedComps.Remove(compType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,54 +237,23 @@ namespace Robust.Shared.GameObjects
|
||||
if(!_subscriptions.TryGetValue(compType, out var compSubs))
|
||||
return;
|
||||
|
||||
if(!compSubs.TryGetValue(eventType, out var sub))
|
||||
if(!compSubs.TryGetValue(eventType, out var handler))
|
||||
return;
|
||||
|
||||
var (handler, _) = sub;
|
||||
var component = _entMan.ComponentManager.GetComponent(euid, compType);
|
||||
|
||||
handler(euid, component, args);
|
||||
}
|
||||
}
|
||||
|
||||
public void CollectOrdered(
|
||||
EntityUid euid,
|
||||
Type eventType,
|
||||
List<(EventHandler, OrderingData?)> found)
|
||||
{
|
||||
var eventTable = _eventTables[euid];
|
||||
|
||||
if(!eventTable.TryGetValue(eventType, out var subscribedComps))
|
||||
return;
|
||||
|
||||
foreach (var compType in subscribedComps)
|
||||
{
|
||||
if(!_subscriptions.TryGetValue(compType, out var compSubs))
|
||||
return;
|
||||
|
||||
if(!compSubs.TryGetValue(eventType, out var sub))
|
||||
return;
|
||||
|
||||
var (handler, order) = sub;
|
||||
var component = _entMan.ComponentManager.GetComponent(euid, compType);
|
||||
|
||||
found.Add((ev => handler(euid, component, (EntityEventArgs) ev), order));
|
||||
}
|
||||
}
|
||||
|
||||
public void DispatchComponent(EntityUid euid, IComponent component, Type eventType, EntityEventArgs args)
|
||||
{
|
||||
foreach (var type in GetReferences(component.GetType()))
|
||||
{
|
||||
if (!_subscriptions.TryGetValue(type, out var compSubs))
|
||||
continue;
|
||||
if (!_subscriptions.TryGetValue(component.GetType(), out var compSubs))
|
||||
return;
|
||||
|
||||
if (!compSubs.TryGetValue(eventType, out var sub))
|
||||
continue;
|
||||
if (!compSubs.TryGetValue(eventType, out var handler))
|
||||
return;
|
||||
|
||||
var (handler, _) = sub;
|
||||
handler(euid, component, args);
|
||||
}
|
||||
handler(euid, component, args);
|
||||
}
|
||||
|
||||
public void ClearEntities()
|
||||
@@ -345,22 +281,6 @@ namespace Robust.Shared.GameObjects
|
||||
_eventTables = null!;
|
||||
_subscriptions = null!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the type's component references, returning the type itself last.
|
||||
/// </summary>
|
||||
private IEnumerable<Type> GetReferences(Type type)
|
||||
{
|
||||
var list = _comFac.GetRegistration(type).References;
|
||||
|
||||
foreach (var t in list)
|
||||
{
|
||||
if (t == type) continue;
|
||||
yield return t;
|
||||
}
|
||||
|
||||
yield return type;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
internal partial class EntityEventBus
|
||||
{
|
||||
// TODO: Topological sort is currently done every time an event is emitted.
|
||||
// This should be fine for low-volume stuff like interactions, but definitely not for anything high volume.
|
||||
// Not sure if we could pre-cache the topological sort, here.
|
||||
|
||||
// Ordered event raising is slow so if this event has any ordering dependencies we use a slower path.
|
||||
private readonly HashSet<Type> _orderedEvents = new();
|
||||
|
||||
private void ProcessSingleEventOrdered(EventSource source, object eventArgs, Type eventType)
|
||||
{
|
||||
var found = new List<(EventHandler, OrderingData?)>();
|
||||
|
||||
CollectBroadcastOrdered(source, eventType, found);
|
||||
|
||||
DispatchOrderedEvents(eventArgs, found);
|
||||
}
|
||||
|
||||
private void CollectBroadcastOrdered(
|
||||
EventSource source,
|
||||
Type eventType,
|
||||
List<(EventHandler, OrderingData?)> found)
|
||||
{
|
||||
if (!_eventSubscriptions.TryGetValue(eventType, out var subs))
|
||||
return;
|
||||
|
||||
foreach (var handler in subs)
|
||||
{
|
||||
if ((handler.Mask & source) != 0)
|
||||
found.Add((handler.Handler, handler.Ordering));
|
||||
}
|
||||
}
|
||||
|
||||
private void RaiseLocalOrdered<TEvent>(
|
||||
EntityUid uid,
|
||||
TEvent args,
|
||||
bool broadcast)
|
||||
where TEvent : EntityEventArgs
|
||||
{
|
||||
var found = new List<(EventHandler, OrderingData?)>();
|
||||
|
||||
if (broadcast)
|
||||
CollectBroadcastOrdered(EventSource.Local, typeof(TEvent), found);
|
||||
|
||||
_eventTables.CollectOrdered(uid, typeof(TEvent), found);
|
||||
|
||||
DispatchOrderedEvents(args, found);
|
||||
|
||||
if (broadcast)
|
||||
ProcessAwaitingMessages(EventSource.Local, args, typeof(TEvent));
|
||||
}
|
||||
|
||||
private static void DispatchOrderedEvents(object eventArgs, List<(EventHandler, OrderingData?)> found)
|
||||
{
|
||||
var nodes = TopologicalSort.FromBeforeAfter(
|
||||
found.Where(f => f.Item2 != null),
|
||||
n => n.Item2!.OrderType,
|
||||
n => n.Item1!,
|
||||
n => n.Item2!.Before ?? Array.Empty<Type>(),
|
||||
n => n.Item2!.After ?? Array.Empty<Type>(),
|
||||
allowMissing: true);
|
||||
|
||||
foreach (var handler in TopologicalSort.Sort(nodes))
|
||||
{
|
||||
handler(eventArgs);
|
||||
}
|
||||
|
||||
// Go over all handlers that don't have ordering so weren't included in the sort.
|
||||
foreach (var (handler, orderData) in found)
|
||||
{
|
||||
if (orderData == null)
|
||||
handler(eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleOrderRegistration(Type eventType, OrderingData? data)
|
||||
{
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
if (data.Before != null || data.After != null)
|
||||
_orderedEvents.Add(eventType);
|
||||
}
|
||||
|
||||
private sealed record OrderingData(Type OrderType, Type[]? Before, Type[]? After);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -142,11 +142,6 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
#region Entity Management
|
||||
|
||||
public IEntity CreateEntityUninitialized(string? prototypeName, EntityUid? euid)
|
||||
{
|
||||
return CreateEntity(prototypeName, euid);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEntity CreateEntityUninitialized(string? prototypeName)
|
||||
{
|
||||
|
||||
@@ -7,22 +7,16 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
private List<SubBase>? _subscriptions;
|
||||
|
||||
// NOTE: EntityEventHandler<T> and EntitySessionEventHandler<T> CANNOT BE ORDERED BETWEEN EACH OTHER.
|
||||
|
||||
protected void SubscribeNetworkEvent<T>(
|
||||
EntityEventHandler<T> handler,
|
||||
Type[]? before=null, Type[]? after=null)
|
||||
protected void SubscribeNetworkEvent<T>(EntityEventHandler<T> handler)
|
||||
where T : notnull
|
||||
{
|
||||
EntityManager.EventBus.SubscribeEvent(EventSource.Network, this, handler, GetType(), before, after);
|
||||
EntityManager.EventBus.SubscribeEvent(EventSource.Network, this, handler);
|
||||
|
||||
_subscriptions ??= new();
|
||||
_subscriptions.Add(new SubBroadcast<T>(EventSource.Network));
|
||||
}
|
||||
|
||||
protected void SubscribeNetworkEvent<T>(
|
||||
EntitySessionEventHandler<T> handler,
|
||||
Type[]? before=null, Type[]? after=null)
|
||||
protected void SubscribeNetworkEvent<T>(EntitySessionEventHandler<T> handler)
|
||||
where T : notnull
|
||||
{
|
||||
EntityManager.EventBus.SubscribeSessionEvent(EventSource.Network, this, handler);
|
||||
@@ -31,20 +25,16 @@ namespace Robust.Shared.GameObjects
|
||||
_subscriptions.Add(new SubBroadcast<EntitySessionMessage<T>>(EventSource.Network));
|
||||
}
|
||||
|
||||
protected void SubscribeLocalEvent<T>(
|
||||
EntityEventHandler<T> handler,
|
||||
Type[]? before=null, Type[]? after=null)
|
||||
protected void SubscribeLocalEvent<T>(EntityEventHandler<T> handler)
|
||||
where T : notnull
|
||||
{
|
||||
EntityManager.EventBus.SubscribeEvent(EventSource.Local, this, handler, GetType(), before, after);
|
||||
EntityManager.EventBus.SubscribeEvent(EventSource.Local, this, handler);
|
||||
|
||||
_subscriptions ??= new();
|
||||
_subscriptions.Add(new SubBroadcast<T>(EventSource.Local));
|
||||
}
|
||||
|
||||
protected void SubscribeLocalEvent<T>(
|
||||
EntitySessionEventHandler<T> handler,
|
||||
Type[]? before=null, Type[]? after=null)
|
||||
protected void SubscribeLocalEvent<T>(EntitySessionEventHandler<T> handler)
|
||||
where T : notnull
|
||||
{
|
||||
EntityManager.EventBus.SubscribeSessionEvent(EventSource.Local, this, handler);
|
||||
@@ -67,9 +57,8 @@ namespace Robust.Shared.GameObjects
|
||||
EntityManager.EventBus.UnsubscribeEvent<T>(EventSource.Local, this);
|
||||
}
|
||||
|
||||
protected void SubscribeLocalEvent<TComp, TEvent>(
|
||||
ComponentEventHandler<TComp, TEvent> handler,
|
||||
Type[]? before=null, Type[]? after=null)
|
||||
|
||||
protected void SubscribeLocalEvent<TComp, TEvent>(ComponentEventHandler<TComp, TEvent> handler)
|
||||
where TComp : IComponent
|
||||
where TEvent : EntityEventArgs
|
||||
{
|
||||
|
||||
@@ -9,23 +9,11 @@ using Robust.Shared.Log;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
#if EXCEPTION_TOLERANCE
|
||||
using Robust.Shared.Exceptions;
|
||||
#endif
|
||||
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public class EntitySystemManager : IEntitySystemManager
|
||||
{
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _typeFactory = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
|
||||
#endif
|
||||
|
||||
private static readonly Histogram _tickUsageHistogram = Metrics.CreateHistogram("robust_entity_systems_update_usage",
|
||||
"Amount of time spent processing each entity system", new HistogramConfiguration
|
||||
{
|
||||
@@ -33,6 +21,12 @@ namespace Robust.Shared.GameObjects
|
||||
Buckets = Histogram.ExponentialBuckets(0.000_001, 1.5, 25)
|
||||
});
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _typeFactory = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
[ViewVariables]
|
||||
private readonly List<Type> _extraLoadedTypes = new();
|
||||
|
||||
@@ -166,15 +160,15 @@ namespace Robust.Shared.GameObjects
|
||||
Dictionary<Type, IEntitySystem>.ValueCollection systems,
|
||||
Dictionary<Type, IEntitySystem> supertypeSystems)
|
||||
{
|
||||
var allNodes = new List<TopologicalSort.GraphNode<IEntitySystem>>();
|
||||
var typeToNode = new Dictionary<Type, TopologicalSort.GraphNode<IEntitySystem>>();
|
||||
var allNodes = new List<GraphNode<IEntitySystem>>();
|
||||
var typeToNode = new Dictionary<Type, GraphNode<IEntitySystem>>();
|
||||
|
||||
foreach (var system in systems)
|
||||
{
|
||||
var node = new TopologicalSort.GraphNode<IEntitySystem>(system);
|
||||
var node = new GraphNode<IEntitySystem>(system);
|
||||
|
||||
typeToNode.Add(system.GetType(), node);
|
||||
allNodes.Add(node);
|
||||
typeToNode.Add(system.GetType(), node);
|
||||
}
|
||||
|
||||
foreach (var (type, system) in supertypeSystems)
|
||||
@@ -183,30 +177,52 @@ namespace Robust.Shared.GameObjects
|
||||
typeToNode[type] = node;
|
||||
}
|
||||
|
||||
foreach (var node in typeToNode.Values)
|
||||
foreach (var node in allNodes)
|
||||
{
|
||||
foreach (var after in node.Value.UpdatesAfter)
|
||||
foreach (var after in node.System.UpdatesAfter)
|
||||
{
|
||||
var system = typeToNode[after];
|
||||
|
||||
system.Dependant.Add(node);
|
||||
node.DependsOn.Add(system);
|
||||
}
|
||||
|
||||
foreach (var before in node.Value.UpdatesBefore)
|
||||
foreach (var before in node.System.UpdatesBefore)
|
||||
{
|
||||
var system = typeToNode[before];
|
||||
|
||||
node.Dependant.Add(system);
|
||||
system.DependsOn.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
var order = TopologicalSort.Sort(allNodes).ToArray();
|
||||
var order = TopologicalSort(allNodes).Select(p => p.System).ToArray();
|
||||
var frameUpdate = order.Where(p => NeedsFrameUpdate(p.GetType()));
|
||||
var update = order.Where(p => NeedsUpdate(p.GetType()));
|
||||
|
||||
return (frameUpdate, update);
|
||||
}
|
||||
|
||||
internal static IEnumerable<GraphNode<T>> TopologicalSort<T>(IEnumerable<GraphNode<T>> nodes)
|
||||
{
|
||||
var elems = nodes.ToDictionary(node => node,
|
||||
node => new HashSet<GraphNode<T>>(node.DependsOn));
|
||||
while (elems.Count > 0)
|
||||
{
|
||||
var elem =
|
||||
elems.FirstOrDefault(x => x.Value.Count == 0);
|
||||
if (elem.Key == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Found circular dependency when resolving entity system update dependency graph");
|
||||
}
|
||||
elems.Remove(elem.Key);
|
||||
foreach (var selem in elems)
|
||||
{
|
||||
selem.Value.Remove(elem.Key);
|
||||
}
|
||||
yield return elem.Key;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Type> GetBaseTypes(Type type) {
|
||||
if(type.BaseType == null) return type.GetInterfaces();
|
||||
|
||||
@@ -252,7 +268,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_runtimeLog.LogException(e, "entsys");
|
||||
Logger.ErrorS("entsys", e.ToString());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -277,7 +293,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_runtimeLog.LogException(e, "entsys");
|
||||
Logger.ErrorS("entsys", e.ToString());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -322,6 +338,18 @@ namespace Robust.Shared.GameObjects
|
||||
return mFrameUpdate!.DeclaringType != typeof(EntitySystem);
|
||||
}
|
||||
|
||||
[DebuggerDisplay("GraphNode: {" + nameof(System) + "}")]
|
||||
internal sealed class GraphNode<T>
|
||||
{
|
||||
public readonly T System;
|
||||
public readonly List<GraphNode<T>> DependsOn = new();
|
||||
|
||||
public GraphNode(T system)
|
||||
{
|
||||
System = system;
|
||||
}
|
||||
}
|
||||
|
||||
private struct UpdateReg
|
||||
{
|
||||
[ViewVariables] public IEntitySystem System;
|
||||
|
||||
@@ -252,7 +252,5 @@ namespace Robust.Shared.GameObjects
|
||||
/// Culls all components from the collection that are marked as deleted. This needs to be called often.
|
||||
/// </summary>
|
||||
void CullRemovedComponents();
|
||||
|
||||
IComponentFactory ComponentFactory { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,6 @@ namespace Robust.Shared.GameObjects
|
||||
event EventHandler<EntityUid>? EntityStarted;
|
||||
event EventHandler<EntityUid>? EntityDeleted;
|
||||
|
||||
IEntity CreateEntityUninitialized(string? prototypeName, EntityUid? euid);
|
||||
|
||||
IEntity CreateEntityUninitialized(string? prototypeName);
|
||||
|
||||
IEntity CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates);
|
||||
|
||||
@@ -12,23 +12,13 @@ namespace Robust.Shared.GameObjects
|
||||
void MapInit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed on an entity when the map is initialized.
|
||||
/// </summary>
|
||||
public class MapInitEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
public static class MapInitExt
|
||||
{
|
||||
private static readonly MapInitEvent MapInit = new MapInitEvent();
|
||||
|
||||
public static void RunMapInit(this IEntity entity)
|
||||
{
|
||||
DebugTools.Assert(entity.LifeStage == EntityLifeStage.Initialized);
|
||||
entity.LifeStage = EntityLifeStage.MapInitialized;
|
||||
|
||||
entity.EntityManager.EventBus.RaiseLocalEvent(entity.Uid, MapInit, false);
|
||||
foreach (var init in entity.GetAllComponents<IMapInit>())
|
||||
{
|
||||
init.MapInit();
|
||||
|
||||
@@ -1,61 +1,33 @@
|
||||
using System.Linq;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public sealed class CollisionWakeSystem : EntitySystem
|
||||
public class CollisionWakeSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<CollisionWakeComponent, EntityInitializedMessage>(HandleInitialize);
|
||||
|
||||
SubscribeLocalEvent<CollisionWakeComponent, PhysicsWakeMessage>(HandleWake);
|
||||
SubscribeLocalEvent<CollisionWakeComponent, PhysicsSleepMessage>(HandleSleep);
|
||||
SubscribeLocalEvent<PhysicsWakeMessage>(HandleWake);
|
||||
SubscribeLocalEvent<PhysicsSleepMessage>(HandleSleep);
|
||||
SubscribeLocalEvent<CollisionWakeComponent, CollisionWakeStateMessage>(HandleCollisionWakeState);
|
||||
|
||||
SubscribeLocalEvent<CollisionWakeComponent, JointAddedEvent>(HandleJointAdd);
|
||||
SubscribeLocalEvent<CollisionWakeComponent, JointRemovedEvent>(HandleJointRemove);
|
||||
}
|
||||
|
||||
private void HandleInitialize(EntityUid uid, CollisionWakeComponent component, EntityInitializedMessage args)
|
||||
private void HandleWake(PhysicsWakeMessage message)
|
||||
{
|
||||
component.RaiseStateChange();
|
||||
if (!message.Body.Owner.TryGetComponent<CollisionWakeComponent>(out var comp) || !comp.Enabled) return;
|
||||
message.Body.CanCollide = true;
|
||||
}
|
||||
|
||||
private void HandleJointRemove(EntityUid uid, CollisionWakeComponent component, JointRemovedEvent args)
|
||||
private void HandleSleep(PhysicsSleepMessage message)
|
||||
{
|
||||
if (component.Owner.TryGetComponent(out PhysicsComponent? body) && body.Joints.Any()) return;
|
||||
|
||||
// Force an update
|
||||
component.RaiseStateChange();
|
||||
}
|
||||
|
||||
private void HandleJointAdd(EntityUid uid, CollisionWakeComponent component, JointAddedEvent args)
|
||||
{
|
||||
if (!ComponentManager.TryGetComponent(uid, out PhysicsComponent body)) return;
|
||||
body.CanCollide = true;
|
||||
}
|
||||
|
||||
private void HandleWake(EntityUid uid, CollisionWakeComponent component, PhysicsWakeMessage args)
|
||||
{
|
||||
if (!component.Enabled) return;
|
||||
|
||||
args.Body.CanCollide = true;
|
||||
}
|
||||
|
||||
private void HandleSleep(EntityUid uid, CollisionWakeComponent component, PhysicsSleepMessage args)
|
||||
{
|
||||
if (!component.Enabled) return;
|
||||
|
||||
args.Body.CanCollide = false;
|
||||
if (!message.Body.Owner.TryGetComponent<CollisionWakeComponent>(out var comp) || !comp.Enabled) return;
|
||||
message.Body.CanCollide = false;
|
||||
}
|
||||
|
||||
private void HandleCollisionWakeState(EntityUid uid, CollisionWakeComponent component, CollisionWakeStateMessage args)
|
||||
{
|
||||
if (!ComponentManager.TryGetComponent<PhysicsComponent>(uid, out var body)) return;
|
||||
|
||||
body.CanCollide = !component.Enabled || body.Awake || body.Joints.Any();
|
||||
if(ComponentManager.TryGetComponent<IPhysBody>(uid, out var body))
|
||||
body.CanCollide = !component.Enabled || body.Awake;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
if (entity.TryGetComponent<IPhysBody>(out var component))
|
||||
{
|
||||
return GetEntitiesIntersecting(entity.Transform.MapID, component.GetWorldAABB(), approximate);
|
||||
return GetEntitiesIntersecting(entity.Transform.MapID, component.GetWorldAABB(_mapManager), approximate);
|
||||
}
|
||||
|
||||
return GetEntitiesIntersecting(entity.Transform.Coordinates, approximate);
|
||||
@@ -295,7 +295,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
if (entity.TryGetComponent(out IPhysBody? component))
|
||||
{
|
||||
if (component.GetWorldAABB().Contains(mapPosition))
|
||||
if (component.GetWorldAABB(_mapManager).Contains(mapPosition))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -341,7 +341,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
if (entity.TryGetComponent<IPhysBody>(out var component))
|
||||
{
|
||||
return GetEntitiesInRange(entity.Transform.MapID, component.GetWorldAABB(), range, approximate);
|
||||
return GetEntitiesInRange(entity.Transform.MapID, component.GetWorldAABB(_mapManager), range, approximate);
|
||||
}
|
||||
|
||||
return GetEntitiesInRange(entity.Transform.Coordinates, range, approximate);
|
||||
@@ -476,11 +476,10 @@ namespace Robust.Shared.GameObjects
|
||||
if (ent.Deleted)
|
||||
return new Box2(0, 0, 0, 0);
|
||||
|
||||
var pos = ent.Transform.WorldPosition;
|
||||
|
||||
if (ent.TryGetComponent(out IPhysBody? collider))
|
||||
return collider.GetWorldAABB(pos);
|
||||
return collider.GetWorldAABB(_mapManager);
|
||||
|
||||
var pos = ent.Transform.WorldPosition;
|
||||
return new Box2(pos, pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
#if DEBUG
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class RequestGridTileLookupMessage : EntityEventArgs
|
||||
{
|
||||
public GridId GridId;
|
||||
public Vector2i Indices;
|
||||
|
||||
public RequestGridTileLookupMessage(GridId gridId, Vector2i indices)
|
||||
{
|
||||
GridId = gridId;
|
||||
Indices = indices;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SendGridTileLookupMessage : EntityEventArgs
|
||||
{
|
||||
public GridId GridId;
|
||||
public Vector2i Indices;
|
||||
|
||||
public List<EntityUid> Entities { get; }
|
||||
|
||||
public SendGridTileLookupMessage(GridId gridId, Vector2i indices, List<EntityUid> entities)
|
||||
{
|
||||
Entities = entities;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -14,7 +14,6 @@ using Robust.Shared.Physics.Controllers;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using DependencyAttribute = Robust.Shared.IoC.DependencyAttribute;
|
||||
using Logger = Robust.Shared.Log.Logger;
|
||||
|
||||
@@ -108,8 +107,6 @@ namespace Robust.Shared.GameObjects
|
||||
SubscribeLocalEvent<EntRemovedFromContainerMessage>(HandleContainerRemoved);
|
||||
BuildControllers();
|
||||
Logger.DebugS("physics", $"Found {_controllers.Count} physics controllers.");
|
||||
|
||||
IoCManager.Resolve<IIslandManager>().Initialize();
|
||||
}
|
||||
|
||||
|
||||
@@ -117,24 +114,45 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
var reflectionManager = IoCManager.Resolve<IReflectionManager>();
|
||||
var typeFactory = IoCManager.Resolve<IDynamicTypeFactory>();
|
||||
var instantiated = new List<VirtualController>();
|
||||
var allControllerTypes = new List<Type>();
|
||||
|
||||
foreach (var type in reflectionManager.GetAllChildren(typeof(VirtualController)))
|
||||
{
|
||||
if (type.IsAbstract)
|
||||
continue;
|
||||
|
||||
instantiated.Add(typeFactory.CreateInstance<VirtualController>(type));
|
||||
if (type.IsAbstract) continue;
|
||||
allControllerTypes.Add(type);
|
||||
}
|
||||
|
||||
var nodes = TopologicalSort.FromBeforeAfter(
|
||||
instantiated,
|
||||
c => c.GetType(),
|
||||
c => c,
|
||||
c => c.UpdatesBefore,
|
||||
c => c.UpdatesAfter);
|
||||
var instantiated = new Dictionary<Type, VirtualController>();
|
||||
|
||||
_controllers = TopologicalSort.Sort(nodes).ToList();
|
||||
foreach (var type in allControllerTypes)
|
||||
{
|
||||
instantiated.Add(type, (VirtualController) typeFactory.CreateInstance(type));
|
||||
}
|
||||
|
||||
// Build dependency graph, copied from EntitySystemManager *COUGH
|
||||
|
||||
var nodes = new Dictionary<Type, EntitySystemManager.GraphNode<VirtualController>>();
|
||||
|
||||
foreach (var (_, controller) in instantiated)
|
||||
{
|
||||
var node = new EntitySystemManager.GraphNode<VirtualController>(controller);
|
||||
nodes[controller.GetType()] = node;
|
||||
}
|
||||
|
||||
foreach (var (type, node) in nodes)
|
||||
{
|
||||
foreach (var before in instantiated[type].UpdatesBefore)
|
||||
{
|
||||
nodes[before].DependsOn.Add(node);
|
||||
}
|
||||
|
||||
foreach (var after in instantiated[type].UpdatesAfter)
|
||||
{
|
||||
node.DependsOn.Add(nodes[after]);
|
||||
}
|
||||
}
|
||||
|
||||
_controllers = GameObjects.EntitySystemManager.TopologicalSort(nodes.Values).Select(c => c.System).ToList();
|
||||
|
||||
foreach (var controller in _controllers)
|
||||
{
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Shared.GameStates
|
||||
{
|
||||
public class ComponentHandleState : EntityEventArgs
|
||||
{
|
||||
public ComponentState? Current { get; }
|
||||
public ComponentState? Next { get; }
|
||||
|
||||
public ComponentHandleState(ComponentState? current, ComponentState? next)
|
||||
{
|
||||
Current = current;
|
||||
Next = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Input.Binding
|
||||
{
|
||||
@@ -14,8 +13,8 @@ namespace Robust.Shared.Input.Binding
|
||||
// handlers in the order they should be resolved for the given key function.
|
||||
// internally we use a graph to construct this but we render it down to a flattened
|
||||
// list so we don't need to do any graph traversal at query time
|
||||
private Dictionary<BoundKeyFunction, List<InputCmdHandler>> _bindingsForKey = new();
|
||||
private bool _graphDirty = false;
|
||||
private Dictionary<BoundKeyFunction, List<InputCmdHandler>> _bindingsForKey =
|
||||
new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register<TOwner>(CommandBinds commandBinds)
|
||||
@@ -42,15 +41,12 @@ namespace Robust.Shared.Input.Binding
|
||||
_bindings.Add(new TypedCommandBind(owner, binding));
|
||||
}
|
||||
|
||||
_graphDirty = true;
|
||||
RebuildGraph();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<InputCmdHandler> GetHandlers(BoundKeyFunction function)
|
||||
{
|
||||
if (_graphDirty)
|
||||
RebuildGraph();
|
||||
|
||||
if (_bindingsForKey.TryGetValue(function, out var handlers))
|
||||
{
|
||||
return handlers;
|
||||
@@ -62,8 +58,7 @@ namespace Robust.Shared.Input.Binding
|
||||
public void Unregister(Type owner)
|
||||
{
|
||||
_bindings.RemoveAll(binding => binding.ForType == owner);
|
||||
|
||||
_graphDirty = true;
|
||||
RebuildGraph();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -72,7 +67,7 @@ namespace Robust.Shared.Input.Binding
|
||||
Unregister(typeof(TOwner));
|
||||
}
|
||||
|
||||
internal void RebuildGraph()
|
||||
private void RebuildGraph()
|
||||
{
|
||||
_bindingsForKey.Clear();
|
||||
|
||||
@@ -82,7 +77,6 @@ namespace Robust.Shared.Input.Binding
|
||||
|
||||
}
|
||||
|
||||
_graphDirty = false;
|
||||
}
|
||||
|
||||
private Dictionary<BoundKeyFunction, List<TypedCommandBind>> FunctionToBindings()
|
||||
@@ -111,16 +105,16 @@ namespace Robust.Shared.Input.Binding
|
||||
//TODO: Probably could be optimized if needed! Generally shouldn't be a big issue since there is a relatively
|
||||
// tiny amount of bindings
|
||||
|
||||
List<TopologicalSort.GraphNode<TypedCommandBind>> allNodes = new();
|
||||
Dictionary<Type,List<TopologicalSort.GraphNode<TypedCommandBind>>> typeToNode = new();
|
||||
List<GraphNode> allNodes = new();
|
||||
Dictionary<Type,List<GraphNode>> typeToNode = new();
|
||||
// build the dict for quick lookup on type
|
||||
foreach (var binding in bindingsForFunction)
|
||||
{
|
||||
if (!typeToNode.ContainsKey(binding.ForType))
|
||||
{
|
||||
typeToNode[binding.ForType] = new List<TopologicalSort.GraphNode<TypedCommandBind>>();
|
||||
typeToNode[binding.ForType] = new List<GraphNode>();
|
||||
}
|
||||
var newNode = new TopologicalSort.GraphNode<TypedCommandBind>(binding);
|
||||
var newNode = new GraphNode(binding);
|
||||
typeToNode[binding.ForType].Add(newNode);
|
||||
allNodes.Add(newNode);
|
||||
}
|
||||
@@ -128,7 +122,7 @@ namespace Robust.Shared.Input.Binding
|
||||
//add the graph edges
|
||||
foreach (var curBinding in allNodes)
|
||||
{
|
||||
foreach (var afterType in curBinding.Value.CommandBind.After)
|
||||
foreach (var afterType in curBinding.TypedCommandBind.CommandBind.After)
|
||||
{
|
||||
// curBinding should always fire after bindings associated with this afterType, i.e.
|
||||
// this binding DEPENDS ON afterTypes' bindings
|
||||
@@ -136,12 +130,11 @@ namespace Robust.Shared.Input.Binding
|
||||
{
|
||||
foreach (var afterBinding in afterBindings)
|
||||
{
|
||||
afterBinding.Dependant.Add(curBinding);
|
||||
curBinding.DependsOn.Add(afterBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var beforeType in curBinding.Value.CommandBind.Before)
|
||||
foreach (var beforeType in curBinding.TypedCommandBind.CommandBind.Before)
|
||||
{
|
||||
// curBinding should always fire before bindings associated with this beforeType, i.e.
|
||||
// beforeTypes' bindings DEPENDS ON this binding
|
||||
@@ -149,7 +142,7 @@ namespace Robust.Shared.Input.Binding
|
||||
{
|
||||
foreach (var beforeBinding in beforeBindings)
|
||||
{
|
||||
curBinding.Dependant.Add(beforeBinding);
|
||||
beforeBinding.DependsOn.Add(curBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,7 +151,54 @@ namespace Robust.Shared.Input.Binding
|
||||
//TODO: Log graph structure for debugging
|
||||
|
||||
//use toposort to build the final result
|
||||
return TopologicalSort.Sort(allNodes).Select(c => c.CommandBind.Handler).ToList();
|
||||
var topoSorted = TopologicalSort(allNodes, function);
|
||||
List<InputCmdHandler> result = new();
|
||||
|
||||
foreach (var node in topoSorted)
|
||||
{
|
||||
result.Add(node.TypedCommandBind.CommandBind.Handler);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//Adapted from https://stackoverflow.com/a/24058279
|
||||
private static IEnumerable<GraphNode> TopologicalSort(IEnumerable<GraphNode> nodes, BoundKeyFunction function)
|
||||
{
|
||||
var elems = nodes.ToDictionary(node => node,
|
||||
node => new HashSet<GraphNode>(node.DependsOn));
|
||||
while (elems.Count > 0)
|
||||
{
|
||||
var elem =
|
||||
elems.FirstOrDefault(x => x.Value.Count == 0);
|
||||
if (elem.Key == null)
|
||||
{
|
||||
throw new InvalidOperationException("Found circular dependency when resolving" +
|
||||
$" command binding handler order for key function {function.FunctionName}." +
|
||||
$" Please check the systems which register bindings for" +
|
||||
$" this function and eliminate the circular dependency.");
|
||||
}
|
||||
elems.Remove(elem.Key);
|
||||
foreach (var selem in elems)
|
||||
{
|
||||
selem.Value.Remove(elem.Key);
|
||||
}
|
||||
yield return elem.Key;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// node in our temporary dependency graph
|
||||
/// </summary>
|
||||
private class GraphNode
|
||||
{
|
||||
public List<GraphNode> DependsOn = new();
|
||||
public readonly TypedCommandBind TypedCommandBind;
|
||||
|
||||
public GraphNode(TypedCommandBind typedCommandBind)
|
||||
{
|
||||
TypedCommandBind = typedCommandBind;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -33,8 +33,6 @@ namespace Robust.Shared.Input
|
||||
/// </summary>
|
||||
/// <param name="function">Function to remove.</param>
|
||||
void RemoveFunction(BoundKeyFunction function);
|
||||
|
||||
string Name { get; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -42,25 +40,20 @@ namespace Robust.Shared.Input
|
||||
{
|
||||
private readonly List<BoundKeyFunction> _commands = new();
|
||||
private readonly IInputCmdContext? _parent;
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="InputCmdContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent context.</param>
|
||||
internal InputCmdContext(IInputCmdContext? parent, string name)
|
||||
internal InputCmdContext(IInputCmdContext? parent)
|
||||
{
|
||||
_parent = parent;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a instance of <see cref="InputCmdContext"/> with no parent.
|
||||
/// </summary>
|
||||
internal InputCmdContext(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
internal InputCmdContext() { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddFunction(BoundKeyFunction function)
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace Robust.Shared.Input
|
||||
{
|
||||
var icc = _deferredContextSwitch;
|
||||
_deferredContextSwitch = null;
|
||||
_setActiveContextImmediately( icc);
|
||||
_setActiveContextImmediately(icc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,7 +132,7 @@ namespace Robust.Shared.Input
|
||||
/// </summary>
|
||||
public InputContextContainer()
|
||||
{
|
||||
_contexts.Add(DefaultContextName, new InputCmdContext(DefaultContextName));
|
||||
_contexts.Add(DefaultContextName, new InputCmdContext());
|
||||
SetActiveContext(DefaultContextName);
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace Robust.Shared.Input
|
||||
if (_contexts.ContainsKey(uniqueName))
|
||||
throw new ArgumentException($"Context with name {uniqueName} already exists.", nameof(uniqueName));
|
||||
|
||||
var newContext = new InputCmdContext(parentContext, uniqueName);
|
||||
var newContext = new InputCmdContext(parentContext);
|
||||
_contexts.Add(uniqueName, newContext);
|
||||
return newContext;
|
||||
}
|
||||
@@ -168,7 +168,7 @@ namespace Robust.Shared.Input
|
||||
if (parent == null)
|
||||
throw new ArgumentNullException(nameof(parent));
|
||||
|
||||
var newContext = new InputCmdContext(parent, uniqueName);
|
||||
var newContext = new InputCmdContext(parent);
|
||||
_contexts.Add(uniqueName, newContext);
|
||||
return newContext;
|
||||
}
|
||||
|
||||
@@ -104,23 +104,6 @@ namespace Robust.Shared.IoC
|
||||
_container.Value!.Register<TInterface, TImplementation>(overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register an implementation, to make it accessible to <see cref="Resolve{T}"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type that will be resolvable and implementation.</typeparam>
|
||||
/// <param name="overwrite">
|
||||
/// If true, do not throw an <see cref="InvalidOperationException"/> if an interface is already registered,
|
||||
/// replace the current implementation instead.
|
||||
/// </param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if <paramref name="overwrite"/> is false and <typeparamref name="T"/> has been registered before,
|
||||
/// or if an already instantiated interface (by <see cref="BuildGraph"/>) is attempting to be overwritten.
|
||||
/// </exception>
|
||||
public static void Register<T>(bool overwrite = false) where T : class
|
||||
{
|
||||
Register<T, T>(overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an interface to an implementation, to make it accessible to <see cref="Resolve{T}"/>
|
||||
/// <see cref="BuildGraph"/> MUST be called after this method to make the new interface available.
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Robust.Shared.Localization
|
||||
/// Does not log a warning if the message does not exist.
|
||||
/// Does however log errors if any occur while formatting.
|
||||
/// </remarks>
|
||||
bool TryGetString(string messageId, [NotNullWhen(true)] out string? value, params (string, object)[] keyArgs);
|
||||
bool TryGetString(string messageId, [NotNullWhen(true)] out string? value, params (string, object)[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Default culture used by other methods when no culture is explicitly specified.
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Robust.Shared.Localization
|
||||
private static ILocalizationManager LocalizationManager => IoCManager.Resolve<ILocalizationManager>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a language appropriate string represented by the supplied messageId.
|
||||
/// Gets a language approrpiate string represented by the supplied messageId.
|
||||
/// </summary>
|
||||
/// <param name="messageId">Unique Identifier for a translated message.</param>
|
||||
/// <returns>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Fluent.Net;
|
||||
using JetBrains.Annotations;
|
||||
using Linguini.Bundle;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Shared.Localization
|
||||
@@ -25,13 +25,13 @@ namespace Robust.Shared.Localization
|
||||
[PublicAPI]
|
||||
public readonly struct LocContext
|
||||
{
|
||||
public CultureInfo Culture => Bundle.Culture;
|
||||
public CultureInfo Culture => Context.Culture;
|
||||
|
||||
internal readonly FluentBundle Bundle;
|
||||
internal readonly MessageContext Context;
|
||||
|
||||
internal LocContext(FluentBundle bundle)
|
||||
internal LocContext(MessageContext ctx)
|
||||
{
|
||||
Bundle = bundle;
|
||||
Context = ctx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Robust.Shared.Localization
|
||||
/// <summary>
|
||||
/// Checks if this value matches a string in a select expression.
|
||||
/// </summary>
|
||||
bool Matches(LocContext bundle, string matchValue)
|
||||
bool Matches(LocContext ctx, string matchValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ namespace Robust.Shared.Localization
|
||||
public abstract string Format(LocContext ctx);
|
||||
|
||||
/*
|
||||
public virtual bool Matches(LocContext bundle, string matchValue)
|
||||
public virtual bool Matches(LocContext ctx, string matchValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -150,7 +150,6 @@ namespace Robust.Shared.Localization
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Stores an "invalid" string value. Produced by e.g. unresolved variable references.
|
||||
/// </summary>
|
||||
@@ -174,13 +173,13 @@ namespace Robust.Shared.Localization
|
||||
|
||||
/*public sealed record LocValueBool(bool Value) : LocValue<bool>(Value)
|
||||
{
|
||||
public override string Format(LocContext bundle)
|
||||
public override string Format(LocContext ctx)
|
||||
{
|
||||
return Value.ToString(bundle.Culture);
|
||||
return Value.ToString(ctx.Culture);
|
||||
}
|
||||
|
||||
/*
|
||||
public override bool Matches(LocContext bundle, string matchValue)
|
||||
public override bool Matches(LocContext ctx, string matchValue)
|
||||
{
|
||||
var word = Value ? "true" : "false";
|
||||
return word.Equals(matchValue, StringComparison.InvariantCultureIgnoreCase);
|
||||
@@ -190,14 +189,14 @@ namespace Robust.Shared.Localization
|
||||
|
||||
public sealed record LocValueEnum(Enum Value) : LocValue<Enum>(Value)
|
||||
{
|
||||
public override string Format(LocContext bundle)
|
||||
public override string Format(LocContext ctx)
|
||||
{
|
||||
return Value.ToString().ToLowerInvariant();
|
||||
}
|
||||
|
||||
/*public override bool Matches(LocContext bundle, string matchValue)
|
||||
/*public override bool Matches(LocContext ctx, string matchValue)
|
||||
{
|
||||
return matchValue.Equals(Value.ToString(), StringComparison.InvariantCultureIgnoreCase);
|
||||
}#1#
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Linguini.Bundle.Errors;
|
||||
using Linguini.Syntax.Parser.Error;
|
||||
|
||||
namespace Robust.Shared.Localization
|
||||
{
|
||||
internal static class LocHelper
|
||||
{
|
||||
public static string FormatCompileErrors(this ParseError self, ReadOnlyMemory<char> resource)
|
||||
{
|
||||
ErrorSpan span = new(self.Row, self.Slice!.Value.Start.Value, self.Slice.Value.End.Value,
|
||||
self.Position.Start.Value, self.Position.End.Value);
|
||||
return FormatErrors(self.Message, span, resource);
|
||||
}
|
||||
|
||||
private static string FormatErrors(string message, ErrorSpan span, ReadOnlyMemory<char> resource)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var row = $" {span.Row} ";
|
||||
var errContext = resource.Slice(span.StartSpan, span.EndSpan - span.StartSpan).ToString();
|
||||
sb.Append(row).Append('|')
|
||||
.AppendLine(errContext);
|
||||
sb.Append(' ', row.Length).Append('|')
|
||||
.Append(' ', span.StartMark - span.StartSpan - 1).Append('^', span.EndMark - span.StartMark)
|
||||
.AppendLine($" {message}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Linguini.Bundle.Errors;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Localization;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -42,7 +41,7 @@ namespace Robust.Shared.Localization
|
||||
// Flush caches conservatively on prototype/localization changes.
|
||||
private void OnPrototypesReloaded(PrototypesReloadedEventArgs args)
|
||||
{
|
||||
if (!args.ByType.ContainsKey(typeof(EntityPrototype)))
|
||||
if (!args.ByType.TryGetValue(typeof(EntityPrototype), out var changeSet))
|
||||
return;
|
||||
|
||||
FlushEntityCache();
|
||||
@@ -53,58 +52,48 @@ namespace Robust.Shared.Localization
|
||||
string? name = null;
|
||||
string? desc = null;
|
||||
string? suffix = null;
|
||||
Dictionary<string, string>? attributes = null;
|
||||
Dictionary<string, string>? attribs = null;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var prototype = _prototype.Index<EntityPrototype>(prototypeId);
|
||||
var locId = prototype.CustomLocalizationID ?? $"ent-{prototypeId}";
|
||||
|
||||
if (TryGetMessage(locId, out var bundle, out var msg))
|
||||
if (TryGetMessage(locId, out var ctx, out var msg))
|
||||
{
|
||||
// Localization override exists.
|
||||
var msgAttrs = msg.Attributes;
|
||||
var mAttribs = msg.Attributes;
|
||||
|
||||
if (name == null && msg.Value != null)
|
||||
{
|
||||
// Only set name if the value isn't empty.
|
||||
// So that you can override *only* a desc/suffix.
|
||||
name = bundle.FormatPattern(msg.Value, null, out var fmtErr);
|
||||
WriteWarningForErrs(fmtErr, locId);
|
||||
name = ctx.Format(msg.Value);
|
||||
}
|
||||
|
||||
if (msgAttrs.Count != 0)
|
||||
if (mAttribs != null && mAttribs.Count != 0)
|
||||
{
|
||||
foreach (var (attrId, pattern) in msg.Attributes)
|
||||
if (desc == null && mAttribs.TryGetValue("desc", out var mDesc))
|
||||
{
|
||||
var attrib = attrId.ToString();
|
||||
if (attrib.Equals("desc")
|
||||
|| attrib.Equals("suffix"))
|
||||
desc = ctx.Format(mDesc);
|
||||
}
|
||||
|
||||
if (suffix == null && mAttribs.TryGetValue("suffix", out var mSuffix))
|
||||
{
|
||||
suffix = ctx.Format(mSuffix);
|
||||
}
|
||||
|
||||
foreach (var (attrib, value) in msg.Attributes)
|
||||
{
|
||||
if (attrib is "desc" or "suffix")
|
||||
continue;
|
||||
|
||||
attributes ??= new Dictionary<string, string>();
|
||||
if (!attributes.ContainsKey(attrib))
|
||||
attribs ??= new Dictionary<string, string>();
|
||||
if (!attribs.ContainsKey(attrib))
|
||||
{
|
||||
var value = bundle.FormatPattern(pattern, null, out var errors);
|
||||
WriteWarningForErrs(errors, locId);
|
||||
attributes[attrib] = value;
|
||||
attribs[attrib] = ctx.Format(value);
|
||||
}
|
||||
}
|
||||
|
||||
var allErrors = new List<FluentError>();
|
||||
if (desc == null
|
||||
&& bundle.TryGetMsg(locId, "desc", null, out var err1, out desc))
|
||||
{
|
||||
allErrors.AddRange(err1);
|
||||
}
|
||||
|
||||
if (suffix == null
|
||||
&& bundle.TryGetMsg(locId, "suffix", null, out var err, out suffix))
|
||||
{
|
||||
allErrors.AddRange(err);
|
||||
}
|
||||
|
||||
WriteWarningForErrs(allErrors, locId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,10 +105,10 @@ namespace Robust.Shared.Localization
|
||||
{
|
||||
foreach (var (attrib, value) in prototype.LocProperties)
|
||||
{
|
||||
attributes ??= new Dictionary<string, string>();
|
||||
if (!attributes.ContainsKey(attrib))
|
||||
attribs ??= new Dictionary<string, string>();
|
||||
if (!attribs.ContainsKey(attrib))
|
||||
{
|
||||
attributes[attrib] = value;
|
||||
attribs[attrib] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,10 +123,9 @@ namespace Robust.Shared.Localization
|
||||
name ?? "",
|
||||
desc ?? "",
|
||||
suffix,
|
||||
attributes?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty);
|
||||
attribs?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty);
|
||||
}
|
||||
|
||||
|
||||
public EntityLocData GetEntityData(string prototypeId)
|
||||
{
|
||||
return _entityCache.GetOrAdd(prototypeId, (id, t) => t.CalcEntityLoc(id), this);
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Linguini.Bundle;
|
||||
using Linguini.Bundle.Types;
|
||||
using Linguini.Shared.Types.Bundle;
|
||||
using Fluent.Net;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Localization;
|
||||
@@ -13,51 +9,25 @@ namespace Robust.Shared.Localization
|
||||
{
|
||||
internal sealed partial class LocalizationManager
|
||||
{
|
||||
private void AddBuiltInFunctions(FluentBundle bundle)
|
||||
private void AddBuiltinFunctions(MessageContext context)
|
||||
{
|
||||
// Grammatical gender / pronouns
|
||||
AddCtxFunction(bundle, "GENDER", FuncGender);
|
||||
AddCtxFunction(bundle, "SUBJECT", FuncSubject);
|
||||
AddCtxFunction(bundle, "OBJECT", FuncObject);
|
||||
AddCtxFunction(bundle, "POSS-ADJ", FuncPossAdj);
|
||||
AddCtxFunction(bundle, "POSS-PRONOUN", FuncPossPronoun);
|
||||
AddCtxFunction(bundle, "REFLEXIVE", FuncReflexive);
|
||||
|
||||
// Conjugation
|
||||
AddCtxFunction(bundle, "CONJUGATE-BE", FuncConjugateBe);
|
||||
AddCtxFunction(bundle, "CONJUGATE-HAVE", FuncConjugateHave);
|
||||
// Grammatical gender
|
||||
AddCtxFunction(context, "GENDER", FuncGender);
|
||||
|
||||
// Proper nouns
|
||||
AddCtxFunction(bundle, "PROPER", FuncProper);
|
||||
AddCtxFunction(bundle, "THE", FuncThe);
|
||||
AddCtxFunction(context, "PROPER", FuncProper);
|
||||
|
||||
// Misc
|
||||
AddCtxFunction(bundle, "ATTRIB", args => FuncAttrib(bundle, args));
|
||||
AddCtxFunction(bundle, "CAPITALIZE", FuncCapitalize);
|
||||
// Misc Attribs
|
||||
AddCtxFunction(context, "ATTRIB", args => FuncAttrib(context, args));
|
||||
|
||||
AddCtxFunction(context, "THE", FuncThe);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of the entity passed in, prepended with "the" if it is not a proper noun.
|
||||
/// </summary>
|
||||
private ILocValue FuncThe(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-the", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string passed in, with the first letter capitalized.
|
||||
/// </summary>
|
||||
private ILocValue FuncCapitalize(LocArgs args)
|
||||
{
|
||||
var input = args.Args[0].Format(new LocContext());
|
||||
if (!String.IsNullOrEmpty(input))
|
||||
return new LocValueString(input[0].ToString().ToUpper() + input.Substring(1));
|
||||
else return new LocValueString("");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the gender of the entity passed in; either Male, Female, Neuter or Epicene.
|
||||
/// </summary>
|
||||
private ILocValue FuncGender(LocArgs args)
|
||||
{
|
||||
if (args.Args.Count < 1) return new LocValueString(nameof(Gender.Neuter));
|
||||
@@ -65,7 +35,7 @@ namespace Robust.Shared.Localization
|
||||
ILocValue entity0 = args.Args[0];
|
||||
if (entity0.Value != null)
|
||||
{
|
||||
IEntity entity = (IEntity)entity0.Value;
|
||||
IEntity entity = (IEntity) entity0.Value;
|
||||
|
||||
if (entity.TryGetComponent<GrammarComponent>(out var grammar) && grammar.Gender.HasValue)
|
||||
{
|
||||
@@ -81,72 +51,16 @@ namespace Robust.Shared.Localization
|
||||
return new LocValueString(nameof(Gender.Neuter));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective subject pronoun (he, she, they, it) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncSubject(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-subject-pronoun", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective object pronoun (him, her, them, it) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncObject(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-object-pronoun", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective possessive adjective (his, her, their, its) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncPossAdj(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-possessive-adjective", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective possessive pronoun (his, hers, theirs, its) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncPossPronoun(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-possessive-pronoun", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective reflexive pronoun (himself, herself, themselves, itself) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncReflexive(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-reflexive-pronoun", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective conjugated form of "to be" (is for male/female/neuter, are for epicene) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncConjugateBe(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-conjugate-be", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective conjugated form of "to have" (has for male/female/neuter, have for epicene) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncConjugateHave(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-conjugate-have", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
private ILocValue FuncAttrib(FluentBundle bundle, LocArgs args)
|
||||
private ILocValue FuncAttrib(MessageContext context, LocArgs args)
|
||||
{
|
||||
if (args.Args.Count < 2) return new LocValueString("other");
|
||||
|
||||
ILocValue entity0 = args.Args[0];
|
||||
if (entity0.Value != null)
|
||||
{
|
||||
IEntity entity = (IEntity)entity0.Value;
|
||||
IEntity entity = (IEntity) entity0.Value;
|
||||
ILocValue attrib0 = args.Args[1];
|
||||
if (TryGetEntityLocAttrib(entity, attrib0.Format(new LocContext(bundle)), out var attrib))
|
||||
if (TryGetEntityLocAttrib(entity, attrib0.Format(new LocContext(context)), out var attrib))
|
||||
{
|
||||
return new LocValueString(attrib);
|
||||
}
|
||||
@@ -155,9 +69,6 @@ namespace Robust.Shared.Localization
|
||||
return new LocValueString("other");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the passed in entity's name is proper or not.
|
||||
/// </summary>
|
||||
private ILocValue FuncProper(LocArgs args)
|
||||
{
|
||||
if (args.Args.Count < 1) return new LocValueString("false");
|
||||
@@ -165,7 +76,7 @@ namespace Robust.Shared.Localization
|
||||
ILocValue entity0 = args.Args[0];
|
||||
if (entity0.Value != null)
|
||||
{
|
||||
IEntity entity = (IEntity)entity0.Value;
|
||||
IEntity entity = (IEntity) entity0.Value;
|
||||
|
||||
if (entity.TryGetComponent<GrammarComponent>(out var grammar) && grammar.ProperNoun.HasValue)
|
||||
{
|
||||
@@ -181,108 +92,39 @@ namespace Robust.Shared.Localization
|
||||
return new LocValueString("false");
|
||||
}
|
||||
|
||||
|
||||
private void AddCtxFunction(FluentBundle ctx, string name, LocFunction function)
|
||||
private void AddCtxFunction(MessageContext ctx, string name, LocFunction function)
|
||||
{
|
||||
ctx.AddFunction(name, (args, options)
|
||||
=> CallFunction(function, args, options), out _, InsertBehavior.Overriding);
|
||||
}
|
||||
|
||||
private IFluentType CallFunction(LocFunction function,
|
||||
IList<IFluentType> positionalArgs, IDictionary<string, IFluentType> namedArgs)
|
||||
{
|
||||
var args = new ILocValue[positionalArgs.Count];
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
args[i] = positionalArgs[i].ToLocValue();
|
||||
}
|
||||
|
||||
var options = new Dictionary<string, ILocValue>(namedArgs.Count);
|
||||
foreach (var (k, v) in namedArgs)
|
||||
{
|
||||
options.Add(k, v.ToLocValue());
|
||||
}
|
||||
|
||||
var argStruct = new LocArgs(args, options);
|
||||
return function.Invoke(argStruct).FluentFromVal();
|
||||
ctx.Functions.Add(name, (args, options) => CallFunction(function, args, options));
|
||||
}
|
||||
|
||||
public void AddFunction(CultureInfo culture, string name, LocFunction function)
|
||||
{
|
||||
var bundle = _contexts[culture];
|
||||
var context = _contexts[culture];
|
||||
|
||||
bundle.AddFunction(name, (args, options)
|
||||
=> CallFunction(function, args, options), out _, InsertBehavior.Overriding);
|
||||
context.Functions.Add(name, (args, options) => CallFunction(function, args, options));
|
||||
}
|
||||
|
||||
private FluentType CallFunction(
|
||||
LocFunction function,
|
||||
IList<object> fluentArgs, IDictionary<string, object> fluentOptions)
|
||||
{
|
||||
var args = new ILocValue[fluentArgs.Count];
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
args[i] = ValFromFluent(fluentArgs[i]);
|
||||
}
|
||||
|
||||
var options = new Dictionary<string, ILocValue>(fluentOptions.Count);
|
||||
foreach (var (k, v) in fluentOptions)
|
||||
{
|
||||
options.Add(k, ValFromFluent(v));
|
||||
}
|
||||
|
||||
var argStruct = new LocArgs(args, options);
|
||||
|
||||
var ret = function(argStruct);
|
||||
|
||||
return ValToFluent(ret);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class FluentLocWrapperType : IFluentType
|
||||
{
|
||||
public readonly ILocValue WrappedValue;
|
||||
|
||||
public FluentLocWrapperType(ILocValue wrappedValue)
|
||||
{
|
||||
WrappedValue = wrappedValue;
|
||||
}
|
||||
|
||||
public string AsString()
|
||||
{
|
||||
return WrappedValue.Format(new LocContext());
|
||||
}
|
||||
|
||||
public IFluentType Copy()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
static class LinguiniAdapter
|
||||
{
|
||||
internal static ILocValue ToLocValue(this IFluentType arg)
|
||||
{
|
||||
return arg switch
|
||||
{
|
||||
FluentNone => new LocValueNone(""),
|
||||
FluentNumber number => new LocValueNumber(number),
|
||||
FluentString str => new LocValueString(str),
|
||||
FluentLocWrapperType value => value.WrappedValue,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(arg)),
|
||||
};
|
||||
}
|
||||
|
||||
public static IFluentType FluentFromObject(this object obj)
|
||||
{
|
||||
return obj switch
|
||||
{
|
||||
ILocValue wrap => new FluentLocWrapperType(wrap),
|
||||
IEntity entity => new FluentLocWrapperType(new LocValueEntity(entity)),
|
||||
DateTime dateTime => new FluentLocWrapperType(new LocValueDateTime(dateTime)),
|
||||
bool or Enum => (FluentString)obj.ToString()!.ToLowerInvariant(),
|
||||
string str => (FluentString)str,
|
||||
byte num => (FluentNumber)num,
|
||||
sbyte num => (FluentNumber)num,
|
||||
short num => (FluentNumber)num,
|
||||
ushort num => (FluentNumber)num,
|
||||
int num => (FluentNumber)num,
|
||||
uint num => (FluentNumber)num,
|
||||
long num => (FluentNumber)num,
|
||||
ulong num => (FluentNumber)num,
|
||||
double dbl => (FluentNumber)dbl,
|
||||
float dbl => (FluentNumber)dbl,
|
||||
_ => (FluentString)obj.ToString()!,
|
||||
};
|
||||
}
|
||||
|
||||
public static IFluentType FluentFromVal(this ILocValue locValue)
|
||||
{
|
||||
return locValue switch
|
||||
{
|
||||
LocValueNone => FluentNone.None,
|
||||
LocValueNumber number => (FluentNumber)number.Value,
|
||||
LocValueString str => (FluentString)str.Value,
|
||||
LocValueDateTime dateTime => new FluentLocWrapperType(dateTime),
|
||||
_ => new FluentLocWrapperType(locValue),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,9 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Fluent.Net;
|
||||
using Fluent.Net.RuntimeAst;
|
||||
using JetBrains.Annotations;
|
||||
using Linguini.Bundle;
|
||||
using Linguini.Bundle.Builder;
|
||||
using Linguini.Bundle.Errors;
|
||||
using Linguini.Shared.Types.Bundle;
|
||||
using Linguini.Syntax.Ast;
|
||||
using Linguini.Syntax.Parser;
|
||||
using Linguini.Syntax.Parser.Error;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -29,7 +24,7 @@ namespace Robust.Shared.Localization
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
private ISawmill _logSawmill = default!;
|
||||
private readonly Dictionary<CultureInfo, FluentBundle> _contexts = new();
|
||||
private readonly Dictionary<CultureInfo, MessageContext> _contexts = new();
|
||||
|
||||
private CultureInfo? _defaultCulture;
|
||||
|
||||
@@ -53,6 +48,16 @@ namespace Robust.Shared.Localization
|
||||
return msg;
|
||||
}
|
||||
|
||||
public bool TryGetString(string messageId, [NotNullWhen(true)] out string? value)
|
||||
{
|
||||
if (!TryGetNode(messageId, out var context, out var node))
|
||||
{
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return DoFormat(messageId, out value, context, node);
|
||||
}
|
||||
|
||||
public string GetString(string messageId, params (string, object)[] args0)
|
||||
{
|
||||
@@ -68,38 +73,144 @@ namespace Robust.Shared.Localization
|
||||
return msg;
|
||||
}
|
||||
|
||||
public bool TryGetString(string messageId, [NotNullWhen(true)] out string? value)
|
||||
{
|
||||
return TryGetString(messageId, out value, null);
|
||||
}
|
||||
|
||||
public bool TryGetString(string messageId, [NotNullWhen(true)] out string? value,
|
||||
params (string, object)[]? keyArgs)
|
||||
params (string, object)[] args0)
|
||||
{
|
||||
var args = new Dictionary<string, IFluentType>();
|
||||
if (keyArgs != null)
|
||||
{
|
||||
foreach (var (k, v) in keyArgs)
|
||||
{
|
||||
args.Add(k, v.FluentFromObject());
|
||||
}
|
||||
}
|
||||
|
||||
if (!HasMessage(messageId, out var bundle))
|
||||
if (!TryGetNode(messageId, out var context, out var node))
|
||||
{
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
var args = new Dictionary<string, object>();
|
||||
foreach (var (k, v) in args0)
|
||||
{
|
||||
var result = bundle.TryGetAttrMsg(messageId, args, out var errs, out value);
|
||||
foreach (var err in errs)
|
||||
var val = v switch
|
||||
{
|
||||
_logSawmill.Error("{culture}/{messageId}: {error}", _defaultCulture!.Name, messageId, err);
|
||||
IEntity entity => new LocValueEntity(entity),
|
||||
bool or Enum => v.ToString()!.ToLowerInvariant(),
|
||||
_ => v,
|
||||
};
|
||||
|
||||
if (val is ILocValue locVal)
|
||||
val = new FluentLocWrapperType(locVal);
|
||||
|
||||
args.Add(k, val);
|
||||
}
|
||||
|
||||
return DoFormat(messageId, out value, context, node, args);
|
||||
}
|
||||
|
||||
private bool TryGetMessage(
|
||||
string messageId,
|
||||
[NotNullWhen(true)] out MessageContext? ctx,
|
||||
[NotNullWhen(true)] out Message? message)
|
||||
{
|
||||
if (_defaultCulture == null)
|
||||
{
|
||||
ctx = null;
|
||||
message = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx = _contexts[_defaultCulture];
|
||||
message = ctx.GetMessage(messageId);
|
||||
return message != null;
|
||||
}
|
||||
|
||||
private bool TryGetNode(
|
||||
string messageId,
|
||||
[NotNullWhen(true)] out MessageContext? ctx,
|
||||
[NotNullWhen(true)] out Node? node)
|
||||
{
|
||||
string? attribName = null;
|
||||
|
||||
if (messageId.Contains('.'))
|
||||
{
|
||||
var split = messageId.Split('.');
|
||||
messageId = split[0];
|
||||
attribName = split[1];
|
||||
}
|
||||
|
||||
if (!TryGetMessage(messageId, out ctx, out var message))
|
||||
{
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attribName != null)
|
||||
{
|
||||
if (message.Attributes == null || !message.Attributes.TryGetValue(attribName, out var attrib))
|
||||
{
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return result;
|
||||
node = attrib;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = message;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ReloadLocalizations()
|
||||
{
|
||||
foreach (var (culture, context) in _contexts.ToArray())
|
||||
{
|
||||
// Fluent.Net doesn't allow us to remove messages so...
|
||||
var newContext = new MessageContext(
|
||||
culture.Name,
|
||||
new MessageContextOptions
|
||||
{
|
||||
UseIsolating = false,
|
||||
Functions = context.Functions
|
||||
}
|
||||
);
|
||||
|
||||
_contexts[culture] = newContext;
|
||||
|
||||
_loadData(_res, culture, newContext);
|
||||
}
|
||||
|
||||
FlushEntityCache();
|
||||
}
|
||||
|
||||
private static ILocValue ValFromFluent(object arg)
|
||||
{
|
||||
return arg switch
|
||||
{
|
||||
FluentNone none => new LocValueNone(none.Value),
|
||||
FluentNumber number => new LocValueNumber(double.Parse(number.Value)),
|
||||
FluentString str => new LocValueString(str.Value),
|
||||
FluentDateTime dateTime =>
|
||||
new LocValueDateTime(DateTime.Parse(dateTime.Value, null, DateTimeStyles.RoundtripKind)),
|
||||
FluentLocWrapperType wrap => wrap.WrappedValue,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(arg))
|
||||
};
|
||||
}
|
||||
|
||||
private static FluentType ValToFluent(ILocValue arg)
|
||||
{
|
||||
return arg switch
|
||||
{
|
||||
LocValueNone =>
|
||||
throw new NotSupportedException("Cannot currently return LocValueNone from loc functions."),
|
||||
LocValueNumber number => new FluentNumber(number.Value.ToString("R")),
|
||||
LocValueString str => new FluentString(str.Value),
|
||||
LocValueDateTime dateTime => new FluentDateTime(dateTime.Value),
|
||||
_ => new FluentLocWrapperType(arg)
|
||||
};
|
||||
}
|
||||
|
||||
private bool DoFormat(string messageId, out string? value, MessageContext context, Node node, IDictionary<string, object>? args = null)
|
||||
{
|
||||
var errs = new List<FluentError>();
|
||||
try
|
||||
{
|
||||
value = context.Format(node, args, errs);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -107,52 +218,13 @@ namespace Robust.Shared.Localization
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasMessage(
|
||||
string messageId,
|
||||
[NotNullWhen(true)] out FluentBundle? bundle)
|
||||
{
|
||||
if (_defaultCulture == null)
|
||||
foreach (var err in errs)
|
||||
{
|
||||
bundle = null;
|
||||
return false;
|
||||
_logSawmill.Error("{culture}/{messageId}: {error}", _defaultCulture!.Name, messageId, err);
|
||||
}
|
||||
|
||||
bundle = _contexts[_defaultCulture];
|
||||
if (messageId.Contains('.'))
|
||||
{
|
||||
var split = messageId.Split('.');
|
||||
return bundle.HasMessage(split[0]);
|
||||
}
|
||||
|
||||
return bundle.HasMessage(messageId);
|
||||
}
|
||||
|
||||
private bool TryGetMessage(
|
||||
string messageId,
|
||||
[NotNullWhen(true)] out FluentBundle? bundle,
|
||||
[NotNullWhen(true)] out AstMessage? message)
|
||||
{
|
||||
if (_defaultCulture == null)
|
||||
{
|
||||
bundle = null;
|
||||
message = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
bundle = _contexts[_defaultCulture];
|
||||
return bundle.TryGetAstMessage(messageId, out message);
|
||||
}
|
||||
|
||||
public void ReloadLocalizations()
|
||||
{
|
||||
foreach (var (culture, context) in _contexts.ToArray())
|
||||
{
|
||||
_loadData(_res, culture, context);
|
||||
}
|
||||
|
||||
FlushEntityCache();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -189,18 +261,26 @@ namespace Robust.Shared.Localization
|
||||
|
||||
public void LoadCulture(CultureInfo culture)
|
||||
{
|
||||
var bundle = LinguiniBuilder.Builder()
|
||||
.CultureInfo(culture)
|
||||
.SkipResources()
|
||||
.SetUseIsolating(false)
|
||||
.UseConcurrent()
|
||||
.UncheckedBuild();
|
||||
var context = new MessageContext(
|
||||
culture.Name,
|
||||
new MessageContextOptions
|
||||
{
|
||||
UseIsolating = false,
|
||||
// Have to pass empty dict here or else Fluent.Net will fuck up
|
||||
// and share the same dict between multiple message contexts.
|
||||
// Yes, you read that right.
|
||||
Functions = new Dictionary<string, Resolver.ExternalFunction>(),
|
||||
}
|
||||
);
|
||||
AddBuiltinFunctions(context);
|
||||
|
||||
_contexts.Add(culture, bundle);
|
||||
AddBuiltInFunctions(bundle);
|
||||
_contexts.Add(culture, context);
|
||||
|
||||
_loadData(_res, culture, bundle);
|
||||
DefaultCulture ??= culture;
|
||||
_loadData(_res, culture, context);
|
||||
if (DefaultCulture == null)
|
||||
{
|
||||
DefaultCulture = culture;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLoadedToStringSerializer(IRobustMappedStringSerializer serializer)
|
||||
@@ -227,7 +307,7 @@ namespace Robust.Shared.Localization
|
||||
*/
|
||||
}
|
||||
|
||||
private void _loadData(IResourceManager resourceManager, CultureInfo culture, FluentBundle context)
|
||||
private void _loadData(IResourceManager resourceManager, CultureInfo culture, MessageContext context)
|
||||
{
|
||||
// Load data from .ftl files.
|
||||
// Data is loaded from /Locale/<language-code>/*
|
||||
@@ -243,33 +323,40 @@ namespace Robust.Shared.Localization
|
||||
using var fileStream = resourceManager.ContentFileRead(path);
|
||||
using var reader = new StreamReader(fileStream, EncodingHelpers.UTF8);
|
||||
|
||||
var parser = new LinguiniParser(reader);
|
||||
var resource = parser.Parse();
|
||||
return (path, resource, parser.GetReadonlyData);
|
||||
var resource = FluentResource.FromReader(reader);
|
||||
return (path, resource);
|
||||
});
|
||||
|
||||
foreach (var (path, resource, data) in resources)
|
||||
foreach (var (path, resource) in resources)
|
||||
{
|
||||
var errors = resource.Errors;
|
||||
context.AddResourceOverriding(resource);
|
||||
WriteWarningForErrs(path, errors, data);
|
||||
var errors = context.AddResource(resource);
|
||||
foreach (var error in errors)
|
||||
{
|
||||
_logSawmill.Error("{path}: {exception}", path, error.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteWarningForErrs(ResourcePath path, List<ParseError> errs, ReadOnlyMemory<char> resource)
|
||||
private sealed class FluentLocWrapperType : FluentType
|
||||
{
|
||||
foreach (var err in errs)
|
||||
{
|
||||
_logSawmill.Warning("{path}:\n{exception}", path, err.FormatCompileErrors(resource));
|
||||
}
|
||||
}
|
||||
public readonly ILocValue WrappedValue;
|
||||
|
||||
private void WriteWarningForErrs(IList<FluentError> errs, string locId)
|
||||
{
|
||||
foreach (var err in errs)
|
||||
public FluentLocWrapperType(ILocValue wrappedValue)
|
||||
{
|
||||
_logSawmill.Warning("Error extracting `{locId}`\n{e1}", locId, err);
|
||||
WrappedValue = wrappedValue;
|
||||
}
|
||||
|
||||
public override string Format(MessageContext ctx)
|
||||
{
|
||||
return WrappedValue.Format(new LocContext(ctx));
|
||||
}
|
||||
|
||||
public override bool Match(MessageContext ctx, object obj)
|
||||
{
|
||||
return false;
|
||||
/*var strVal = obj is IFluentType ft ? ft.Value : obj.ToString() ?? "";
|
||||
return WrappedValue.Matches(new LocContext(ctx), strVal);*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,11 +220,6 @@ namespace Robust.Shared.Map
|
||||
|
||||
public MapId CreateMap(MapId? mapID = null)
|
||||
{
|
||||
return CreateMap(mapID, null);
|
||||
}
|
||||
|
||||
public MapId CreateMap(MapId? mapID, EntityUid? entityUid)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugTools.Assert(_dbgGuardRunning);
|
||||
#endif
|
||||
@@ -274,7 +269,7 @@ namespace Robust.Shared.Map
|
||||
}
|
||||
else
|
||||
{
|
||||
var newEnt = (Entity) _entityManager.CreateEntityUninitialized(null, entityUid);
|
||||
var newEnt = (Entity) _entityManager.CreateEntityUninitialized(null, EntityCoordinates.Invalid);
|
||||
_mapEntities.Add(actualID, newEnt.Uid);
|
||||
|
||||
var mapComp = newEnt.AddComponent<MapComponent>();
|
||||
@@ -405,15 +400,10 @@ namespace Robust.Shared.Map
|
||||
|
||||
public IMapGrid CreateGrid(MapId currentMapID, GridId? gridID = null, ushort chunkSize = 16)
|
||||
{
|
||||
return CreateGridImpl(currentMapID, gridID, chunkSize, true, null);
|
||||
return CreateGridImpl(currentMapID, gridID, chunkSize, true);
|
||||
}
|
||||
|
||||
public IMapGrid CreateGrid(MapId currentMapID, GridId gridID, ushort chunkSize, EntityUid euid)
|
||||
{
|
||||
return CreateGridImpl(currentMapID, gridID, chunkSize, true, euid);
|
||||
}
|
||||
|
||||
private IMapGridInternal CreateGridImpl(MapId currentMapID, GridId? gridID, ushort chunkSize, bool createEntity, EntityUid? euid)
|
||||
private IMapGridInternal CreateGridImpl(MapId currentMapID, GridId? gridID, ushort chunkSize, bool createEntity)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugTools.Assert(_dbgGuardRunning);
|
||||
@@ -465,23 +455,20 @@ namespace Robust.Shared.Map
|
||||
}
|
||||
else
|
||||
{
|
||||
var gridEnt = (Entity) EntityManager.CreateEntityUninitialized(null, euid);
|
||||
|
||||
grid.GridEntityId = gridEnt.Uid;
|
||||
var newEnt =
|
||||
(Entity) _entityManager.CreateEntityUninitialized(null,
|
||||
new MapCoordinates(Vector2.Zero, currentMapID));
|
||||
grid.GridEntityId = newEnt.Uid;
|
||||
|
||||
Logger.DebugS("map", $"Binding grid {actualID} to entity {grid.GridEntityId}");
|
||||
|
||||
var gridComp = gridEnt.AddComponent<MapGridComponent>();
|
||||
var gridComp = newEnt.AddComponent<MapGridComponent>();
|
||||
gridComp.GridIndex = grid.Index;
|
||||
|
||||
//TODO: This is a hack to get TransformComponent.MapId working before entity states
|
||||
//are applied. After they are applied the parent may be different, but the MapId will
|
||||
//be the same. This causes TransformComponent.ParentUid of a grid to be unsafe to
|
||||
//use in transform states anytime before the state parent is properly set.
|
||||
gridEnt.Transform.AttachParent(GetMapEntity(currentMapID));
|
||||
|
||||
gridEnt.InitializeComponents();
|
||||
gridEnt.StartAllComponents();
|
||||
newEnt.Transform.AttachParent(_entityManager.GetEntity(_mapEntities[currentMapID]));
|
||||
|
||||
newEnt.InitializeComponents();
|
||||
newEnt.StartAllComponents();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -495,7 +482,7 @@ namespace Robust.Shared.Map
|
||||
|
||||
public IMapGridInternal CreateGridNoEntity(MapId currentMapID, GridId? gridID = null, ushort chunkSize = 16)
|
||||
{
|
||||
return CreateGridImpl(currentMapID, gridID, chunkSize, false, null);
|
||||
return CreateGridImpl(currentMapID, gridID, chunkSize, false);
|
||||
}
|
||||
|
||||
public IMapGrid GetGrid(GridId gridID)
|
||||
|
||||
@@ -128,25 +128,10 @@ namespace Robust.Shared.Network
|
||||
/// The side of the network this message is accepted on.
|
||||
/// If we are not on the side specified, the receive callback will not be registered even if provided.
|
||||
/// </param>
|
||||
[Obsolete("Use the method without a name argument instead")]
|
||||
void RegisterNetMessage<T>(string name, ProcessMessage<T>? rxCallback = null,
|
||||
NetMessageAccept accept = NetMessageAccept.Both)
|
||||
where T : NetMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a NetMessage to be sent or received.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type to register.</typeparam>
|
||||
/// <param name="name">String ID of the message.</param>
|
||||
/// <param name="rxCallback">Callback function to process the received message.</param>
|
||||
/// <param name="accept">
|
||||
/// The side of the network this message is accepted on.
|
||||
/// If we are not on the side specified, the receive callback will not be registered even if provided.
|
||||
/// </param>
|
||||
void RegisterNetMessage<T>(ProcessMessage<T>? rxCallback = null,
|
||||
NetMessageAccept accept = NetMessageAccept.Both)
|
||||
where T : NetMessage, new();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new NetMessage to be sent.
|
||||
/// </summary>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user