mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbb45f1c13 | ||
|
|
6284e16b64 | ||
|
|
f6c55085fe | ||
|
|
30eafd26e7 | ||
|
|
63423d96b4 | ||
|
|
474334aff2 | ||
|
|
be102f86bf | ||
|
|
d7962c7190 | ||
|
|
7fe9385c3b | ||
|
|
a9d9d1348a | ||
|
|
4eaf624555 | ||
|
|
e37c131fb4 | ||
|
|
9df4606492 | ||
|
|
3be8070274 | ||
|
|
8a440d705f | ||
|
|
5849474022 | ||
|
|
b7d67c0ece | ||
|
|
b04cf71bc0 | ||
|
|
ea87df649a | ||
|
|
dab7a9112f | ||
|
|
d3dc89832a | ||
|
|
3ade9ca447 | ||
|
|
149e9a2613 |
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -54,6 +54,60 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 148.4.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add recursive PVS overrides and remove IsOverride()
|
||||
|
||||
|
||||
## 148.3.0
|
||||
|
||||
### New features
|
||||
|
||||
* Happy eyeballs delay can be configured.
|
||||
* Added more colors.
|
||||
* Allow pre-startup components to be shut down.
|
||||
* Added tile texture reload command.
|
||||
* Add implementation of Random.Pick(ValueList<T> ..).
|
||||
* Add IntegrationInstance fields for common dependencies.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Prevent invalid prototypes from being spawned.
|
||||
* Change default value of EntityLastModifiedTick from zero to one.
|
||||
* Make DiscordRichPresence icon CVars server-side with replication.
|
||||
|
||||
|
||||
## 148.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* `SpinBox.LineEditControl` exposes the underlying `LineEdit`.
|
||||
* Add VV attributes to various fields across overlay and sessions.
|
||||
* Add IsPaused to EntityManager to check if an entity is paused.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix SetActiveTheme not updating the theme.
|
||||
|
||||
|
||||
## 148.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added IgnoreUIChecksComponent that lets entities ignore bound user interface range checks which would normally close the UI.
|
||||
* Add support for F16-F24 keybinds.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix gamestate bug where PVS is disabled.
|
||||
|
||||
### Other
|
||||
|
||||
* EntityQuery.HasComponent override for nullable entity uids.
|
||||
|
||||
|
||||
## 148.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
@@ -558,3 +558,6 @@ cmd-vfs_ls-help = Usage: vfs_list <path>
|
||||
|
||||
cmd-vfs_ls-err-args = Need exactly 1 argument.
|
||||
cmd-vfs_ls-hint-path = <path>
|
||||
|
||||
cmd-reloadtiletextures-desc = Reloads the tile texture atlas to allow hot reloading tile sprites
|
||||
cmd-reloadtiletextures-help = Usage: reloadtiletextures
|
||||
|
||||
@@ -18,6 +18,15 @@ input-key-F12 = F12
|
||||
input-key-F13 = F13
|
||||
input-key-F14 = F14
|
||||
input-key-F15 = F15
|
||||
input-key-F16 = F16
|
||||
input-key-F17 = F17
|
||||
input-key-F18 = F18
|
||||
input-key-F19 = F19
|
||||
input-key-F20 = F20
|
||||
input-key-F21 = F21
|
||||
input-key-F22 = F22
|
||||
input-key-F23 = F23
|
||||
input-key-F24 = F24
|
||||
input-key-Pause = Pause
|
||||
input-key-Left = Left
|
||||
input-key-Up = Up
|
||||
|
||||
@@ -68,6 +68,7 @@ namespace Robust.Client
|
||||
deps.Register<IComponentFactory, ComponentFactory>();
|
||||
deps.Register<ITileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<IClydeTileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<ClydeTileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<GameController, GameController>();
|
||||
deps.Register<IGameController, GameController>();
|
||||
deps.Register<IGameControllerInternal, GameController>();
|
||||
|
||||
@@ -207,6 +207,15 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{GlfwKey.F13, Key.F13},
|
||||
{GlfwKey.F14, Key.F14},
|
||||
{GlfwKey.F15, Key.F15},
|
||||
{GlfwKey.F16, Key.F16},
|
||||
{GlfwKey.F17, Key.F17},
|
||||
{GlfwKey.F18, Key.F18},
|
||||
{GlfwKey.F19, Key.F19},
|
||||
{GlfwKey.F20, Key.F20},
|
||||
{GlfwKey.F21, Key.F21},
|
||||
{GlfwKey.F22, Key.F22},
|
||||
{GlfwKey.F23, Key.F23},
|
||||
{GlfwKey.F24, Key.F24},
|
||||
{GlfwKey.Pause, Key.Pause},
|
||||
{GlfwKey.World1, Key.World1},
|
||||
};
|
||||
|
||||
@@ -191,6 +191,15 @@ internal partial class Clyde
|
||||
MapKey(SDL_SCANCODE_F13, Key.F13);
|
||||
MapKey(SDL_SCANCODE_F14, Key.F14);
|
||||
MapKey(SDL_SCANCODE_F15, Key.F15);
|
||||
MapKey(SDL_SCANCODE_F16, Key.F16);
|
||||
MapKey(SDL_SCANCODE_F17, Key.F17);
|
||||
MapKey(SDL_SCANCODE_F18, Key.F18);
|
||||
MapKey(SDL_SCANCODE_F19, Key.F19);
|
||||
MapKey(SDL_SCANCODE_F20, Key.F20);
|
||||
MapKey(SDL_SCANCODE_F21, Key.F21);
|
||||
MapKey(SDL_SCANCODE_F22, Key.F22);
|
||||
MapKey(SDL_SCANCODE_F23, Key.F23);
|
||||
MapKey(SDL_SCANCODE_F24, Key.F24);
|
||||
MapKey(SDL_SCANCODE_PAUSE, Key.Pause);
|
||||
|
||||
KeyMapReverse = new Dictionary<Key, SDL_Scancode>();
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
@@ -11,6 +12,7 @@ namespace Robust.Client.Graphics
|
||||
{
|
||||
[Dependency] private readonly ILogManager _logMan = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<Type, Overlay> _overlays = new Dictionary<Type, Overlay>();
|
||||
private ISawmill _logger = default!;
|
||||
|
||||
|
||||
@@ -161,6 +161,15 @@ namespace Robust.Client.Input
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
F16,
|
||||
F17,
|
||||
F18,
|
||||
F19,
|
||||
F20,
|
||||
F21,
|
||||
F22,
|
||||
F23,
|
||||
F24,
|
||||
Pause,
|
||||
World1,
|
||||
}
|
||||
|
||||
@@ -3,13 +3,16 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Map;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Utility;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
@@ -52,8 +55,11 @@ namespace Robust.Client.Map
|
||||
_genTextureAtlas();
|
||||
}
|
||||
|
||||
private void _genTextureAtlas()
|
||||
internal void _genTextureAtlas()
|
||||
{
|
||||
_tileRegions.Clear();
|
||||
_tileTextureAtlas = null;
|
||||
|
||||
var defList = TileDefs.Where(t => t.Sprite != null).ToList();
|
||||
|
||||
// If there are no tile definitions, we do nothing.
|
||||
@@ -144,4 +150,17 @@ namespace Robust.Client.Map
|
||||
_tileTextureAtlas = Texture.LoadFromImage(sheet, "Tile Atlas");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ReloadTileTexturesCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly ClydeTileDefinitionManager _tile = default!;
|
||||
|
||||
public override string Command => "reloadtiletextures";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
_tile._genTextureAtlas();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,14 +3,18 @@ using System.Collections.Generic;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.Player
|
||||
{
|
||||
public interface IPlayerManager : Shared.Players.ISharedPlayerManager
|
||||
{
|
||||
new IEnumerable<ICommonSession> Sessions { get; }
|
||||
|
||||
[ViewVariables]
|
||||
IReadOnlyDictionary<NetUserId, ICommonSession> SessionsDict { get; }
|
||||
|
||||
[ViewVariables]
|
||||
LocalPlayer? LocalPlayer { get; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,6 +2,7 @@ using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.Player
|
||||
{
|
||||
@@ -17,11 +18,14 @@ namespace Robust.Client.Player
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public EntityUid? AttachedEntity { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public NetUserId UserId { get; }
|
||||
|
||||
[ViewVariables]
|
||||
internal string Name { get; set; } = "<Unknown>";
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -31,9 +35,11 @@ namespace Robust.Client.Player
|
||||
set => this.Name = value;
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
internal short Ping { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public INetChannel ConnectedClient { get; internal set; } = null!;
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public const string LeftButtonStyle = "spinbox-left";
|
||||
public const string RightButtonStyle = "spinbox-right";
|
||||
public const string MiddleButtonStyle = "spinbox-middle";
|
||||
private LineEdit _lineEdit;
|
||||
public LineEdit LineEditControl { get; }
|
||||
private List<Button> _leftButtons = new();
|
||||
private List<Button> _rightButtons = new();
|
||||
private int _stepSize = 1;
|
||||
@@ -35,7 +35,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
return;
|
||||
}
|
||||
_value = value;
|
||||
_lineEdit.Text = value.ToString();
|
||||
LineEditControl.Text = value.ToString();
|
||||
ValueChanged?.Invoke(new ValueChangedEventArgs(value));
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
return;
|
||||
}
|
||||
_value = value;
|
||||
_lineEdit.Text = value.ToString();
|
||||
LineEditControl.Text = value.ToString();
|
||||
}
|
||||
|
||||
public event Action<ValueChangedEventArgs>? ValueChanged;
|
||||
@@ -62,17 +62,17 @@ namespace Robust.Client.UserInterface.Controls
|
||||
Orientation = LayoutOrientation.Horizontal;
|
||||
MouseFilter = MouseFilterMode.Pass;
|
||||
|
||||
_lineEdit = new LineEdit
|
||||
LineEditControl = new LineEdit
|
||||
{
|
||||
MinSize = new Vector2(40, 0),
|
||||
HorizontalExpand = true
|
||||
};
|
||||
AddChild(_lineEdit);
|
||||
AddChild(LineEditControl);
|
||||
|
||||
Value = 0;
|
||||
|
||||
_lineEdit.IsValid = (str) => int.TryParse(str, out var i) && (IsValid == null || IsValid(i));
|
||||
_lineEdit.OnTextChanged += (args) =>
|
||||
LineEditControl.IsValid = (str) => int.TryParse(str, out var i) && (IsValid == null || IsValid(i));
|
||||
LineEditControl.OnTextChanged += (args) =>
|
||||
{
|
||||
if (int.TryParse(args.Text, out int i))
|
||||
Value = i;
|
||||
@@ -143,8 +143,8 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// </summary>
|
||||
public bool LineEditDisabled
|
||||
{
|
||||
get => !_lineEdit.Editable;
|
||||
set => _lineEdit.Editable = !value;
|
||||
get => !LineEditControl.Editable;
|
||||
set => LineEditControl.Editable = !value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -185,7 +185,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
base.MouseWheel(args);
|
||||
|
||||
if (!_lineEdit.HasKeyboardFocus())
|
||||
if (!LineEditControl.HasKeyboardFocus())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ internal partial class UserInterfaceManager
|
||||
public void SetActiveTheme(string themeName)
|
||||
{
|
||||
if (!_themes.TryGetValue(themeName, out var theme) || (theme == CurrentTheme)) return;
|
||||
CurrentTheme = theme;
|
||||
UpdateTheme(theme);
|
||||
}
|
||||
|
||||
public void SetDefaultTheme(string themeId)
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Server.GameObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Lets any entities with this component ignore user interface range checks that would normally
|
||||
/// close the UI automatically.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class IgnoreUIRangeComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -17,6 +17,8 @@ namespace Robust.Server.GameObjects
|
||||
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
||||
[Dependency] private readonly TransformSystem _xformSys = default!;
|
||||
|
||||
private EntityQuery<IgnoreUIRangeComponent> _ignoreUIRangeQuery;
|
||||
|
||||
private readonly List<IPlayerSession> _sessionCache = new();
|
||||
|
||||
private readonly Dictionary<IPlayerSession, List<BoundUserInterface>> _openInterfaces = new();
|
||||
@@ -30,6 +32,8 @@ namespace Robust.Server.GameObjects
|
||||
SubscribeLocalEvent<ServerUserInterfaceComponent, ComponentInit>(OnUserInterfaceInit);
|
||||
SubscribeLocalEvent<ServerUserInterfaceComponent, ComponentShutdown>(OnUserInterfaceShutdown);
|
||||
_playerMan.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
|
||||
_ignoreUIRangeQuery = GetEntityQuery<IgnoreUIRangeComponent>();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
@@ -174,6 +178,9 @@ namespace Robust.Server.GameObjects
|
||||
if (!query.TryGetComponent(session.AttachedEntity, out var xform))
|
||||
continue;
|
||||
|
||||
if (_ignoreUIRangeQuery.HasComponent(session.AttachedEntity))
|
||||
continue;
|
||||
|
||||
if (uiMap != xform.MapID)
|
||||
{
|
||||
CloseUi(ui, session, activeUis);
|
||||
|
||||
@@ -81,30 +81,35 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
private protected override EntityUid CreateEntity(string? prototypeName, EntityUid uid = default, IEntityLoadContext? context = null)
|
||||
{
|
||||
var entity = base.CreateEntity(prototypeName, uid, context);
|
||||
if (prototypeName == null)
|
||||
return base.CreateEntity(prototypeName, uid, context);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(prototypeName))
|
||||
{
|
||||
var prototype = PrototypeManager.Index<EntityPrototype>(prototypeName);
|
||||
if (!PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype))
|
||||
throw new EntityCreationException($"Attempted to spawn an entity with an invalid prototype: {prototypeName}");
|
||||
|
||||
// At this point in time, all data configure on the entity *should* be purely from the prototype.
|
||||
// As such, we can reset the modified ticks to Zero,
|
||||
// which indicates "not different from client's own deserialization".
|
||||
// So the initial data for the component or even the creation doesn't have to be sent over the wire.
|
||||
foreach (var (netId, component) in GetNetComponents(entity))
|
||||
{
|
||||
// Make sure to ONLY get components that are defined in the prototype.
|
||||
// Others could be instantiated directly by AddComponent (e.g. ContainerManager).
|
||||
// And those aren't guaranteed to exist on the client, so don't clear them.
|
||||
var compName = ComponentFactory.GetComponentName(component.GetType());
|
||||
if (prototype.Components.ContainsKey(compName))
|
||||
component.ClearTicks();
|
||||
}
|
||||
}
|
||||
var entity = base.CreateEntity(prototype, uid, context);
|
||||
|
||||
// At this point in time, all data configure on the entity *should* be purely from the prototype.
|
||||
// As such, we can reset the modified ticks to Zero,
|
||||
// which indicates "not different from client's own deserialization".
|
||||
// So the initial data for the component or even the creation doesn't have to be sent over the wire.
|
||||
ClearTicks(entity, prototype);
|
||||
return entity;
|
||||
}
|
||||
|
||||
private void ClearTicks(EntityUid entity, EntityPrototype prototype)
|
||||
{
|
||||
foreach (var (netId, component) in GetNetComponents(entity))
|
||||
{
|
||||
// Make sure to ONLY get components that are defined in the prototype.
|
||||
// Others could be instantiated directly by AddComponent (e.g. ContainerManager).
|
||||
// And those aren't guaranteed to exist on the client, so don't clear them.
|
||||
var compName = ComponentFactory.GetComponentName(netId);
|
||||
if (prototype.Components.ContainsKey(compName))
|
||||
component.ClearTicks();
|
||||
}
|
||||
}
|
||||
|
||||
public override EntityStringRepresentation ToPrettyString(EntityUid uid)
|
||||
{
|
||||
TryGetComponent(uid, out ActorComponent? actor);
|
||||
|
||||
@@ -77,11 +77,21 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// </summary>
|
||||
private readonly HashSet<TIndex> _globalOverrides = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="TIndex"/> that should always get sent along with all of their children.
|
||||
/// </summary>
|
||||
private readonly HashSet<TIndex> _globalRecursiveOverrides = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="TIndex"/> that should always get sent.
|
||||
/// </summary>
|
||||
public HashSet<TIndex>.Enumerator GlobalOverridesEnumerator => _globalOverrides.GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="TIndex"/> that should always get sent along with all of their children.
|
||||
/// </summary>
|
||||
public HashSet<TIndex>.Enumerator GlobalRecursiveOverridesEnumerator => _globalRecursiveOverrides.GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="TIndex"/> that should always get sent to a certain <see cref="ICommonSession"/>.
|
||||
/// </summary>
|
||||
@@ -203,8 +213,11 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
{
|
||||
switch (location)
|
||||
{
|
||||
case GlobalOverride _:
|
||||
_globalOverrides.Add(index);
|
||||
case GlobalOverride global:
|
||||
if (global.Recursive)
|
||||
_globalRecursiveOverrides.Add(index);
|
||||
else
|
||||
_globalOverrides.Add(index);
|
||||
break;
|
||||
case GridChunkLocation gridChunkLocation:
|
||||
// might be gone due to grid-deletions
|
||||
@@ -239,8 +252,11 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
// since we can find the index, we can assume the dicts will be there too & dont need to do any checks. gaming.
|
||||
switch (location)
|
||||
{
|
||||
case GlobalOverride _:
|
||||
_globalOverrides.Remove(index);
|
||||
case GlobalOverride global:
|
||||
if (global.Recursive)
|
||||
_globalRecursiveOverrides.Remove(index);
|
||||
else
|
||||
_globalOverrides.Remove(index);
|
||||
break;
|
||||
case GridChunkLocation gridChunkLocation:
|
||||
_gridChunkContents[gridChunkLocation.GridId][gridChunkLocation.ChunkIndices].Remove(index);
|
||||
@@ -376,15 +392,10 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
|
||||
#region UpdateIndex
|
||||
|
||||
private bool IsOverride(TIndex index)
|
||||
private bool TryGetLocation(TIndex index, out IIndexLocation? location)
|
||||
{
|
||||
if (_locationChangeBuffer.TryGetValue(index, out var change) &&
|
||||
change is GlobalOverride or LocalOverride) return true;
|
||||
|
||||
if (_indexLocations.TryGetValue(index, out var indexLoc) &&
|
||||
indexLoc is GlobalOverride or LocalOverride) return true;
|
||||
|
||||
return false;
|
||||
return _locationChangeBuffer.TryGetValue(index, out location)
|
||||
|| _indexLocations.TryGetValue(index, out location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -392,15 +403,25 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// </summary>
|
||||
/// <param name="index">The <see cref="TIndex"/> to update.</param>
|
||||
/// <param name="removeFromOverride">An index at an override position will not be updated unless you set this flag.</param>
|
||||
public void UpdateIndex(TIndex index, bool removeFromOverride = false)
|
||||
/// <param name="recursive">If true, this will also recursively send any children of the given index.</param>
|
||||
public void AddGlobalOverride(TIndex index, bool removeFromOverride, bool recursive)
|
||||
{
|
||||
if(!removeFromOverride && IsOverride(index))
|
||||
if (!TryGetLocation(index, out var oldLocation))
|
||||
{
|
||||
RegisterUpdate(index, new GlobalOverride(recursive));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!removeFromOverride && oldLocation is LocalOverride)
|
||||
return;
|
||||
|
||||
if (_indexLocations.TryGetValue(index, out var oldLocation) &&
|
||||
oldLocation is GlobalOverride) return;
|
||||
if (oldLocation is GlobalOverride global &&
|
||||
(!removeFromOverride || global.Recursive == recursive))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterUpdate(index, new GlobalOverride());
|
||||
RegisterUpdate(index, new GlobalOverride(recursive));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -411,12 +432,20 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <param name="removeFromOverride">An index at an override position will not be updated unless you set this flag.</param>
|
||||
public void UpdateIndex(TIndex index, ICommonSession session, bool removeFromOverride = false)
|
||||
{
|
||||
if(!removeFromOverride && IsOverride(index))
|
||||
if (!TryGetLocation(index, out var oldLocation))
|
||||
{
|
||||
RegisterUpdate(index, new LocalOverride(session));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!removeFromOverride || oldLocation is GlobalOverride)
|
||||
return;
|
||||
|
||||
if (_indexLocations.TryGetValue(index, out var oldLocation) &&
|
||||
oldLocation is LocalOverride local &&
|
||||
local.Session == session) return;
|
||||
if (oldLocation is LocalOverride local &&
|
||||
(!removeFromOverride || local.Session == session))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterUpdate(index, new LocalOverride(session));
|
||||
}
|
||||
@@ -429,34 +458,42 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <param name="removeFromOverride">An index at an override position will not be updated unless you set this flag.</param>
|
||||
public void UpdateIndex(TIndex index, EntityCoordinates coordinates, bool removeFromOverride = false)
|
||||
{
|
||||
if(!removeFromOverride && IsOverride(index))
|
||||
if (!removeFromOverride
|
||||
&& TryGetLocation(index, out var oldLocation)
|
||||
&& oldLocation is GlobalOverride or LocalOverride)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entityManager.TryGetComponent(coordinates.EntityId, out TransformComponent? xform))
|
||||
return;
|
||||
|
||||
var gridIdOpt = coordinates.GetGridUid(_entityManager);
|
||||
if (gridIdOpt is EntityUid gridId && gridId.IsValid())
|
||||
if (xform.GridUid is { } gridId && gridId.IsValid())
|
||||
{
|
||||
var gridIndices = GetChunkIndices(coordinates.Position);
|
||||
UpdateIndex(index, gridId, gridIndices, true); //skip overridecheck bc we already did it (saves some dict lookups)
|
||||
return;
|
||||
}
|
||||
|
||||
var mapCoordinates = coordinates.ToMap(_entityManager, _transformSystem);
|
||||
var mapIndices = GetChunkIndices(coordinates.Position);
|
||||
UpdateIndex(index, mapCoordinates.MapId, mapIndices, true); //skip overridecheck bc we already did it (saves some dict lookups)
|
||||
var worldPos = _transformSystem.GetWorldMatrix(xform).Transform(coordinates.Position);
|
||||
var mapIndices = GetChunkIndices(worldPos);
|
||||
UpdateIndex(index, xform.MapID, mapIndices, true); //skip overridecheck bc we already did it (saves some dict lookups)
|
||||
}
|
||||
|
||||
public IChunkIndexLocation GetChunkIndex(EntityCoordinates coordinates)
|
||||
{
|
||||
var gridIdOpt = coordinates.GetGridUid(_entityManager);
|
||||
if (gridIdOpt is EntityUid gridId && gridId.IsValid())
|
||||
if (!_entityManager.TryGetComponent(coordinates.EntityId, out TransformComponent? xform))
|
||||
return new MapChunkLocation(default, default);
|
||||
|
||||
if (xform.GridUid is { } gridId && gridId.IsValid())
|
||||
{
|
||||
var gridIndices = GetChunkIndices(coordinates.Position);
|
||||
return new GridChunkLocation(gridId, gridIndices);
|
||||
}
|
||||
|
||||
var mapCoordinates = coordinates.ToMap(_entityManager, _transformSystem);
|
||||
var mapIndices = GetChunkIndices(coordinates.Position);
|
||||
return new MapChunkLocation(mapCoordinates.MapId, mapIndices);
|
||||
var worldPos = _transformSystem.GetWorldMatrix(xform).Transform(coordinates.Position);
|
||||
var mapIndices = GetChunkIndices(worldPos);
|
||||
return new MapChunkLocation(xform.MapID, mapIndices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -469,11 +506,14 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <param name="forceDirty">If true, this will mark the previous chunk as dirty even if the entity did not move from that chunk.</param>
|
||||
public void UpdateIndex(TIndex index, EntityUid gridId, Vector2i chunkIndices, bool removeFromOverride = false, bool forceDirty = false)
|
||||
{
|
||||
if(!removeFromOverride && IsOverride(index))
|
||||
_locationChangeBuffer.TryGetValue(index, out var bufferedLocation);
|
||||
_indexLocations.TryGetValue(index, out var oldLocation);
|
||||
|
||||
//removeFromOverride is false 99% of the time.
|
||||
if ((bufferedLocation ?? oldLocation) is GlobalOverride or LocalOverride && !removeFromOverride)
|
||||
return;
|
||||
|
||||
if (_indexLocations.TryGetValue(index, out var oldLocation) &&
|
||||
oldLocation is GridChunkLocation oldGrid &&
|
||||
if (oldLocation is GridChunkLocation oldGrid &&
|
||||
oldGrid.ChunkIndices == chunkIndices &&
|
||||
oldGrid.GridId == gridId)
|
||||
{
|
||||
@@ -497,22 +537,26 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <param name="forceDirty">If true, this will mark the previous chunk as dirty even if the entity did not move from that chunk.</param>
|
||||
public void UpdateIndex(TIndex index, MapId mapId, Vector2i chunkIndices, bool removeFromOverride = false, bool forceDirty = false)
|
||||
{
|
||||
if(!removeFromOverride && IsOverride(index))
|
||||
_locationChangeBuffer.TryGetValue(index, out var bufferedLocation);
|
||||
_indexLocations.TryGetValue(index, out var oldLocation);
|
||||
|
||||
//removeFromOverride is false 99% of the time.
|
||||
if ((bufferedLocation ?? oldLocation) is GlobalOverride or LocalOverride && !removeFromOverride)
|
||||
return;
|
||||
|
||||
if (_indexLocations.TryGetValue(index, out var oldLocation) &&
|
||||
oldLocation is MapChunkLocation oldMap &&
|
||||
// Is this entity just returning to its old location?
|
||||
if (oldLocation is MapChunkLocation oldMap &&
|
||||
oldMap.ChunkIndices == chunkIndices &&
|
||||
oldMap.MapId == mapId)
|
||||
{
|
||||
_locationChangeBuffer.Remove(index);
|
||||
if (bufferedLocation != null)
|
||||
_locationChangeBuffer.Remove(index);
|
||||
|
||||
if (forceDirty)
|
||||
_dirtyChunks.Add(oldMap);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
RegisterUpdate(index, new MapChunkLocation(mapId, chunkIndices));
|
||||
}
|
||||
|
||||
@@ -584,7 +628,18 @@ public struct GridChunkLocation : IIndexLocation, IChunkIndexLocation, IEquatabl
|
||||
}
|
||||
}
|
||||
|
||||
public struct GlobalOverride : IIndexLocation { }
|
||||
public struct GlobalOverride : IIndexLocation
|
||||
{
|
||||
/// <summary>
|
||||
/// If true, this will also send all children of the override.
|
||||
/// </summary>
|
||||
public readonly bool Recursive;
|
||||
|
||||
public GlobalOverride(bool recursive)
|
||||
{
|
||||
Recursive = recursive;
|
||||
}
|
||||
}
|
||||
|
||||
public struct LocalOverride : IIndexLocation
|
||||
{
|
||||
|
||||
@@ -12,20 +12,23 @@ public sealed class PvsOverrideSystem : EntitySystem
|
||||
[Shared.IoC.Dependency] private readonly PvsSystem _pvs = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Used to ensure that an entity is always sent to every client. Overrides any client-specific overrides.
|
||||
/// Used to ensure that an entity is always sent to every client. By default this overrides any client-specific overrides.
|
||||
/// </summary>
|
||||
public void AddGlobalOverride(EntityUid uid)
|
||||
/// <param name="removeExistingOverride">Whether or not to supersede existing overrides.</param>
|
||||
/// <param name="recursive">If true, this will also recursively send any children of the given index.</param>
|
||||
public void AddGlobalOverride(EntityUid uid, bool removeExistingOverride = true, bool recursive = false)
|
||||
{
|
||||
_pvs.EntityPVSCollection.UpdateIndex(uid, true);
|
||||
_pvs.EntityPVSCollection.AddGlobalOverride(uid, removeExistingOverride, recursive);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to ensure that an entity is always sent to a specific client. Overrides any global or pre-existing
|
||||
/// client-specific overrides.
|
||||
/// </summary>
|
||||
public void AddSessionOverride(EntityUid uid, ICommonSession session)
|
||||
/// <param name="removeExistingOverride">Whether or not to supersede existing overrides.</param>
|
||||
public void AddSessionOverride(EntityUid uid, ICommonSession session,bool removeExistingOverride = true)
|
||||
{
|
||||
_pvs.EntityPVSCollection.UpdateIndex(uid, session, true);
|
||||
_pvs.EntityPVSCollection.UpdateIndex(uid, session, removeExistingOverride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Robust.Server.Configuration;
|
||||
@@ -13,11 +12,9 @@ using Robust.Shared.Collections;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Threading;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -107,6 +104,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
private EntityQuery<EyeComponent> _eyeQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -114,6 +112,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
_eyeQuery = GetEntityQuery<EyeComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
|
||||
_entityPvsCollection = RegisterPVSCollection<EntityUid>();
|
||||
|
||||
@@ -387,7 +386,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
pvsCollection.AddGrid(gridId);
|
||||
}
|
||||
|
||||
_entityPvsCollection.UpdateIndex(gridId);
|
||||
_entityPvsCollection.AddGlobalOverride(gridId, true, false);
|
||||
}
|
||||
|
||||
private void OnMapDestroyed(MapChangedEvent e)
|
||||
@@ -407,7 +406,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
if(e.Map == MapId.Nullspace) return;
|
||||
var uid = _mapManager.GetMapEntityId(e.Map);
|
||||
_entityPvsCollection.UpdateIndex(uid);
|
||||
_entityPvsCollection.AddGlobalOverride(uid, true, false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -749,6 +748,16 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
globalEnumerator.Dispose();
|
||||
|
||||
|
||||
var globalRecursiveEnumerator = _entityPvsCollection.GlobalRecursiveOverridesEnumerator;
|
||||
while (globalRecursiveEnumerator.MoveNext())
|
||||
{
|
||||
var uid = globalRecursiveEnumerator.Current;
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
|
||||
}
|
||||
globalRecursiveEnumerator.Dispose();
|
||||
|
||||
var localEnumerator = _entityPvsCollection.GetElementsForSession(session);
|
||||
while (localEnumerator.MoveNext())
|
||||
{
|
||||
@@ -764,7 +773,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
|
||||
var expandEvent = new ExpandPvsEvent(session, new List<EntityUid>());
|
||||
var expandEvent = new ExpandPvsEvent(session);
|
||||
RaiseLocalEvent(ref expandEvent);
|
||||
foreach (var entityUid in expandEvent.Entities)
|
||||
{
|
||||
@@ -772,6 +781,12 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
|
||||
foreach (var entityUid in expandEvent.RecursiveEntities)
|
||||
{
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
|
||||
}
|
||||
|
||||
var entityStates = new List<EntityState>(entStateCount);
|
||||
|
||||
foreach (var (uid, visiblity) in visibleEnts)
|
||||
@@ -898,8 +913,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
public bool RecursivelyAddOverride(
|
||||
in EntityUid uid,
|
||||
public bool RecursivelyAddOverride(in EntityUid uid,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<EntityUid, PvsEntityVisibility> toSend,
|
||||
@@ -911,34 +925,74 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
ref int enteredEntityCount,
|
||||
ref int entStateCount,
|
||||
in int newEntityBudget,
|
||||
in int enteredEntityBudget)
|
||||
in int enteredEntityBudget,
|
||||
bool addChildren = false)
|
||||
{
|
||||
//are we valid?
|
||||
//sometimes uids gets added without being valid YET (looking at you mapmanager) (mapcreate & gridcreated fire before the uids becomes valid)
|
||||
if (!uid.IsValid()) return false;
|
||||
if (!uid.IsValid())
|
||||
return false;
|
||||
|
||||
var parent = transQuery.GetComponent(uid).ParentUid;
|
||||
var xform = transQuery.GetComponent(uid);
|
||||
var parent = xform.ParentUid;
|
||||
if (parent.IsValid() && !RecursivelyAddOverride(in parent, lastAcked, lastSent, toSend, lastSeen, in metaQuery, in transQuery, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget))
|
||||
return false;
|
||||
|
||||
//did we already get added?
|
||||
if (toSend.ContainsKey(uid)) return true;
|
||||
// Note that we check this AFTER adding parents. This is because while this entity may already have been added
|
||||
// to the toSend set, it doesn't guarantee that its parents have been. E.g., if a player ghost just teleported
|
||||
// to follow a far away entity, the player's own entity is still being sent, but we need to ensure that we also
|
||||
// send the new parents, which may otherwise be delayed because of the PVS budget..
|
||||
if (!toSend.ContainsKey(uid))
|
||||
{
|
||||
// TODO PERFORMANCE.
|
||||
// ProcessEntry() unnecessarily checks lastSent.ContainsKey() and maybe lastSeen.Contains(). Given that at this
|
||||
// point the budgets are just ignored, this should just bypass those checks. But then again 99% of the time this
|
||||
// is just the player's own entity + maybe a singularity. So currently not all that performance intensive.
|
||||
var (entered, _) = ProcessEntry(in uid, lastAcked, lastSent, lastSeen, ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
AddToSendSet(in uid, metaQuery.GetComponent(uid), toSend, fromTick, in entered, ref entStateCount);
|
||||
}
|
||||
|
||||
// TODO PERFORMANCE.
|
||||
// ProcessEntry() unnecessarily checks lastSent.ContainsKey() and maybe lastSeen.Contains(). Given that at this
|
||||
// point the budgets are just ignored, this should just bypass those checks. But then again 99% of the time this
|
||||
// is just the player's own entity + maybe a singularity. So currently not all that performance intensive.
|
||||
var (entered, _) = ProcessEntry(in uid, lastAcked, lastSent, lastSeen, ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
if (addChildren)
|
||||
{
|
||||
RecursivelyAddChildren(xform, lastAcked, lastSent, toSend, lastSeen, fromTick, ref newEntityCount,
|
||||
ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
|
||||
AddToSendSet(in uid, metaQuery.GetComponent(uid), toSend, fromTick, in entered, ref entStateCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RecursivelyAddChildren(TransformComponent xform,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<EntityUid, PvsEntityVisibility> toSend,
|
||||
Dictionary<EntityUid, GameTick> lastSeen,
|
||||
in GameTick fromTick,
|
||||
ref int newEntityCount,
|
||||
ref int enteredEntityCount,
|
||||
ref int entStateCount,
|
||||
in int newEntityBudget,
|
||||
in int enteredEntityBudget)
|
||||
{
|
||||
foreach (var child in xform.ChildEntities)
|
||||
{
|
||||
if (!_xformQuery.TryGetComponent(child, out var childXform))
|
||||
continue;
|
||||
|
||||
if (!toSend.ContainsKey(child))
|
||||
{
|
||||
var (entered, _) = ProcessEntry(in child, lastAcked, lastSent, lastSeen, ref newEntityCount,
|
||||
ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
|
||||
AddToSendSet(in child, _metaQuery.GetComponent(child), toSend, fromTick, in entered, ref entStateCount);
|
||||
}
|
||||
|
||||
RecursivelyAddChildren(childXform, lastAcked, lastSent, toSend, lastSeen, fromTick, ref newEntityCount,
|
||||
ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
}
|
||||
|
||||
private (bool Entered, bool ShouldAdd) ProcessEntry(in EntityUid uid,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
@@ -1034,26 +1088,20 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
while (query.MoveNext(out var uid, out var md))
|
||||
{
|
||||
DebugTools.Assert(md.EntityLifeStage >= EntityLifeStage.Initialized);
|
||||
DebugTools.Assert(md.EntityLifeStage < EntityLifeStage.Terminating);
|
||||
if (md.EntityLastModifiedTick <= fromTick)
|
||||
continue;
|
||||
|
||||
var state = GetEntityState(player, uid, fromTick, md);
|
||||
|
||||
// Temporary debugging code.
|
||||
// TODO REMOVE TEMPORARY CODE
|
||||
if (state.Empty)
|
||||
{
|
||||
var msg = $"{nameof(GetEntityState)} returned an empty state while enumerating all. Entity {ToPrettyString(uid)}. Net component Data:";
|
||||
foreach (var (_, cmp) in EntityManager.GetNetComponents(uid))
|
||||
{
|
||||
msg += $"\nName: {_factory.GetComponentName(cmp.GetType())}" +
|
||||
$"Enabled: {cmp.NetSyncEnabled}, " +
|
||||
$"Lifestage: {cmp.LifeStage}, " +
|
||||
$"OwnerOnly: {cmp.SendOnlyToOwner}, " +
|
||||
$"SessionSpecific: {cmp.SessionSpecific}, " +
|
||||
$"LastModified: {cmp.LastModifiedTick}";
|
||||
}
|
||||
Log.Error(msg);
|
||||
Log.Error($@"{nameof(GetEntityState)} returned an empty state while enumerating entities.
|
||||
Tick: {fromTick}--{_gameTiming.CurTick}
|
||||
Entity: {ToPrettyString(uid)}
|
||||
Last modified: {md.EntityLastModifiedTick}
|
||||
Metadata last modified: {md.LastModifiedTick}
|
||||
Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
}
|
||||
|
||||
stateEntities.Add(state);
|
||||
@@ -1077,26 +1125,21 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(md.EntityLifeStage >= EntityLifeStage.Initialized);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick >= md.CreationTick || md.EntityLastModifiedTick == GameTick.Zero);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick > fromTick || md.EntityLastModifiedTick == GameTick.Zero);
|
||||
DebugTools.Assert(md.EntityLifeStage < EntityLifeStage.Terminating);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick >= md.CreationTick);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick > fromTick);
|
||||
|
||||
var state = GetEntityState(player, uid, fromTick, md);
|
||||
|
||||
// Temporary debugging code.
|
||||
// TODO REMOVE TEMPORARY CODE
|
||||
if (state.Empty)
|
||||
{
|
||||
var msg = $"{nameof(GetEntityState)} returned an empty state for new entity {ToPrettyString(uid)}. Net component Data:";
|
||||
foreach (var (_, cmp) in EntityManager.GetNetComponents(uid))
|
||||
{
|
||||
msg += $"\nName: {_factory.GetComponentName(cmp.GetType())}" +
|
||||
$"Enabled: {cmp.NetSyncEnabled}, " +
|
||||
$"Lifestage: {cmp.LifeStage}, " +
|
||||
$"OwnerOnly: {cmp.SendOnlyToOwner}, " +
|
||||
$"SessionSpecific: {cmp.SessionSpecific}, " +
|
||||
$"LastModified: {cmp.LastModifiedTick}";
|
||||
}
|
||||
Log.Error(msg);
|
||||
Log.Error($@"{nameof(GetEntityState)} returned an empty state for a new entity.
|
||||
Tick: {fromTick}--{_gameTiming.CurTick}
|
||||
Entity: {ToPrettyString(uid)}
|
||||
Last modified: {md.EntityLastModifiedTick}
|
||||
Metadata last modified: {md.LastModifiedTick}
|
||||
Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
continue;
|
||||
}
|
||||
|
||||
stateEntities.Add(state);
|
||||
@@ -1109,8 +1152,9 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(md.EntityLifeStage >= EntityLifeStage.Initialized);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick >= md.CreationTick || md.EntityLastModifiedTick == GameTick.Zero);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick > fromTick || md.EntityLastModifiedTick == GameTick.Zero);
|
||||
DebugTools.Assert(md.EntityLifeStage < EntityLifeStage.Terminating);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick >= md.CreationTick);
|
||||
DebugTools.Assert(md.EntityLastModifiedTick > fromTick);
|
||||
|
||||
var state = GetEntityState(player, uid, fromTick, md);
|
||||
if (!state.Empty)
|
||||
@@ -1330,11 +1374,20 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
public readonly struct ExpandPvsEvent
|
||||
{
|
||||
public readonly IPlayerSession Session;
|
||||
public readonly List<EntityUid> Entities;
|
||||
|
||||
public ExpandPvsEvent(IPlayerSession session, List<EntityUid> entities)
|
||||
/// <summary>
|
||||
/// List of entities that will get added to this session's PVS set.
|
||||
/// </summary>
|
||||
public readonly List<EntityUid> Entities = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of entities that will get added to this session's PVS set. Unlike <see cref="Entities"/> this will also
|
||||
/// recursively add all children of the given entity.
|
||||
/// </summary>
|
||||
public readonly List<EntityUid> RecursiveEntities = new();
|
||||
|
||||
public ExpandPvsEvent(IPlayerSession session)
|
||||
{
|
||||
Session = session;
|
||||
Entities = entities;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ namespace Robust.Server.GameStates
|
||||
|
||||
using (_usageHistogram.WithLabels("Clean Dirty").NewTimer())
|
||||
{
|
||||
_pvs.CleanupDirty(_playerManager.ServerSessions);
|
||||
_pvs.CleanupDirty(players);
|
||||
}
|
||||
|
||||
// keep the deletion history buffers clean
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<PackageReference Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.Extensions.Primitives" Version="6.0.0" />
|
||||
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.2" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" PrivateAssets="compile"/>
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" PrivateAssets="compile" />
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.2" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.Sodium" Version="0.2.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="SharpZstd.Interop" Version="1.5.2-beta2" PrivateAssets="compile" />
|
||||
|
||||
@@ -29,7 +29,6 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Xml.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Utility;
|
||||
using SysVector3 = System.Numerics.Vector3;
|
||||
@@ -1638,6 +1637,11 @@ namespace Robust.Shared.Maths
|
||||
/// </summary>
|
||||
public static Color RoyalBlue => new(65, 105, 225, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (204, 71, 120, 255).
|
||||
/// </summary>
|
||||
public static Color Ruber => new(204, 71, 120, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (139, 69, 19, 255).
|
||||
/// </summary>
|
||||
@@ -1653,6 +1657,11 @@ namespace Robust.Shared.Maths
|
||||
/// </summary>
|
||||
public static Color SandyBrown => new(244, 164, 96, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (0, 66, 153, 255).
|
||||
/// </summary>
|
||||
public static Color SeaBlue => new(0, 66, 153, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (46, 139, 87, 255).
|
||||
/// </summary>
|
||||
@@ -1733,6 +1742,16 @@ namespace Robust.Shared.Maths
|
||||
/// </summary>
|
||||
public static Color Violet => new(238, 130, 238, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (126, 3, 168, 255).
|
||||
/// </summary>
|
||||
public static Color BetterViolet => new(126, 3, 168, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (255, 153, 0, 255).
|
||||
/// </summary>
|
||||
public static Color VividGamboge => new(255, 153, 0, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system color with (R, G, B, A) = (245, 222, 179, 255).
|
||||
/// </summary>
|
||||
@@ -1767,6 +1786,7 @@ namespace Robust.Shared.Maths
|
||||
["aquamarine"] = Aquamarine,
|
||||
["azure"] = Azure,
|
||||
["beige"] = Beige,
|
||||
["betterviolet"] = BetterViolet,
|
||||
["bisque"] = Bisque,
|
||||
["black"] = Black,
|
||||
["blanchedalmond"] = BlanchedAlmond,
|
||||
@@ -1877,9 +1897,11 @@ namespace Robust.Shared.Maths
|
||||
["red"] = Red,
|
||||
["rosybrown"] = RosyBrown,
|
||||
["royalblue"] = RoyalBlue,
|
||||
["ruber"] = Ruber,
|
||||
["saddlebrown"] = SaddleBrown,
|
||||
["salmon"] = Salmon,
|
||||
["sandybrown"] = SandyBrown,
|
||||
["seablue"] = SeaBlue,
|
||||
["seagreen"] = SeaGreen,
|
||||
["seashell"] = SeaShell,
|
||||
["sienna"] = Sienna,
|
||||
@@ -1896,6 +1918,7 @@ namespace Robust.Shared.Maths
|
||||
["tomato"] = Tomato,
|
||||
["turquoise"] = Turquoise,
|
||||
["violet"] = Violet,
|
||||
["vividgamboge"] = VividGamboge,
|
||||
["wheat"] = Wheat,
|
||||
["white"] = White,
|
||||
["whitesmoke"] = WhiteSmoke,
|
||||
|
||||
@@ -283,6 +283,12 @@ namespace Robust.Shared
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> NetFakeDuplicates = CVarDef.Create("net.fakeduplicates", 0f, CVar.CHEAT);
|
||||
|
||||
/// <summary>
|
||||
/// When using Happy Eyeballs to try both IPv6 over IPv4, the delay that IPv4 gets to get less priority.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> NetHappyEyeballsDelay =
|
||||
CVarDef.Create("net.happy_eyeballs_delay", 0.025f, CVar.CLIENTONLY);
|
||||
|
||||
/**
|
||||
* SUS
|
||||
*/
|
||||
@@ -1189,10 +1195,10 @@ namespace Robust.Shared
|
||||
CVarDef.Create("discord.enabled", true, CVar.CLIENTONLY);
|
||||
|
||||
public static readonly CVarDef<string> DiscordRichPresenceMainIconId =
|
||||
CVarDef.Create("discord.rich_main_icon_id", "devstation", CVar.CLIENTONLY);
|
||||
CVarDef.Create("discord.rich_main_icon_id", "devstation", CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
public static readonly CVarDef<string> DiscordRichPresenceSecondIconId =
|
||||
CVarDef.Create("discord.rich_second_icon_id", "logo", CVar.CLIENTONLY);
|
||||
CVarDef.Create("discord.rich_second_icon_id", "logo", CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
/*
|
||||
* RES
|
||||
|
||||
@@ -100,9 +100,15 @@ namespace Robust.Shared.GameObjects
|
||||
/// </remarks>
|
||||
internal void LifeShutdown(IEntityManager entManager)
|
||||
{
|
||||
// Starting allows a component to remove itself in it's own Startup function.
|
||||
DebugTools.Assert(LifeStage == ComponentLifeStage.Starting || LifeStage == ComponentLifeStage.Running);
|
||||
DebugTools.Assert(LifeStage is >= ComponentLifeStage.Initializing and < ComponentLifeStage.Stopping);
|
||||
|
||||
if (LifeStage <= ComponentLifeStage.Initialized)
|
||||
{
|
||||
// Component was never started, no shutdown logic necessary. Simply mark it as stopped.
|
||||
LifeStage = ComponentLifeStage.Stopped;
|
||||
return;
|
||||
}
|
||||
|
||||
LifeStage = ComponentLifeStage.Stopping;
|
||||
entManager.EventBus.RaiseComponentEvent(this, CompShutdownInstance);
|
||||
LifeStage = ComponentLifeStage.Stopped;
|
||||
|
||||
@@ -328,6 +328,12 @@ namespace Robust.Shared.GameObjects
|
||||
return GetRegistration(componentType).Name;
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public string GetComponentName(ushort netID)
|
||||
{
|
||||
return GetRegistration(netID).Name;
|
||||
}
|
||||
|
||||
public ComponentRegistration GetRegistration(ushort netID)
|
||||
{
|
||||
if (_networkedComponents is null)
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// Every entity starts at tick 1, because they are conceptually created in the time between 0->1
|
||||
[ViewVariables]
|
||||
public GameTick EntityLastModifiedTick { get; internal set; } = GameTick.Zero;
|
||||
public GameTick EntityLastModifiedTick { get; internal set; } = GameTick.First;
|
||||
|
||||
/// <summary>
|
||||
/// This is the tick at which the client last applied state data received from the server.
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
foreach (var comp in comps)
|
||||
{
|
||||
if (comp is { LifeStage: < ComponentLifeStage.Initialized })
|
||||
if (comp is { LifeStage: ComponentLifeStage.Added })
|
||||
comp.LifeInitialize(this, CompIdx.Index(comp.GetType()));
|
||||
}
|
||||
|
||||
@@ -490,7 +490,7 @@ namespace Robust.Shared.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.Running)
|
||||
if (component.LifeStage >= ComponentLifeStage.Initialized && component.LifeStage <= ComponentLifeStage.Running)
|
||||
component.LifeShutdown(this);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
}
|
||||
@@ -1409,6 +1409,13 @@ namespace Robust.Shared.GameObjects
|
||||
return _traitDict.TryGetValue(uid, out var comp) && !comp.Deleted;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public bool HasComponent(EntityUid? uid)
|
||||
{
|
||||
return uid != null && HasComponent(uid.Value);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public bool Resolve(EntityUid uid, [NotNullWhen(true)] ref TComp1? component, bool logMissing = true)
|
||||
|
||||
@@ -661,6 +661,15 @@ namespace Robust.Shared.GameObjects
|
||||
return uid.HasValue && EntityExists(uid.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPaused(EntityUid? uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
if (uid == null)
|
||||
return false;
|
||||
|
||||
return _metaQuery.Resolve(uid.Value, ref metadata) && metadata.EntityPaused;
|
||||
}
|
||||
|
||||
public bool Deleted(EntityUid uid)
|
||||
{
|
||||
return !_entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()].TryGetValue(uid, out var comp) || ((MetaDataComponent) comp).EntityDeleted;
|
||||
@@ -742,8 +751,17 @@ namespace Robust.Shared.GameObjects
|
||||
if (prototypeName == null)
|
||||
return AllocEntity(out _, uid);
|
||||
|
||||
PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype);
|
||||
if (!PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype))
|
||||
throw new EntityCreationException($"Attempted to spawn an entity with an invalid prototype: {prototypeName}");
|
||||
|
||||
return CreateEntity(prototype, uid, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates an entity and loads components but does not do initialization.
|
||||
/// </summary>
|
||||
private protected EntityUid CreateEntity(EntityPrototype prototype, EntityUid uid = default, IEntityLoadContext? context = null)
|
||||
{
|
||||
var entity = AllocEntity(prototype, out var metadata, uid);
|
||||
try
|
||||
{
|
||||
@@ -755,7 +773,7 @@ namespace Robust.Shared.GameObjects
|
||||
// Exception during entity loading.
|
||||
// Need to delete the entity to avoid corrupt state causing crashes later.
|
||||
DeleteEntity(entity);
|
||||
throw new EntityCreationException($"Exception inside CreateEntity with prototype {prototypeName}", e);
|
||||
throw new EntityCreationException($"Exception inside CreateEntity with prototype {prototype.ID}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -182,6 +182,12 @@ public partial class EntitySystem
|
||||
|
||||
#region Entity Metadata
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool IsPaused(EntityUid? uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
return EntityManager.IsPaused(uid, metadata);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks an entity as dirty.
|
||||
/// </summary>
|
||||
|
||||
@@ -156,6 +156,17 @@ namespace Robust.Shared.GameObjects
|
||||
/// </exception>
|
||||
[Pure]
|
||||
string GetComponentName(Type componentType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of a component, throwing an exception if it does not exist.
|
||||
/// </summary>
|
||||
/// <param name="netID">The network ID corresponding to the component.</param>
|
||||
/// <returns>The registered name of the component</returns>
|
||||
/// <exception cref="UnknownComponentException">
|
||||
/// Thrown if no component with id <see cref="netID"/> exists.
|
||||
/// </exception>
|
||||
[Pure]
|
||||
string GetComponentName(ushort netID);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the registration belonging to a component, throwing an exception if it does not exist.
|
||||
|
||||
@@ -134,6 +134,11 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
bool EntityExists([NotNullWhen(true)] EntityUid? uid);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if entity is valid and paused.
|
||||
/// </summary>
|
||||
bool IsPaused([NotNullWhen(true)] EntityUid? uid, MetaDataComponent? metadata = null);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an entity with the specified ID has been deleted or is nonexistent.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.Network
|
||||
{
|
||||
@@ -28,10 +29,13 @@ namespace Robust.Shared.Network
|
||||
/// On the server, this is the session ID for this client.
|
||||
/// On the client, this is the session ID for the client.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
NetUserId UserId { get; }
|
||||
|
||||
[ViewVariables]
|
||||
string UserName { get; }
|
||||
|
||||
[ViewVariables]
|
||||
LoginType AuthType { get; }
|
||||
|
||||
/// <summary>
|
||||
@@ -47,11 +51,13 @@ namespace Robust.Shared.Network
|
||||
/// <summary>
|
||||
/// Average round trip time in milliseconds between the remote peer and us.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
short Ping { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the channel is currently connected to a remote peer.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
bool IsConnected { get; }
|
||||
|
||||
NetUserData UserData { get; }
|
||||
|
||||
@@ -324,7 +324,8 @@ namespace Robust.Shared.Network
|
||||
{
|
||||
DebugTools.AssertNotNull(second);
|
||||
// Connecting via second peer is delayed by 25ms to give an advantage to IPv6, if it works.
|
||||
await Task.Delay(25, cancellationToken);
|
||||
var delay = TimeSpan.FromSeconds(_config.GetCVar(CVars.NetHappyEyeballsDelay));
|
||||
await Task.Delay(delay, cancellationToken);
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Random
|
||||
@@ -29,6 +30,12 @@ namespace Robust.Shared.Random
|
||||
return list[index];
|
||||
}
|
||||
|
||||
public static ref T Pick<T>(this IRobustRandom random, ValueList<T> list)
|
||||
{
|
||||
var index = random.Next(list.Count);
|
||||
return ref list[index];
|
||||
}
|
||||
|
||||
/// <summary>Picks a random element from a collection.</summary>
|
||||
/// <remarks>
|
||||
/// This is O(n).
|
||||
|
||||
@@ -13,6 +13,7 @@ using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Server;
|
||||
@@ -26,7 +27,10 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
using ServerProgram = Robust.Server.Program;
|
||||
@@ -259,6 +263,38 @@ namespace Robust.UnitTesting
|
||||
|
||||
public virtual IntegrationOptions? Options { get; internal set; }
|
||||
|
||||
public IEntityManager EntMan { get; private set; } = default!;
|
||||
public IPrototypeManager ProtoMan { get; private set; } = default!;
|
||||
public IConfigurationManager CfgMan { get; private set; } = default!;
|
||||
public ISharedPlayerManager PlayerMan { get; private set; } = default!;
|
||||
public IGameTiming Timing { get; private set; } = default!;
|
||||
public IMapManager MapMan { get; private set; } = default!;
|
||||
|
||||
protected virtual void ResolveIoC(IDependencyCollection deps)
|
||||
{
|
||||
EntMan = deps.Resolve<IEntityManager>();
|
||||
ProtoMan = deps.Resolve<IPrototypeManager>();
|
||||
CfgMan = deps.Resolve<IConfigurationManager>();
|
||||
PlayerMan = deps.Resolve<ISharedPlayerManager>();
|
||||
Timing = deps.Resolve<IGameTiming>();
|
||||
MapMan = deps.Resolve<IMapManager>();
|
||||
}
|
||||
|
||||
public T System<T>() where T : IEntitySystem
|
||||
{
|
||||
return EntMan.System<T>();
|
||||
}
|
||||
|
||||
public TransformComponent Transform(EntityUid uid)
|
||||
{
|
||||
return EntMan.GetComponent<TransformComponent>(uid);
|
||||
}
|
||||
|
||||
public MetaDataComponent MetaData(EntityUid uid)
|
||||
{
|
||||
return EntMan.GetComponent<MetaDataComponent>(uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the instance is still alive.
|
||||
/// "Alive" indicates that it is able to receive and process commands.
|
||||
@@ -688,6 +724,7 @@ namespace Robust.UnitTesting
|
||||
server.SetupMainLoop();
|
||||
|
||||
GameLoop.RunInit();
|
||||
ResolveIoC(deps);
|
||||
|
||||
return server;
|
||||
}
|
||||
@@ -695,6 +732,11 @@ namespace Robust.UnitTesting
|
||||
|
||||
public sealed class ClientIntegrationInstance : IntegrationInstance
|
||||
{
|
||||
public LocalPlayer? Player => ((IPlayerManager) PlayerMan).LocalPlayer;
|
||||
public ICommonSession? Session => Player?.Session;
|
||||
public NetUserId? User => Session?.UserId;
|
||||
public EntityUid? AttachedEntity => Session?.AttachedEntity;
|
||||
|
||||
public ClientIntegrationInstance(ClientIntegrationOptions? options) : base(options)
|
||||
{
|
||||
ClientOptions = options;
|
||||
@@ -859,6 +901,7 @@ namespace Robust.UnitTesting
|
||||
client.StartupContinue(GameController.DisplayMode.Headless);
|
||||
|
||||
GameLoop.RunInit();
|
||||
ResolveIoC(deps);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
@@ -65,12 +65,12 @@ public sealed class PvsSystemTests : RobustIntegrationTest
|
||||
var mapCoords = new EntityCoordinates(map, new Vector2(2, 2));
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
player = sEntMan.SpawnEntity("", gridCoords);
|
||||
other = sEntMan.SpawnEntity("", gridCoords);
|
||||
player = sEntMan.SpawnEntity(null, gridCoords);
|
||||
other = sEntMan.SpawnEntity(null, gridCoords);
|
||||
otherXform = sEntMan.GetComponent<TransformComponent>(other);
|
||||
|
||||
// Ensure map PVS chunk is not empty
|
||||
sEntMan.SpawnEntity("", mapCoords);
|
||||
sEntMan.SpawnEntity(null, mapCoords);
|
||||
|
||||
// Attach player.
|
||||
var session = (IPlayerSession) sPlayerMan.Sessions.First();
|
||||
|
||||
@@ -10,7 +10,6 @@ using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.UnitTesting.Server.Maps
|
||||
{
|
||||
@@ -59,18 +58,19 @@ entities:
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
// For some reason RobustUnitTest doesn't discover PVSSystem but this does here so ?
|
||||
var syssy = IoCManager.Resolve<IEntitySystemManager>();
|
||||
syssy.Shutdown();
|
||||
syssy.Initialize();
|
||||
|
||||
var compFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
compFactory.RegisterClass<MapDeserializeTestComponent>();
|
||||
compFactory.RegisterClass<VisibilityComponent>();
|
||||
compFactory.RegisterClass<ActorComponent>();
|
||||
compFactory.RegisterClass<IgnoreUIRangeComponent>();
|
||||
compFactory.GenerateNetIds();
|
||||
IoCManager.Resolve<ISerializationManager>().Initialize();
|
||||
|
||||
// For some reason RobustUnitTest doesn't discover PVSSystem but this does here so ?
|
||||
var syssy = IoCManager.Resolve<IEntitySystemManager>();
|
||||
syssy.Shutdown();
|
||||
syssy.Initialize();
|
||||
|
||||
var resourceManager = IoCManager.Resolve<IResourceManagerInternal>();
|
||||
resourceManager.Initialize(null);
|
||||
resourceManager.MountString("/TestMap.yml", MapData);
|
||||
|
||||
@@ -84,7 +84,7 @@ public sealed class DeletionNetworkingTests : RobustIntegrationTest
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var coords = new EntityCoordinates(grid1, new Vector2(0.5f, 0.5f));
|
||||
player = sEntMan.SpawnEntity("", coords);
|
||||
player = sEntMan.SpawnEntity(null, coords);
|
||||
var session = (IPlayerSession) sPlayerMan.Sessions.First();
|
||||
session.AttachToEntity(player);
|
||||
session.JoinGame();
|
||||
@@ -107,10 +107,10 @@ public sealed class DeletionNetworkingTests : RobustIntegrationTest
|
||||
var coords = new EntityCoordinates(grid2, new Vector2(0.5f, 0.5f));
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
entA = sEntMan.SpawnEntity("", coords);
|
||||
entB = sEntMan.SpawnEntity("", coords);
|
||||
childA = sEntMan.SpawnEntity("", new EntityCoordinates(entA, default));
|
||||
childB = sEntMan.SpawnEntity("", new EntityCoordinates(entB, default));
|
||||
entA = sEntMan.SpawnEntity(null, coords);
|
||||
entB = sEntMan.SpawnEntity(null, coords);
|
||||
childA = sEntMan.SpawnEntity(null, new EntityCoordinates(entA, default));
|
||||
childB = sEntMan.SpawnEntity(null, new EntityCoordinates(entB, default));
|
||||
});
|
||||
|
||||
await RunTicks();
|
||||
@@ -122,10 +122,10 @@ public sealed class DeletionNetworkingTests : RobustIntegrationTest
|
||||
EntityUid clientChildB = default;
|
||||
await client.WaitPost(() =>
|
||||
{
|
||||
entC = cEntMan.SpawnEntity("", coords);
|
||||
childC = cEntMan.SpawnEntity("", new EntityCoordinates(entC, default));
|
||||
clientChildA = cEntMan.SpawnEntity("", new EntityCoordinates(entA, default));
|
||||
clientChildB = cEntMan.SpawnEntity("", new EntityCoordinates(entB, default));
|
||||
entC = cEntMan.SpawnEntity(null, coords);
|
||||
childC = cEntMan.SpawnEntity(null, new EntityCoordinates(entC, default));
|
||||
clientChildA = cEntMan.SpawnEntity(null, new EntityCoordinates(entA, default));
|
||||
clientChildB = cEntMan.SpawnEntity(null, new EntityCoordinates(entB, default));
|
||||
});
|
||||
|
||||
await RunTicks();
|
||||
|
||||
@@ -202,7 +202,9 @@ namespace Robust.UnitTesting.Shared.Maths
|
||||
Assert.That(sysColor, Is.EqualTo((System.Drawing.Color) color));
|
||||
}
|
||||
|
||||
static IEnumerable<string> DefaultColorNames => Color.GetAllDefaultColors().Select(e => e.Key);
|
||||
static IEnumerable<string> DefaultColorNames => Color.GetAllDefaultColors()
|
||||
.Where(e => System.Drawing.Color.FromName(e.Key).IsKnownColor)
|
||||
.Select(e => e.Key);
|
||||
|
||||
[Test]
|
||||
public void GetAllDefaultColorsFromName([ValueSource(nameof(DefaultColorNames))] string colorName)
|
||||
|
||||
@@ -74,7 +74,7 @@ public sealed class BroadphaseNetworkingTest : RobustIntegrationTest
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var coords = new EntityCoordinates(grid1, new Vector2(0.5f, 0.5f));
|
||||
player = sEntMan.SpawnEntity("", coords);
|
||||
player = sEntMan.SpawnEntity(null, coords);
|
||||
|
||||
// Enable physics
|
||||
var physics = sEntMan.AddComponent<PhysicsComponent>(player);
|
||||
|
||||
Reference in New Issue
Block a user