Compare commits

..

2 Commits

Author SHA1 Message Date
Pieter-Jan Briers
32fa6ebb53 Version: 136.0.2 2024-03-10 20:47:22 +01:00
Pieter-Jan Briers
27378d3620 Backport 859f150404
(cherry picked from commit 24d5c26fa6)
(cherry picked from commit b89d13d39ffa53b9ca687946c6b56e86449d50bd)
2024-03-10 20:47:22 +01:00
367 changed files with 4379 additions and 13225 deletions

View File

@@ -23,7 +23,7 @@
<PropertyGroup Condition="'$(FullRelease)' != 'True'">
<DefineConstants>$(DefineConstants);DEVELOPMENT</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release' Or '$(Configuration)' == 'Tools'">
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DefineConstants>$(DefineConstants);EXCEPTION_TOLERANCE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(EnableClientScripting)' == 'True'">

View File

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

View File

@@ -54,386 +54,7 @@ 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
* Several NuGet dependencies are now private assets.
* Added `IViewportControl.PixelToMap()` and `PixelToMapEvent`. These are variants of the existing screen-to-map functions that should account for distortion effects.
### New features
* Added several new rich-text tags, including italic and bold-italic.
### Bugfixes
* Fixed log messages for unknown components not working due to threaded IoC issues.
* Replay recordings no longer record invalid prototype uploads.
## 147.0.0
### Breaking changes
* Renamed one of the EntitySystem.Dirty() methods to `DirtyEntity()` to avoid confusion with the component-dirtying methods.
### New features
* Added debug commands that return the entity system update order.
### Bugfixes
* Fixed a bug in MetaDataSystem that was causing the metadata component to not be marked as dirty.
## 146.0.0
### Breaking changes
* Remove readOnly for DataFields and rename some ShaderPrototype C# fields internally to align with the normal schema.
### Bugfixes
* Add InvariantCulture to angle validation.
### Internal
* Add some additional EntityQuery<T> usages and remove a redundant CanCollide call on fixture shutdown.
## 145.0.0
### Breaking changes
* Removed some old SpriteComponent data-fields ("rsi", and "layerDatums").
### New features
* Added `ActorSystem.TryGetActorFromUserId()`.
* Added IPrototypeManager.EnumerateKinds().
### Bugfixes
* Fixed SpriteSpecifierSerializer yaml validation not working properly.
* Fixed IoC/Threading exceptions in `Resource.Load()`.
* Fixed `TransformSystem.SetCoordinates()` throwing uninformative client-side errors.
* Fixed `IResourceManager.ContentFileExists()` and `TryContentFileRead()` throwing exceptions on windows when trying to open a directory.
## 144.0.1
### Bugfixes
* Fix some EntityLookup queries incorrectly being double transformed internally.
* Shrink TileEnlargement even further for EntityLookup default queries.
## 144.0.0
### Breaking changes
* Add new args to entitylookup methods to allow for shrinkage of tile-bounds checks. Default changed to shrink the grid-local AABB by the polygon skin to avoid clipping neighboring tile entities.
* Non-hard fixtures will no longer count by default for EntityLookup.
### New features
* Added new EntityLookup flag to return non-hard fixtures or not.
## 143.3.0
### New features
* Entity placement and spawn commands now raise informative events that content can handle.
* Replay clients can now optionally ignore some errors instead of refusing to load the replay.
### Bugfixes
* `AudioParams.PlayOffsetSecond` will no longer apply an offset that is larger then the length of the audio stream.
* Fixed yaml serialization of arrays of virtual/abstract objects.
### Other
* Removed an incorrect gamestate debug assert.
## 143.2.0
### New features
* Add support for tests to load extra prototypes from multiple sources.
### Bugfixes
* Fix named toolshed command.
* Unsubscribe from grid rendering events on shutdown.
### Other
* Remove unnecessary test prototypes.
## 143.1.0
### New features
* Add locale support for grammatical measure words.
### Bugfixes
* Don't raise contact events for entities that were QueueDeleted during the tick.
* Exception on duplicate broadcast subscriptions as this was unsupported behaviour.
### Other
* Add VV ReadWrite to PhysicsComponent BodyStatus.
## 143.0.0
### New features
- Toolshed, a tacit shell language, has been introduced.
- Use Robust.Shared.ToolshedManager to invoke commands, with optional input and output.
- Implement IInvocationContext for custom invocation contexts i.e. scripting systems.
## 142.1.2
### Other
* Don't log an error on failing to resolve for joint relay refreshing.
## 142.1.1
### Bugfixes
* Fixed a bad debug assert in `DetachParentToNull()`
## 142.1.0
### New features
* `IHttpClientHolder` holds a shared `HttpClient` for use by content. It has Happy Eyeballs fixed and an appropriate `User-Agent`.
* Added `DataNode.ToString()`. Makes it easier to save yaml files and debug code.
* Added some cvars to modify discord rich presence icons.
* .ogg files now read the `Artist` and `Title` tags and make them available via new fields in `AudioStream`.
* The default fragment shaders now have access to the local light level (`lowp vec3 lightSample`).
* Added `IPrototypeManager.ValidateAllPrototypesSerializable()`, which can be used to check that all currently loaded prototypes can be serialised & deserialised.
### Bugfixes
* Fix certain debug commands and tools crashing on non-SS14 RobustToolbox games due to a missing font.
* Discord rich presence strings are now truncated if they are too long.
* Fixed a couple of broadphase/entity-lookup update bugs that were affecting containers and entities attached to other (non-grid/map) entities.
* Fixed `INetChannel.Disconnect()` not properly disconnecting clients in integration tests.
### Other
* Outgoing HTTP requests now all use Happy Eyeballs to try to prioritize IPv6. This is necessary because .NET still does not support this critical feature itself.
* Made various physics related component properties VV-editable.
* The default EntitySystem sawmill log level now defaults to `Info` instead of `Verbose`. The level remains verbose when in debug mode.
### Internal
* The debug asserts in `DetachParentToNull()` are now more informative.
## 142.0.1
### Bugfixes
* Fix Enum serialization.
## 142.0.0
### Breaking changes
* `EntityManager.GetAllComponents()` now returns a (EntityUid, Component) tuple
### New features
* Added `IPrototypeManager.ValidateFields()`, which uses reflection to validate that the default values of c# string fields correspond to valid entity prototypes. Validates any fields with a `ValidatePrototypeIdAttribute` and any data-field that uses the PrototypeIdSerializer custom type serializer.
### Other
* Replay playback will now log errors when encountering unhandled messages.
* Made `GetAssemblyByName()` throw descriptive error messages.
* Improved performance of various EntityLookupSystem functions
## 141.2.1
### Bugfixes
* Fix component trait dictionaries not clearing on reconnect leading to bad GetComponent in areas (e.g. entire game looks black due to no entities).
## 141.2.0
### Other
* Fix bug in `NetManager` that allowed exception spam through protocol abuse.
## 141.1.0
### New features
* MapInitEvent is run clientside for placementmanager entities to predict entity appearances.
* Add CollisionLayerChangeEvent for physics fixtures.
## 141.0.0
### Breaking changes
* Component.Initialize has been fully replaced with the Eventbus.
### Bugfixes
* Fixed potential crashes if buffered audio sources (e.g. MIDI) fail to create due to running out of audio streams.
### Other
* Pressing `^C` twice on the server will now cause it to hard-exit immediately.
* `Tools` now has `EXCEPTION_TOLERANCE` enabled.
## 140.0.0
### Breaking changes
* `IReplayRecordingManager.RecordingFinished` now takes a `ReplayRecordingFinished` object as argument.
* `IReplayRecordingManager.GetReplayStats` now returns a `ReplayRecordingStats` struct instead of a tuple. The units have also been normalized
### New features
* `IReplayRecordingManager` can now track a "state" object for an active recording.
* If the path given to `IReplayRecordingManager.TryStartRecording` is rooted, the base replay directory is ignored.
### Other
* `IReplayRecordingManager` no longer considers itself recording inside `RecordingFinished`.
* `IReplayRecordingManager.Initialize()` was moved to an engine-internal interface.
## 139.0.0
### Breaking changes
* Remove Component.Startup(), fully replacing it with the Eventbus.
## 138.1.0
### New features
* Add rotation methods to TransformSystem for no lerp.
### Bugfixes
* Fix AnimationCompleted ordering.
## 138.0.0
### Breaking changes
* Obsoleted unused `IMidiRenderer.VolumeBoost` property. Use `IMidiRenderer.VelocityOverride` instead.
* `IMidiRenderer.TrackedCoordinates` is now a `MapCoordinates`.
### New features
* Added `Master` property to `IMidiRenderer`, which allows it to copy all MIDI events from another renderer.
* Added `FilteredChannels` property to `IMidiRenderer`, which allows it to filter out notes from certain channels.
* Added `SystemReset` helper property to `IMidiRenderer`, which allows you to easily send it a SystemReset MIDI message.
### Bugfixes
* Fixed some cases were `MidiRenderer` would not respect the `MidiBank` and `MidiProgram.
* Fixed user soundfonts not loading.
* Fixed `ItemList` item selection unselecting everything when in `Multiple` mode.
## 137.1.0
### New features
* Added BQL `paused` selector.
* `ModUpdateLevel.PostInput` allows running content code after network and async task processing.
### Other
* BQL `with` now includes paused entities.
* The game loop now times more accurately and avoids sleeping more than necessary.
* Sandboxing (and thus, client startup) should be much faster when ran from the launcher.
## 137.0.0
### Breaking changes
* Component network state handler methods have been fully deprecated and replaced with the eventbus event equivalents (ComponentGetState and ComponentHandleState).
## 136.0.2
## 136.0.1

View File

@@ -1,6 +1,6 @@
- type: uiTheme
id: Default
path: /Textures/Interface/Default/
path: /Textures/Interface/Default
colors:
# Root
rootBackground: "#000000"

View File

@@ -17,15 +17,15 @@ cmd-error-dir-not-found = Could not find directory: {$dir}.
cmd-failure-no-attached-entity = There is no entity attached to this shell.
## 'help' command
cmd-oldhelp-desc = Display general help or help text for a specific command
cmd-oldhelp-help = Usage: help [command name]
cmd-help-desc = Display general help or help text for a specific command
cmd-help-help = Usage: help [command name]
When no command name is provided, displays general-purpose help text. If a command name is provided, displays help text for that command.
cmd-oldhelp-no-args = To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'. To search for commands, use 'list <filter>'.
cmd-oldhelp-unknown = Unknown command: { $command }
cmd-oldhelp-top = { $command } - { $description }
cmd-oldhelp-invalid-args = Invalid amount of arguments.
cmd-oldhelp-arg-cmdname = [command name]
cmd-help-no-args = To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'. To search for commands, use 'list <filter>'.
cmd-help-unknown = Unknown command: { $command }
cmd-help-top = { $command } - { $description }
cmd-help-invalid-args = Invalid amount of arguments.
cmd-help-arg-cmdname = [command name]
## 'cvar' command
cmd-cvar-desc = Gets or sets a CVar.
@@ -558,6 +558,3 @@ 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

View File

@@ -1,5 +1,4 @@
discord-rpc-in-main-menu = In Main Menu
discord-rpc-in-main-menu-logo-text = I think coolsville SUCKS
discord-rpc-character = Username: {$username}
discord-rpc-on-server = On Server: {$servername}
discord-rpc-players = Players: {$players}/{$maxplayers}
discord-rpc-players = Players: {$players}/{$maxplayers}

View File

@@ -18,15 +18,6 @@ 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

View File

@@ -22,7 +22,7 @@ cmd-replay-skip-hint = Ticks or timespan (HH:MM:SS).
cmd-replay-set-time-desc = Jump forwards or backwards to some specific time.
cmd-replay-set-time-help = replay_set <tick or time>
cmd-replay-set-time-hint = Tick or timespan (HH:MM:SS), starting from
cmd-replay-set-time-hint = Tick or timespan (HH:MM:SS), starting from
cmd-replay-error-time = "{$time}" is not an integer or timespan.
cmd-replay-error-args = Wrong number of arguments.
@@ -33,7 +33,7 @@ cmd-replay-error-run-level = You cannot load a replay while connected to a serve
# Recording commands
cmd-replay-recording-start-desc = Starts a replay recording, optionally with some time limit.
cmd-replay-recording-start-help = Usage: replay_recording_start [name] [overwrite] [time limit]
cmd-replay-recording-start-help = Usage: replay_recording_start [name] [overwrite] [time limit]
cmd-replay-recording-start-success = Started recording a replay.
cmd-replay-recording-start-already-recording = Already recording a replay.
cmd-replay-recording-start-error = An error occurred while trying to start the recording.
@@ -48,7 +48,7 @@ cmd-replay-recording-stop-not-recording = Not currently recording a replay.
cmd-replay-recording-stats-desc = Displays information about the current replay recording.
cmd-replay-recording-stats-help = Usage: replay_recording_stats
cmd-replay-recording-stats-result = Duration: {$time} min, Ticks: {$ticks}, Size: {$size} MB, rate: {$rate} MB/min.
cmd-replay-recording-stats-result = Duration: {$time} min, Ticks: {$ticks}, Size: {$size} mb, rate: {$rate} mb/min.
# Time Control UI
@@ -56,4 +56,4 @@ replay-time-box-scrubbing-label = Dynamic Scrubbing
replay-time-box-replay-time-label = Recording Time: {$current} / {$end} ({$percentage}%)
replay-time-box-server-time-label = Server Time: {$current} / {$end}
replay-time-box-index-label = Index: {$current} / {$total}
replay-time-box-tick-label = Tick: {$current} / {$total}
replay-time-box-tick-label = Tick: {$current} / {$total}

View File

@@ -1,169 +0,0 @@
command-description-tpto =
Teleport the given entities to some target entity.
command-description-player-list =
Returns a list of all player sessions.
command-description-player-self =
Returns the current player session.
command-description-player-imm =
Returns the session associated with the player given as argument.
command-description-player-entity =
Returns the entities of the input sessions.
command-description-self =
Returns the current attached entity.
command-description-physics-velocity =
Returns the velocity of the input entities.
command-description-physics-angular-velocity =
Returns the angular velocity of the input entities.
command-description-buildinfo =
Provides information about the build of the game.
command-description-cmd-list =
Returns a list of all commands, for this side.
command-description-explain =
Explains the given expression, providing command descriptions and signatures.
command-description-search =
Searches through the input for the provided value.
command-description-stopwatch =
Measures the execution time of the given expression.
command-description-types-consumers =
Provides all commands that can consume the given type.
command-description-types-tree =
Debug tool to return all types the command interpreter can downcast the input to.
command-description-types-gettype =
Returns the type of the input.
command-description-types-fullname =
Returns the full name of the input type according to CoreCLR.
command-description-as =
Casts the input to the given type.
Effectively a type hint if you know the type but the interpreter does not.
command-description-count =
Counts the amount of entries in it's input, returning an integer.
command-description-map =
Maps the input over the given block, with the provided expected return type.
This command may be modified to not need an explicit return type in the future.
command-description-select =
Selects N objects or N% of objects from the input.
One can additionally invert this command with not to make it select everything except N objects instead.
command-description-comp =
Returns the given component from the input entities, discarding entities without that component.
command-description-delete =
Deletes the input entities.
command-description-ent =
Returns the provided entity ID.
command-description-entities =
Returns all entities on the server.
command-description-paused =
Filters the input entities by whether or not they are paused.
This command can be inverted with not.
command-description-with =
Filters the input entities by whether or not they have the given component.
This command can be inverted with not.
command-description-fuck =
Throws an exception.
command-description-ecscomp-listty =
Lists every type of component registered.
command-description-cd =
Changes the session's current directory to the given relative or absolute path.
command-description-ls-here =
Lists the contents of the current directory.
command-description-ls-in =
Lists the contents of the given relative or absolute path.
command-description-methods-get =
Returns all methods associated with the input type.
command-description-methods-overrides =
Returns all methods overriden on the input type.
command-description-methods-overridesfrom =
Returns all methods overriden from the given type on the input type.
command-description-cmd-moo =
Asks the important questions.
command-description-cmd-descloc =
Returns the localization string for a command's description.
command-description-cmd-getshim =
Returns a command's execution shim.
command-description-help =
Provides a quick rundown of how to use toolshed.
command-description-ioc-registered =
Returns all the types registered with IoCManager on the current thread (usually the game thread)
command-description-ioc-get =
Gets an instance of an IoC registration.
command-description-loc-tryloc =
Tries to get a localization string, returning null if unable.
command-description-loc-loc =
Gets a localization string, returning the unlocalized string if unable.
command-description-physics-angular_velocity =
Returns the angular velocity of the given entities.
command-description-vars =
Provides a list of all variables set in this session.
command-description-any =
Returns true if there's any values in the input, otherwise false.
command-description-ArrowCommand =
Assigns the input to a variable.
command-description-isempty =
Returns true if the input is empty, otherwise false.
command-description-isnull =
Returns true if the input is null, otherwise false.
command-description-unique =
Filters the input sequence for uniqueness, removing duplicate values.
command-description-where =
Given some input sequence IEnumerable<T>, takes a block of signature T -> bool that decides if each input value should be included in the output sequence.
command-description-do =
Backwards compatibility with BQL, applies the given old commands over the input sequence.
command-description-named =
Filters the input entities by their name, with the regex ^selector$.
command-description-prototyped =
Filters the input entities by their prototype.
command-description-nearby =
Creates a new list of all entities nearby the inputs within the given range.
command-description-first =
Returns the first entry of the given enumerable.
command-description-splat =
"Splats" a block, value, or variable, creating N copies of it in a list.
command-description-val =
Casts the given value, block, or variable to the given type. This is mostly a workaround for current limitations of variables.
command-description-actor-controlled =
Filters entities by whether or not they're actively controlled.
command-description-actor-session =
Returns the sessions associated with the input entities.
command-description-physics-parent =
Returns the parent(s) of the input entities.
command-description-emplace =
Runs the given block over it's inputs, with the input value placed into the variable $value within the block.
Additionally breaks out $wx, $wy, $proto, $desc, $name, and $paused for entities.
Can also have breakout values for other types, consult the documentation for that type for further info.
command-description-AddCommand =
Performs numeric addition.
command-description-SubtractCommand =
Performs numeric subtraction.
command-description-MultiplyCommand =
Performs numeric multiplication.
command-description-DivideCommand =
Performs numeric division.
command-description-min =
Returns the minimum of two values.
command-description-max =
Returns the maximum of two values.
command-description-BitAndCommand =
Performs bitwise AND.
command-description-BitOrCommand =
Performs bitwise OR.
command-description-BitXorCommand =
Performs bitwise XOR.
command-description-neg =
Negates the input.
command-description-GreaterThanCommand =
Performs a greater-than comparison, x > y.
command-description-LessThanCommand =
Performs a less-than comparison, x < y.
command-description-GreaterThanOrEqualCommand =
Performs a greater-than-or-equal comparison, x >= y.
command-description-LessThanOrEqualCommand =
Performs a less-than-or-equal comparison, x <= y.
command-description-EqualCommand =
Performs an equality comparison, returning true if the inputs are equal.
command-description-NotEqualCommand =
Performs an equality comparison, returning true if the inputs are not equal.
command-description-entitysystemupdateorder-tick =
Lists the tick update order of entity systems.
command-description-entitysystemupdateorder-frame =
Lists the frame update order of entity systems.

View File

@@ -1,75 +0,0 @@
using BenchmarkDotNet.Attributes;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.UnitTesting.Server;
namespace Robust.Benchmarks.EntityManager;
public class ComponentIteratorBenchmark
{
private ISimulation _simulation = default!;
private IEntityManager _entityManager = default!;
[UsedImplicitly]
[Params(1, 10, 100, 1000)]
public int N;
public A[] Comps = default!;
[GlobalSetup]
public void GlobalSetup()
{
_simulation = RobustServerSimulation
.NewSimulation()
.RegisterComponents(f => f.RegisterClass<A>())
.InitializeInstance();
_entityManager = _simulation.Resolve<IEntityManager>();
Comps = new A[N+2];
var coords = new MapCoordinates(0, 0, new MapId(1));
_simulation.AddMap(coords.MapId);
for (var i = 0; i < N; i++)
{
var uid = _entityManager.SpawnEntity(null, coords);
_entityManager.AddComponent<A>(uid);
}
}
[Benchmark]
public A[] ComponentStructEnumerator()
{
var query = _entityManager.EntityQueryEnumerator<A>();
var i = 0;
while (query.MoveNext(out var comp))
{
Comps[i] = comp;
i++;
}
return Comps;
}
[Benchmark]
public A[] ComponentIEnumerable()
{
var i = 0;
foreach (var comp in _entityManager.EntityQuery<A>())
{
Comps[i] = comp;
i++;
}
return Comps;
}
[ComponentProtoName("A")]
public sealed class A : Component
{
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Robust.Client.Graphics;
namespace Robust.Client.Audio;
@@ -8,17 +8,13 @@ public sealed class AudioStream
public TimeSpan Length { get; }
internal ClydeHandle? ClydeHandle { get; }
public string? Name { get; }
public string? Title { get; }
public string? Artist { get; }
public int ChannelCount { get; }
internal AudioStream(ClydeHandle handle, TimeSpan length, int channelCount, string? name = null, string? title = null, string? artist = null)
internal AudioStream(ClydeHandle handle, TimeSpan length, int channelCount, string? name = null)
{
ClydeHandle = handle;
Length = length;
ChannelCount = channelCount;
Name = name;
Title = title;
Artist = artist;
}
}

View File

@@ -1,6 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Robust.Client.Graphics;
using Robust.Shared.Audio.Midi;
using Robust.Shared.GameObjects;
@@ -17,6 +15,7 @@ public enum MidiRendererStatus : byte
public interface IMidiRenderer : IDisposable
{
/// <summary>
/// The buffered audio source of this renderer.
/// </summary>
@@ -35,7 +34,6 @@ public interface IMidiRenderer : IDisposable
/// <summary>
/// This increases all note on velocities to 127.
/// </summary>
[Obsolete($"Use {nameof(VelocityOverride)} instead, you can set it to 127 to achieve the same effect.")]
bool VolumeBoost { get; set; }
/// <summary>
@@ -96,27 +94,6 @@ public interface IMidiRenderer : IDisposable
/// </summary>
double SequencerTimeScale { get; }
/// <summary>
/// Whether this renderer will subscribe to another and copy its events.
/// See <see cref="FilteredChannels"/> to filter specific channels.
/// </summary>
IMidiRenderer? Master { get; set; }
// NOTE: Why is the properties below BitArray, you ask?
// Well see, MIDI 2.0 supports up to 256(!) channels as opposed to MIDI 1.0's meekly 16 channels...
// I'd like us to support MIDI 2.0 one day so I'm just future-proofing here. Also BitArray is cool!
/// <summary>
/// Allows you to filter out note events from certain channels.
/// Only NoteOn will be filtered.
/// </summary>
BitArray FilteredChannels { get; }
/// <summary>
/// Allows you to override all NoteOn velocities. Set to null to disable.
/// </summary>
byte? VelocityOverride { get; set; }
/// <summary>
/// Start listening for midi input.
/// </summary>
@@ -143,11 +120,6 @@ public interface IMidiRenderer : IDisposable
/// </summary>
void StopAllNotes();
/// <summary>
/// Reset renderer back to a clean state.
/// </summary>
void SystemReset();
/// <summary>
/// Clears all scheduled events.
/// </summary>
@@ -184,7 +156,7 @@ public interface IMidiRenderer : IDisposable
/// This is only used if <see cref="Mono"/> is set to True
/// and <see cref="TrackingEntity"/> is null.
/// </summary>
MapCoordinates? TrackingCoordinates { get; set; }
EntityCoordinates? TrackingCoordinates { get; set; }
MidiRendererState RendererState { get; }
@@ -192,8 +164,7 @@ public interface IMidiRenderer : IDisposable
/// Send a midi event for the renderer to play.
/// </summary>
/// <param name="midiEvent">The midi event to be played</param>
/// <param name="raiseEvent">Whether to raise an event for this event.</param>
void SendMidiEvent(RobustMidiEvent midiEvent, bool raiseEvent = true);
void SendMidiEvent(RobustMidiEvent midiEvent);
/// <summary>
/// Schedule a MIDI event to be played at a later time.
@@ -206,7 +177,7 @@ public interface IMidiRenderer : IDisposable
/// <summary>
/// Apply a certain state to the renderer.
/// </summary>
void ApplyState(MidiRendererState state, bool filterChannels = false);
void ApplyState(MidiRendererState state);
/// <summary>
/// Actually disposes of this renderer. Do NOT use outside the MIDI thread.

View File

@@ -3,27 +3,20 @@ using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using NFluidsynth;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared;
using Robust.Shared.Asynchronous;
using Robust.Shared.Audio.Midi;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Exceptions;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Threading;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
@@ -33,13 +26,6 @@ internal sealed partial class MidiManager : IMidiManager
{
public const string SoundfontEnvironmentVariable = "ROBUST_SOUNDFONT_OVERRIDE";
private int _minRendererParallel;
private float _occlusionUpdateDelay;
private float _positionUpdateDelay;
[ViewVariables] private TimeSpan _nextOcclusionUpdate = TimeSpan.Zero;
[ViewVariables] private TimeSpan _nextPositionUpdate = TimeSpan.Zero;
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IResourceCacheInternal _resourceManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
@@ -47,9 +33,6 @@ internal sealed partial class MidiManager : IMidiManager
[Dependency] private readonly IClydeAudio _clydeAudio = default!;
[Dependency] private readonly ITaskManager _taskManager = default!;
[Dependency] private readonly ILogManager _logger = default!;
[Dependency] private readonly IParallelManager _parallel = default!;
[Dependency] private readonly IRuntimeLog _runtime = default!;
[Dependency] private readonly IGameTiming _timing = default!;
private SharedPhysicsSystem _broadPhaseSystem = default!;
@@ -76,10 +59,11 @@ internal sealed partial class MidiManager : IMidiManager
}
}
[ViewVariables] private readonly List<IMidiRenderer> _renderers = new();
[ViewVariables]
private readonly List<IMidiRenderer> _renderers = new();
private bool _alive = true;
[ViewVariables] private Settings? _settings;
private Settings? _settings;
private Thread? _midiThread;
private ISawmill _midiSawmill = default!;
private float _volume = 0f;
@@ -131,7 +115,7 @@ internal sealed partial class MidiManager : IMidiManager
private bool _failedInitialize;
private NFluidsynth.Logger.LoggerDelegate _loggerDelegate = default!;
private ISawmill _fluidsynthSawmill = default!;
private ISawmill _sawmill = default!;
private float _maxCastLength;
[ViewVariables(VVAccess.ReadWrite)]
@@ -146,28 +130,20 @@ internal sealed partial class MidiManager : IMidiManager
{
if (FluidsynthInitialized || _failedInitialize) return;
_volume = _cfgMan.GetCVar(CVars.MidiVolume);
_cfgMan.OnValueChanged(CVars.MidiVolume, value =>
{
_volume = value;
_volumeDirty = true;
}, true);
_cfgMan.OnValueChanged(CVars.MidiMinRendererParallel,
value => _minRendererParallel = value, true);
_cfgMan.OnValueChanged(CVars.MidiOcclusionUpdateDelay,
value => _occlusionUpdateDelay = value, true);
_cfgMan.OnValueChanged(CVars.MidiPositionUpdateDelay,
value => _positionUpdateDelay = value, true);
_midiSawmill = _logger.GetSawmill("midi");
#if DEBUG
_midiSawmill.Level = LogLevel.Debug;
#else
_midiSawmill.Level = LogLevel.Error;
#endif
_fluidsynthSawmill = _logger.GetSawmill("midi.fluidsynth");
_sawmill = _logger.GetSawmill("midi.fluidsynth");
_loggerDelegate = LoggerDelegate;
if (!_resourceManager.UserData.Exists(CustomSoundfontDirectory))
@@ -191,6 +167,8 @@ internal sealed partial class MidiManager : IMidiManager
_settings["synth.lock-memory"].IntValue = 0;
_settings["synth.threadsafe-api"].IntValue = 1;
_settings["synth.gain"].DoubleValue = 1.0d;
_settings["synth.polyphony"].IntValue = 1024;
_settings["synth.cpu-cores"].IntValue = 2;
_settings["synth.midi-channels"].IntValue = 16;
_settings["synth.overflow.age"].DoubleValue = 3000;
_settings["audio.driver"].StringValue = "file";
@@ -198,11 +176,8 @@ internal sealed partial class MidiManager : IMidiManager
_settings["audio.period-size"].IntValue = 4096;
_settings["midi.autoconnect"].IntValue = 1;
_settings["player.reset-synth"].IntValue = 0;
_settings["synth.midi-channels"].IntValue = Math.Clamp(RobustMidiEvent.MaxChannels, 16, 256);
_settings["synth.midi-bank-select"].StringValue = "gm";
//_settings["synth.verbose"].IntValue = 1; // Useful for debugging.
_parallel.AddAndInvokeParallelCountChanged(UpdateParallelCount);
}
catch (Exception e)
{
@@ -220,18 +195,6 @@ internal sealed partial class MidiManager : IMidiManager
FluidsynthInitialized = true;
}
private void UpdateParallelCount()
{
if (_settings == null)
return;
_settings["synth.polyphony"].IntValue = Math.Clamp(1024 + (int)(Math.Log2(_parallel.ParallelProcessCount) * 2048), 1, 65535);
_settings["synth.cpu-cores"].IntValue = Math.Clamp(_parallel.ParallelProcessCount, 1, 256);
_midiSawmill.Debug($"Synth Cores: {_settings["synth.cpu-cores"].IntValue}");
_midiSawmill.Debug($"Synth Polyphony: {_settings["synth.polyphony"].IntValue}");
}
private void OnRaycastLengthChanged(float value)
{
_maxCastLength = value;
@@ -248,7 +211,7 @@ internal sealed partial class MidiManager : IMidiManager
NFluidsynth.Logger.LogLevel.Debug => LogLevel.Debug,
_ => LogLevel.Debug
};
_fluidsynthSawmill.Log(rLevel, message);
_sawmill.Log(rLevel, message);
}
public IMidiRenderer? GetNewRenderer(bool mono = true)
@@ -275,7 +238,7 @@ internal sealed partial class MidiManager : IMidiManager
var renderer = new MidiRenderer(_settings!, soundfontLoader, mono, this, _clydeAudio, _taskManager, _midiSawmill);
_midiSawmill.Debug($"Loading fallback soundfont {FallbackSoundfont}");
_midiSawmill.Debug($"Loading soundfont {FallbackSoundfont}");
// Since the last loaded soundfont takes priority, we load the fallback soundfont before the soundfont.
renderer.LoadSoundfont(FallbackSoundfont);
@@ -289,8 +252,8 @@ internal sealed partial class MidiManager : IMidiManager
try
{
_midiSawmill.Debug($"Loading OS soundfont {filepath}");
renderer.LoadSoundfont(filepath);
_midiSawmill.Debug($"Loaded Linux soundfont {filepath}");
}
catch (Exception)
{
@@ -304,7 +267,7 @@ internal sealed partial class MidiManager : IMidiManager
{
if (File.Exists(OsxSoundfont) && SoundFont.IsSoundFont(OsxSoundfont))
{
_midiSawmill.Debug($"Loading OS soundfont {OsxSoundfont}");
_midiSawmill.Debug($"Loading soundfont {OsxSoundfont}");
renderer.LoadSoundfont(OsxSoundfont);
}
}
@@ -312,7 +275,7 @@ internal sealed partial class MidiManager : IMidiManager
{
if (File.Exists(WindowsSoundfont) && SoundFont.IsSoundFont(WindowsSoundfont))
{
_midiSawmill.Debug($"Loading OS soundfont {WindowsSoundfont}");
_midiSawmill.Debug($"Loading soundfont {WindowsSoundfont}");
renderer.LoadSoundfont(WindowsSoundfont);
}
}
@@ -323,31 +286,27 @@ internal sealed partial class MidiManager : IMidiManager
{
if (File.Exists(soundfontOverride) && SoundFont.IsSoundFont(soundfontOverride))
{
_midiSawmill.Debug($"Loading environment variable soundfont {soundfontOverride}");
_midiSawmill.Debug($"Loading soundfont {soundfontOverride} from environment variable.");
renderer.LoadSoundfont(soundfontOverride);
}
}
// Load content-specific custom soundfonts, which should override the system/fallback soundfont.
_midiSawmill.Debug($"Loading soundfonts from content directory {ContentCustomSoundfontDirectory}");
_midiSawmill.Debug($"Loading soundfonts from {ContentCustomSoundfontDirectory}");
foreach (var file in _resourceManager.ContentFindFiles(ContentCustomSoundfontDirectory))
{
if (file.Extension != "sf2" && file.Extension != "dls" && file.Extension != "sf3") continue;
_midiSawmill.Debug($"Loading content soundfont {file}");
_midiSawmill.Debug($"Loading soundfont {file}");
renderer.LoadSoundfont(file.ToString());
}
var userDataPath = _resourceManager.UserData.RootDir == null
? CustomSoundfontDirectory
: new ResPath(_resourceManager.UserData.RootDir) / CustomSoundfontDirectory.ToRelativePath();
// Load every soundfont from the user data directory last, since those may override any other soundfont.
_midiSawmill.Debug($"Loading soundfonts from user data directory {userDataPath}");
var enumerator = _resourceManager.UserData.Find($"{CustomSoundfontDirectory.ToRelativePath()}*").Item1;
_midiSawmill.Debug($"Loading soundfonts from {{USERDATA}} {CustomSoundfontDirectory}");
var enumerator = _resourceManager.UserData.Find($"{CustomSoundfontDirectory.ToRelativePath()}/*").Item1;
foreach (var file in enumerator)
{
if (file.Extension != "sf2" && file.Extension != "dls" && file.Extension != "sf3") continue;
_midiSawmill.Debug($"Loading user soundfont {file}");
_midiSawmill.Debug($"Loading soundfont {{USERDATA}} {file}");
renderer.LoadSoundfont(file.ToString());
}
@@ -377,108 +336,72 @@ internal sealed partial class MidiManager : IMidiManager
lock (_renderers)
{
if (_renderers.Count == 0)
return;
var transQuery = _entityManager.GetEntityQuery<TransformComponent>();
var physicsQuery = _entityManager.GetEntityQuery<PhysicsComponent>();
var opts = new ParallelOptions { MaxDegreeOfParallelism = _parallel.ParallelProcessCount };
if (_renderers.Count > _minRendererParallel)
foreach (var renderer in _renderers)
{
Parallel.ForEach(_renderers, opts, renderer => UpdateRenderer(renderer, transQuery, physicsQuery));
}
else
{
foreach (var renderer in _renderers)
if (renderer.Disposed)
continue;
if(_volumeDirty)
renderer.Source.SetVolume(Volume);
if (!renderer.Mono)
{
UpdateRenderer(renderer, transQuery, physicsQuery);
renderer.Source.SetGlobal();
continue;
}
MapCoordinates? mapPos = null;
var trackingEntity = renderer.TrackingEntity != null && !_entityManager.Deleted(renderer.TrackingEntity);
if (trackingEntity)
{
renderer.TrackingCoordinates = _entityManager.GetComponent<TransformComponent>(renderer.TrackingEntity!.Value).Coordinates;
}
if (renderer.TrackingCoordinates != null)
{
mapPos = renderer.TrackingCoordinates.Value.ToMap(_entityManager);
}
if (mapPos != null && mapPos.Value.MapId == _eyeManager.CurrentMap)
{
var pos = mapPos.Value;
var sourceRelative = pos.Position - _eyeManager.CurrentEye.Position.Position;
var occlusion = 0f;
if (sourceRelative.Length() > 0)
{
occlusion = _broadPhaseSystem.IntersectRayPenetration(
pos.MapId,
new CollisionRay(
_eyeManager.CurrentEye.Position.Position,
sourceRelative.Normalized(),
OcclusionCollisionMask),
MathF.Min(sourceRelative.Length(), _maxCastLength),
renderer.TrackingEntity);
}
renderer.Source.SetOcclusion(occlusion);
if (!renderer.Source.SetPosition(pos.Position))
{
return;
}
if (trackingEntity)
{
var vel = _broadPhaseSystem.GetMapLinearVelocity(renderer.TrackingEntity!.Value);
renderer.Source.SetVelocity(vel);
}
}
else
{
renderer.Source.SetOcclusion(float.MaxValue);
}
}
}
if (_nextOcclusionUpdate < _timing.RealTime)
_nextOcclusionUpdate = _timing.RealTime.Add(TimeSpan.FromSeconds(_occlusionUpdateDelay));
if (_nextPositionUpdate < _timing.RealTime)
_nextPositionUpdate = _timing.RealTime.Add(TimeSpan.FromSeconds(_positionUpdateDelay));
_volumeDirty = false;
}
private void UpdateRenderer(IMidiRenderer renderer, EntityQuery<TransformComponent> transQuery,
EntityQuery<PhysicsComponent> physicsQuery)
{
try
{
if (renderer.Disposed)
return;
if (_volumeDirty)
renderer.Source.SetVolume(Volume);
if (!renderer.Mono)
{
renderer.Source.SetGlobal();
return;
}
if (_nextPositionUpdate < _timing.RealTime)
{
if (renderer.TrackingEntity is {} trackedEntity && !_entityManager.Deleted(trackedEntity))
{
renderer.TrackingCoordinates = transQuery.GetComponent(renderer.TrackingEntity!.Value).MapPosition;
}
else if (renderer.TrackingCoordinates == null)
{
return;
}
if (!renderer.Source.SetPosition(renderer.TrackingCoordinates.Value.Position))
{
return;
}
var vel = _broadPhaseSystem.GetMapLinearVelocity(renderer.TrackingEntity!.Value,
xformQuery: transQuery, physicsQuery: physicsQuery);
renderer.Source.SetVelocity(vel);
}
if (renderer.TrackingCoordinates != null && renderer.TrackingCoordinates.Value.MapId == _eyeManager.CurrentMap)
{
if (_nextOcclusionUpdate >= _timing.RealTime)
return;
var pos = renderer.TrackingCoordinates.Value;
var sourceRelative = pos.Position - _eyeManager.CurrentEye.Position.Position;
var occlusion = 0f;
if (sourceRelative.Length() > 0)
{
occlusion = _broadPhaseSystem.IntersectRayPenetration(
pos.MapId,
new CollisionRay(
_eyeManager.CurrentEye.Position.Position,
sourceRelative.Normalized(),
OcclusionCollisionMask),
MathF.Min(sourceRelative.Length(), _maxCastLength),
renderer.TrackingEntity);
}
renderer.Source.SetOcclusion(occlusion);
}
else
{
renderer.Source.SetOcclusion(float.MaxValue);
}
}
catch (Exception ex)
{
_runtime.LogException(ex, _midiSawmill.Name);
}
}
/// <summary>
/// Main method for the thread rendering the midi audio.
@@ -493,12 +416,7 @@ internal sealed partial class MidiManager : IMidiManager
{
var renderer = _renderers[i];
if (!renderer.Disposed)
{
if (renderer.Master is { Disposed: true })
renderer.Master = null;
renderer.Render();
}
else
{
renderer.InternalDispose();

View File

@@ -1,6 +1,4 @@
using System;
using System.Collections;
using JetBrains.Annotations;
using NFluidsynth;
using Robust.Client.Graphics;
using Robust.Shared.Asynchronous;
@@ -11,6 +9,7 @@ using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
using Logger = Robust.Shared.Log.Logger;
namespace Robust.Client.Audio.Midi;
@@ -22,13 +21,14 @@ internal sealed class MidiRenderer : IMidiRenderer
// TODO: Make this a replicated CVar in MidiManager
private const int MidiSizeLimit = 2000000;
private const double BytesToMegabytes = 0.000001d;
private const int ChannelCount = RobustMidiEvent.MaxChannels;
private const int ChannelCount = 16;
private readonly ISawmill _midiSawmill;
private readonly Settings _settings;
[ViewVariables(VVAccess.ReadWrite)] private bool _debugEvents = false;
[ViewVariables(VVAccess.ReadWrite)]
private bool _debugEvents = false;
// Kept around to avoid the loader callbacks getting GC'd
// ReSharper disable once NotAccessedField.Local
@@ -48,9 +48,8 @@ internal sealed class MidiRenderer : IMidiRenderer
private readonly SequencerClientId _robustRegister;
private readonly SequencerClientId _debugRegister;
[ViewVariables] private MidiRendererState _rendererState = new();
private IMidiRenderer? _master;
[ViewVariables]
private MidiRendererState _rendererState = new();
public MidiRendererState RendererState => _rendererState;
public IClydeBufferedAudioSource Source { get; set; }
IClydeBufferedAudioSource IMidiRenderer.Source => Source;
@@ -71,8 +70,8 @@ internal sealed class MidiRenderer : IMidiRenderer
{
for (byte i = 0; i < ChannelCount; i++)
{
// Don't change percussion channel instrument.
if (i == RobustMidiEvent.PercussionChannel)
// Channel 9 is the percussion channel. Let's not change its instrument...
if (i == 9)
continue;
SendMidiEvent(RobustMidiEvent.ProgramChange(i, value, SequencerTick));
@@ -97,14 +96,11 @@ internal sealed class MidiRenderer : IMidiRenderer
{
for (byte i = 0; i < ChannelCount; i++)
{
// Don't change percussion channel bank.
if (i == RobustMidiEvent.PercussionChannel)
// Channel 9 is the percussion channel. Let's not change its bank...
if (i == 9)
continue;
SendMidiEvent(RobustMidiEvent.BankSelect(i, value, SequencerTick));
// Re-select program.
SendMidiEvent(RobustMidiEvent.ProgramChange(i, _midiProgram, SequencerTick));
}
}
@@ -132,11 +128,7 @@ internal sealed class MidiRenderer : IMidiRenderer
}
[ViewVariables(VVAccess.ReadWrite)]
public bool DisablePercussionChannel
{
get => FilteredChannels[RobustMidiEvent.PercussionChannel];
set => FilteredChannels[RobustMidiEvent.PercussionChannel] = value;
}
public bool DisablePercussionChannel { get; set; } = true;
[ViewVariables(VVAccess.ReadWrite)]
public bool DisableProgramChangeEvent { get; set; } = true;
@@ -189,62 +181,13 @@ internal sealed class MidiRenderer : IMidiRenderer
}
[ViewVariables(VVAccess.ReadWrite)]
[Obsolete($"Use {nameof(VelocityOverride)} instead, you can set it to 127 to achieve the same effect.")]
public bool VolumeBoost
{
get => VelocityOverride == 127;
set => VelocityOverride = value ? 127 : null;
}
public bool VolumeBoost { get; set; }
[ViewVariables(VVAccess.ReadWrite)]
public EntityUid? TrackingEntity { get; set; } = null;
[ViewVariables(VVAccess.ReadWrite)]
public MapCoordinates? TrackingCoordinates { get; set; } = null;
[ViewVariables]
public BitArray FilteredChannels { get; } = new(RobustMidiEvent.MaxChannels);
[ViewVariables(VVAccess.ReadWrite)]
public byte? VelocityOverride { get; set; } = null;
[ViewVariables(VVAccess.ReadWrite)]
public IMidiRenderer? Master
{
get => _master;
set
{
if (value == _master)
return;
if (_master is { Disposed: false })
{
try
{
_master.OnMidiEvent -= SendMidiEvent;
}
catch
{
// ignored
}
}
_master = value;
if (_master == null)
return;
_master.OnMidiEvent += SendMidiEvent;
ApplyState(_master.RendererState, true);
MidiBank = _midiBank;
}
}
[ViewVariables, UsedImplicitly]
private double CpuLoad => !_synth.Disposed ? _synth.CpuLoad : 0;
public event Action<RobustMidiEvent>? OnMidiEvent;
public event Action? OnMidiPlayerFinished;
public EntityCoordinates? TrackingCoordinates { get; set; } = null;
internal MidiRenderer(Settings settings, SoundFontLoader soundFontLoader, bool mono,
IMidiManager midiManager, IClydeAudio clydeAudio, ITaskManager taskManager, ISawmill midiSawmill)
@@ -411,11 +354,6 @@ internal sealed class MidiRenderer : IMidiRenderer
}
}
public void SystemReset()
{
SendMidiEvent(RobustMidiEvent.SystemReset(SequencerTick));
}
public void ClearAllEvents()
{
_sequencer.RemoveEvents(SequencerClientId.Wildcard, SequencerClientId.Wildcard, -1);
@@ -430,6 +368,9 @@ internal sealed class MidiRenderer : IMidiRenderer
}
}
public event Action<RobustMidiEvent>? OnMidiEvent;
public event Action? OnMidiPlayerFinished;
void IMidiRenderer.Render()
{
Render();
@@ -491,7 +432,7 @@ internal sealed class MidiRenderer : IMidiRenderer
if (!Source.IsPlaying) Source.StartPlaying();
}
public void ApplyState(MidiRendererState state, bool filterChannels = false)
public void ApplyState(MidiRendererState state)
{
lock (_playerStateLock)
{
@@ -499,9 +440,6 @@ internal sealed class MidiRenderer : IMidiRenderer
for (var channel = 0; channel < ChannelCount; channel++)
{
if (filterChannels && !FilteredChannels[channel])
continue;
_synth.AllNotesOff(channel);
_synth.PitchBend(channel, state.PitchBend.AsSpan[channel]);
@@ -524,8 +462,7 @@ internal sealed class MidiRenderer : IMidiRenderer
}
}
var program = DisableProgramChangeEvent ? MidiProgram : state.Program.AsSpan[channel];
_synth.ProgramChange(channel, program);
_synth.ProgramChange(channel, state.Program.AsSpan[channel]);
for (var key = 0; key < state.NoteVelocities.AsSpan[channel].AsSpan.Length; key++)
{
@@ -550,12 +487,7 @@ internal sealed class MidiRenderer : IMidiRenderer
}
}
private void SendMidiEvent(RobustMidiEvent midiEvent)
{
SendMidiEvent(midiEvent, true);
}
public void SendMidiEvent(RobustMidiEvent midiEvent, bool raiseEvent)
public void SendMidiEvent(RobustMidiEvent midiEvent)
{
if (Disposed)
return;
@@ -573,10 +505,11 @@ internal sealed class MidiRenderer : IMidiRenderer
break;
case RobustMidiCommand.NoteOn:
if (FilteredChannels[midiEvent.Channel])
break;
// Channel 9 is the percussion channel. We only block NoteOn events to it.
if (DisablePercussionChannel && midiEvent.Channel == 9)
return;
var velocity = VelocityOverride ?? midiEvent.Velocity;
var velocity = (byte)(VolumeBoost ? 127 : midiEvent.Velocity);
_rendererState.NoteVelocities.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Key] = velocity;
_synth.NoteOn(midiEvent.Channel, midiEvent.Key, velocity);
@@ -590,7 +523,7 @@ internal sealed class MidiRenderer : IMidiRenderer
case RobustMidiCommand.ControlChange:
// CC0 is bank selection
if (midiEvent.Control == 0x0 && DisableProgramChangeEvent)
break;
return;
_rendererState.Controllers.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Control] = midiEvent.Value;
if(midiEvent.Control != 0x0)
@@ -601,7 +534,7 @@ internal sealed class MidiRenderer : IMidiRenderer
case RobustMidiCommand.ProgramChange:
if (DisableProgramChangeEvent)
break;
return;
_rendererState.Program.AsSpan[midiEvent.Channel] = midiEvent.Program;
_synth.ProgramChange(midiEvent.Channel, midiEvent.Program);
@@ -628,14 +561,14 @@ internal sealed class MidiRenderer : IMidiRenderer
switch (midiEvent.Control)
{
case 0x0 when midiEvent.Status == 0xFF:
_rendererState = new MidiRendererState();
_rendererState = new ();
_synth.SystemReset();
// Reset the instrument to the one we were using.
if (DisableProgramChangeEvent)
{
MidiBank = _midiBank;
MidiProgram = _midiProgram;
MidiBank = _midiBank;
}
break;
@@ -664,10 +597,7 @@ internal sealed class MidiRenderer : IMidiRenderer
//_midiSawmill.Error("Exception while sending midi event of type {0}: {1}", midiEvent.Type, e, midiEvent);
}
if (raiseEvent)
{
_taskManager.RunOnMainThread(() => OnMidiEvent?.Invoke(midiEvent));
}
_taskManager.RunOnMainThread(() => OnMidiEvent?.Invoke(midiEvent));
}
public void ScheduleMidiEvent(RobustMidiEvent midiEvent, uint time, bool absolute = false)
@@ -703,9 +633,6 @@ internal sealed class MidiRenderer : IMidiRenderer
/// <inheritdoc />
void IMidiRenderer.InternalDispose()
{
OnMidiEvent = null;
OnMidiPlayerFinished = null;
Source?.Dispose();
_driver?.Dispose();

View File

@@ -1,7 +1,6 @@
using System;
using System.Runtime.InteropServices;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Robust.Client.Audio.Midi;
@@ -13,7 +12,7 @@ public struct MidiRendererState
internal FixedArray16<byte> ChannelPressure;
internal FixedArray16<ushort> PitchBend;
[ViewVariables] internal Span<byte> AsSpan => MemoryMarshal.CreateSpan(ref NoteVelocities._00._00, 4160);
internal Span<byte> AsSpan => MemoryMarshal.CreateSpan(ref NoteVelocities._00._00, 4160);
static unsafe MidiRendererState()
{

View File

@@ -68,7 +68,6 @@ 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>();
@@ -85,7 +84,6 @@ namespace Robust.Client
deps.Register<IReplayLoadManager, ReplayLoadManager>();
deps.Register<IReplayPlaybackManager, ReplayPlaybackManager>();
deps.Register<IReplayRecordingManager, ReplayRecordingManager>();
deps.Register<IReplayRecordingManagerInternal, ReplayRecordingManager>();
deps.Register<IClientGameStateManager, ClientGameStateManager>();
deps.Register<IBaseClient, BaseClient>();
deps.Register<IPlayerManager, PlayerManager>();

View File

@@ -14,7 +14,7 @@ internal sealed partial class ClientConsoleHost
private int _completionSeq;
public async Task<CompletionResult> GetCompletions(List<string> args, string argStr, CancellationToken cancel)
public async Task<CompletionResult> GetCompletions(List<string> args, CancellationToken cancel)
{
// Last element is the command currently being typed. May be empty.
@@ -24,10 +24,10 @@ internal sealed partial class ClientConsoleHost
if (delay > 0)
await Task.Delay((int)(delay * 1000), cancel);
return await CalcCompletions(args, argStr, cancel);
return await CalcCompletions(args, cancel);
}
private Task<CompletionResult> CalcCompletions(List<string> args, string argStr, CancellationToken cancel)
private Task<CompletionResult> CalcCompletions(List<string> args, CancellationToken cancel)
{
if (args.Count == 1)
{
@@ -44,10 +44,10 @@ internal sealed partial class ClientConsoleHost
if (!AvailableCommands.TryGetValue(args[0], out var cmd))
return Task.FromResult(CompletionResult.Empty);
return cmd.GetCompletionAsync(LocalShell, args.ToArray()[1..], argStr, cancel).AsTask();
return cmd.GetCompletionAsync(LocalShell, args.ToArray()[1..], cancel).AsTask();
}
private Task<CompletionResult> DoServerCompletions(List<string> args, string argStr, CancellationToken cancel)
private Task<CompletionResult> DoServerCompletions(List<string> args, CancellationToken cancel)
{
var tcs = new TaskCompletionSource<CompletionResult>();
var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel);
@@ -62,7 +62,6 @@ internal sealed partial class ClientConsoleHost
var msg = new MsgConCompletion
{
Args = args.ToArray(),
ArgString = argStr,
Seq = seq
};

View File

@@ -10,7 +10,6 @@ using Robust.Shared.Console;
using Robust.Shared.Enums;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Players;
@@ -22,13 +21,13 @@ namespace Robust.Client.Console
{
public sealed class AddStringArgs : EventArgs
{
public FormattedMessage Text { get; }
public string Text { get; }
public bool Local { get; }
public bool Error { get; }
public AddStringArgs(FormattedMessage text, bool local, bool error)
public AddStringArgs(string text, bool local, bool error)
{
Text = text;
Local = local;
@@ -133,17 +132,10 @@ namespace Robust.Client.Console
AddFormatted?.Invoke(this, new AddFormattedMessageArgs(message));
}
public override void WriteLine(ICommonSession? session, FormattedMessage msg)
{
AddFormattedLine(msg);
}
/// <inheritdoc />
public override void WriteError(ICommonSession? session, string text)
{
var msg = new FormattedMessage();
msg.AddText(text);
OutputText(msg, true, true);
OutputText(text, true, true);
}
public bool IsCmdServer(IConsoleCommand cmd)
@@ -159,13 +151,8 @@ namespace Robust.Client.Console
if (string.IsNullOrWhiteSpace(command))
return;
WriteLine(null, "");
var msg = new FormattedMessage();
msg.PushColor(Color.Gold);
msg.AddText("> " + command);
msg.Pop();
// echo the command locally
OutputText(msg, true, false);
WriteLine(null, "> " + command);
//Commands are processed locally and then sent to the server to be processed there again.
var args = new List<string>();
@@ -218,9 +205,7 @@ namespace Robust.Client.Console
/// <inheritdoc />
public override void WriteLine(ICommonSession? session, string text)
{
var msg = new FormattedMessage();
msg.AddText(text);
OutputText(msg, true, false);
OutputText(text, true, false);
}
/// <inheritdoc />
@@ -229,12 +214,12 @@ namespace Robust.Client.Console
// We don't have anything to dispose.
}
private void OutputText(FormattedMessage text, bool local, bool error)
private void OutputText(string text, bool local, bool error)
{
AddString?.Invoke(this, new AddStringArgs(text, local, error));
var level = error ? LogLevel.Warning : LogLevel.Info;
_conLogger.Log(level, text.ToString());
_conLogger.Log(level, text);
}
private void OnNetworkConnected(object? sender, NetChannelArgs netChannelArgs)
@@ -244,7 +229,7 @@ namespace Robust.Client.Console
private void HandleConCmdAck(MsgConCmdAck msg)
{
OutputText(msg.Text, false, msg.Error);
OutputText("< " + msg.Text, false, msg.Error);
}
private void HandleConCmdReg(MsgConCmdReg msg)
@@ -318,14 +303,13 @@ namespace Robust.Client.Console
public async ValueTask<CompletionResult> GetCompletionAsync(
IConsoleShell shell,
string[] args,
string argStr,
CancellationToken cancel)
{
var host = (ClientConsoleHost)shell.ConsoleHost;
var argsList = args.ToList();
argsList.Insert(0, Command);
return await host.DoServerCompletions(argsList, argStr, cancel);
return await host.DoServerCompletions(argsList, cancel);
}
}
@@ -343,11 +327,10 @@ namespace Robust.Client.Console
public override async ValueTask<CompletionResult> GetCompletionAsync(
IConsoleShell shell,
string[] args,
string argStr,
CancellationToken cancel)
{
var host = (ClientConsoleHost)shell.ConsoleHost;
return await host.DoServerCompletions(args.ToList(), argStr[">".Length..], cancel);
return await host.DoServerCompletions(args.ToList(), cancel);
}
}
}

View File

@@ -621,7 +621,6 @@ namespace Robust.Client.Console.Commands
internal sealed class ChunkInfoCommand : LocalizedCommands
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IMapManager _map = default!;
[Dependency] private readonly IEyeManager _eye = default!;
[Dependency] private readonly IInputManager _input = default!;
@@ -630,19 +629,18 @@ namespace Robust.Client.Console.Commands
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mousePos = _eye.PixelToMap(_input.MouseScreenPosition);
var mousePos = _eye.ScreenToMap(_input.MouseScreenPosition);
if (!_map.TryFindGridAt(mousePos, out var gridUid, out var grid))
if (!_map.TryFindGridAt(mousePos, out _, out var grid))
{
shell.WriteLine("No grid under your mouse cursor.");
return;
}
var mapSystem = _entManager.System<SharedMapSystem>();
var chunkIndex = mapSystem.LocalToChunkIndices(gridUid, grid, grid.MapToGrid(mousePos));
var chunk = mapSystem.GetOrAddChunk(gridUid, grid, chunkIndex);
var chunkIndex = grid.LocalToChunkIndices(grid.MapToGrid(mousePos));
var chunk = grid.GetOrAddChunk(chunkIndex);
shell.WriteLine($"worldBounds: {mapSystem.CalcWorldAABB(gridUid, grid, chunk)} localBounds: {chunk.CachedBounds}");
shell.WriteLine($"worldBounds: {grid.CalcWorldAABB(chunk)} localBounds: {chunk.CachedBounds}");
}
}

View File

@@ -19,6 +19,6 @@ namespace Robust.Client.Console
void AddFormattedLine(FormattedMessage message);
Task<CompletionResult> GetCompletions(List<string> args, string argStr, CancellationToken cancel);
Task<CompletionResult> GetCompletions(List<string> args, CancellationToken cancel);
}
}

View File

@@ -61,7 +61,7 @@ namespace Robust.Client.Debugging
}
var mouseSpot = _inputManager.MouseScreenPosition;
var spot = _eyeManager.PixelToMap(mouseSpot);
var spot = _eyeManager.ScreenToMap(mouseSpot);
if (!_mapManager.TryFindGridAt(spot, out var gridUid, out var grid))
{

View File

@@ -218,7 +218,7 @@ namespace Robust.Client.Debugging
_debugPhysicsSystem = system;
_lookup = lookup;
_physicsSystem = physicsSystem;
_font = new VectorFont(cache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 10);
_font = new VectorFont(cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
}
private void DrawWorld(DrawingHandleWorld worldHandle, OverlayDrawArgs args)
@@ -371,7 +371,7 @@ namespace Robust.Client.Debugging
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.ShapeInfo) != 0x0)
{
var hoverBodies = new List<PhysicsComponent>();
var bounds = Box2.UnitCentered.Translated(_eyeManager.PixelToMap(mousePos.Position).Position);
var bounds = Box2.UnitCentered.Translated(_eyeManager.ScreenToMap(mousePos.Position).Position);
foreach (var physBody in _physicsSystem.GetCollidingEntities(mapId, bounds))
{
@@ -404,7 +404,7 @@ namespace Robust.Client.Debugging
if ((_debugPhysicsSystem.Flags & PhysicsDebugFlags.Distance) != 0x0)
{
var mapPos = _eyeManager.PixelToMap(mousePos);
var mapPos = _eyeManager.ScreenToMap(mousePos);
if (mapPos.MapId != args.MapId)
return;

View File

@@ -33,7 +33,6 @@ using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Profiling;
using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
using Robust.Shared.Replays;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
@@ -85,8 +84,7 @@ namespace Robust.Client
[Dependency] private readonly NetworkResourceManager _netResMan = default!;
[Dependency] private readonly IReplayLoadManager _replayLoader = default!;
[Dependency] private readonly IReplayPlaybackManager _replayPlayback = default!;
[Dependency] private readonly IReplayRecordingManagerInternal _replayRecording = default!;
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
[Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
private IWebViewManagerHook? _webViewHook;
@@ -164,7 +162,6 @@ namespace Robust.Client
// before prototype load.
ProgramShared.FinishCheckBadFileExtensions(checkBadExtensions);
_reflectionManager.Initialize();
_prototypeManager.Initialize();
_prototypeManager.LoadDefaultPrototypes();
_prototypeManager.ResolveResults();
@@ -203,12 +200,7 @@ namespace Robust.Client
// Setup main loop
if (_mainLoop == null)
{
_mainLoop = new GameLoop(
_gameTiming,
_runtimeLog,
_prof,
_logManager.GetSawmill("eng"),
GameLoopOptions.FromCVars(_configurationManager))
_mainLoop = new GameLoop(_gameTiming, _runtimeLog, _prof, _logManager.GetSawmill("eng"))
{
SleepMode = displayMode == DisplayMode.Headless ? SleepMode.Delay : SleepMode.None
};
@@ -568,11 +560,6 @@ namespace Robust.Client
{
_taskManager.ProcessPendingTasks(); // tasks like connect
}
using (_prof.Group("Content post engine"))
{
_modLoader.BroadcastUpdate(ModUpdateLevel.InputPostEngine, frameEventArgs);
}
}
private void Tick(FrameEventArgs frameEventArgs)
@@ -769,8 +756,6 @@ namespace Robust.Client
internal void CleanupGameThread()
{
_replayRecording.Shutdown();
_modLoader.Shutdown();
// CEF specifically makes a massive silent stink of it if we don't shut it down from the correct thread.

View File

@@ -15,12 +15,15 @@ namespace Robust.Client.GameObjects
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[ViewVariables] internal Eye? _eye = default!;
[ViewVariables]
private Eye? _eye = default!;
// Horrible hack to get around ordering issues.
internal bool _setCurrentOnInitialize;
[DataField("drawFov")] internal bool _setDrawFovOnInitialize = true;
[DataField("zoom")] internal Vector2 _setZoomOnInitialize = Vector2.One;
private bool _setCurrentOnInitialize;
[DataField("drawFov")]
private bool _setDrawFovOnInitialize = true;
[DataField("zoom")]
private Vector2 _setZoomOnInitialize = Vector2.One;
/// <summary>
/// If not null, this entity is used to update the eye's position instead of just using the component's owner.
@@ -116,6 +119,54 @@ namespace Robust.Client.GameObjects
[ViewVariables]
public MapCoordinates? Position => _eye?.Position;
/// <inheritdoc />
protected override void Initialize()
{
base.Initialize();
_eye = new Eye
{
Position = _entityManager.GetComponent<TransformComponent>(Owner).MapPosition,
Zoom = _setZoomOnInitialize,
DrawFov = _setDrawFovOnInitialize
};
if ((_eyeManager.CurrentEye == _eye) != _setCurrentOnInitialize)
{
if (_setCurrentOnInitialize)
{
_eyeManager.ClearCurrentEye();
}
else
{
_eyeManager.CurrentEye = _eye;
}
}
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (curState is not EyeComponentState state)
{
return;
}
DrawFov = state.DrawFov;
// TODO: Should be a way for content to override lerping and lerp the zoom
Zoom = state.Zoom;
Offset = state.Offset;
VisibilityMask = state.VisibilityMask;
}
protected override void OnRemove()
{
base.OnRemove();
Current = false;
}
/// <summary>
/// Updates the Eye of this entity with the transform position. This has to be called every frame to
/// keep the view following the entity.

View File

@@ -167,9 +167,40 @@ namespace Robust.Client.GameObjects
public bool TreeUpdateQueued { get; set; }
[DataField("layerDatums")]
private List<PrototypeLayerData> LayerDatums
{
get
{
var layerDatums = new List<PrototypeLayerData>();
foreach (var layer in Layers)
{
layerDatums.Add(layer.ToPrototypeData());
}
return layerDatums;
}
set
{
if (value == null) return;
Layers.Clear();
foreach (var layerDatum in value)
{
AddLayer(layerDatum);
}
_layerMapShared = true;
QueueUpdateRenderTree();
QueueUpdateIsInert();
}
}
private RSI? _baseRsi;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("rsi", priority: 2)]
public RSI? BaseRSI
{
get => _baseRsi;
@@ -326,16 +357,7 @@ namespace Robust.Client.GameObjects
if (layerDatums.Count != 0)
{
LayerMap.Clear();
Layers.Clear();
foreach (var datum in layerDatums)
{
AddLayer(datum);
}
_layerMapShared = true;
QueueUpdateRenderTree();
QueueUpdateIsInert();
LayerDatums = layerDatums;
}
UpdateLocalMatrix();

View File

@@ -314,9 +314,9 @@ public sealed class AudioSystem : SharedAudioSystem
return source != null;
}
private PlayingStream CreateAndStartPlayingStream(IClydeAudioSource source, AudioParams? audioParams, AudioStream stream)
private PlayingStream CreateAndStartPlayingStream(IClydeAudioSource source, AudioParams? audioParams)
{
ApplyAudioParams(audioParams, source, stream);
ApplyAudioParams(audioParams, source);
source.StartPlaying();
var playing = new PlayingStream
{
@@ -365,7 +365,7 @@ public sealed class AudioSystem : SharedAudioSystem
source.SetGlobal();
return CreateAndStartPlayingStream(source, audioParams, stream);
return CreateAndStartPlayingStream(source, audioParams);
}
/// <summary>
@@ -416,7 +416,7 @@ public sealed class AudioSystem : SharedAudioSystem
if (!source.SetPosition(worldPos))
return Play(stream, fallbackCoordinates.Value, fallbackCoordinates.Value, audioParams);
var playing = CreateAndStartPlayingStream(source, audioParams, stream);
var playing = CreateAndStartPlayingStream(source, audioParams);
playing.TrackingEntity = entity;
playing.TrackingFallbackCoordinates = fallbackCoordinates != EntityCoordinates.Invalid ? fallbackCoordinates : null;
return playing;
@@ -469,7 +469,7 @@ public sealed class AudioSystem : SharedAudioSystem
return null;
}
var playing = CreateAndStartPlayingStream(source, audioParams, stream);
var playing = CreateAndStartPlayingStream(source, audioParams);
playing.TrackingCoordinates = coordinates;
playing.TrackingFallbackCoordinates = fallbackCoordinates != EntityCoordinates.Invalid ? fallbackCoordinates : null;
return playing;
@@ -493,7 +493,7 @@ public sealed class AudioSystem : SharedAudioSystem
return null;
}
private void ApplyAudioParams(AudioParams? audioParams, IClydeAudioSource source, AudioStream audio)
private void ApplyAudioParams(AudioParams? audioParams, IClydeAudioSource source)
{
if (!audioParams.HasValue)
return;
@@ -508,12 +508,8 @@ public sealed class AudioSystem : SharedAudioSystem
source.SetRolloffFactor(audioParams.Value.RolloffFactor);
source.SetMaxDistance(audioParams.Value.MaxDistance);
source.SetReferenceDistance(audioParams.Value.ReferenceDistance);
source.SetPlaybackPosition(audioParams.Value.PlayOffsetSeconds);
source.IsLooping = audioParams.Value.Loop;
// TODO clamp the offset inside of SetPlaybackPosition() itself.
var offset = audioParams.Value.PlayOffsetSeconds;
offset = Math.Clamp(offset, 0f, (float) audio.Length.TotalSeconds);
source.SetPlaybackPosition(offset);
}
public sealed class PlayingStream : IPlayingAudioStream

View File

@@ -40,7 +40,7 @@ internal sealed class ClientOccluderSystem : OccluderSystem
return;
comp.Enabled = enabled;
Dirty(uid, comp);
Dirty(comp);
var xform = Transform(uid);
QueueTreeUpdate(uid, comp, xform);

View File

@@ -38,8 +38,7 @@ public sealed class DebugEntityLookupSystem : EntitySystem
IoCManager.Resolve<IOverlayManager>().AddOverlay(
new EntityLookupOverlay(
EntityManager,
EntityManager.System<EntityLookupSystem>(),
EntityManager.System<SharedTransformSystem>()));
Get<EntityLookupSystem>()));
}
else
{
@@ -53,35 +52,31 @@ public sealed class DebugEntityLookupSystem : EntitySystem
public sealed class EntityLookupOverlay : Overlay
{
private readonly IEntityManager _entityManager;
private readonly EntityLookupSystem _lookup;
private readonly SharedTransformSystem _transform;
private EntityQuery<TransformComponent> _xformQuery;
private IEntityManager _entityManager = default!;
private EntityLookupSystem _lookup = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public EntityLookupOverlay(IEntityManager entManager, EntityLookupSystem lookup, SharedTransformSystem transform)
public EntityLookupOverlay(IEntityManager entManager, EntityLookupSystem lookup)
{
_entityManager = entManager;
_lookup = lookup;
_xformQuery = entManager.GetEntityQuery<TransformComponent>();
_transform = transform;
}
protected internal override void Draw(in OverlayDrawArgs args)
{
var worldHandle = args.WorldHandle;
var worldBounds = args.WorldBounds;
var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
// TODO: Static version
_lookup.FindLookupsIntersecting(args.MapId, worldBounds, (uid, lookup) =>
foreach (var lookup in _lookup.FindLookupsIntersecting(args.MapId, args.WorldBounds))
{
var (_, rotation, matrix, invMatrix) = _transform.GetWorldPositionRotationMatrixWithInv(uid);
var lookupXform = xformQuery.GetComponent(lookup.Owner);
var (_, rotation, matrix, invMatrix) = lookupXform.GetWorldPositionRotationMatrixWithInv();
worldHandle.SetTransform(matrix);
var lookupAABB = invMatrix.TransformBox(worldBounds);
var lookupAABB = invMatrix.TransformBox(args.WorldBounds);
var ents = new List<EntityUid>();
lookup.DynamicTree.QueryAabb(ref ents, static (ref List<EntityUid> state, in FixtureProxy value) =>
@@ -110,22 +105,20 @@ public sealed class EntityLookupOverlay : Overlay
foreach (var ent in ents)
{
if (_entityManager.Deleted(ent))
continue;
var xform = _xformQuery.GetComponent(ent);
if (_entityManager.Deleted(ent)) continue;
var xform = xformQuery.GetComponent(ent);
//DebugTools.Assert(!ent.IsInContainer(_entityManager));
var (entPos, entRot) = _transform.GetWorldPositionRotation(ent);
var (entPos, entRot) = xform.GetWorldPositionRotation();
var lookupPos = invMatrix.Transform(entPos);
var lookupRot = entRot - rotation;
var aabb = _lookup.GetAABB(ent, lookupPos, lookupRot, xform, _xformQuery);
var aabb = _lookup.GetAABB(ent, lookupPos, lookupRot, xform, xformQuery);
worldHandle.DrawRect(aabb, Color.Blue.WithAlpha(0.2f));
}
});
}
worldHandle.SetTransform(Matrix3.Identity);
}

View File

@@ -1,60 +0,0 @@
using Robust.Client.Graphics;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
namespace Robust.Client.GameObjects;
public sealed class EyeSystem : SharedEyeSystem
{
[Dependency] private readonly IEyeManager _eyeManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<EyeComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<EyeComponent, ComponentRemove>(OnRemove);
SubscribeLocalEvent<EyeComponent, ComponentHandleState>(OnHandleState);
}
private void OnInit(EntityUid uid, EyeComponent component, ComponentInit args)
{
component._eye = new Eye
{
Position = Transform(uid).MapPosition,
Zoom = component._setZoomOnInitialize,
DrawFov = component._setDrawFovOnInitialize
};
if ((_eyeManager.CurrentEye == component._eye) != component._setCurrentOnInitialize)
{
if (component._setCurrentOnInitialize)
{
_eyeManager.ClearCurrentEye();
}
else
{
_eyeManager.CurrentEye = component._eye;
}
}
}
private void OnRemove(EntityUid uid, EyeComponent component, ComponentRemove args)
{
component.Current = false;
}
private void OnHandleState(EntityUid uid, EyeComponent component, ref ComponentHandleState args)
{
if (args.Current is not EyeComponentState state)
{
return;
}
component.DrawFov = state.DrawFov;
// TODO: Should be a way for content to override lerping and lerp the zoom
component.Zoom = state.Zoom;
component.Offset = state.Offset;
component.VisibilityMask = state.VisibilityMask;
}
}

View File

@@ -16,9 +16,4 @@ internal sealed class GridRenderingSystem : EntitySystem
{
_clyde.RegisterGridEcsEvents();
}
public override void Shutdown()
{
_clyde.ShutdownGridEcsEvents();
}
}

View File

@@ -37,7 +37,7 @@ namespace Robust.Client.GameObjects
return;
comp.ContainerOccluded = occluded;
Dirty(uid, comp);
Dirty(comp);
if (comp.Enabled)
_lightTree.QueueTreeUpdate(uid, comp);
@@ -50,7 +50,7 @@ namespace Robust.Client.GameObjects
comp._enabled = enabled;
RaiseLocalEvent(uid, new PointLightToggleEvent(comp.Enabled));
Dirty(uid, comp);
Dirty(comp);
var cast = (PointLightComponent)comp;
if (!cast.ContainerOccluded)
@@ -63,7 +63,7 @@ namespace Robust.Client.GameObjects
return;
comp._radius = radius;
Dirty(uid, comp);
Dirty(comp);
var cast = (PointLightComponent)comp;
if (cast.TreeUid != null)

View File

@@ -22,13 +22,6 @@ public sealed partial class TransformSystem
base.SetLocalPositionNoLerp(xform, value);
}
public override void SetLocalRotationNoLerp(TransformComponent xform, Angle angle)
{
xform.NextRotation = null;
xform.LerpParent = EntityUid.Invalid;
base.SetLocalRotationNoLerp(xform, angle);
}
public override void SetLocalRotation(TransformComponent xform, Angle angle)
{
xform.PrevRotation = xform._localRotation;

View File

@@ -532,6 +532,7 @@ namespace Robust.Client.GameStates
var handleState = new ComponentHandleState(compState, null);
_entities.EventBus.RaiseComponentEvent(comp, ref handleState);
comp.HandleComponentState(compState, null);
comp.LastModifiedTick = _timing.LastRealTick;
}
@@ -560,6 +561,7 @@ namespace Robust.Client.GameStates
var stateEv = new ComponentHandleState(state, null);
_entities.EventBus.RaiseComponentEvent(comp, ref stateEv);
comp.HandleComponentState(state, null);
comp.ClearCreationTick(); // don't undo the re-adding.
comp.LastModifiedTick = _timing.LastRealTick;
}
@@ -745,6 +747,10 @@ namespace Robust.Client.GameStates
}
}
var contQuery = _entities.GetEntityQuery<ContainerManagerComponent>();
var physicsQuery = _entities.GetEntityQuery<PhysicsComponent>();
var fixturesQuery = _entities.GetEntityQuery<FixturesComponent>();
var broadQuery = _entities.GetEntityQuery<BroadphaseComponent>();
var queuedBroadphaseUpdates = new List<(EntityUid, TransformComponent)>(enteringPvs);
// Apply entity states.
@@ -1166,6 +1172,7 @@ namespace Robust.Client.GameStates
{
var handleState = new ComponentHandleState(cur, next);
bus.RaiseComponentEvent(comp, ref handleState);
comp.HandleComponentState(cur, next);
}
catch (Exception e)
{
@@ -1216,7 +1223,7 @@ namespace Robust.Client.GameStates
return;
using var _ = _timing.StartStateApplicationArea();
ResetEnt(uid, meta, false);
ResetEnt(meta, false);
}
/// <summary>
@@ -1294,24 +1301,24 @@ namespace Robust.Client.GameStates
{
using var _ = _timing.StartStateApplicationArea();
var query = _entityManager.AllEntityQueryEnumerator<MetaDataComponent>();
while (query.MoveNext(out var uid, out var meta))
foreach (var meta in _entities.EntityQuery<MetaDataComponent>(true))
{
ResetEnt(uid, meta);
ResetEnt(meta);
}
}
/// <summary>
/// Reset a given entity to the most recent server state.
/// </summary>
private void ResetEnt(EntityUid uid, MetaDataComponent meta, bool skipDetached = true)
private void ResetEnt(MetaDataComponent meta, bool skipDetached = true)
{
if (skipDetached && (meta.Flags & MetaDataFlags.Detached) != 0)
return;
meta.Flags &= ~MetaDataFlags.Detached;
var uid = meta.Owner;
if (!_processor.TryGetLastServerStates(uid, out var lastState))
return;
@@ -1327,6 +1334,7 @@ namespace Robust.Client.GameStates
var handleState = new ComponentHandleState(state, null);
_entityManager.EventBus.RaiseComponentEvent(comp, ref handleState);
comp.HandleComponentState(state, null);
}
// ensure we don't have any extra components

View File

@@ -41,7 +41,7 @@ namespace Robust.Client.GameStates
{
IoCManager.InjectDependencies(this);
var cache = IoCManager.Resolve<IResourceCache>();
_font = new VectorFont(cache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 10);
_font = new VectorFont(cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
_lineHeight = _font.GetLineHeight(1);
_gameStateManager.GameStateApplied += HandleGameStateApplied;

View File

@@ -53,7 +53,7 @@ namespace Robust.Client.GameStates
{
IoCManager.InjectDependencies(this);
var cache = IoCManager.Resolve<IResourceCache>();
_font = new VectorFont(cache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 10);
_font = new VectorFont(cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
_gameStateManager.GameStateApplied += HandleGameStateApplied;
}

View File

@@ -27,7 +27,7 @@ namespace Robust.Client.Graphics.Audio
readSamples += read;
}
return new OggVorbisData(totalSamples, sampleRate, channels, buffer, vorbis.Tags.Title, vorbis.Tags.Artist);
return new OggVorbisData(totalSamples, sampleRate, channels, buffer);
}
}
@@ -37,17 +37,13 @@ namespace Robust.Client.Graphics.Audio
public readonly long SampleRate;
public readonly long Channels;
public readonly ReadOnlyMemory<float> Data;
public readonly string Title;
public readonly string Artist;
public OggVorbisData(long totalSamples, long sampleRate, long channels, ReadOnlyMemory<float> data, string title, string artist)
public OggVorbisData(long totalSamples, long sampleRate, long channels, ReadOnlyMemory<float> data)
{
TotalSamples = totalSamples;
SampleRate = sampleRate;
Channels = channels;
Data = data;
Title = title;
Artist = artist;
}
}
}

View File

@@ -261,9 +261,6 @@ namespace Robust.Client.Graphics.Audio
{
var source = AL.GenSource();
if (!AL.IsSource(source))
throw new Exception("Failed to generate source. Too many simultaneous audio streams?");
// ReSharper disable once PossibleInvalidOperationException
var audioSource = new BufferedAudioSource(this, source, AL.GenBuffers(buffers), floatAudio);
@@ -329,7 +326,7 @@ namespace Robust.Client.Graphics.Audio
var handle = new ClydeHandle(_audioSampleBuffers.Count);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
var length = TimeSpan.FromSeconds(vorbis.TotalSamples / (double) vorbis.SampleRate);
return new AudioStream(handle, length, (int) vorbis.Channels, name, vorbis.Title, vorbis.Artist);
return new AudioStream(handle, length, (int) vorbis.Channels, name);
}
public AudioStream LoadAudioWav(Stream stream, string? name = null)

View File

@@ -24,7 +24,6 @@ namespace Robust.Client.Graphics
[Dependency] private readonly IClyde _displayManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
private ISawmill _logMill = default!;
// We default to this when we get set to a null eye.
private readonly FixedEye _defaultEye = new();
@@ -54,7 +53,6 @@ namespace Robust.Client.Graphics
void IEyeManager.Initialize()
{
MainViewport = _uiManager.MainViewport;
_logMill = IoCManager.Resolve<ILogManager>().RootSawmill;
}
/// <inheritdoc />
@@ -131,14 +129,14 @@ namespace Robust.Client.Graphics
/// <inheritdoc />
public ScreenCoordinates CoordinatesToScreen(EntityCoordinates point)
{
return MapToScreen(point.ToMap(_entityManager, _entityManager.System<SharedTransformSystem>()));
return MapToScreen(point.ToMap(_entityManager));
}
public ScreenCoordinates MapToScreen(MapCoordinates point)
{
if (CurrentEye.Position.MapId != point.MapId)
{
_logMill.Error($"Attempted to convert map coordinates ({point}) to screen coordinates with an eye on another map ({CurrentEye.Position.MapId})");
Logger.Error($"Attempted to convert map coordinates ({point}) to screen coordinates with an eye on another map ({CurrentEye.Position.MapId})");
return new(default, WindowId.Invalid);
}
@@ -148,10 +146,12 @@ namespace Robust.Client.Graphics
/// <inheritdoc />
public MapCoordinates ScreenToMap(ScreenCoordinates point)
{
var (pos, window) = point;
if (_uiManager.MouseGetControl(point) is not IViewportControl viewport)
return default;
return viewport.ScreenToMap(point.Position);
return viewport.ScreenToMap(pos);
}
/// <inheritdoc />
@@ -159,21 +159,6 @@ namespace Robust.Client.Graphics
{
return MainViewport.ScreenToMap(point);
}
/// <inheritdoc />
public MapCoordinates PixelToMap(ScreenCoordinates point)
{
if (_uiManager.MouseGetControl(point) is not IViewportControl viewport)
return default;
return viewport.PixelToMap(point.Position);
}
/// <inheritdoc />
public MapCoordinates PixelToMap(Vector2 point)
{
return MainViewport.PixelToMap(point);
}
}
public sealed class CurrentEyeChangedEvent : EntityEventArgs

View File

@@ -79,16 +79,6 @@ namespace Robust.Client.Graphics
/// <returns>Corresponding point in the world.</returns>
MapCoordinates ScreenToMap(Vector2 point);
/// <summary>
/// Similar to <see cref="ScreenToMap(ScreenCoordinates)"/>, except it should compensate for the effects of shaders on viewports.
/// </summary>
MapCoordinates PixelToMap(ScreenCoordinates point);
/// <summary>
/// Similar to <see cref="ScreenToMap(Vector2)"/>, except it should compensate for the effects of shaders on viewports.
/// </summary>
MapCoordinates PixelToMap(Vector2 point);
void ClearCurrentEye();
void Initialize();
}

View File

@@ -176,14 +176,6 @@ namespace Robust.Client.Graphics.Clyde
_entityManager.EventBus.SubscribeEvent<GridModifiedEvent>(EventSource.Local, this, _updateOnGridModified);
}
public void ShutdownGridEcsEvents()
{
_entityManager.EventBus.UnsubscribeEvent<TileChangedEvent>(EventSource.Local, this);
_entityManager.EventBus.UnsubscribeEvent<GridStartupEvent>(EventSource.Local, this);
_entityManager.EventBus.UnsubscribeEvent<GridRemovalEvent>(EventSource.Local, this);
_entityManager.EventBus.UnsubscribeEvent<GridModifiedEvent>(EventSource.Local, this);
}
private void GLInitBindings(bool gles)
{
_glBindingsContext = _glContext!.BindingsContext;

View File

@@ -88,11 +88,6 @@ namespace Robust.Client.Graphics.Clyde
// Nada.
}
public void ShutdownGridEcsEvents()
{
}
public void SetWindowTitle(string title)
{
// Nada.

View File

@@ -12,9 +12,9 @@ void main()
lowp vec4 COLOR;
lowp vec3 lightSample = texture2D(lightMap, Pos).rgb;
// [SHADER_CODE]
lowp vec3 lightSample = texture2D(lightMap, Pos).rgb;
gl_FragColor = zAdjustResult(COLOR * VtxModulate * vec4(lightSample, 1.0));
}

View File

@@ -207,15 +207,6 @@ 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},
};

View File

@@ -191,15 +191,6 @@ 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>();

View File

@@ -65,8 +65,6 @@ namespace Robust.Client.Graphics
void RegisterGridEcsEvents();
void ShutdownGridEcsEvents();
void RunOnWindowThread(Action action);
}
}

View File

@@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
namespace Robust.Client.Graphics
{
@@ -12,7 +11,6 @@ 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!;

View File

@@ -20,7 +20,7 @@ namespace Robust.Client.Graphics
public sealed class ShaderPrototype : IPrototype, ISerializationHooks
{
[ViewVariables]
[IdDataField]
[IdDataFieldAttribute]
public string ID { get; } = default!;
[ViewVariables] private ShaderKind Kind;
@@ -65,12 +65,12 @@ namespace Robust.Client.Graphics
case ShaderKind.Canvas:
var hasLight = _rawMode != "unshaded";
var hasLight = rawMode != "unshaded";
ShaderBlendMode? blend = null;
if (_rawBlendMode != null)
if (rawBlendMode != null)
{
if (!Enum.TryParse<ShaderBlendMode>(_rawBlendMode.ToUpper(), out var parsed))
Logger.Error($"invalid mode: {_rawBlendMode}");
if (!Enum.TryParse<ShaderBlendMode>(rawBlendMode.ToUpper(), out var parsed))
Logger.Error($"invalid mode: {rawBlendMode}");
else
blend = parsed;
}
@@ -94,20 +94,11 @@ namespace Robust.Client.Graphics
return Instance().Duplicate();
}
[DataField("kind", required: true)]
private readonly string _rawKind = default!;
[DataField("path")]
private readonly ResPath? _path;
[DataField("params")]
private readonly Dictionary<string, string>? _paramMapping;
[DataField("light_mode")]
private readonly string? _rawMode;
[DataField("blend_mode")]
private readonly string? _rawBlendMode;
[DataField("kind", readOnly: true, required: true)] private string _rawKind = default!;
[DataField("path", readOnly: true)] private ResPath path;
[DataField("params", readOnly: true)] private Dictionary<string, string>? paramMapping;
[DataField("light_mode", readOnly: true)] private string? rawMode;
[DataField("blend_mode", readOnly: true)] private string? rawBlendMode;
void ISerializationHooks.AfterDeserialization()
{
@@ -115,22 +106,18 @@ namespace Robust.Client.Graphics
{
case "source":
Kind = ShaderKind.Source;
if (path == null) throw new InvalidOperationException();
_source = IoCManager.Resolve<IResourceCache>().GetResource<ShaderSourceResource>(path);
// TODO use a custom type serializer.
if (_path == null)
throw new InvalidOperationException("Source shaders must specify a source file.");
_source = IoCManager.Resolve<IResourceCache>().GetResource<ShaderSourceResource>(_path.Value);
if (_paramMapping != null)
if (paramMapping != null)
{
_params = new Dictionary<string, object>();
foreach (var item in _paramMapping!)
foreach (var item in paramMapping!)
{
var name = item.Key;
if (!_source.ParsedShader.Uniforms.TryGetValue(name, out var uniformDefinition))
{
Logger.ErrorS("shader", "Shader param '{0}' does not exist on shader '{1}'", name, _path);
Logger.ErrorS("shader", "Shader param '{0}' does not exist on shader '{1}'", name, path);
continue;
}

View File

@@ -161,15 +161,6 @@ namespace Robust.Client.Input
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24,
Pause,
World1,
}

View File

@@ -3,16 +3,13 @@ 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;
@@ -55,11 +52,8 @@ namespace Robust.Client.Map
_genTextureAtlas();
}
internal void _genTextureAtlas()
private void _genTextureAtlas()
{
_tileRegions.Clear();
_tileTextureAtlas = null;
var defList = TileDefs.Where(t => t.Sprite != null).ToList();
// If there are no tile definitions, we do nothing.
@@ -150,17 +144,4 @@ 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();
}
}
}

View File

@@ -23,7 +23,7 @@ namespace Robust.Client.Physics
{
if (_enableDebug == value) return;
Log.Info($"Set grid fixture debug to {value}");
Sawmill.Info($"Set grid fixture debug to {value}");
_enableDebug = value;
if (_enableDebug)
@@ -59,7 +59,7 @@ namespace Robust.Client.Physics
private void OnDebugMessage(ChunkSplitDebugMessage ev)
{
Log.Info($"Received grid fixture debug data");
Sawmill.Info($"Received grid fixture debug data");
if (!_enableDebug) return;
_nodes[ev.Grid] = ev.Nodes;

View File

@@ -532,7 +532,7 @@ namespace Robust.Client.Placement
return false;
}
coordinates = EntityCoordinates.FromMap(MapManager,
EyeManager.PixelToMap(InputManager.MouseScreenPosition));
EyeManager.ScreenToMap(InputManager.MouseScreenPosition));
return true;
}
}
@@ -694,9 +694,6 @@ namespace Robust.Client.Placement
IsActive = true;
CurrentPlacementOverlayEntity = EntityManager.SpawnEntity(templateName, MapCoordinates.Nullspace);
EntityManager.RunMapInit(
CurrentPlacementOverlayEntity.Value,
EntityManager.GetComponent<MetaDataComponent>(CurrentPlacementOverlayEntity.Value));
}
public void PreparePlacementSprite(SpriteComponent sprite)

View File

@@ -134,7 +134,7 @@ namespace Robust.Client.Placement
public IEnumerable<EntityCoordinates> LineCoordinates()
{
var mouseScreen = pManager.InputManager.MouseScreenPosition;
var mousePos = pManager.EyeManager.PixelToMap(mouseScreen);
var mousePos = pManager.EyeManager.ScreenToMap(mouseScreen);
if (mousePos.MapId == MapId.Nullspace)
yield break;
@@ -165,7 +165,7 @@ namespace Robust.Client.Placement
public IEnumerable<EntityCoordinates> GridCoordinates()
{
var mouseScreen = pManager.InputManager.MouseScreenPosition;
var mousePos = pManager.EyeManager.PixelToMap(mouseScreen);
var mousePos = pManager.EyeManager.ScreenToMap(mouseScreen);
if (mousePos.MapId == MapId.Nullspace)
yield break;
@@ -254,7 +254,7 @@ namespace Robust.Client.Placement
protected EntityCoordinates ScreenToCursorGrid(ScreenCoordinates coords)
{
var mapCoords = pManager.EyeManager.PixelToMap(coords.Position);
var mapCoords = pManager.EyeManager.ScreenToMap(coords.Position);
if (!pManager.MapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid))
{
return EntityCoordinates.FromMap(pManager.MapManager, mapCoords);

View File

@@ -3,18 +3,14 @@ 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>

View File

@@ -2,7 +2,6 @@ using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.Network;
using Robust.Shared.Players;
using Robust.Shared.ViewVariables;
namespace Robust.Client.Player
{
@@ -18,14 +17,11 @@ 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 />
@@ -35,11 +31,9 @@ namespace Robust.Client.Player
set => this.Name = value;
}
[ViewVariables]
internal short Ping { get; set; }
/// <inheritdoc />
[ViewVariables]
public INetChannel ConnectedClient { get; internal set; } = null!;
/// <inheritdoc />

View File

@@ -24,7 +24,7 @@ public sealed class LiveProfileViewControl : Control
{
IoCManager.InjectDependencies(this);
if (!_resourceCache.TryGetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf", out var font))
if (!_resourceCache.TryGetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf", out var font))
return;
_font = font.MakeDefault();

View File

@@ -7,7 +7,6 @@ using Robust.Shared.Network;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.Threading.Tasks;
using Robust.Client.Upload.Commands;
using Robust.Shared;
using Robust.Shared.GameObjects;
using Robust.Shared.Replays;
@@ -132,16 +131,12 @@ public sealed partial class ReplayLoadManager
var ticksSinceLastCheckpoint = 0;
var spawnedTracker = 0;
var stateTracker = 0;
var curState = state0;
for (var i = 1; i < states.Count; i++)
{
if (i % 10 == 0)
await callback(i, states.Count, LoadingState.ProcessingFiles, false);
var lastState = curState;
curState = states[i];
DebugTools.Assert(curState.FromSequence <= lastState.ToSequence);
var curState = states[i];
UpdatePlayerStates(curState.PlayerStates.Span, playerStates);
UpdateEntityStates(curState.EntityStates.Span, entStates, ref spawnedTracker, ref stateTracker, detached);
UpdateMessages(messages[i], uploadedFiles, prototypes, cvars, detachQueue, ref timeBase);
@@ -227,13 +222,7 @@ public sealed partial class ReplayLoadManager
// forwards. Also, note that files HAVE to be uploaded while generating checkpoints, in case
// someone spawns an entity that relies on uploaded data.
if (!ignoreDuplicates)
{
var msg = $"Overwriting an existing file upload! Path: {path}";
if (_confMan.GetCVar(CVars.ReplayIgnoreErrors))
_sawmill.Error(msg);
else
throw new NotSupportedException(msg);
}
throw new NotSupportedException("Overwriting an existing file is not yet supported by replays.");
message.Messages.RemoveSwap(i);
break;
@@ -251,56 +240,31 @@ public sealed partial class ReplayLoadManager
continue;
message.Messages.RemoveSwap(i);
var changed = new Dictionary<Type, HashSet<string>>();
_protoMan.LoadString(protoUpload.PrototypeData, true, changed);
try
foreach (var (kind, ids) in changed)
{
LoadPrototype(protoUpload.PrototypeData, prototypes, ignoreDuplicates);
var protos = prototypes[kind];
var count = protos.Count;
protos.UnionWith(ids);
if (!ignoreDuplicates && ids.Count + count != protos.Count)
{
// An existing prototype was overwritten. Much like for resource uploading, supporting this
// requires tracking the last-modified time of prototypes and either resetting or applying
// prototype changes when jumping around in time. This also requires reworking how the initial
// implicit state data is generated, because we can't simply cache it anymore.
// Also, does reloading prototypes in release mode modify existing entities?
throw new NotSupportedException($"Overwriting an existing prototype is not yet supported by replays.");
}
}
catch (Exception e)
{
if (e is NotSupportedException || !_confMan.GetCVar(CVars.ReplayIgnoreErrors))
throw;
var msg = $"Caught exception while parsing uploaded prototypes in a replay. Exception: {e}";
_sawmill.Error(msg);
}
_protoMan.ResolveResults();
_protoMan.ReloadPrototypes(changed);
_locMan.ReloadLocalizations();
}
}
private void LoadPrototype(
string data,
Dictionary<Type, HashSet<string>> prototypes,
bool ignoreDuplicates)
{
var changed = new Dictionary<Type, HashSet<string>>();
_protoMan.LoadString(data, true, changed);
foreach (var (kind, ids) in changed)
{
var protos = prototypes[kind];
var count = protos.Count;
protos.UnionWith(ids);
if (!ignoreDuplicates && ids.Count + count != protos.Count)
{
// An existing prototype was overwritten. Much like for resource uploading, supporting this
// requires tracking the last-modified time of prototypes and either resetting or applying
// prototype changes when jumping around in time. This also requires reworking how the initial
// implicit state data is generated, because we can't simply cache it anymore.
// Also, does reloading prototypes in release mode modify existing entities?
var msg = $"Overwriting an existing prototype! Kind: {kind.Name}. Ids: {string.Join(", ", ids)}";
if (_confMan.GetCVar(CVars.ReplayIgnoreErrors))
_sawmill.Error(msg);
else
throw new NotSupportedException(msg);
}
}
_protoMan.ResolveResults();
_protoMan.ReloadPrototypes(changed);
_locMan.ReloadLocalizations();
}
private void UpdateDeletions(NetListAsArray<EntityUid> entityDeletions,
Dictionary<EntityUid, EntityState> entStates, HashSet<EntityUid> detached)
{

View File

@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using Robust.Client.GameStates;
using Robust.Shared;
using Robust.Shared.GameObjects;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -66,18 +64,6 @@ public sealed partial class ReplayLoadManager
}
}
if (!entState.ComponentChanges.HasContents)
{
// This shouldn't be possible, yet it has happened?
// TODO this should probably also throw an exception.
_sawmill.Error($"Encountered blank entity state? Entity: {entState.Uid}. Last modified: {entState.EntityLastModified}. Attempting to continue.");
return null;
}
if (!_confMan.GetCVar(CVars.ReplayIgnoreErrors))
throw new MissingMetadataException(entState.Uid);
_sawmill.Error($"Missing metadata component. Entity: {entState.Uid}. Last modified: {entState.EntityLastModified}.");
return null;
throw new Exception("Missing metadata component");
}
}

View File

@@ -11,7 +11,6 @@ using Robust.Shared.Utility;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Robust.Shared;
using Robust.Shared.Replays;
using static Robust.Shared.Replays.ReplayConstants;
@@ -137,21 +136,9 @@ public sealed partial class ReplayLoadManager
if (data == null)
throw new Exception("Failed to load yaml metadata");
TimeSpan duration;
var finalData = LoadYamlFinalMetadata(fileReader);
if (finalData == null)
{
var msg = "Failed to load final yaml metadata";
if (!_confMan.GetCVar(CVars.ReplayIgnoreErrors))
throw new Exception(msg);
_sawmill.Error(msg);
duration = TimeSpan.FromDays(1);
}
else
{
duration = TimeSpan.Parse(((ValueDataNode) finalData[MetaFinalKeyDuration]).Value);
}
throw new Exception("Failed to load final yaml metadata");
var typeHash = Convert.FromHexString(((ValueDataNode) data[MetaKeyTypeHash]).Value);
var stringHash = Convert.FromHexString(((ValueDataNode) data[MetaKeyStringHash]).Value);
@@ -159,6 +146,7 @@ public sealed partial class ReplayLoadManager
var timeBaseTick = ((ValueDataNode) data[MetaKeyBaseTick]).Value;
var timeBaseTimespan = ((ValueDataNode) data[MetaKeyBaseTime]).Value;
var clientSide = bool.Parse(((ValueDataNode) data[MetaKeyIsClientRecording]).Value);
var duration = TimeSpan.Parse(((ValueDataNode) finalData[MetaFinalKeyDuration]).Value);
if (!typeHash.SequenceEqual(_serializer.GetSerializableTypesHash()))
throw new Exception($"{nameof(IRobustSerializer)} hashes do not match. Loading replays using a bad replay-client version?");

View File

@@ -97,8 +97,6 @@ internal sealed partial class ReplayPlaybackManager
if (message is EntityEventArgs args)
_entMan.DispatchReceivedNetworkMsg(args);
else if (_warned.Add(message.GetType()))
_sawmill.Error($"Unhandled replay message: {message.GetType()}.");
}
}
}

View File

@@ -55,7 +55,6 @@ internal sealed partial class ReplayPlaybackManager : IReplayPlaybackManager
private bool _initialized;
private ISawmill _sawmill = default!;
private HashSet<Type> _warned = new();
public bool Playing
{

View File

@@ -78,16 +78,15 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
IWritableDirProvider directory,
string? name = null,
bool overwrite = false,
TimeSpan? duration = null,
object? state = null)
TimeSpan? duration = null)
{
if (!base.TryStartRecording(directory, name, overwrite, duration, state))
if (!base.TryStartRecording(directory, name, overwrite, duration))
return false;
var (gameState, detachMsg) = CreateFullState();
var (state, detachMsg) = CreateFullState();
if (detachMsg != null)
RecordReplayMessage(detachMsg);
Update(gameState);
Update(state);
return true;
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Robust.Client.Graphics;
using Robust.Shared.ContentPack;
using Robust.Shared.Utility;
@@ -41,9 +40,5 @@ namespace Robust.Client.ResourceManagement
// Resource load callbacks so content can hook stuff like click maps.
event Action<TextureLoadedEventArgs> OnRawTextureLoaded;
event Action<RsiLoadedEventArgs> OnRsiLoaded;
IClyde Clyde { get; }
IClydeAudio ClydeAudio { get; }
IFontManager FontManager { get; }
}
}

View File

@@ -18,9 +18,7 @@ namespace Robust.Client.ResourceManagement
{
internal partial class ResourceCache
{
[field: Dependency] public IClyde Clyde { get; } = default!;
[field: Dependency] public IClydeAudio ClydeAudio { get; } = default!;
[field: Dependency] public IFontManager FontManager { get; } = default!;
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
@@ -72,7 +70,7 @@ namespace Robust.Client.ResourceManagement
try
{
TextureResource.LoadTexture(Clyde, data);
TextureResource.LoadTexture(_clyde, data);
}
catch (Exception e)
{
@@ -200,7 +198,7 @@ namespace Robust.Client.ResourceManagement
void FinalizeMetaAtlas(int toIndex, Image<Rgba32> sheet)
{
var atlas = Clyde.LoadTextureFromImage(sheet);
var atlas = _clyde.LoadTextureFromImage(sheet);
for (int i = finalized + 1; i <= toIndex; i++)
{
var rsi = rsiList[i];

View File

@@ -20,13 +20,14 @@ namespace Robust.Client.ResourceManagement
using (var fileStream = cache.ContentFileRead(path))
{
var clyde = IoCManager.Resolve<IClydeAudio>();
if (path.Extension == "ogg")
{
AudioStream = cache.ClydeAudio.LoadAudioOggVorbis(fileStream, path.ToString());
AudioStream = clyde.LoadAudioOggVorbis(fileStream, path.ToString());
}
else if (path.Extension == "wav")
{
AudioStream = cache.ClydeAudio.LoadAudioWav(fileStream, path.ToString());
AudioStream = clyde.LoadAudioWav(fileStream, path.ToString());
}
else
{

View File

@@ -18,7 +18,7 @@ namespace Robust.Client.ResourceManagement
using (stream)
{
FontFaceHandle = ((IFontManagerInternal)cache.FontManager).Load(stream);
FontFaceHandle = IoCManager.Resolve<IFontManagerInternal>().Load(stream);
}
}

View File

@@ -34,10 +34,12 @@ namespace Robust.Client.ResourceManagement
public override void Load(IResourceCache cache, ResPath path)
{
var clyde = IoCManager.Resolve<IClyde>();
var loadStepData = new LoadStepData {Path = path};
LoadPreTexture(cache, loadStepData);
loadStepData.AtlasTexture = cache.Clyde.LoadTextureFromImage(
loadStepData.AtlasTexture = clyde.LoadTextureFromImage(
loadStepData.AtlasSheet,
loadStepData.Path.ToString());

View File

@@ -1,7 +1,6 @@
using System.IO;
using System.Threading;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Clyde;
using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
@@ -28,7 +27,8 @@ namespace Robust.Client.ResourceManagement
ParsedShader = ShaderParser.Parse(reader, cache);
}
ClydeHandle = ((IClydeInternal)cache.Clyde).LoadShader(ParsedShader, path.ToString());
var clyde = IoCManager.Resolve<IClydeInternal>();
ClydeHandle = clyde.LoadShader(ParsedShader, path.ToString());
}
public override void Reload(IResourceCache cache, ResPath path, CancellationToken ct = default)
@@ -57,7 +57,8 @@ namespace Robust.Client.ResourceManagement
}
}
((IClydeInternal)cache.Clyde).ReloadShader(ClydeHandle, ParsedShader);
var clyde = IoCManager.Resolve<IClydeInternal>();
clyde.ReloadShader(ClydeHandle, ParsedShader);
}
}
}

View File

@@ -20,6 +20,8 @@ namespace Robust.Client.ResourceManagement
public override void Load(IResourceCache cache, ResPath path)
{
var clyde = IoCManager.Resolve<IClyde>();
if (path.Directory.Filename.EndsWith(".rsi"))
{
Logger.WarningS(
@@ -31,7 +33,7 @@ namespace Robust.Client.ResourceManagement
var data = new LoadStepData {Path = path};
LoadPreTexture(cache, data);
LoadTexture(cache.Clyde, data);
LoadTexture(clyde, data);
LoadFinish(cache, data);
}

View File

@@ -10,26 +10,25 @@
<RobustILLink>true</RobustILLink>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DiscordRichPresence" Version="1.0.175" PrivateAssets="compile" />
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.9" PrivateAssets="compile" />
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' == 'True'" PrivateAssets="compile" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.NFluidsynth" Version="0.1.1" PrivateAssets="compile" />
<PackageReference Include="NVorbis" Version="0.10.1" PrivateAssets="compile" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
<PackageReference Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" PrivateAssets="compile" />
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.SharpFont" Version="1.0.1" PrivateAssets="compile" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.9" />
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' == 'True'" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' != 'True'" />
<PackageReference Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
<PackageReference Include="NVorbis" Version="0.10.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
<PackageReference Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" />
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" />
<PackageReference Include="SpaceWizards.SharpFont" Version="1.0.1" />
<PackageReference Include="Robust.Natives" Version="0.1.1" />
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" PrivateAssets="compile" />
<PackageReference Condition="'$(FullRelease)' != 'True'" Include="JetBrains.Profiler.Api" Version="1.2.0" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.Sodium" Version="0.2.1" PrivateAssets="compile" />
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" />
<PackageReference Condition="'$(FullRelease)' != 'True'" Include="JetBrains.Profiler.Api" Version="1.2.0" />
</ItemGroup>
<ItemGroup Condition="'$(EnableClientScripting)' == 'True'">
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.0.1" PrivateAssets="compile" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.0.1" PrivateAssets="compile" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1" PrivateAssets="compile" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.0.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.0.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1" />
<ProjectReference Include="..\Robust.Shared.Scripting\Robust.Shared.Scripting.csproj" />
</ItemGroup>
@@ -51,7 +50,6 @@
<!-- ILLink configuration -->
<ItemGroup>
<RobustLinkRoots Include="Robust.Client" />
<RobustLinkRoots Include="Robust.Shared" />
<RobustLinkAssemblies Include="TerraFX.Interop.Windows" />
<RobustLinkAssemblies Include="OpenToolkit.Graphics" />
</ItemGroup>

View File

@@ -1,4 +1,5 @@
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -28,7 +29,7 @@ public sealed class ClientSpriteSpecifierSerializer : SpriteSpecifierSerializer
}
var res = dependencies.Resolve<IResourceCache>();
var rsiPath = TextureRoot / valuePathNode.Value;
var rsiPath = SpriteSpecifierSerializer.TextureRoot / valuePathNode.Value;
if (!res.TryGetResource(rsiPath, out RSIResource? resource))
{
return new ErrorNode(node, "Failed to load RSI");
@@ -39,10 +40,6 @@ public sealed class ClientSpriteSpecifierSerializer : SpriteSpecifierSerializer
return new ErrorNode(node, "Invalid RSI state");
}
return new ValidatedMappingNode(new()
{
{ new ValidatedValueNode(new ValueDataNode("sprite")), new ValidatedValueNode(pathNode)},
{ new ValidatedValueNode(new ValueDataNode("state")), new ValidatedValueNode(valueStateNode)},
});
return new ValidatedMappingNode(new());
}
}

View File

@@ -55,12 +55,12 @@ namespace Robust.Client.UserInterface
continue;
toRemove.Add(key);
AnimationCompleted?.Invoke(key);
}
foreach (var key in toRemove)
{
_playingAnimations.Remove(key);
AnimationCompleted?.Invoke(key);
}
}
}

View File

@@ -88,9 +88,9 @@ namespace Robust.Client.UserInterface.Controls
_scrollBar.MoveToEnd();
}
public Item AddItem(string text, Texture? icon = null, bool selectable = true, object? metadata = null)
public Item AddItem(string text, Texture? icon = null, bool selectable = true)
{
var item = new Item(this) {Text = text, Icon = icon, Selectable = selectable, Metadata = metadata};
var item = new Item(this) {Text = text, Icon = icon, Selectable = selectable};
Add(item);
return item;
}
@@ -448,8 +448,7 @@ namespace Robust.Client.UserInterface.Controls
{
if (item.Selected && SelectMode != ItemListSelectMode.Button)
{
if(SelectMode != ItemListSelectMode.Multiple)
ClearSelected();
ClearSelected();
item.Selected = false;
return;
}

View File

@@ -89,7 +89,7 @@ namespace Robust.Client.UserInterface.Controls
public void AddMessage(FormattedMessage message)
{
var entry = new RichTextEntry(message, this, _tagManager, null);
var entry = new RichTextEntry(message, this, _tagManager);
entry.Update(_getFont(), _getContentBox().Width, UIScale);

View File

@@ -1,5 +1,4 @@
using System;
using System.Numerics;
using System.Numerics;
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.RichText;
@@ -22,18 +21,18 @@ namespace Robust.Client.UserInterface.Controls
IoCManager.InjectDependencies(this);
}
public void SetMessage(FormattedMessage message, Type[]? tagsAllowed = null, Color? defaultColor = null)
public void SetMessage(FormattedMessage message)
{
_message = message;
_entry = new RichTextEntry(_message, this, _tagManager, tagsAllowed, defaultColor);
_entry = new RichTextEntry(_message, this, _tagManager);
InvalidateMeasure();
}
public void SetMessage(string message, Type[]? tagsAllowed = null, Color? defaultColor = null)
public void SetMessage(string message)
{
var msg = new FormattedMessage();
msg.AddText(message);
SetMessage(msg, tagsAllowed, defaultColor);
SetMessage(msg);
}
public string? GetMessage() => _message?.ToMarkup();

View File

@@ -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";
public LineEdit LineEditControl { get; }
private LineEdit _lineEdit;
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;
LineEditControl.Text = value.ToString();
_lineEdit.Text = value.ToString();
ValueChanged?.Invoke(new ValueChangedEventArgs(value));
}
}
@@ -52,7 +52,7 @@ namespace Robust.Client.UserInterface.Controls
return;
}
_value = value;
LineEditControl.Text = value.ToString();
_lineEdit.Text = value.ToString();
}
public event Action<ValueChangedEventArgs>? ValueChanged;
@@ -62,17 +62,17 @@ namespace Robust.Client.UserInterface.Controls
Orientation = LayoutOrientation.Horizontal;
MouseFilter = MouseFilterMode.Pass;
LineEditControl = new LineEdit
_lineEdit = new LineEdit
{
MinSize = new Vector2(40, 0),
HorizontalExpand = true
};
AddChild(LineEditControl);
AddChild(_lineEdit);
Value = 0;
LineEditControl.IsValid = (str) => int.TryParse(str, out var i) && (IsValid == null || IsValid(i));
LineEditControl.OnTextChanged += (args) =>
_lineEdit.IsValid = (str) => int.TryParse(str, out var i) && (IsValid == null || IsValid(i));
_lineEdit.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 => !LineEditControl.Editable;
set => LineEditControl.Editable = !value;
get => !_lineEdit.Editable;
set => _lineEdit.Editable = !value;
}
/// <summary>
@@ -185,7 +185,7 @@ namespace Robust.Client.UserInterface.Controls
{
base.MouseWheel(args);
if (!LineEditControl.HasKeyboardFocus())
if (!_lineEdit.HasKeyboardFocus())
{
return;
}

View File

@@ -1,7 +1,7 @@
<Control xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics">
<BoxContainer Orientation="Vertical">
<OutputPanel Name="Output" VerticalExpand="True" StyleClasses="monospace">
<OutputPanel Name="Output" VerticalExpand="True">
<OutputPanel.StyleBoxOverride>
<gfx:StyleBoxFlat BackgroundColor="#25252add"
ContentMarginLeftOverride="3" ContentMarginRightOverride="3"

View File

@@ -90,7 +90,7 @@ public sealed partial class DebugConsole
private async void TypeUpdateCompletions(bool fullUpdate)
{
var (args, _, _, str) = CalcTypingArgs();
var (args, _, _) = CalcTypingArgs();
if (args.Count != _compParamCount)
{
@@ -101,7 +101,7 @@ public sealed partial class DebugConsole
if (fullUpdate)
{
var seq = ++_compSeqSend;
var task = _consoleHost.GetCompletions(args, str, _compCancel.Token);
var task = _consoleHost.GetCompletions(args, _compCancel.Token);
if (!task.IsCompleted)
{
@@ -140,7 +140,7 @@ public sealed partial class DebugConsole
if (_compCurResult == null)
return;
var (_, curTyping, _, _) = CalcTypingArgs();
var (_, curTyping, _) = CalcTypingArgs();
var curSelected = _compFiltered?.Length > 0 ? _compFiltered[_compSelected] : default;
_compFiltered = FilterCompletions(_compCurResult.Options, curTyping);
@@ -168,7 +168,7 @@ public sealed partial class DebugConsole
DebugTools.AssertNotNull(_compCurResult);
var (_, _, endRange, _) = CalcTypingArgs();
var (_, _, endRange) = CalcTypingArgs();
var offset = CommandBar.GetOffsetAtIndex(endRange.start);
// Logger.Debug($"Offset: {offset}");
@@ -231,7 +231,7 @@ public sealed partial class DebugConsole
}
}
private (List<string> args, string curTyping, (int start, int end) lastRange, string argStr) CalcTypingArgs()
private (List<string> args, string curTyping, (int start, int end) lastRange) CalcTypingArgs()
{
var cursor = CommandBar.CursorPosition;
// Don't consider text after the cursor.
@@ -252,7 +252,7 @@ public sealed partial class DebugConsole
else
lastRange = (cursor, cursor);
return (args, args[^1], lastRange, text.ToString());
return (args, args[^1], lastRange);
}
private CompletionOption[] FilterCompletions(IEnumerable<CompletionOption> completions, string curTyping)
@@ -270,7 +270,7 @@ public sealed partial class DebugConsole
{
// Figure out typing word so we know how much to replace.
var (completion, _, completionFlags) = _compFiltered[_compSelected];
var (_, _, lastRange, _) = CalcTypingArgs();
var (_, _, lastRange) = CalcTypingArgs();
// Replace the full word from the start.
// This means that letter casing will match the completion suggestion.

View File

@@ -23,9 +23,9 @@ namespace Robust.Client.UserInterface.CustomControls
/// <summary>
/// Write a line with a specific color to the console window.
/// </summary>
void AddLine(FormattedMessage text, Color color);
void AddLine(string text, Color color);
void AddLine(FormattedMessage text);
void AddLine(string text);
void AddFormattedLine(FormattedMessage message);
@@ -108,7 +108,7 @@ namespace Robust.Client.UserInterface.CustomControls
private Color DetermineColor(bool local, bool error)
{
return error ? Color.Red : Color.White;
return Color.White;
}
protected override void FrameUpdate(FrameEventArgs args)
@@ -136,16 +136,16 @@ namespace Robust.Client.UserInterface.CustomControls
_flushHistoryToDisk();
}
public void AddLine(FormattedMessage text, Color color)
public void AddLine(string text, Color color)
{
var formatted = new FormattedMessage(3);
formatted.PushColor(color);
formatted.AddMessage(text);
formatted.AddText(text);
formatted.Pop();
AddFormattedLine(formatted);
}
public void AddLine(FormattedMessage text)
public void AddLine(string text)
{
AddLine(text, Color.White);
}

View File

@@ -62,19 +62,18 @@ namespace Robust.Client.UserInterface.CustomControls.DebugMonitorControls
var screenSize = _displayManager.ScreenSize;
var screenScale = _displayManager.MainWindow.ContentScale;
MapCoordinates mouseWorldMap;
EntityCoordinates mouseGridPos;
TileRef tile;
var mouseWorldMap = _eyeManager.PixelToMap(mouseScreenPos);
mouseWorldMap = _eyeManager.ScreenToMap(mouseScreenPos);
if (mouseWorldMap == MapCoordinates.Nullspace)
return;
var mapSystem = _entityManager.System<SharedMapSystem>();
if (_mapManager.TryFindGridAt(mouseWorldMap, out var mouseGridUid, out var mouseGrid))
if (_mapManager.TryFindGridAt(mouseWorldMap, out _, out var mouseGrid))
{
mouseGridPos = mapSystem.MapToGrid(mouseGridUid, mouseWorldMap);
tile = mapSystem.GetTileRef(mouseGridUid, mouseGrid, mouseGridPos);
mouseGridPos = mouseGrid.MapToGrid(mouseWorldMap);
tile = mouseGrid.GetTileRef(mouseGridPos);
}
else
{

View File

@@ -24,11 +24,6 @@ namespace Robust.Client.UserInterface.CustomControls
/// </param>
MapCoordinates ScreenToMap(Vector2 coords);
/// <summary>
/// Similar to <see cref="ScreenToMap(Vector2)"/>, except it should compensate for the effects of shaders on viewports.
/// </summary>
MapCoordinates PixelToMap(Vector2 point);
/// <summary>
/// Converts a point on the map to screen coordinates.
/// </summary>

View File

@@ -1,34 +0,0 @@
using Robust.Client.Graphics;
using Robust.Shared.GameObjects;
using System.Numerics;
namespace Robust.Client.UserInterface.CustomControls;
/// <summary>
/// An event used to reverse distortion effects applied by shaders.
/// Used to find the map position that visible pixels originate from so that severe distortion shaders do not make interaction nigh-impossible.
/// </summary>
[ByRefEvent]
public record struct PixelToMapEvent(Vector2 LocalPosition, IViewportControl Control, IClydeViewport Viewport)
{
/// <summary>
/// The local position of the pixel within the <see cref="Control"/> that we are trying to convert to a map position.
/// </summary>
public readonly Vector2 LocalPosition = LocalPosition;
/// <summary>
/// The original (or WIP) location of the pixel within the <see cref="Control"/> that we are trying to convert to a map position.
/// Used as the output of the event.
/// </summary>
public Vector2 VisiblePosition = LocalPosition;
/// <summary>
/// The control the pixel we are considering is located within.
/// </summary>
public readonly IViewportControl Control = Control;
/// <summary>
/// The viewport being displayed by the control we are considering.
/// </summary>
public readonly IClydeViewport Viewport = Viewport;
}

View File

@@ -1,7 +1,6 @@
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.IoC;
using Robust.Shared.Map;
@@ -14,9 +13,8 @@ namespace Robust.Client.UserInterface.CustomControls
[Virtual]
public class ViewportContainer : Control, IViewportControl
{
[Dependency] private readonly IClyde _displayManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
private readonly IClyde _displayManager;
private readonly IInputManager _inputManager;
public IClydeViewport? Viewport { get; set; }
@@ -38,7 +36,8 @@ namespace Robust.Client.UserInterface.CustomControls
public ViewportContainer()
{
IoCManager.InjectDependencies(this);
_displayManager = IoCManager.Resolve<IClyde>();
_inputManager = IoCManager.Resolve<IInputManager>();
MouseFilter = MouseFilterMode.Stop;
Resized();
}
@@ -100,16 +99,6 @@ namespace Robust.Client.UserInterface.CustomControls
// -- Handlers: In --
// -- Utils / S2M-M2S Base --
public MapCoordinates LocalCoordsToMap(Vector2 point)
{
if (Viewport == null)
return default;
// pre-scaler
point *= _viewportResolution;
return Viewport.LocalToWorld(point);
}
public MapCoordinates LocalPixelToMap(Vector2 point)
{
@@ -118,10 +107,8 @@ namespace Robust.Client.UserInterface.CustomControls
// pre-scaler
point *= _viewportResolution;
var ev = new PixelToMapEvent(point, this, Viewport);
_entityManager.EventBus.RaiseEvent(EventSource.Local, ref ev);
return Viewport.LocalToWorld(ev.VisiblePosition);
return Viewport.LocalToWorld(point);
}
public Vector2 WorldToLocalPixel(Vector2 point)
@@ -140,12 +127,6 @@ namespace Robust.Client.UserInterface.CustomControls
// -- Utils / S2M-M2S Extended --
public MapCoordinates ScreenToMap(Vector2 point)
{
return LocalCoordsToMap(point - GlobalPixelPosition);
}
/// <inheritdoc/>
public MapCoordinates PixelToMap(Vector2 point)
{
return LocalPixelToMap(point - GlobalPixelPosition);
}

View File

@@ -1,29 +0,0 @@
using Robust.Client.ResourceManagement;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Robust.Client.UserInterface.RichText;
public sealed class BoldItalicTag : IMarkupTag
{
public const string BoldItalicFont = "DefaultBoldItalic";
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public string Name => "bolditalic";
/// <inheritdoc/>
public void PushDrawContext(MarkupNode node, MarkupDrawingContext context)
{
var font = FontTag.CreateFont(context.Font, node, _resourceCache, _prototypeManager, BoldItalicFont);
context.Font.Push(font);
}
/// <inheritdoc/>
public void PopDrawContext(MarkupNode node, MarkupDrawingContext context)
{
context.Font.Pop();
}
}

View File

@@ -1,4 +1,3 @@
using System.Linq;
using Robust.Client.ResourceManagement;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
@@ -15,18 +14,12 @@ public sealed class BoldTag : IMarkupTag
public string Name => "bold";
/// <inheritdoc/>
public void PushDrawContext(MarkupNode node, MarkupDrawingContext context)
{
var font = FontTag.CreateFont(context.Font, node, _resourceCache, _prototypeManager,
context.Tags.Any(static x => x is ItalicTag)
? BoldItalicTag.BoldItalicFont
: BoldFont
);
var font = FontTag.CreateFont(context.Font, node, _resourceCache, _prototypeManager, BoldFont);
context.Font.Push(font);
}
/// <inheritdoc/>
public void PopDrawContext(MarkupNode node, MarkupDrawingContext context)
{
context.Font.Pop();

View File

@@ -1,11 +0,0 @@
using Robust.Shared.Utility;
namespace Robust.Client.UserInterface.RichText;
public sealed class BulletTag : IMarkupTag
{
public string Name => "bullet";
/// <inheritdoc/>
public string TextBefore(MarkupNode _) => " · ";
}

View File

@@ -8,7 +8,7 @@ namespace Robust.Client.UserInterface.RichText;
/// </summary>
public sealed class ColorTag : IMarkupTag
{
public static readonly Color DefaultColor = new(200, 200, 200);
private static readonly Color DefaultColor = new(200, 200, 200);
public string Name => "color";

View File

@@ -14,14 +14,12 @@ namespace Robust.Client.UserInterface.RichText;
public sealed class FontTag : IMarkupTag
{
public const string DefaultFont = "Default";
public const int DefaultSize = 12;
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public string Name => "font";
/// <inheritdoc/>
public void PushDrawContext(MarkupNode node, MarkupDrawingContext context)
{
string fontId = node.Value.StringValue ?? DefaultFont;
@@ -30,7 +28,6 @@ public sealed class FontTag : IMarkupTag
context.Font.Push(font);
}
/// <inheritdoc/>
public void PopDrawContext(MarkupNode node, MarkupDrawingContext context)
{
context.Font.Pop();
@@ -47,7 +44,7 @@ public sealed class FontTag : IMarkupTag
IPrototypeManager prototypeManager,
string fontId)
{
var size = DefaultSize;
var size = 12;
if (contextFontStack.TryPeek(out var previousFont))
{

View File

@@ -1,36 +0,0 @@
using System;
using Robust.Client.ResourceManagement;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Robust.Client.UserInterface.RichText;
public sealed class HeadingTag : IMarkupTag
{
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public string Name => "head";
/// <inheritdoc/>
public void PushDrawContext(MarkupNode node, MarkupDrawingContext context)
{
if (!node.Value.TryGetLong(out var levelParam))
return;
var level = Math.Min(Math.Max((int)levelParam, 1), 3);
node.Attributes["size"] = new MarkupParameter(
(int)Math.Ceiling(FontTag.DefaultSize * 2 / Math.Sqrt(level))
);
var font = FontTag.CreateFont(context.Font, node, _resourceCache, _prototypeManager, BoldTag.BoldFont);
context.Font.Push(font);
}
/// <inheritdoc/>
public void PopDrawContext(MarkupNode node, MarkupDrawingContext context)
{
context.Font.Pop();
}
}

View File

@@ -1,4 +1,3 @@
using System.Linq;
using Robust.Client.ResourceManagement;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
@@ -12,21 +11,14 @@ public sealed class ItalicTag : IMarkupTag
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public string Name => "italic";
/// <inheritdoc/>
public void PushDrawContext(MarkupNode node, MarkupDrawingContext context)
{
var font = FontTag.CreateFont(context.Font, node, _resourceCache, _prototypeManager,
context.Tags.Any(static x => x is BoldTag)
? BoldItalicTag.BoldItalicFont
: ItalicFont
);
var font = FontTag.CreateFont(context.Font, node, _resourceCache, _prototypeManager, ItalicFont);
context.Font.Push(font);
}
/// <inheritdoc/>
public void PopDrawContext(MarkupNode node, MarkupDrawingContext context)
{
context.Font.Pop();

View File

@@ -8,26 +8,22 @@ public sealed class MarkupDrawingContext
{
public readonly Stack<Color> Color;
public readonly Stack<Font> Font;
public readonly List<IMarkupTag> Tags;
public MarkupDrawingContext()
{
Color = new Stack<Color>();
Font = new Stack<Font>();
Tags = new List<IMarkupTag>();
}
public MarkupDrawingContext(int capacity)
{
Color = new Stack<Color>(capacity);
Font = new Stack<Font>(capacity);
Tags = new List<IMarkupTag>();
}
public void Clear()
{
Color.Clear();
Font.Clear();
Tags.Clear();
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
using Robust.Shared.Sandboxing;
@@ -16,29 +15,24 @@ public sealed class MarkupTagManager
/// <summary>
/// Tags defined in engine need to be instantiated here because of sandboxing
/// </summary>
private readonly Dictionary<string, IMarkupTag> _markupTagTypes = new IMarkupTag[] {
new BoldItalicTag(),
new BoldTag(),
new BulletTag(),
new ColorTag(),
new CommandLinkTag(),
new FontTag(),
new HeadingTag(),
new ItalicTag()
}.ToDictionary(x => x.Name.ToLower(), x => x);
private readonly Dictionary<string, IMarkupTag> _markupTagTypes = new()
{
{"color", new ColorTag()},
{"cmdlink", new CommandLinkTag()},
{"font", new FontTag()},
{"bold", new BoldTag()},
{"italic", new ItalicTag()}
};
/// <summary>
/// A list of <see cref="IMarkupTag"/> types that shouldn't be instantiated through reflection
/// </summary>
private readonly List<Type> _engineTypes = new()
{
typeof(BoldItalicTag),
typeof(BoldTag),
typeof(BulletTag),
typeof(ColorTag),
typeof(CommandLinkTag),
typeof(FontTag),
typeof(HeadingTag),
typeof(BoldTag),
typeof(ItalicTag)
};
@@ -65,11 +59,9 @@ public sealed class MarkupTagManager
return _markupTagTypes.GetValueOrDefault(name);
}
public bool TryGetMarkupTag(string name, Type[]? tagsAllowed, [NotNullWhen(true)] out IMarkupTag? tag)
public bool TryGetMarkupTag(string name, [NotNullWhen(true)] out IMarkupTag? tag)
{
if (_markupTagTypes.TryGetValue(name, out var markupTag)
// Using a whitelist prevents new tags from sneaking in.
&& (tagsAllowed == null || Array.IndexOf(tagsAllowed, markupTag.GetType()) != -1))
if (_markupTagTypes.TryGetValue(name, out var markupTag))
{
tag = markupTag;
return true;

View File

@@ -16,9 +16,9 @@ namespace Robust.Client.UserInterface
/// </summary>
internal struct RichTextEntry
{
private readonly Color _defaultColor;
private static readonly Color DefaultColor = new(200, 200, 200);
private readonly MarkupTagManager _tagManager;
private readonly Type[]? _tagsAllowed;
public readonly FormattedMessage Message;
@@ -39,15 +39,13 @@ namespace Robust.Client.UserInterface
private readonly Dictionary<int, Control> _tagControls = new();
public RichTextEntry(FormattedMessage message, Control parent, MarkupTagManager tagManager, Type[]? tagsAllowed = null, Color? defaultColor = null)
public RichTextEntry(FormattedMessage message, Control parent, MarkupTagManager tagManager)
{
Message = message;
Height = 0;
Width = 0;
LineBreaks = default;
_defaultColor = defaultColor ?? new(200, 200, 200);
_tagManager = tagManager;
_tagsAllowed = tagsAllowed;
var nodeIndex = -1;
foreach (var node in Message.Nodes)
@@ -57,7 +55,7 @@ namespace Robust.Client.UserInterface
if (node.Name == null)
continue;
if (!_tagManager.TryGetMarkupTag(node.Name, _tagsAllowed, out var tag) || !tag.TryGetControl(node, out var control))
if (!_tagManager.TryGetMarkupTag(node.Name, out var tag) || !tag.TryGetControl(node, out var control))
continue;
parent.Children.Add(control);
@@ -84,7 +82,7 @@ namespace Robust.Client.UserInterface
var wordWrap = new WordWrap(maxSizeX);
var context = new MarkupDrawingContext();
context.Font.Push(defaultFont);
context.Color.Push(_defaultColor);
context.Color.Push(DefaultColor);
// Go over every node.
// Nodes can change the markup drawing context and return additional text.
@@ -173,7 +171,7 @@ namespace Robust.Client.UserInterface
float uiScale)
{
context.Clear();
context.Color.Push(_defaultColor);
context.Color.Push(DefaultColor);
context.Font.Push(defaultFont);
var globalBreakCounter = 0;
@@ -188,7 +186,7 @@ namespace Robust.Client.UserInterface
var text = ProcessNode(node, context);
if (!context.Color.TryPeek(out var color) || !context.Font.TryPeek(out var font))
{
color = _defaultColor;
color = DefaultColor;
font = defaultFont;
}
@@ -228,17 +226,15 @@ namespace Robust.Client.UserInterface
return node.Value.StringValue ?? "";
//Skip the node if there is no markup tag for it.
if (!_tagManager.TryGetMarkupTag(node.Name, _tagsAllowed, out var tag))
if (!_tagManager.TryGetMarkupTag(node.Name, out var tag))
return "";
if (!node.Closing)
{
context.Tags.Add(tag);
tag.PushDrawContext(node, context);
return tag.TextBefore(node);
}
context.Tags.Remove(tag);
tag.PopDrawContext(node, context);
return tag.TextAfter(node);
}

View File

@@ -15,9 +15,6 @@ public sealed class DefaultStylesheet
{
var notoSansFont = res.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf");
var notoSansFont12 = new VectorFont(notoSansFont, 12);
var notoSansMonoFont = res.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSansMono-Regular.ttf");
var notoSansMono12 = new VectorFont(notoSansMonoFont, 12);
var theme = userInterfaceManager.CurrentTheme;
@@ -41,13 +38,6 @@ public sealed class DefaultStylesheet
Stylesheet = new Stylesheet(new StyleRule[]
{
/*
* Debug console and other monospace things.
*/
Element().Class("monospace")
.Prop("font", notoSansMono12),
/*
* OS Window defaults
*/

View File

@@ -29,7 +29,7 @@ internal partial class UserInterfaceManager
public void SetActiveTheme(string themeName)
{
if (!_themes.TryGetValue(themeName, out var theme) || (theme == CurrentTheme)) return;
UpdateTheme(theme);
CurrentTheme = theme;
}
public void SetDefaultTheme(string themeId)

View File

@@ -1,5 +1,3 @@
using System;
using System.Text;
using DiscordRPC;
using DiscordRPC.Logging;
using Robust.Shared;
@@ -27,17 +25,13 @@ namespace Robust.Client.Utility
public void Initialize()
{
var state = _loc.GetString("discord-rpc-in-main-menu");
var largeImageKey = _configurationManager.GetCVar(CVars.DiscordRichPresenceSecondIconId);
var largeImageText = _loc.GetString("discord-rpc-in-main-menu-logo-text");
_defaultPresence = new()
{
State = Truncate(state, 128),
State = _loc.GetString("discord-rpc-in-main-menu"),
Assets = new Assets
{
LargeImageKey = Truncate(largeImageKey, 32),
LargeImageText = Truncate(largeImageText, 128),
LargeImageKey = "logo",
LargeImageText = "I think coolsville SUCKS"
}
};
_configurationManager.OnValueChanged(CVars.DiscordEnabled, newValue =>
@@ -101,60 +95,18 @@ namespace Robust.Client.Utility
public void Update(string serverName, string username, string maxUsers, string users)
{
if (_client == null)
return;
try
_activePresence = new RichPresence
{
var details = _loc.GetString("discord-rpc-on-server", ("servername", serverName));
var state = _loc.GetString("discord-rpc-players", ("players", users), ("maxplayers", maxUsers));
var largeImageText = _loc.GetString("discord-rpc-character", ("username", username));
var largeImageKey = _configurationManager.GetCVar(CVars.DiscordRichPresenceMainIconId);
var smallImageKey = _configurationManager.GetCVar(CVars.DiscordRichPresenceSecondIconId);
// Strings are limited by byte count. See the setters in RichPresence. Hence the truncate calls.
_activePresence = new RichPresence
Details = _loc.GetString("discord-rpc-on-server", ("servername", serverName)),
State = _loc.GetString("discord-rpc-players", ("players", users), ("maxplayers", maxUsers)),
Assets = new Assets
{
Details = Truncate(details, 128),
State = Truncate(state, 128),
Assets = new Assets
{
LargeImageKey = Truncate(largeImageKey, 32),
LargeImageText = Truncate(largeImageText, 128),
SmallImageKey = Truncate(smallImageKey, 32)
}
};
_client.SetPresence(_activePresence);
}
catch (Exception ex)
{
_client.Logger.Error($"Caught exception while updating discord rich presence. Exception:\n{ex}");
}
}
private string Truncate(string value, int bytes, string postfix = "...")
=> Truncate(value, bytes, postfix, Encoding.UTF8);
/// <summary>
/// Truncate strings down to some minimum byte count. If the string gets truncated, it will have the postfix appended.
/// </summary>
private string Truncate(string value, int bytes, string postfix, Encoding encoding)
{
value = value.Trim();
var output = value;
// Theres probably a better way of doing this, but I don't know how.
// If this wasn't a crude hack this function should
while (encoding.GetByteCount(output) > bytes)
{
if (value.Length == 0)
return string.Empty;
value = value.Substring(0, value.Length - 1);
output = value + postfix;
}
return output;
LargeImageKey = "devstation",
LargeImageText = _loc.GetString("discord-rpc-character", ("username", username)),
SmallImageKey = "logo"
}
};
_client?.SetPresence(_activePresence);
}
public void ClearPresence()

View File

@@ -88,7 +88,7 @@ namespace Robust.Client.ViewVariables.Instances
var stringified = PrettyPrint.PrintUserFacingWithType(obj, out var typeStringified);
if (typeStringified != "")
{
//var smallFont = new VectorFont(_resourceCache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 10);
//var smallFont = new VectorFont(_resourceCache.GetResource<FontResource>("/Fonts/CALIBRI.TTF"), 10);
// Custom ToString() implementation.
var headBox = new BoxContainer
{

View File

@@ -121,7 +121,7 @@ namespace Robust.Client.ViewVariables
}
//var smallFont =
// new VectorFont(IoCManager.Resolve<IResourceCache>().GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"),
// new VectorFont(IoCManager.Resolve<IResourceCache>().GetResource<FontResource>("/Fonts/CALIBRI.TTF"),
// 10);
// Custom ToString() implementation.

View File

@@ -57,7 +57,7 @@ namespace Robust.Client.ViewVariables
};
VBox.AddChild(BottomContainer);
//var smallFont = new VectorFont(_resourceCache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 10);
//var smallFont = new VectorFont(_resourceCache.GetResource<FontResource>("/Fonts/CALIBRI.TTF"), 10);
_bottomLabel = new Label
{

View File

@@ -30,9 +30,7 @@ using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Profiling;
using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
using Robust.Shared.Replays;
using Robust.Shared.Toolshed;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Threading;
@@ -100,10 +98,9 @@ namespace Robust.Server
[Dependency] private readonly ISerializationManager _serialization = default!;
[Dependency] private readonly IStatusHost _statusHost = default!;
[Dependency] private readonly IComponentFactory _componentFactory = default!;
[Dependency] private readonly IReplayRecordingManagerInternal _replay = default!;
[Dependency] private readonly IReplayRecordingManager _replay = default!;
[Dependency] private readonly IGamePrototypeLoadManager _protoLoadMan = default!;
[Dependency] private readonly NetworkResourceManager _netResMan = default!;
[Dependency] private readonly IReflectionManager _refMan = default!;
private readonly Stopwatch _uptimeStopwatch = new();
@@ -370,9 +367,7 @@ namespace Robust.Server
_prototype.Initialize();
_prototype.LoadDefaultPrototypes();
_prototype.ResolveResults();
_refMan.Initialize();
IoCManager.Resolve<ToolshedManager>().Initialize();
_consoleHost.Initialize();
_entityManager.Startup();
_mapManager.Startup();
@@ -515,12 +510,7 @@ namespace Robust.Server
{
if (_mainLoop == null)
{
_mainLoop = new GameLoop(
_time,
_runtimeLog,
_prof,
_log.GetSawmill("eng"),
GameLoopOptions.FromCVars(_config))
_mainLoop = new GameLoop(_time, _runtimeLog, _prof, _log.GetSawmill("eng"))
{
SleepMode = SleepMode.Delay,
DetectSoftLock = true,
@@ -638,8 +628,6 @@ namespace Robust.Server
// called right before main loop returns, do all saving/cleanup in here
public void Cleanup()
{
_replay.Shutdown();
_modLoader.Shutdown();
_playerManager.Shutdown();
@@ -682,8 +670,6 @@ namespace Robust.Server
_network.ProcessPackets();
_taskManager.ProcessPendingTasks();
_modLoader.BroadcastUpdate(ModUpdateLevel.InputPostEngine, args);
}
private void Update(FrameEventArgs frameEventArgs)

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