mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Merge branch 'master' into robust-client-CEF
This commit is contained in:
@@ -628,10 +628,15 @@ namespace Robust.Client.Audio.Midi
|
||||
void IMidiRenderer.InternalDispose()
|
||||
{
|
||||
Source?.Dispose();
|
||||
_driver?.Dispose();
|
||||
|
||||
// Do NOT dispose of the sequencer after the synth or it'll cause a segfault for some fucking reason.
|
||||
_sequencer?.UnregisterClient(_debugRegister);
|
||||
_sequencer?.UnregisterClient(_synthRegister);
|
||||
_sequencer?.Dispose();
|
||||
|
||||
_synth?.Dispose();
|
||||
_player?.Dispose();
|
||||
_driver?.Dispose();
|
||||
_sequencer?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
Robust.Client/Console/Commands/LightBBCommand.cs
Normal file
19
Robust.Client/Console/Commands/LightBBCommand.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
#if DEBUG
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
internal sealed class LightDebugCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "lightbb";
|
||||
public string Description => "Toggles whether to show light bounding boxes";
|
||||
public string Help => $"{Command}";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<DebugLightTreeSystem>().Enabled ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -7,13 +7,13 @@ namespace Robust.Client
|
||||
#if FULL_RELEASE
|
||||
throw new System.InvalidOperationException("ContentStart.Start is not available on a full release.");
|
||||
#else
|
||||
GameController.Start(args, true);
|
||||
GameController.Start(args, new GameControllerOptions(), true);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void StartLibrary(string[] args, GameControllerOptions options)
|
||||
{
|
||||
GameController.Start(args, true, null, options);
|
||||
GameController.Start(args, options, true, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,8 +177,9 @@ namespace Robust.Client.Debugging
|
||||
foreach (var fixture in physBody.Fixtures)
|
||||
{
|
||||
var shape = fixture.Shape;
|
||||
var sleepPercent = physBody.Awake ? physBody.SleepTime / sleepThreshold : 1.0f;
|
||||
var sleepPercent = physBody.Awake ? 0.0f : 1.0f;
|
||||
shape.DebugDraw(drawing, transform.WorldMatrix, in viewport, sleepPercent);
|
||||
drawing.SetTransform(in Matrix3.Identity);
|
||||
}
|
||||
|
||||
foreach (var joint in physBody.Joints)
|
||||
@@ -187,6 +188,7 @@ namespace Robust.Client.Debugging
|
||||
drawnJoints.Add(joint);
|
||||
|
||||
joint.DebugDraw(drawing, in viewport);
|
||||
drawing.SetTransform(in Matrix3.Identity);
|
||||
}
|
||||
|
||||
if (worldBox.Contains(mouseWorldPos))
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client
|
||||
{
|
||||
public void Main(IMainArgs args)
|
||||
{
|
||||
Start(args.Args, contentStart: false, args);
|
||||
Start(args.Args, new GameControllerOptions(), contentStart: false, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ namespace Robust.Client
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Start(args);
|
||||
Start(args, new GameControllerOptions());
|
||||
}
|
||||
|
||||
public static void Start(string[] args, bool contentStart = false, IMainArgs? loaderArgs=null, GameControllerOptions? options = null)
|
||||
public static void Start(string[] args, GameControllerOptions options, bool contentStart = false, IMainArgs? loaderArgs=null)
|
||||
{
|
||||
if (_hasStarted)
|
||||
{
|
||||
@@ -40,7 +40,7 @@ namespace Robust.Client
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParsedMain(CommandLineArgs args, bool contentStart, IMainArgs? loaderArgs, GameControllerOptions? options)
|
||||
private static void ParsedMain(CommandLineArgs args, bool contentStart, IMainArgs? loaderArgs, GameControllerOptions options)
|
||||
{
|
||||
IoCManager.InitThread();
|
||||
|
||||
@@ -51,15 +51,13 @@ namespace Robust.Client
|
||||
var gc = IoCManager.Resolve<GameController>();
|
||||
gc.SetCommandLineArgs(args);
|
||||
gc._loaderArgs = loaderArgs;
|
||||
if(options != null)
|
||||
gc.Options = options;
|
||||
|
||||
// When the game is ran with the startup executable being content,
|
||||
// we have to disable the separate load context.
|
||||
// Otherwise the content assemblies will be loaded twice which causes *many* fun bugs.
|
||||
gc.ContentStart = contentStart;
|
||||
|
||||
gc.Run(mode);
|
||||
gc.Run(mode, options);
|
||||
}
|
||||
|
||||
public void OverrideMainLoop(IGameLoop gameLoop)
|
||||
@@ -67,9 +65,9 @@ namespace Robust.Client
|
||||
_mainLoop = gameLoop;
|
||||
}
|
||||
|
||||
public void Run(DisplayMode mode, Func<ILogHandler>? logHandlerFactory = null)
|
||||
public void Run(DisplayMode mode, GameControllerOptions options, Func<ILogHandler>? logHandlerFactory = null)
|
||||
{
|
||||
if (!StartupSystemSplash(logHandlerFactory))
|
||||
if (!StartupSystemSplash(options, logHandlerFactory))
|
||||
{
|
||||
Logger.Fatal("Failed to start game controller!");
|
||||
return;
|
||||
|
||||
@@ -75,8 +75,6 @@ namespace Robust.Client
|
||||
public GameControllerOptions Options { get; private set; } = new();
|
||||
public InitialLaunchState LaunchState { get; private set; } = default!;
|
||||
|
||||
public bool LoadConfigAndUserData { get; set; } = true;
|
||||
|
||||
public void SetCommandLineArgs(CommandLineArgs args)
|
||||
{
|
||||
_commandLineArgs = args;
|
||||
@@ -95,7 +93,7 @@ namespace Robust.Client
|
||||
_modLoader.SetUseLoadContext(!ContentStart);
|
||||
_modLoader.SetEnableSandboxing(Options.Sandboxing);
|
||||
|
||||
if (!_modLoader.TryLoadModulesFrom(new ResourcePath("/Assemblies/"), Options.ContentModulePrefix))
|
||||
if (!_modLoader.TryLoadModulesFrom(Options.AssemblyDirectory, Options.ContentModulePrefix))
|
||||
{
|
||||
Logger.Fatal("Errors while loading content assemblies.");
|
||||
return false;
|
||||
@@ -196,8 +194,9 @@ namespace Robust.Client
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool StartupSystemSplash(Func<ILogHandler>? logHandlerFactory)
|
||||
internal bool StartupSystemSplash(GameControllerOptions options, Func<ILogHandler>? logHandlerFactory)
|
||||
{
|
||||
Options = options;
|
||||
ReadInitialLaunchState();
|
||||
|
||||
SetupLogging(_logManager, logHandlerFactory ?? (() => new ConsoleLogHandler()));
|
||||
@@ -234,7 +233,7 @@ namespace Robust.Client
|
||||
_configurationManager.LoadCVarsFromAssembly(typeof(GameController).Assembly); // Client
|
||||
_configurationManager.LoadCVarsFromAssembly(typeof(IConfigurationManager).Assembly); // Shared
|
||||
|
||||
if (LoadConfigAndUserData)
|
||||
if (Options.LoadConfigAndUserData)
|
||||
{
|
||||
var configFile = Path.Combine(userDataDir, Options.ConfigFileName);
|
||||
if (File.Exists(configFile))
|
||||
@@ -258,13 +257,13 @@ namespace Robust.Client
|
||||
|
||||
ProfileOptSetup.Setup(_configurationManager);
|
||||
|
||||
_resourceCache.Initialize(LoadConfigAndUserData ? userDataDir : null);
|
||||
_resourceCache.Initialize(Options.LoadConfigAndUserData ? userDataDir : null);
|
||||
|
||||
var mountOptions = _commandLineArgs != null
|
||||
? MountOptions.Merge(_commandLineArgs.MountOptions, Options.MountOptions) : Options.MountOptions;
|
||||
|
||||
ProgramShared.DoMounts(_resourceCache, mountOptions, Options.ContentBuildDirectory,
|
||||
_loaderArgs != null && !Options.ResourceMountDisabled, ContentStart);
|
||||
ProgramShared.DoMounts(_resourceCache, mountOptions, Options.ContentBuildDirectory, Options.AssemblyDirectory,
|
||||
Options.LoadContentResources, _loaderArgs != null && !Options.ResourceMountDisabled, ContentStart);
|
||||
|
||||
if (_loaderArgs != null)
|
||||
{
|
||||
|
||||
@@ -42,6 +42,11 @@ namespace Robust.Client
|
||||
/// </summary>
|
||||
public string ContentBuildDirectory { get; init; } = "Content.Client";
|
||||
|
||||
/// <summary>
|
||||
/// Directory to load all assemblies from.
|
||||
/// </summary>
|
||||
public ResourcePath AssemblyDirectory { get; init; } = new(@"/Assemblies/");
|
||||
|
||||
/// <summary>
|
||||
/// Directory to load all prototypes from.
|
||||
/// </summary>
|
||||
@@ -52,6 +57,16 @@ namespace Robust.Client
|
||||
/// </summary>
|
||||
public bool ResourceMountDisabled { get; init; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to mount content resources when not on FULL_RELEASE.
|
||||
/// </summary>
|
||||
public bool LoadContentResources { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to load config and user data.
|
||||
/// </summary>
|
||||
public bool LoadConfigAndUserData { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to disable command line args server auto-connecting.
|
||||
/// </summary>
|
||||
|
||||
@@ -3,8 +3,10 @@ using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -168,6 +170,8 @@ namespace Robust.Client.GameObjects
|
||||
get => _radius;
|
||||
set
|
||||
{
|
||||
if (MathHelper.CloseTo(value, _radius)) return;
|
||||
|
||||
_radius = MathF.Max(value, 0.01f); // setting radius to 0 causes exceptions, so just use a value close enough to zero that it's unnoticeable.
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PointLightRadiusChangedEvent(this));
|
||||
}
|
||||
@@ -181,17 +185,8 @@ namespace Robust.Client.GameObjects
|
||||
Mask = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// What MapId we are intersecting for RenderingTreeSystem.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
internal MapId IntersectingMapId { get; set; } = MapId.Nullspace;
|
||||
|
||||
/// <summary>
|
||||
/// What grids we're on for RenderingTreeSystem.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
internal List<GridId> IntersectingGrids = new();
|
||||
internal RenderingTreeComponent? RenderTree { get; set; }
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
|
||||
@@ -131,17 +131,8 @@ namespace Robust.Client.GameObjects
|
||||
[DataField("directional")]
|
||||
private bool _directional = true;
|
||||
|
||||
/// <summary>
|
||||
/// What MapId we are intersecting for RenderingTreeSystem.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
internal MapId IntersectingMapId { get; set; } = MapId.Nullspace;
|
||||
|
||||
/// <summary>
|
||||
/// What grids we're on for RenderingTreeSystem.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
internal List<GridId> IntersectingGrids { get; } = new();
|
||||
internal RenderingTreeComponent? RenderTree { get; set; } = null;
|
||||
|
||||
[DataField("layerDatums")]
|
||||
private List<PrototypeLayerData> LayerDatums
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class RenderingTreeComponent : Component
|
||||
{
|
||||
public override string Name => "RenderingTree";
|
||||
|
||||
internal DynamicTree<SpriteComponent> SpriteTree { get; private set; } = new(SpriteAabbFunc);
|
||||
internal DynamicTree<PointLightComponent> LightTree { get; private set; } = new(LightAabbFunc);
|
||||
|
||||
private static Box2 SpriteAabbFunc(in SpriteComponent value)
|
||||
{
|
||||
var worldPos = value.Owner.Transform.WorldPosition;
|
||||
return new Box2(worldPos, worldPos);
|
||||
}
|
||||
|
||||
private static Box2 LightAabbFunc(in PointLightComponent value)
|
||||
{
|
||||
var worldPos = value.Owner.Transform.WorldPosition;
|
||||
|
||||
var boxSize = value.Radius * 2;
|
||||
return Box2.CenteredAround(worldPos, (boxSize, boxSize));
|
||||
}
|
||||
|
||||
internal static Box2 SpriteAabbFunc(SpriteComponent value, Vector2? worldPos = null)
|
||||
{
|
||||
worldPos ??= value.Owner.Transform.WorldPosition;
|
||||
|
||||
return new Box2(worldPos.Value, worldPos.Value);
|
||||
}
|
||||
|
||||
internal static Box2 LightAabbFunc(PointLightComponent value, Vector2? worldPos = null)
|
||||
{
|
||||
worldPos ??= value.Owner.Transform.WorldPosition;
|
||||
var boxSize = value.Radius * 2;
|
||||
return Box2.CenteredAround(worldPos.Value, (boxSize, boxSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#if DEBUG
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
internal sealed class DebugLightTreeSystem : EntitySystem
|
||||
{
|
||||
private DebugLightOverlay? _lightOverlay;
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set
|
||||
{
|
||||
if (_enabled == value) return;
|
||||
|
||||
_enabled = value;
|
||||
var overlayManager = IoCManager.Resolve<IOverlayManager>();
|
||||
|
||||
if (_enabled)
|
||||
{
|
||||
_lightOverlay = new DebugLightOverlay(
|
||||
IoCManager.Resolve<IEntityLookup>(),
|
||||
IoCManager.Resolve<IEyeManager>(),
|
||||
IoCManager.Resolve<IMapManager>(),
|
||||
Get<RenderingTreeSystem>());
|
||||
|
||||
overlayManager.AddOverlay(_lightOverlay);
|
||||
}
|
||||
else
|
||||
{
|
||||
overlayManager.RemoveOverlay(_lightOverlay!);
|
||||
_lightOverlay = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _enabled;
|
||||
|
||||
private sealed class DebugLightOverlay : Overlay
|
||||
{
|
||||
private IEntityLookup _lookup;
|
||||
private IEyeManager _eyeManager;
|
||||
private IMapManager _mapManager;
|
||||
|
||||
private RenderingTreeSystem _tree;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public DebugLightOverlay(IEntityLookup lookup, IEyeManager eyeManager, IMapManager mapManager, RenderingTreeSystem tree)
|
||||
{
|
||||
_lookup = lookup;
|
||||
_eyeManager = eyeManager;
|
||||
_mapManager = mapManager;
|
||||
_tree = tree;
|
||||
}
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var map = _eyeManager.CurrentMap;
|
||||
if (map == MapId.Nullspace) return;
|
||||
|
||||
var viewport = _eyeManager.GetWorldViewport();
|
||||
|
||||
foreach (var tree in _tree.GetLightTrees(map, viewport))
|
||||
{
|
||||
foreach (var light in tree)
|
||||
{
|
||||
var aabb = _lookup.GetWorldAabbFromEntity(light.Owner);
|
||||
if (!aabb.Intersects(viewport)) continue;
|
||||
|
||||
args.WorldHandle.DrawRect(aabb, Color.Green.WithAlpha(0.1f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -3,8 +3,11 @@ using System.Drawing;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Physics;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
@@ -18,25 +21,58 @@ namespace Robust.Client.GameObjects
|
||||
[UsedImplicitly]
|
||||
public sealed class RenderingTreeSystem : EntitySystem
|
||||
{
|
||||
internal const string LoggerSawmill = "rendertree";
|
||||
|
||||
// Nullspace is not indexed. Keep that in mind.
|
||||
|
||||
[Dependency] private readonly IMapManagerInternal _mapManager = default!;
|
||||
|
||||
private readonly Dictionary<MapId, Dictionary<GridId, MapTrees>> _gridTrees = new();
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
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)
|
||||
/// <summary>
|
||||
/// <see cref="CVars.MaxLightRadius"/>
|
||||
/// </summary>
|
||||
public float MaxLightRadius { get; private set; }
|
||||
|
||||
internal IEnumerable<RenderingTreeComponent> GetRenderTrees(MapId mapId, Box2 worldAABB)
|
||||
{
|
||||
return _gridTrees[map][grid].SpriteTree;
|
||||
if (mapId == MapId.Nullspace) yield break;
|
||||
|
||||
var enclosed = false;
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldAABB))
|
||||
{
|
||||
yield return EntityManager.GetEntity(grid.GridEntityId).GetComponent<RenderingTreeComponent>();
|
||||
|
||||
// if we're enclosed then we know no other grids relevant + don't need the map's rendertree
|
||||
if (grid.WorldBounds.Encloses(in worldAABB))
|
||||
{
|
||||
enclosed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!enclosed)
|
||||
yield return _mapManager.GetMapEntity(mapId).GetComponent<RenderingTreeComponent>();
|
||||
}
|
||||
|
||||
internal DynamicTree<PointLightComponent> GetLightTreeForMap(MapId map, GridId grid)
|
||||
internal IEnumerable<DynamicTree<SpriteComponent>> GetSpriteTrees(MapId mapId, Box2 worldAABB)
|
||||
{
|
||||
return _gridTrees[map][grid].LightTree;
|
||||
foreach (var comp in GetRenderTrees(mapId, worldAABB))
|
||||
{
|
||||
yield return comp.SpriteTree;
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<DynamicTree<PointLightComponent>> GetLightTrees(MapId mapId, Box2 worldAABB)
|
||||
{
|
||||
foreach (var comp in GetRenderTrees(mapId, worldAABB))
|
||||
{
|
||||
yield return comp.LightTree;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
@@ -48,9 +84,7 @@ namespace Robust.Client.GameObjects
|
||||
UpdatesAfter.Add(typeof(PhysicsSystem));
|
||||
|
||||
_mapManager.MapCreated += MapManagerOnMapCreated;
|
||||
_mapManager.MapDestroyed += MapManagerOnMapDestroyed;
|
||||
_mapManager.OnGridCreated += MapManagerOnGridCreated;
|
||||
_mapManager.OnGridRemoved += MapManagerOnGridRemoved;
|
||||
|
||||
// Due to how recursion works, this must be done.
|
||||
SubscribeLocalEvent<MoveEvent>(AnythingMoved);
|
||||
@@ -65,6 +99,11 @@ namespace Robust.Client.GameObjects
|
||||
SubscribeLocalEvent<PointLightComponent, PointLightRadiusChangedEvent>(PointLightRadiusChanged);
|
||||
SubscribeLocalEvent<PointLightComponent, RenderTreeRemoveLightEvent>(RemoveLight);
|
||||
SubscribeLocalEvent<PointLightComponent, PointLightUpdateEvent>(HandleLightUpdate);
|
||||
|
||||
SubscribeLocalEvent<RenderingTreeComponent, ComponentRemove>(HandleTreeRemove);
|
||||
|
||||
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
configManager.OnValueChanged(CVars.MaxLightRadius, value => MaxLightRadius = value, true);
|
||||
}
|
||||
|
||||
private void HandleLightUpdate(EntityUid uid, PointLightComponent component, PointLightUpdateEvent args)
|
||||
@@ -88,12 +127,12 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
// 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;
|
||||
sender.Owner.HasComponent<RenderingTreeComponent>()) return;
|
||||
|
||||
// This recursive search is needed, as MoveEvent is defined to not care about indirect events like children.
|
||||
// WHATEVER YOU DO, DON'T REPLACE THIS WITH SPAMMING EVENTS UNLESS YOU HAVE A GUARANTEE IT WON'T LAG THE GC.
|
||||
// (Struct-based events ok though)
|
||||
// Ironically this was lagging the GC lolz
|
||||
if (sender.Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
QueueSpriteUpdate(sprite);
|
||||
|
||||
@@ -129,16 +168,10 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private void ClearSprite(SpriteComponent component)
|
||||
{
|
||||
if (_gridTrees.TryGetValue(component.IntersectingMapId, out var gridTrees))
|
||||
{
|
||||
foreach (var gridId in component.IntersectingGrids)
|
||||
{
|
||||
if (!gridTrees.TryGetValue(gridId, out var tree)) continue;
|
||||
tree.SpriteTree.Remove(component);
|
||||
}
|
||||
}
|
||||
if (component.RenderTree == null) return;
|
||||
|
||||
component.IntersectingGrids.Clear();
|
||||
component.RenderTree.SpriteTree.Remove(component);
|
||||
component.RenderTree = null;
|
||||
}
|
||||
|
||||
private void QueueSpriteUpdate(SpriteComponent component)
|
||||
@@ -173,16 +206,10 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private void ClearLight(PointLightComponent component)
|
||||
{
|
||||
if (_gridTrees.TryGetValue(component.IntersectingMapId, out var gridTrees))
|
||||
{
|
||||
foreach (var gridId in component.IntersectingGrids)
|
||||
{
|
||||
if (!gridTrees.TryGetValue(gridId, out var tree)) continue;
|
||||
tree.LightTree.Remove(component);
|
||||
}
|
||||
}
|
||||
if (component.RenderTree == null) return;
|
||||
|
||||
component.IntersectingGrids.Clear();
|
||||
component.RenderTree.LightTree.Remove(component);
|
||||
component.RenderTree = null;
|
||||
}
|
||||
|
||||
private void QueueLightUpdate(PointLightComponent component)
|
||||
@@ -198,31 +225,23 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
base.Shutdown();
|
||||
_mapManager.MapCreated -= MapManagerOnMapCreated;
|
||||
_mapManager.MapDestroyed -= MapManagerOnMapDestroyed;
|
||||
_mapManager.OnGridCreated -= MapManagerOnGridCreated;
|
||||
_mapManager.OnGridRemoved -= MapManagerOnGridRemoved;
|
||||
}
|
||||
|
||||
private void MapManagerOnMapDestroyed(object? sender, MapEventArgs e)
|
||||
private void HandleTreeRemove(EntityUid uid, RenderingTreeComponent component, ComponentRemove args)
|
||||
{
|
||||
foreach (var (_, gridTree) in _gridTrees[e.Map])
|
||||
foreach (var sprite in component.SpriteTree)
|
||||
{
|
||||
foreach (var comp in gridTree.LightTree)
|
||||
{
|
||||
comp.IntersectingGrids.Clear();
|
||||
}
|
||||
|
||||
foreach (var comp in gridTree.SpriteTree)
|
||||
{
|
||||
comp.IntersectingGrids.Clear();
|
||||
}
|
||||
|
||||
// Just in case?
|
||||
gridTree.LightTree.Clear();
|
||||
gridTree.SpriteTree.Clear();
|
||||
sprite.RenderTree = null;
|
||||
}
|
||||
|
||||
_gridTrees.Remove(e.Map);
|
||||
foreach (var light in component.LightTree)
|
||||
{
|
||||
light.RenderTree = null;
|
||||
}
|
||||
|
||||
component.SpriteTree.Clear();
|
||||
component.LightTree.Clear();
|
||||
}
|
||||
|
||||
private void MapManagerOnMapCreated(object? sender, MapEventArgs e)
|
||||
@@ -232,35 +251,31 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
_gridTrees.Add(e.Map, new Dictionary<GridId, MapTrees>
|
||||
{
|
||||
{GridId.Invalid, new MapTrees()}
|
||||
});
|
||||
_mapManager.GetMapEntity(e.Map).EnsureComponent<RenderingTreeComponent>();
|
||||
}
|
||||
|
||||
private void MapManagerOnGridCreated(MapId mapId, GridId gridId)
|
||||
{
|
||||
_gridTrees[mapId].Add(gridId, new MapTrees());
|
||||
EntityManager.GetEntity(_mapManager.GetGrid(gridId).GridEntityId).EnsureComponent<RenderingTreeComponent>();
|
||||
}
|
||||
|
||||
private void MapManagerOnGridRemoved(MapId mapId, GridId gridId)
|
||||
private RenderingTreeComponent? GetRenderTree(IEntity entity)
|
||||
{
|
||||
var gridTree = _gridTrees[mapId][gridId];
|
||||
|
||||
foreach (var sprite in gridTree.SpriteTree)
|
||||
if (entity.Transform.MapID == MapId.Nullspace ||
|
||||
entity.HasComponent<RenderingTreeComponent>()) return null;
|
||||
|
||||
var parent = entity.Transform.Parent?.Owner;
|
||||
|
||||
while (true)
|
||||
{
|
||||
sprite.IntersectingGrids.Remove(gridId);
|
||||
if (parent == null) break;
|
||||
|
||||
if (parent.TryGetComponent(out RenderingTreeComponent? comp)) return comp;
|
||||
parent = parent.Transform.Parent?.Owner;
|
||||
}
|
||||
|
||||
foreach (var light in gridTree.LightTree)
|
||||
{
|
||||
light.IntersectingGrids.Remove(gridId);
|
||||
}
|
||||
|
||||
// Clear in case
|
||||
gridTree.LightTree.Clear();
|
||||
gridTree.SpriteTree.Clear();
|
||||
_gridTrees[mapId].Remove(gridId);
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
@@ -270,55 +285,43 @@ namespace Robust.Client.GameObjects
|
||||
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)
|
||||
var oldMapTree = sprite.RenderTree;
|
||||
var newMapTree = GetRenderTree(sprite.Owner);
|
||||
// TODO: Temp PVS guard
|
||||
var worldPos = sprite.Owner.Transform.WorldPosition;
|
||||
|
||||
if (float.IsNaN(worldPos.X) || float.IsNaN(worldPos.Y))
|
||||
{
|
||||
ClearSprite(sprite);
|
||||
continue;
|
||||
}
|
||||
|
||||
sprite.IntersectingMapId = mapId;
|
||||
var treePos = newMapTree?.Owner.Transform.WorldPosition ?? Vector2.Zero;
|
||||
var aabb = RenderingTreeComponent.SpriteAabbFunc(sprite, worldPos).Translated(-treePos);
|
||||
|
||||
if (mapId == MapId.Nullspace) continue;
|
||||
|
||||
var mapTree = _gridTrees[mapId];
|
||||
var aabb = MapTrees.SpriteAabbFunc(sprite);
|
||||
var intersectingGrids = _mapManager.FindGridIdsIntersecting(mapId, aabb, true).ToList();
|
||||
|
||||
// Remove from old
|
||||
foreach (var gridId in sprite.IntersectingGrids)
|
||||
// If we're on a new map then clear the old one.
|
||||
if (oldMapTree != newMapTree)
|
||||
{
|
||||
if (intersectingGrids.Contains(gridId)) continue;
|
||||
mapTree[gridId].SpriteTree.Remove(sprite);
|
||||
ClearSprite(sprite);
|
||||
newMapTree?.SpriteTree.Add(sprite, aabb);
|
||||
}
|
||||
|
||||
// Rebuild in the update below
|
||||
sprite.IntersectingGrids.Clear();
|
||||
|
||||
// Update / add to new
|
||||
foreach (var gridId in intersectingGrids)
|
||||
else
|
||||
{
|
||||
var translated = aabb.Translated(gridId == GridId.Invalid
|
||||
? Vector2.Zero
|
||||
: -_mapManager.GetGrid(gridId).WorldPosition);
|
||||
|
||||
mapTree[gridId].SpriteTree.AddOrUpdate(sprite, translated);
|
||||
|
||||
sprite.IntersectingGrids.Add(gridId);
|
||||
newMapTree?.SpriteTree.Update(sprite, aabb);
|
||||
}
|
||||
|
||||
sprite.RenderTree = newMapTree;
|
||||
}
|
||||
|
||||
foreach (var light in _lightQueue)
|
||||
{
|
||||
light.TreeUpdateQueued = false;
|
||||
var mapId = light.Owner.Transform.MapID;
|
||||
|
||||
if (!light.Enabled || light.ContainerOccluded)
|
||||
{
|
||||
@@ -326,72 +329,44 @@ namespace Robust.Client.GameObjects
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're on a new map then clear the old one.
|
||||
if (light.IntersectingMapId != mapId)
|
||||
var oldMapTree = light.RenderTree;
|
||||
var newMapTree = GetRenderTree(light.Owner);
|
||||
// TODO: Temp PVS guard
|
||||
var worldPos = light.Owner.Transform.WorldPosition;
|
||||
|
||||
if (float.IsNaN(worldPos.X) || float.IsNaN(worldPos.Y))
|
||||
{
|
||||
ClearLight(light);
|
||||
continue;
|
||||
}
|
||||
|
||||
light.IntersectingMapId = mapId;
|
||||
|
||||
if (mapId == MapId.Nullspace) continue;
|
||||
|
||||
var mapTree = _gridTrees[mapId];
|
||||
var aabb = MapTrees.LightAabbFunc(light);
|
||||
var intersectingGrids = _mapManager.FindGridIdsIntersecting(mapId, aabb, true).ToList();
|
||||
|
||||
// Remove from old
|
||||
foreach (var gridId in light.IntersectingGrids)
|
||||
// TODO: Events need a bit of cleanup so we only validate this on initialize and radius changed events
|
||||
// this is fine for now IMO as it's 1 float check for every light that moves
|
||||
if (light.Radius > MaxLightRadius)
|
||||
{
|
||||
if (intersectingGrids.Contains(gridId)) continue;
|
||||
mapTree[gridId].LightTree.Remove(light);
|
||||
Logger.WarningS(LoggerSawmill, $"Light radius for {light.Owner} set above max radius of {MaxLightRadius}. This may lead to pop-in.");
|
||||
}
|
||||
|
||||
// Rebuild in the update below
|
||||
light.IntersectingGrids.Clear();
|
||||
var treePos = newMapTree?.Owner.Transform.WorldPosition ?? Vector2.Zero;
|
||||
var aabb = RenderingTreeComponent.LightAabbFunc(light, worldPos).Translated(-treePos);
|
||||
|
||||
// Update / add to new
|
||||
foreach (var gridId in intersectingGrids)
|
||||
// If we're on a new map then clear the old one.
|
||||
if (oldMapTree != newMapTree)
|
||||
{
|
||||
var translated = aabb.Translated(gridId == GridId.Invalid
|
||||
? Vector2.Zero
|
||||
: -_mapManager.GetGrid(gridId).WorldPosition);
|
||||
|
||||
mapTree[gridId].LightTree.AddOrUpdate(light, translated);
|
||||
light.IntersectingGrids.Add(gridId);
|
||||
ClearLight(light);
|
||||
newMapTree?.LightTree.Add(light, aabb);
|
||||
}
|
||||
else
|
||||
{
|
||||
newMapTree?.LightTree.Update(light, aabb);
|
||||
}
|
||||
|
||||
light.RenderTree = newMapTree;
|
||||
}
|
||||
|
||||
_spriteQueue.Clear();
|
||||
_lightQueue.Clear();
|
||||
}
|
||||
|
||||
private sealed class MapTrees
|
||||
{
|
||||
public readonly DynamicTree<SpriteComponent> SpriteTree;
|
||||
public readonly DynamicTree<PointLightComponent> LightTree;
|
||||
|
||||
public MapTrees()
|
||||
{
|
||||
SpriteTree = new DynamicTree<SpriteComponent>(SpriteAabbFunc);
|
||||
LightTree = new DynamicTree<PointLightComponent>(LightAabbFunc);
|
||||
}
|
||||
|
||||
internal static Box2 SpriteAabbFunc(in SpriteComponent value)
|
||||
{
|
||||
var worldPos = value.Owner.Transform.WorldPosition;
|
||||
|
||||
return new Box2(worldPos, worldPos);
|
||||
}
|
||||
|
||||
internal static Box2 LightAabbFunc(in PointLightComponent value)
|
||||
{
|
||||
var worldPos = value.Owner.Transform.WorldPosition;
|
||||
|
||||
var boxSize = value.Radius * 2;
|
||||
return Box2.CenteredAround(worldPos, (boxSize, boxSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class RenderTreeRemoveLightEvent : EntityEventArgs
|
||||
|
||||
@@ -50,13 +50,11 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var gridId in _mapManager.FindGridIdsIntersecting(currentMap, pvsBounds, true))
|
||||
foreach (var comp in _treeSystem.GetRenderTrees(currentMap, pvsBounds))
|
||||
{
|
||||
var gridBounds = gridId == GridId.Invalid ? pvsBounds : pvsBounds.Translated(-_mapManager.GetGrid(gridId).WorldPosition);
|
||||
var bounds = pvsBounds.Translated(-comp.Owner.Transform.WorldPosition);
|
||||
|
||||
var mapTree = _treeSystem.GetSpriteTreeForMap(currentMap, gridId);
|
||||
|
||||
mapTree.QueryAabb(ref frameTime, (ref float state, in SpriteComponent value) =>
|
||||
comp.SpriteTree.QueryAabb(ref frameTime, (ref float state, in SpriteComponent value) =>
|
||||
{
|
||||
if (value.IsInert)
|
||||
{
|
||||
@@ -65,7 +63,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
value.FrameUpdate(state);
|
||||
return true;
|
||||
}, gridBounds, approx: true);
|
||||
}, bounds, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
|
||||
@@ -210,6 +210,11 @@ namespace Robust.Client.GameStates
|
||||
ResetPredictedEntities(_timing.CurTick);
|
||||
}
|
||||
|
||||
if (!curState.Extrapolated)
|
||||
{
|
||||
_processor.UpdateFullRep(curState);
|
||||
}
|
||||
|
||||
// Store last tick we got from the GameStateProcessor.
|
||||
_lastProcessedTick = _timing.CurTick;
|
||||
|
||||
@@ -422,6 +427,7 @@ namespace Robust.Client.GameStates
|
||||
//Known entities
|
||||
if (_entities.TryGetEntity(es.Uid, out var entity))
|
||||
{
|
||||
// Logger.Debug($"[{IGameTiming.TickStampStatic}] MOD {es.Uid}");
|
||||
toApply.Add(entity, (es, null));
|
||||
}
|
||||
else //Unknown entities
|
||||
@@ -432,6 +438,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
throw new InvalidOperationException($"Server sent new entity state for {es.Uid} without metadata component!");
|
||||
}
|
||||
// Logger.Debug($"[{IGameTiming.TickStampStatic}] CREATE {es.Uid} {metaState.PrototypeId}");
|
||||
var newEntity = (Entity)_entities.CreateEntity(metaState.PrototypeId, es.Uid);
|
||||
toApply.Add(newEntity, (es, null));
|
||||
toInitialize.Add(newEntity);
|
||||
@@ -471,6 +478,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var id in deletions)
|
||||
{
|
||||
// Logger.Debug($"[{IGameTiming.TickStampStatic}] DELETE {id}");
|
||||
_entities.DeleteEntity(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -149,11 +149,6 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
Logger.DebugS("net.state", $"Applying State: ext={curState!.Extrapolated}, cTick={_timing.CurTick}, fSeq={curState.FromSequence}, tSeq={curState.ToSequence}, buf={_stateBuffer.Count}");
|
||||
}
|
||||
|
||||
if (!curState!.Extrapolated)
|
||||
{
|
||||
UpdateFullRep(curState);
|
||||
}
|
||||
}
|
||||
|
||||
var cState = curState!;
|
||||
@@ -162,8 +157,10 @@ namespace Robust.Client.GameStates
|
||||
return applyNextState;
|
||||
}
|
||||
|
||||
private void UpdateFullRep(GameState state)
|
||||
public void UpdateFullRep(GameState state)
|
||||
{
|
||||
// Logger.Debug($"UPDATE FULL REP: {string.Join(", ", state.EntityStates?.Select(e => e.Uid) ?? Enumerable.Empty<EntityUid>())}");
|
||||
|
||||
if (state.FromSequence == GameTick.Zero)
|
||||
{
|
||||
// Full state.
|
||||
|
||||
@@ -366,24 +366,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private void ProcessSpriteEntities(MapId map, Box2 worldBounds,
|
||||
RefList<(SpriteComponent sprite, Matrix3 matrix, Angle worldRot, float yWorldPos)> list)
|
||||
{
|
||||
var spriteSystem = _entitySystemManager.GetEntitySystem<RenderingTreeSystem>();
|
||||
|
||||
foreach (var gridId in _mapManager.FindGridIdsIntersecting(map, worldBounds, true))
|
||||
foreach (var comp in _entitySystemManager.GetEntitySystem<RenderingTreeSystem>().GetRenderTrees(map, worldBounds))
|
||||
{
|
||||
Box2 gridBounds;
|
||||
var bounds = worldBounds.Translated(-comp.Owner.Transform.WorldPosition);
|
||||
|
||||
if (gridId == GridId.Invalid)
|
||||
{
|
||||
gridBounds = worldBounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
gridBounds = worldBounds.Translated(-_mapManager.GetGrid(gridId).WorldPosition);
|
||||
}
|
||||
|
||||
var tree = spriteSystem.GetSpriteTreeForMap(map, gridId);
|
||||
|
||||
tree.QueryAabb(ref list, ((
|
||||
comp.SpriteTree.QueryAabb(ref list, ((
|
||||
ref RefList<(SpriteComponent sprite, Matrix3 matrix, Angle worldRot, float yWorldPos)> state,
|
||||
in SpriteComponent value) =>
|
||||
{
|
||||
@@ -398,7 +385,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
entry.yWorldPos = worldPos.Y;
|
||||
return true;
|
||||
|
||||
}), gridBounds, approx: true);
|
||||
}), bounds, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -494,24 +494,16 @@ namespace Robust.Client.Graphics.Clyde
|
||||
GetLightsToRender(MapId map, in Box2 worldBounds)
|
||||
{
|
||||
var renderingTreeSystem = _entitySystemManager.GetEntitySystem<RenderingTreeSystem>();
|
||||
var enlargedBounds = worldBounds.Enlarged(renderingTreeSystem.MaxLightRadius);
|
||||
|
||||
// Use worldbounds for this one as we only care if the light intersects our actual bounds
|
||||
var state = (this, worldBounds, count: 0);
|
||||
|
||||
foreach (var gridId in _mapManager.FindGridIdsIntersecting(map, worldBounds, true))
|
||||
foreach (var comp in renderingTreeSystem.GetRenderTrees(map, enlargedBounds))
|
||||
{
|
||||
Box2 gridBounds;
|
||||
var bounds = worldBounds.Translated(-comp.Owner.Transform.WorldPosition);
|
||||
|
||||
if (gridId == GridId.Invalid)
|
||||
{
|
||||
gridBounds = worldBounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
gridBounds = worldBounds.Translated(-_mapManager.GetGrid(gridId).WorldPosition);
|
||||
}
|
||||
|
||||
var lightTree = renderingTreeSystem.GetLightTreeForMap(map, gridId);
|
||||
|
||||
lightTree.QueryAabb(ref state, (ref (Clyde clyde, Box2 worldBounds, int count) state, in PointLightComponent light) =>
|
||||
comp.LightTree.QueryAabb(ref state, (ref (Clyde clyde, Box2 worldBounds, int count) state, in PointLightComponent light) =>
|
||||
{
|
||||
var transform = light.Owner.Transform;
|
||||
|
||||
@@ -535,7 +527,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
state.clyde._lightsToRenderList[state.count++] = (light, lightPos, distanceSquared);
|
||||
|
||||
return true;
|
||||
}, gridBounds);
|
||||
}, bounds);
|
||||
}
|
||||
|
||||
if (state.count > _maxLightsPerScene)
|
||||
|
||||
@@ -10,8 +10,7 @@ namespace Robust.Client
|
||||
GameControllerOptions Options { get; }
|
||||
bool ContentStart { get; set; }
|
||||
void SetCommandLineArgs(CommandLineArgs args);
|
||||
bool LoadConfigAndUserData { get; set; }
|
||||
void Run(GameController.DisplayMode mode, Func<ILogHandler>? logHandlerFactory = null);
|
||||
void Run(GameController.DisplayMode mode, GameControllerOptions options, Func<ILogHandler>? logHandlerFactory = null);
|
||||
void KeyDown(KeyEventArgs keyEvent);
|
||||
void KeyUp(KeyEventArgs keyEvent);
|
||||
void TextEntered(TextEventArgs textEvent);
|
||||
|
||||
@@ -102,6 +102,8 @@ namespace Robust.Server
|
||||
|
||||
private readonly ManualResetEventSlim _shutdownEvent = new(false);
|
||||
|
||||
public ServerOptions Options { get; private set; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public int MaxPlayers => _config.GetCVar(CVars.GameMaxPlayers);
|
||||
|
||||
@@ -114,7 +116,7 @@ namespace Robust.Server
|
||||
Logger.InfoS("srv", "Restarting Server...");
|
||||
|
||||
Cleanup();
|
||||
Start(_logHandlerFactory);
|
||||
Start(Options, _logHandlerFactory);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -141,15 +143,16 @@ namespace Robust.Server
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Start(Func<ILogHandler>? logHandlerFactory = null)
|
||||
public bool Start(ServerOptions options, Func<ILogHandler>? logHandlerFactory = null)
|
||||
{
|
||||
Options = options;
|
||||
var profilePath = Path.Join(Environment.CurrentDirectory, "AAAAAAAA");
|
||||
ProfileOptimization.SetProfileRoot(profilePath);
|
||||
ProfileOptimization.StartProfile("AAAAAAAAAA");
|
||||
|
||||
_config.Initialize(true);
|
||||
|
||||
if (LoadConfigAndUserData)
|
||||
if (Options.LoadConfigAndUserData)
|
||||
{
|
||||
// Sets up the configMgr
|
||||
// If a config file path was passed, use it literally.
|
||||
@@ -269,22 +272,26 @@ namespace Robust.Server
|
||||
return true;
|
||||
}
|
||||
|
||||
var dataDir = LoadConfigAndUserData
|
||||
var dataDir = Options.LoadConfigAndUserData
|
||||
? _commandLineArgs?.DataDir ?? PathHelpers.ExecutableRelativeFile("data")
|
||||
: null;
|
||||
|
||||
// Set up the VFS
|
||||
_resources.Initialize(dataDir);
|
||||
|
||||
ProgramShared.DoMounts(_resources, _commandLineArgs?.MountOptions, "Content.Server", contentStart:ContentStart);
|
||||
var mountOptions = _commandLineArgs != null
|
||||
? MountOptions.Merge(_commandLineArgs.MountOptions, Options.MountOptions) : Options.MountOptions;
|
||||
|
||||
ProgramShared.DoMounts(_resources, mountOptions, Options.ContentBuildDirectory, Options.AssemblyDirectory,
|
||||
Options.LoadContentResources, Options.ResourceMountDisabled, ContentStart);
|
||||
|
||||
// When the game is ran with the startup executable being content,
|
||||
// we have to disable the separate load context.
|
||||
// Otherwise the content assemblies will be loaded twice which causes *many* fun bugs.
|
||||
_modLoader.SetUseLoadContext(!ContentStart);
|
||||
_modLoader.SetEnableSandboxing(false);
|
||||
_modLoader.SetEnableSandboxing(Options.Sandboxing);
|
||||
|
||||
if (!_modLoader.TryLoadModulesFrom(new ResourcePath("/Assemblies/"), "Content."))
|
||||
if (!_modLoader.TryLoadModulesFrom(Options.AssemblyDirectory, Options.ContentModulePrefix))
|
||||
{
|
||||
Logger.Fatal("Errors while loading content assemblies.");
|
||||
return true;
|
||||
@@ -335,7 +342,7 @@ namespace Robust.Server
|
||||
// otherwise the prototypes will be cleared
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
prototypeManager.Initialize();
|
||||
prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes"));
|
||||
prototypeManager.LoadDirectory(Options.PrototypeDirectory);
|
||||
prototypeManager.Resync();
|
||||
|
||||
IoCManager.Resolve<IServerConsoleHost>().Initialize();
|
||||
@@ -492,7 +499,6 @@ namespace Robust.Server
|
||||
}
|
||||
|
||||
public bool ContentStart { get; set; }
|
||||
public bool LoadConfigAndUserData { private get; set; } = true;
|
||||
|
||||
public void OverrideMainLoop(IGameLoop gameLoop)
|
||||
{
|
||||
|
||||
@@ -7,13 +7,13 @@ namespace Robust.Server
|
||||
#if FULL_RELEASE
|
||||
throw new System.InvalidOperationException("ContentStart.Start is not available on a full release.");
|
||||
#else
|
||||
Program.Start(args, true);
|
||||
Program.Start(args, new ServerOptions(), true);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void StartLibrary(string[] args)
|
||||
public static void StartLibrary(string[] args, ServerOptions options)
|
||||
{
|
||||
Program.Start(args, true);
|
||||
Program.Start(args, options, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Robust.Server
|
||||
/// Sets up the server, loads the game, gets ready for client connections.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
bool Start(Func<ILogHandler>? logHandler = null);
|
||||
bool Start(ServerOptions options, Func<ILogHandler>? logHandler = null);
|
||||
|
||||
/// <summary>
|
||||
/// Hard restarts the server, shutting it down, kicking all players, and starting the server again.
|
||||
@@ -44,11 +44,10 @@ namespace Robust.Server
|
||||
|
||||
internal interface IBaseServerInternal : IBaseServer
|
||||
{
|
||||
ServerOptions Options { get; }
|
||||
bool ContentStart { set; }
|
||||
bool LoadConfigAndUserData { set; }
|
||||
|
||||
void OverrideMainLoop(IGameLoop gameLoop);
|
||||
|
||||
void SetCommandLineArgs(CommandLineArgs args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ namespace Robust.Server
|
||||
|
||||
internal static void Main(string[] args)
|
||||
{
|
||||
Start(args);
|
||||
Start(args, new ServerOptions());
|
||||
}
|
||||
|
||||
internal static void Start(string[] args, bool contentStart = false)
|
||||
internal static void Start(string[] args, ServerOptions options, bool contentStart = false)
|
||||
{
|
||||
if (_hasStarted)
|
||||
{
|
||||
@@ -47,11 +47,11 @@ namespace Robust.Server
|
||||
TaskCreationOptions.LongRunning,
|
||||
TaskContinuationOptions.None,
|
||||
RobustTaskScheduler.Instance
|
||||
).StartNew(() => ParsedMain(parsed, contentStart))
|
||||
).StartNew(() => ParsedMain(parsed, contentStart, options))
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private static void ParsedMain(CommandLineArgs args, bool contentStart)
|
||||
private static void ParsedMain(CommandLineArgs args, bool contentStart, ServerOptions options)
|
||||
{
|
||||
Thread.CurrentThread.Name = "Main Thread";
|
||||
IoCManager.InitThread();
|
||||
@@ -67,7 +67,7 @@ namespace Robust.Server
|
||||
|
||||
Logger.Info("Server -> Starting");
|
||||
|
||||
if (server.Start())
|
||||
if (server.Start(options))
|
||||
{
|
||||
Logger.Fatal("Server -> Can not start server");
|
||||
//Not like you'd see this, haha. Perhaps later for logging.
|
||||
|
||||
54
Robust.Server/ServerOptions.cs
Normal file
54
Robust.Server/ServerOptions.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Server
|
||||
{
|
||||
public class ServerOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether content sandboxing will be enabled & enforced.
|
||||
/// </summary>
|
||||
public bool Sandboxing { get; init; } = false;
|
||||
|
||||
// TODO: Expose mounting methods to games using Robust as a library.
|
||||
/// <summary>
|
||||
/// Lists of mount options to mount.
|
||||
/// </summary>
|
||||
public MountOptions MountOptions { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Assemblies with this prefix will be loaded.
|
||||
/// </summary>
|
||||
public string ContentModulePrefix { get; init; } = "Content.";
|
||||
|
||||
/// <summary>
|
||||
/// Name of the content build directory, for game pack mounting purposes.
|
||||
/// </summary>
|
||||
public string ContentBuildDirectory { get; init; } = "Content.Server";
|
||||
|
||||
/// <summary>
|
||||
/// Directory to load all assemblies from.
|
||||
/// </summary>
|
||||
public ResourcePath AssemblyDirectory { get; init; } = new(@"/Assemblies/");
|
||||
|
||||
/// <summary>
|
||||
/// Directory to load all prototypes from.
|
||||
/// </summary>
|
||||
public ResourcePath PrototypeDirectory { get; init; } = new(@"/Prototypes/");
|
||||
|
||||
/// <summary>
|
||||
/// Whether to disable mounting the "Resources/" folder on FULL_RELEASE.
|
||||
/// </summary>
|
||||
public bool ResourceMountDisabled { get; init; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to mount content resources when not on FULL_RELEASE.
|
||||
/// </summary>
|
||||
public bool LoadContentResources { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to load config and user data.
|
||||
/// </summary>
|
||||
public bool LoadConfigAndUserData { get; init; } = true;
|
||||
}
|
||||
}
|
||||
@@ -211,6 +211,21 @@ namespace Robust.Shared
|
||||
public static readonly CVarDef<bool> LogRuntimeLog =
|
||||
CVarDef.Create("log.runtimelog", true, CVar.ARCHIVE | CVar.SERVERONLY);
|
||||
|
||||
/*
|
||||
* Light
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// This is the maximum the viewport is enlarged to check for any intersecting render-trees for lights.
|
||||
/// This should be set to your maximum light radius.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this value is too small it just means there may be pop-in where a light is located on a render-tree
|
||||
/// outside of our viewport.
|
||||
/// </remarks>
|
||||
public static readonly CVarDef<float> MaxLightRadius =
|
||||
CVarDef.Create("light.max_radius", 20.0f, CVar.CLIENTONLY);
|
||||
|
||||
/*
|
||||
* LOKI
|
||||
*/
|
||||
|
||||
@@ -186,14 +186,14 @@ namespace Robust.Shared.GameObjects
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void RemoveComponent(EntityUid uid, Type type)
|
||||
{
|
||||
RemoveComponentDeferred((Component) GetComponent(uid, type), uid, false);
|
||||
RemoveComponentDeferred((Component)GetComponent(uid, type), uid, false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void RemoveComponent(EntityUid uid, uint netId)
|
||||
{
|
||||
RemoveComponentDeferred((Component) GetComponent(uid, netId), uid, false);
|
||||
RemoveComponentDeferred((Component)GetComponent(uid, netId), uid, false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -205,7 +205,7 @@ namespace Robust.Shared.GameObjects
|
||||
if (component.Owner == null || component.Owner.Uid != uid)
|
||||
throw new InvalidOperationException("Component is not owned by entity.");
|
||||
|
||||
RemoveComponentDeferred((Component) component, uid, false);
|
||||
RemoveComponentDeferred((Component)component, uid, false);
|
||||
}
|
||||
|
||||
private static IEnumerable<Component> InSafeOrder(IEnumerable<Component> comps, bool forCreation = false)
|
||||
@@ -256,26 +256,26 @@ namespace Robust.Shared.GameObjects
|
||||
try
|
||||
{
|
||||
#endif
|
||||
// these two components are required on all entities and cannot be removed normally.
|
||||
if (!removeProtected && (component is ITransformComponent || component is IMetaDataComponent))
|
||||
{
|
||||
DebugTools.Assert("Tried to remove a protected component.");
|
||||
return;
|
||||
}
|
||||
// these two components are required on all entities and cannot be removed normally.
|
||||
if (!removeProtected && (component is ITransformComponent || component is IMetaDataComponent))
|
||||
{
|
||||
DebugTools.Assert("Tried to remove a protected component.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_deleteSet.Add(component))
|
||||
{
|
||||
// already deferred deletion
|
||||
return;
|
||||
}
|
||||
if (!_deleteSet.Add(component))
|
||||
{
|
||||
// already deferred deletion
|
||||
return;
|
||||
}
|
||||
|
||||
if(component.Running)
|
||||
component.LifeShutdown();
|
||||
if (component.Running)
|
||||
component.LifeShutdown();
|
||||
|
||||
if (component.LifeStage != ComponentLifeStage.PreAdd)
|
||||
component.LifeRemoveFromEntity();
|
||||
_componentDependencyManager.OnComponentRemove(uid, component);
|
||||
ComponentRemoved?.Invoke(this, new RemovedComponentEventArgs(component, uid));
|
||||
if (component.LifeStage != ComponentLifeStage.PreAdd)
|
||||
component.LifeRemoveFromEntity();
|
||||
_componentDependencyManager.OnComponentRemove(uid, component);
|
||||
ComponentRemoved?.Invoke(this, new RemovedComponentEventArgs(component, uid));
|
||||
#if EXCEPTION_TOLERANCE
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -299,10 +299,10 @@ namespace Robust.Shared.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
if(component.Running)
|
||||
if (component.Running)
|
||||
component.LifeShutdown();
|
||||
|
||||
if(component.LifeStage != ComponentLifeStage.PreAdd)
|
||||
if (component.LifeStage != ComponentLifeStage.PreAdd)
|
||||
component.LifeRemoveFromEntity(); // Sets delete
|
||||
|
||||
ComponentRemoved?.Invoke(this, new RemovedComponentEventArgs(component, component.Owner.Uid));
|
||||
@@ -377,7 +377,7 @@ namespace Robust.Shared.GameObjects
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T GetComponent<T>(EntityUid uid)
|
||||
{
|
||||
return (T) GetComponent(uid, typeof(T));
|
||||
return (T)GetComponent(uid, typeof(T));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -409,7 +409,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
if (!comp.Deleted)
|
||||
{
|
||||
component = (T) comp;
|
||||
component = (T)comp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -489,7 +489,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
if (comp.Deleted || !includePaused && comp.Paused) continue;
|
||||
|
||||
yield return (T) (object) comp;
|
||||
yield return (T)(object)comp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,7 +510,7 @@ namespace Robust.Shared.GameObjects
|
||||
if (!trait2.TryGetValue(uid, out var t2Comp) || t2Comp.Deleted || !includePaused && kvComp.Value.Paused)
|
||||
continue;
|
||||
|
||||
yield return ((TComp1) (object) kvComp.Value, (TComp2) (object) t2Comp);
|
||||
yield return ((TComp1)(object)kvComp.Value, (TComp2)(object)t2Comp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,9 +534,9 @@ namespace Robust.Shared.GameObjects
|
||||
if (!trait3.TryGetValue(uid, out var t3Comp) || t3Comp.Deleted)
|
||||
continue;
|
||||
|
||||
yield return ((TComp1) (object) kvComp.Value,
|
||||
(TComp2) (object) t2Comp,
|
||||
(TComp3) (object) t3Comp);
|
||||
yield return ((TComp1)(object)kvComp.Value,
|
||||
(TComp2)(object)t2Comp,
|
||||
(TComp3)(object)t3Comp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,10 +565,10 @@ namespace Robust.Shared.GameObjects
|
||||
if (!trait4.TryGetValue(uid, out var t4Comp) || t4Comp.Deleted)
|
||||
continue;
|
||||
|
||||
yield return ((TComp1) (object) kvComp.Value,
|
||||
(TComp2) (object) t2Comp,
|
||||
(TComp3) (object) t3Comp,
|
||||
(TComp4) (object) t4Comp);
|
||||
yield return ((TComp1)(object)kvComp.Value,
|
||||
(TComp2)(object)t2Comp,
|
||||
(TComp3)(object)t3Comp,
|
||||
(TComp4)(object)t4Comp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -81,7 +79,6 @@ namespace Robust.Shared.GameObjects
|
||||
xform.Parent = Owner.Transform;
|
||||
|
||||
// anchor snapping
|
||||
xform.LocalRotation = xform.LocalRotation.GetCardinalDir().ToAngle();
|
||||
xform.LocalPosition = Grid.GridTileToLocal(Grid.TileIndicesFor(xform.LocalPosition)).Position;
|
||||
|
||||
xform.SetAnchored(result);
|
||||
@@ -117,7 +114,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new MapGridComponentState(_gridIndex, Grid.HasGravity);
|
||||
return new MapGridComponentState(_gridIndex);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -125,11 +122,10 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (!(curState is MapGridComponentState state))
|
||||
if (curState is not MapGridComponentState state)
|
||||
return;
|
||||
|
||||
_gridIndex = state.GridIndex;
|
||||
Grid.HasGravity = state.HasGravity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,17 +140,14 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public GridId GridIndex { get; }
|
||||
|
||||
public bool HasGravity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="MapGridComponentState"/>.
|
||||
/// </summary>
|
||||
/// <param name="gridIndex">Index of the grid this component is linked to.</param>
|
||||
public MapGridComponentState(GridId gridIndex, bool hasGravity)
|
||||
public MapGridComponentState(GridId gridIndex)
|
||||
: base(NetIDs.MAP_GRID)
|
||||
{
|
||||
GridIndex = gridIndex;
|
||||
HasGravity = hasGravity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,9 +113,6 @@ namespace Robust.Shared.GameObjects
|
||||
if (_localRotation.EqualsApprox(value, 0.00001))
|
||||
return;
|
||||
|
||||
if(Anchored)
|
||||
return;
|
||||
|
||||
var oldRotation = _localRotation;
|
||||
|
||||
// Set _nextRotation to null to break any active lerps if this is a client side prediction.
|
||||
|
||||
@@ -84,6 +84,7 @@ namespace Robust.Shared.GameObjects
|
||||
return;
|
||||
|
||||
_paused = value;
|
||||
EntityManager.EventBus.RaiseLocalEvent(Uid, new EntityPausedEvent(Uid, value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Prometheus;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -157,9 +154,8 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName);
|
||||
|
||||
if (TryGetEntity(coordinates.EntityId, out var entity))
|
||||
if (coordinates.IsValid(this))
|
||||
{
|
||||
newEntity.Transform.AttachParent(entity);
|
||||
newEntity.Transform.Coordinates = coordinates;
|
||||
}
|
||||
|
||||
@@ -228,12 +224,7 @@ namespace Robust.Shared.GameObjects
|
||||
return false;
|
||||
}
|
||||
|
||||
[Obsolete("IEntityQuery is obsolete")]
|
||||
public IEnumerable<IEntity> GetEntities(IEntityQuery query)
|
||||
{
|
||||
return query.Match(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> GetEntities()
|
||||
{
|
||||
// Need to do an iterator loop to avoid issues with concurrent access.
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// An entity query that will let all entities pass.
|
||||
/// This is the same as matching <c>ITransformComponent</c>, but faster.
|
||||
/// </summary>
|
||||
[PublicAPI, Obsolete]
|
||||
public class AllEntityQuery : IEntityQuery
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool Match(IEntity entity) => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> Match(IEntityManager entityMan)
|
||||
{
|
||||
return entityMan.GetEntities();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An entity query which will match entities based on a predicate.
|
||||
/// If you only want a single type of Component, use <c>TypeEntityQuery</c>.
|
||||
/// </summary>
|
||||
[PublicAPI, Obsolete]
|
||||
public class PredicateEntityQuery : IEntityQuery
|
||||
{
|
||||
private readonly Predicate<IEntity> Predicate;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <c>PredicateEntityQuery</c>.
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
public PredicateEntityQuery(Predicate<IEntity> predicate)
|
||||
{
|
||||
if (predicate == null)
|
||||
throw new ArgumentNullException(nameof(predicate));
|
||||
|
||||
Predicate = predicate;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Match(IEntity entity) => Predicate(entity);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> Match(IEntityManager entityMan)
|
||||
{
|
||||
return entityMan.GetEntities().Where(entity => Predicate(entity));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An entity query that will match one type of component.
|
||||
/// This the fastest and most common query, and should be the default choice.
|
||||
/// </summary>
|
||||
[PublicAPI, Obsolete]
|
||||
public class TypeEntityQuery : IEntityQuery
|
||||
{
|
||||
private readonly Type ComponentType;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <c>TypeEntityQuery</c>.
|
||||
/// </summary>
|
||||
/// <param name="componentType">Type of the component to match.</param>
|
||||
public TypeEntityQuery(Type componentType)
|
||||
{
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(componentType), "componentType must inherit IComponent");
|
||||
|
||||
ComponentType = componentType;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Match(IEntity entity) => entity.HasComponent(ComponentType);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> Match(IEntityManager entityMan)
|
||||
{
|
||||
return entityMan.ComponentManager.GetAllComponents(ComponentType, true).Select(component => component.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An entity query that will match one type of component.
|
||||
/// This the fastest and most common query, and should be the default choice.
|
||||
/// </summary>
|
||||
/// <typeparamref name="T">Type of component to match.</typeparamref>
|
||||
[PublicAPI, Obsolete]
|
||||
public class TypeEntityQuery<T> : IEntityQuery where T : IComponent
|
||||
{
|
||||
public bool Match(IEntity entity) => entity.HasComponent<T>();
|
||||
|
||||
public IEnumerable<IEntity> Match(IEntityManager entityMan)
|
||||
{
|
||||
return entityMan.ComponentManager.EntityQuery<T>(true).Select(component => component.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An entity query that will match all entities that intersect with the argument entity.
|
||||
/// </summary>
|
||||
[PublicAPI, Obsolete]
|
||||
public class IntersectingEntityQuery : IEntityQuery
|
||||
{
|
||||
private readonly IEntity Entity;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <c>TypeEntityQuery</c>.
|
||||
/// </summary>
|
||||
/// <param name="componentType">Type of the component to match.</param>
|
||||
public IntersectingEntityQuery(IEntity entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Match(IEntity entity)
|
||||
{
|
||||
if(Entity.TryGetComponent<IPhysBody>(out var physics))
|
||||
{
|
||||
return physics.Owner.Transform.MapID == entity.Transform.MapID && physics.GetWorldAABB().Contains(entity.Transform.WorldPosition);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerable<IEntity> Match(IEntityManager entityMan)
|
||||
{
|
||||
return entityMan.GetEntities().Where(entity => Match(entity));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An entity query that will match entities that have all of the provided components.
|
||||
/// </summary>
|
||||
[PublicAPI, Obsolete]
|
||||
public class MultipleTypeEntityQuery : IEntityQuery
|
||||
{
|
||||
private readonly List<Type> ComponentTypes;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <c>MultipleTypeEntityQuery</c>.
|
||||
/// </summary>
|
||||
/// <param name="componentTypes">List of component types to match.</param>
|
||||
public MultipleTypeEntityQuery(List<Type> componentTypes)
|
||||
{
|
||||
foreach (var componentType in componentTypes)
|
||||
{
|
||||
DebugTools.Assert(typeof(IComponent).IsAssignableFrom(componentType), "componentType must inherit IComponent");
|
||||
}
|
||||
|
||||
ComponentTypes = componentTypes;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Match(IEntity entity)
|
||||
{
|
||||
foreach (var componentType in ComponentTypes)
|
||||
{
|
||||
if (!entity.HasComponent(componentType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> Match(IEntityManager entityMan)
|
||||
{
|
||||
return entityMan.GetEntities(new TypeEntityQuery(ComponentTypes.First())).Where(entity => Match(entity));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public sealed class EntityPausedEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid Entity { get; }
|
||||
public bool Paused { get; }
|
||||
|
||||
public EntityPausedEvent(EntityUid entity, bool paused)
|
||||
{
|
||||
Entity = entity;
|
||||
Paused = paused;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Prometheus;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
@@ -77,13 +76,9 @@ namespace Robust.Shared.GameObjects
|
||||
bool TryGetEntity(EntityUid uid, [NotNullWhen(true)] out IEntity? entity);
|
||||
|
||||
/// <summary>
|
||||
/// Returns all entities that match with the provided query.
|
||||
/// Returns all entities
|
||||
/// </summary>
|
||||
/// <param name="query">The query to test.</param>
|
||||
/// <returns>An enumerable over all matching entities.</returns>
|
||||
[Obsolete("IEntityQuery is obsolete")]
|
||||
IEnumerable<IEntity> GetEntities(IEntityQuery query);
|
||||
|
||||
/// <returns></returns>
|
||||
IEnumerable<IEntity> GetEntities();
|
||||
|
||||
public void QueueDeleteEntity(IEntity entity);
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// An entity query is a type that can filter entities based on certain criteria,
|
||||
/// for example based on the components that the entity has.
|
||||
/// </summary>
|
||||
[Obsolete("Use any of the other entity query methods instead.")]
|
||||
public interface IEntityQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// Match the entity and see if it passes the criteria.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to test.</param>
|
||||
/// <returns>True if the entity is included in this query, false otherwise</returns>
|
||||
bool Match(IEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Matches every entity in an EntityManager to see if it passes the criteria.
|
||||
/// </summary>
|
||||
/// <param name="entityMan">An EntityManager containing a set of entities.</param>
|
||||
/// <returns>Enumeration of all entities that successfully matched the criteria.</returns>
|
||||
IEnumerable<IEntity> Match(IEntityManager entityMan);
|
||||
}
|
||||
}
|
||||
@@ -14,11 +14,33 @@ namespace Robust.Shared.GameObjects
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MapGridComponent, ComponentRemove>(RemoveHandler);
|
||||
SubscribeLocalEvent<MapGridComponent, ComponentInit>(HandleGridInitialize);
|
||||
}
|
||||
|
||||
private void RemoveHandler(EntityUid uid, MapGridComponent component, ComponentRemove args)
|
||||
{
|
||||
_mapManager.OnComponentRemoved(component);
|
||||
}
|
||||
|
||||
private void HandleGridInitialize(EntityUid uid, MapGridComponent component, ComponentInit args)
|
||||
{
|
||||
var msg = new GridInitializeEvent(uid, component.GridIndex);
|
||||
EntityManager.EventBus.RaiseLocalEvent(uid, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised whenever a grid is being initialized.
|
||||
/// </summary>
|
||||
public sealed class GridInitializeEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid EntityUid { get; }
|
||||
public GridId GridId { get; }
|
||||
|
||||
public GridInitializeEvent(EntityUid uid, GridId gridId)
|
||||
{
|
||||
EntityUid = uid;
|
||||
GridId = gridId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,11 +47,6 @@ namespace Robust.Shared.Map
|
||||
/// </summary>
|
||||
Vector2 WorldPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this grid has gravity
|
||||
/// </summary>
|
||||
bool HasGravity { get; set; }
|
||||
|
||||
#region TileAccess
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -34,30 +34,13 @@ namespace Robust.Shared.Map
|
||||
[ViewVariables]
|
||||
public EntityUid GridEntityId { get; internal set; }
|
||||
|
||||
[ViewVariables]
|
||||
public bool HasGravity
|
||||
{
|
||||
get => _hasGravity;
|
||||
set
|
||||
{
|
||||
_hasGravity = value;
|
||||
|
||||
if (GridEntityId.IsValid())
|
||||
{
|
||||
// HasGravity is synchronized MapGridComponent states.
|
||||
_mapManager.EntityManager.GetEntity(GridEntityId).GetComponent<MapGridComponent>().Dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grid chunks than make up this grid.
|
||||
/// </summary>
|
||||
private readonly Dictionary<Vector2i, IMapChunkInternal> _chunks = new();
|
||||
|
||||
private readonly IMapManagerInternal _mapManager;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private bool _hasGravity;
|
||||
private readonly IEntityManager _entityManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MapGrid"/> class.
|
||||
@@ -76,7 +59,6 @@ namespace Robust.Shared.Map
|
||||
ChunkSize = chunkSize;
|
||||
ParentMapId = parentMapId;
|
||||
LastModifiedTick = CreatedTick = _mapManager.GameTiming.CurTick;
|
||||
HasGravity = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -151,7 +151,6 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
m.R1C2 = modelMatrix.R1C2;
|
||||
handle.SetTransform(m);
|
||||
handle.DrawLine(Vertex1, Vertex2, handle.CalcWakeColor(handle.RectFillColor, sleepPercent));
|
||||
handle.SetTransform(in Matrix3.Identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,6 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
|
||||
handle.SetTransform(m);
|
||||
handle.DrawRect(LocalBounds, handle.CalcWakeColor(handle.RectFillColor, sleepPercent));
|
||||
handle.SetTransform(in Matrix3.Identity);
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
@@ -78,7 +78,6 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
{
|
||||
handle.SetTransform(in modelMatrix);
|
||||
handle.DrawCircle(Vector2.Zero, _radius, handle.CalcWakeColor(handle.RectFillColor, sleepPercent));
|
||||
handle.SetTransform(in Matrix3.Identity);
|
||||
}
|
||||
|
||||
public bool Equals(IPhysShape? other)
|
||||
|
||||
@@ -70,7 +70,6 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
handle.DrawRect(localBox, handle.CalcWakeColor(handle.GridFillColor, sleepPercent));
|
||||
}
|
||||
}
|
||||
handle.SetTransform(in Matrix3.Identity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -60,7 +60,6 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
var rotationMatrix = Matrix3.CreateRotation(Math.PI);
|
||||
handle.SetTransform(rotationMatrix * modelMatrix);
|
||||
handle.DrawRect(Rectangle, handle.CalcWakeColor(handle.RectFillColor, sleepPercent));
|
||||
handle.SetTransform(in Matrix3.Identity);
|
||||
}
|
||||
|
||||
[field: NonSerialized]
|
||||
|
||||
@@ -188,7 +188,6 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
{
|
||||
handle.SetTransform(modelMatrix);
|
||||
handle.DrawPolygonShape(_vertices.ToArray(), handle.CalcWakeColor(handle.RectFillColor, sleepPercent));
|
||||
handle.SetTransform(in Matrix3.Identity);
|
||||
}
|
||||
|
||||
public static explicit operator PolygonShape(PhysShapeAabb aabb)
|
||||
|
||||
@@ -12,6 +12,11 @@ namespace Robust.Shared
|
||||
{
|
||||
return contentStart ? "../../" : "../../../";
|
||||
}
|
||||
|
||||
private static string FindEngineRootDir(bool contentStart)
|
||||
{
|
||||
return contentStart ? "../../RobustToolbox/" : "../../";
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static void PrintRuntimeInfo(ISawmill sawmill)
|
||||
@@ -20,17 +25,21 @@ namespace Robust.Shared
|
||||
sawmill.Debug($"OS: {RuntimeInformation.OSDescription} {RuntimeInformation.OSArchitecture}");
|
||||
}
|
||||
|
||||
internal static void DoMounts(IResourceManagerInternal res, MountOptions? options, string contentBuildDir,
|
||||
internal static void DoMounts(IResourceManagerInternal res, MountOptions? options, string contentBuildDir, ResourcePath assembliesPath, bool loadContentResources = true,
|
||||
bool loader = false, bool contentStart = false)
|
||||
{
|
||||
#if FULL_RELEASE
|
||||
if (!loader)
|
||||
res.MountContentDirectory(@"Resources/");
|
||||
#else
|
||||
var contentRootDir = FindContentRootDir(contentStart);
|
||||
res.MountContentDirectory($@"{contentRootDir}RobustToolbox/Resources/");
|
||||
res.MountContentDirectory($@"{contentRootDir}bin/{contentBuildDir}/", new ResourcePath("/Assemblies/"));
|
||||
res.MountContentDirectory($@"{contentRootDir}Resources/");
|
||||
res.MountContentDirectory($@"{FindEngineRootDir(contentStart)}Resources/");
|
||||
|
||||
if (loadContentResources)
|
||||
{
|
||||
var contentRootDir = FindContentRootDir(contentStart);
|
||||
res.MountContentDirectory($@"{contentRootDir}bin/{contentBuildDir}/", assembliesPath);
|
||||
res.MountContentDirectory($@"{contentRootDir}Resources/");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (options == null)
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Robust.UnitTesting
|
||||
|
||||
public string? ContentRootDir { get; set; }
|
||||
|
||||
public void Run(GameController.DisplayMode mode, Func<ILogHandler>? logHandlerFactory = null)
|
||||
public void Run(GameController.DisplayMode mode, GameControllerOptions options, Func<ILogHandler>? logHandlerFactory = null)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ using Robust.Shared;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
@@ -296,7 +297,7 @@ namespace Robust.UnitTesting
|
||||
{
|
||||
_isSurelyIdle = false;
|
||||
_currentTicksId += 1;
|
||||
_toInstanceWriter.TryWrite(new RunTicksMessage(ticks, 1 / 60f, _currentTicksId));
|
||||
_toInstanceWriter.TryWrite(new RunTicksMessage(ticks, _currentTicksId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -422,7 +423,15 @@ namespace Robust.UnitTesting
|
||||
|
||||
var server = DependencyCollection.Resolve<BaseServer>();
|
||||
|
||||
server.LoadConfigAndUserData = false;
|
||||
var serverOptions = _options != null ? _options.Options : new ServerOptions()
|
||||
{
|
||||
LoadConfigAndUserData = false,
|
||||
LoadContentResources = false,
|
||||
};
|
||||
|
||||
// Autoregister components if options are null or we're NOT starting from content.
|
||||
if(!_options?.ContentStart ?? true)
|
||||
IoCManager.Resolve<IComponentFactory>().DoAutoRegistrations();
|
||||
|
||||
if (_options?.ContentAssemblies != null)
|
||||
{
|
||||
@@ -447,7 +456,7 @@ namespace Robust.UnitTesting
|
||||
|
||||
var failureLevel = _options == null ? LogLevel.Error : _options.FailureLogLevel;
|
||||
server.ContentStart = _options?.ContentStart ?? false;
|
||||
if (server.Start(() => new TestLogHandler("SERVER", failureLevel)))
|
||||
if (server.Start(serverOptions, () => new TestLogHandler("SERVER", failureLevel)))
|
||||
{
|
||||
throw new Exception("Server failed to start.");
|
||||
}
|
||||
@@ -535,13 +544,21 @@ namespace Robust.UnitTesting
|
||||
|
||||
var client = DependencyCollection.Resolve<GameController>();
|
||||
|
||||
var clientOptions = _options != null ? _options.Options : new GameControllerOptions()
|
||||
{
|
||||
LoadContentResources = false,
|
||||
LoadConfigAndUserData = false,
|
||||
};
|
||||
|
||||
// Autoregister components if options are null or we're NOT starting from content.
|
||||
if(!_options?.ContentStart ?? true)
|
||||
IoCManager.Resolve<IComponentFactory>().DoAutoRegistrations();
|
||||
|
||||
if (_options?.ContentAssemblies != null)
|
||||
{
|
||||
IoCManager.Resolve<TestingModLoader>().Assemblies = _options.ContentAssemblies;
|
||||
}
|
||||
|
||||
client.LoadConfigAndUserData = false;
|
||||
|
||||
var cfg = IoCManager.Resolve<IConfigurationManagerInternal>();
|
||||
|
||||
if (_options != null)
|
||||
@@ -556,15 +573,25 @@ namespace Robust.UnitTesting
|
||||
}
|
||||
}
|
||||
|
||||
cfg.OverrideConVars(new[] {(CVars.NetPredictLagBias.Name, "0")});
|
||||
cfg.OverrideConVars(new[]
|
||||
{
|
||||
(CVars.NetPredictLagBias.Name, "0"),
|
||||
|
||||
// Connecting to Discord is a massive waste of time.
|
||||
// Basically just makes the CI logs a mess.
|
||||
(CVars.DiscordEnabled.Name, "false"),
|
||||
|
||||
// Avoid preloading textures.
|
||||
(CVars.TexturePreloadingEnabled.Name, "false"),
|
||||
});
|
||||
|
||||
GameLoop = new IntegrationGameLoop(DependencyCollection.Resolve<IGameTiming>(),
|
||||
_fromInstanceWriter, _toInstanceReader);
|
||||
|
||||
var failureLevel = _options == null ? LogLevel.Error : _options.FailureLogLevel;
|
||||
client.OverrideMainLoop(GameLoop);
|
||||
client.ContentStart = true;
|
||||
client.StartupSystemSplash(() => new TestLogHandler("CLIENT", failureLevel));
|
||||
client.ContentStart = _options?.ContentStart ?? false;
|
||||
client.StartupSystemSplash(clientOptions, () => new TestLogHandler("CLIENT", failureLevel));
|
||||
client.StartupContinue(GameController.DisplayMode.Headless);
|
||||
|
||||
GameLoop.RunInit();
|
||||
@@ -634,7 +661,7 @@ namespace Robust.UnitTesting
|
||||
{
|
||||
case RunTicksMessage msg:
|
||||
_gameTiming.InSimulation = true;
|
||||
var simFrameEvent = new FrameEventArgs(msg.Delta);
|
||||
var simFrameEvent = new FrameEventArgs((float) _gameTiming.TickPeriod.TotalSeconds);
|
||||
for (var i = 0; i < msg.Ticks && Running; i++)
|
||||
{
|
||||
Input?.Invoke(this, simFrameEvent);
|
||||
@@ -674,10 +701,20 @@ namespace Robust.UnitTesting
|
||||
|
||||
public class ServerIntegrationOptions : IntegrationOptions
|
||||
{
|
||||
public virtual ServerOptions Options { get; set; } = new()
|
||||
{
|
||||
LoadConfigAndUserData = false,
|
||||
LoadContentResources = false,
|
||||
};
|
||||
}
|
||||
|
||||
public class ClientIntegrationOptions : IntegrationOptions
|
||||
{
|
||||
public virtual GameControllerOptions Options { get; set; } = new()
|
||||
{
|
||||
LoadContentResources = false,
|
||||
LoadConfigAndUserData = false,
|
||||
};
|
||||
}
|
||||
|
||||
public abstract class IntegrationOptions
|
||||
@@ -698,15 +735,13 @@ namespace Robust.UnitTesting
|
||||
/// </summary>
|
||||
private sealed class RunTicksMessage
|
||||
{
|
||||
public RunTicksMessage(int ticks, float delta, int messageId)
|
||||
public RunTicksMessage(int ticks, int messageId)
|
||||
{
|
||||
Ticks = ticks;
|
||||
Delta = delta;
|
||||
MessageId = messageId;
|
||||
}
|
||||
|
||||
public int Ticks { get; }
|
||||
public float Delta { get; }
|
||||
public int MessageId { get; }
|
||||
}
|
||||
|
||||
|
||||
@@ -55,9 +55,10 @@ namespace Robust.UnitTesting.Server.GameObjects
|
||||
//NOTE: The grids have not moved, so we can assert worldpos == localpos for the test
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test([Values("throwInAdd", "throwsInInitialize", "throwsInStartup")]
|
||||
string prototypeName)
|
||||
[TestCase("throwInAdd")]
|
||||
[TestCase("throwsInInitialize")]
|
||||
[TestCase("throwsInStartup")]
|
||||
public void Test(string prototypeName)
|
||||
{
|
||||
Assert.That(() => EntityManager.SpawnEntity(prototypeName, MapCoordinates.Nullspace),
|
||||
Throws.TypeOf<EntityCreationException>());
|
||||
|
||||
75
Robust.UnitTesting/Shared/EngineIntegrationTest_Test.cs
Normal file
75
Robust.UnitTesting/Shared/EngineIntegrationTest_Test.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using IPlayerManager = Robust.Server.Player.IPlayerManager;
|
||||
|
||||
namespace Robust.UnitTesting.Shared
|
||||
{
|
||||
[TestFixture]
|
||||
public class EngineIntegrationTest_Test : RobustIntegrationTest
|
||||
{
|
||||
[Test]
|
||||
public void ServerStartsCorrectlyTest()
|
||||
{
|
||||
ServerIntegrationInstance? server = null;
|
||||
Assert.DoesNotThrow(() => server = StartServer());
|
||||
Assert.That(server, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClientStartsCorrectlyTest()
|
||||
{
|
||||
ClientIntegrationInstance? client = null;
|
||||
Assert.DoesNotThrow(() => client = StartClient());
|
||||
Assert.That(client, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ServerClientPairConnectCorrectlyTest()
|
||||
{
|
||||
var server = StartServer();
|
||||
var client = StartClient();
|
||||
|
||||
Assert.That(server, Is.Not.Null);
|
||||
Assert.That(client, Is.Not.Null);
|
||||
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
// Connect client to the server...
|
||||
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
||||
client.Post(() => IoCManager.Resolve<IClientNetManager>().ClientConnect(null!, 0, null!));
|
||||
|
||||
// Run 10 synced ticks...
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
await server.WaitRunTicks(1);
|
||||
await client.WaitRunTicks(1);
|
||||
}
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var playerManager = IoCManager.Resolve<IPlayerManager>();
|
||||
|
||||
// There must be a player connected.
|
||||
Assert.That(playerManager.PlayerCount, Is.EqualTo(1));
|
||||
|
||||
// Get the only player...
|
||||
var player = playerManager.GetAllPlayers()[0];
|
||||
|
||||
Assert.That(player.Status, Is.EqualTo(SessionStatus.Connected));
|
||||
Assert.That(player.ConnectedClient.IsConnected, Is.True);
|
||||
});
|
||||
|
||||
await client.WaitAssertion(() =>
|
||||
{
|
||||
var netManager = IoCManager.Resolve<IClientNetManager>();
|
||||
|
||||
Assert.That(netManager.IsConnected, Is.True);
|
||||
Assert.That(netManager.ServerChannel, Is.Not.Null);
|
||||
Assert.That(netManager.ServerChannel!.IsConnected, Is.True);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.Reflection;
|
||||
@@ -64,7 +64,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
},
|
||||
new []
|
||||
{
|
||||
new MapGridComponentState(new GridId(0), true),
|
||||
new MapGridComponentState(new GridId(0)),
|
||||
});
|
||||
|
||||
serializer.Serialize(stream, payload);
|
||||
|
||||
@@ -100,31 +100,6 @@ namespace Robust.UnitTesting.Shared.GameObjects.Systems
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When an entity is anchored to a grid tile, it's local rotation is rounded to the nearest cardinal 4 direction.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnAnchored_WorldRotation_Cardinal()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var mapMan = sim.Resolve<IMapManager>();
|
||||
|
||||
var coordinates = new MapCoordinates(new Vector2(7, 7), TestMapId);
|
||||
|
||||
// can only be anchored to a tile
|
||||
var grid = mapMan.GetGrid(TestGridId);
|
||||
grid.SetTile(grid.TileIndicesFor(coordinates), new Tile(1));
|
||||
|
||||
var ent1 = entMan.SpawnEntity(null, coordinates);
|
||||
ent1.Transform.LocalRotation = Angle.FromDegrees(60);
|
||||
|
||||
// Act
|
||||
ent1.Transform.Anchored = true;
|
||||
|
||||
Assert.That(ent1.Transform.LocalRotation.Degrees, Is.EqualTo(90));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When an entity is anchored to a grid tile, it's parent is set to the grid.
|
||||
/// </summary>
|
||||
@@ -247,47 +222,6 @@ namespace Robust.UnitTesting.Shared.GameObjects.Systems
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Local rotation of an anchored entity cannot be changed (can still rotate/orbit in word via parent).
|
||||
/// Writing to the property is a no-op and is silently ignored.
|
||||
/// Because the position cannot be changed, RotateEvents are not raised when setting the property.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Anchored_SetRotation_Nop()
|
||||
{
|
||||
var sim = SimulationFactory();
|
||||
var entMan = sim.Resolve<IEntityManager>();
|
||||
var mapMan = sim.Resolve<IMapManager>();
|
||||
|
||||
var coordinates = new MapCoordinates(new Vector2(7, 7), TestMapId);
|
||||
|
||||
// can only be anchored to a tile
|
||||
var grid = mapMan.GetGrid(TestGridId);
|
||||
grid.SetTile(grid.TileIndicesFor(coordinates), new Tile(1));
|
||||
|
||||
var subscriber = new Subscriber();
|
||||
int calledCount = 0;
|
||||
var ent1 = entMan.SpawnEntity(null, coordinates);
|
||||
ent1.Transform.NoLocalRotation = false;
|
||||
var testAngle = Angle.FromDegrees(90);
|
||||
ent1.Transform.LocalRotation = testAngle;
|
||||
entMan.EventBus.SubscribeEvent<RotateEvent>(EventSource.Local, subscriber, MoveEventHandler);
|
||||
|
||||
ent1.Transform.Anchored = true;
|
||||
|
||||
// Act
|
||||
ent1.Transform.WorldRotation = Angle.FromDegrees(180);
|
||||
ent1.Transform.LocalRotation = Angle.FromDegrees(180);
|
||||
|
||||
Assert.That(ent1.Transform.LocalRotation, Is.EqualTo(testAngle));
|
||||
Assert.That(calledCount, Is.EqualTo(0));
|
||||
void MoveEventHandler(RotateEvent ev)
|
||||
{
|
||||
Assert.Fail("RotateEvent raised when entity is anchored.");
|
||||
calledCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changing the parent of the entity un-anchors it.
|
||||
/// </summary>
|
||||
@@ -299,7 +233,7 @@ namespace Robust.UnitTesting.Shared.GameObjects.Systems
|
||||
var mapMan = sim.Resolve<IMapManager>();
|
||||
|
||||
var coordinates = new MapCoordinates(new Vector2(7, 7), TestMapId);
|
||||
|
||||
|
||||
var grid = mapMan.GetGrid(TestGridId);
|
||||
|
||||
var ent1 = entMan.SpawnEntity(null, coordinates);
|
||||
@@ -503,7 +437,7 @@ namespace Robust.UnitTesting.Shared.GameObjects.Systems
|
||||
var ent1 = entMan.SpawnEntity(null, new MapCoordinates(new Vector2(7, 7), TestMapId));
|
||||
var tileIndices = grid.TileIndicesFor(ent1.Transform.Coordinates);
|
||||
grid.SetTile(tileIndices, new Tile(1));
|
||||
|
||||
|
||||
var gridEnt = entMan.GetEntity(grid.GridEntityId);
|
||||
var containerMan = gridEnt.AddComponent<ContainerManagerComponent>();
|
||||
var container = containerMan.MakeContainer<Container>("TestContainer");
|
||||
|
||||
Reference in New Issue
Block a user