diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
index 99dc929e41..d6dc02d690 100644
--- a/.github/workflows/build-test.yml
+++ b/.github/workflows/build-test.yml
@@ -28,7 +28,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 7.0.101
+ dotnet-version: 8.0.100
- name: Install dependencies
run: dotnet restore
- name: Build
diff --git a/.github/workflows/compiler-test.yml b/.github/workflows/compiler-test.yml
index 329007ebf9..95e70668c1 100644
--- a/.github/workflows/compiler-test.yml
+++ b/.github/workflows/compiler-test.yml
@@ -27,7 +27,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 7.0.101
+ dotnet-version: 8.0.100
- name: Install dependencies
run: dotnet restore main/OpenDream.sln
- name: Build
diff --git a/.github/workflows/test-tgs.yml b/.github/workflows/test-tgs.yml
index a901f1dc02..a6a34ad7b4 100644
--- a/.github/workflows/test-tgs.yml
+++ b/.github/workflows/test-tgs.yml
@@ -11,7 +11,7 @@ concurrency:
cancel-in-progress: true
env:
- OD_DOTNET_VERSION: 7
+ OD_DOTNET_VERSION: 8
TGS_DOTNET_VERSION: 8
TGS_REFERENCE: dev
diff --git a/Content.IntegrationTests/Content.IntegrationTests.csproj b/Content.IntegrationTests/Content.IntegrationTests.csproj
index 78418cb1b2..71b32f8827 100644
--- a/Content.IntegrationTests/Content.IntegrationTests.csproj
+++ b/Content.IntegrationTests/Content.IntegrationTests.csproj
@@ -5,7 +5,7 @@
..\bin\Content.IntegrationTests\
false
false
- 11
+ 12
diff --git a/Content.Tests/Content.Tests.csproj b/Content.Tests/Content.Tests.csproj
index 9b757865e1..719446e02d 100644
--- a/Content.Tests/Content.Tests.csproj
+++ b/Content.Tests/Content.Tests.csproj
@@ -2,7 +2,7 @@
$(TargetFramework)
- 11
+ 12
false
false
..\bin\Content.Tests\
diff --git a/DMCompiler/DMCompiler.csproj b/DMCompiler/DMCompiler.csproj
index c533fdee4f..53b82d0b6c 100644
--- a/DMCompiler/DMCompiler.csproj
+++ b/DMCompiler/DMCompiler.csproj
@@ -2,7 +2,7 @@
Exe
- net7.0
+ net8.0
enable
Debug;Release;Tools
AnyCPU
diff --git a/DMCompiler/copy_standard.bat b/DMCompiler/copy_standard.bat
index d57f678a2a..d53a132c2d 100644
--- a/DMCompiler/copy_standard.bat
+++ b/DMCompiler/copy_standard.bat
@@ -1,5 +1,5 @@
@echo off
-if not exist bin\Debug\net7.0\DMStandard mkdir bin\Debug\net7.0\DMStandard
-xcopy DMStandard bin\Debug\net7.0\DMStandard /y /s /e
-if not exist bin\Release\net7.0\DMStandard mkdir bin\Release\net7.0\DMStandard
-xcopy DMStandard bin\Release\net7.0\DMStandard /y /s /e
+if not exist bin\Debug\net8.0\DMStandard mkdir bin\Debug\net8.0\DMStandard
+xcopy DMStandard bin\Debug\net8.0\DMStandard /y /s /e
+if not exist bin\Release\net8.0\DMStandard mkdir bin\Release\net8.0\DMStandard
+xcopy DMStandard bin\Release\net8.0\DMStandard /y /s /e
diff --git a/DMCompiler/copy_standard.sh b/DMCompiler/copy_standard.sh
index d1f8bee8ef..3532579229 100755
--- a/DMCompiler/copy_standard.sh
+++ b/DMCompiler/copy_standard.sh
@@ -4,16 +4,16 @@ set -xe
SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT")
-if [ -d "$SCRIPTPATH/bin/Debug/net7.0/DMStandard" ]; then
- cp -r $SCRIPTPATH/DMStandard $SCRIPTPATH/bin/Debug/net7.0/DMStandard
+if [ -d "$SCRIPTPATH/bin/Debug/net8.0/DMStandard" ]; then
+ cp -r $SCRIPTPATH/DMStandard $SCRIPTPATH/bin/Debug/net8.0/DMStandard
else
- mkdir -p $SCRIPTPATH/bin/Debug/net7.0/DMStandard
- cp -r $SCRIPTPATH/DMStandard $SCRIPTPATH/bin/Debug/net7.0/DMStandard
+ mkdir -p $SCRIPTPATH/bin/Debug/net8.0/DMStandard
+ cp -r $SCRIPTPATH/DMStandard $SCRIPTPATH/bin/Debug/net8.0/DMStandard
fi
-if [ -d "$SCRIPTPATH/bin/Release/net7.0/DMStandard" ]; then
- cp -r $SCRIPTPATH/DMStandard $SCRIPTPATH/bin/Release/net7.0/DMStandard
+if [ -d "$SCRIPTPATH/bin/Release/net8.0/DMStandard" ]; then
+ cp -r $SCRIPTPATH/DMStandard $SCRIPTPATH/bin/Release/net8.0/DMStandard
else
- mkdir -p $SCRIPTPATH/bin/Release/net7.0/DMStandard
- cp -r $SCRIPTPATH/DMStandard $SCRIPTPATH/bin/Release/net7.0/DMStandard
+ mkdir -p $SCRIPTPATH/bin/Release/net8.0/DMStandard
+ cp -r $SCRIPTPATH/DMStandard $SCRIPTPATH/bin/Release/net8.0/DMStandard
fi
diff --git a/DMDisassembler/DMDisassembler.csproj b/DMDisassembler/DMDisassembler.csproj
index 231be680b8..e116fcf4e6 100644
--- a/DMDisassembler/DMDisassembler.csproj
+++ b/DMDisassembler/DMDisassembler.csproj
@@ -2,7 +2,7 @@
Exe
- net7.0
+ net8.0
Debug;Release;Tools
AnyCPU
diff --git a/OpenDreamClient/Audio/DreamSoundChannel.cs b/OpenDreamClient/Audio/DreamSoundChannel.cs
index 45ab8a4ec9..525882e14c 100644
--- a/OpenDreamClient/Audio/DreamSoundChannel.cs
+++ b/OpenDreamClient/Audio/DreamSoundChannel.cs
@@ -1,20 +1,12 @@
-using Robust.Client.Graphics;
+using Robust.Client.Audio;
+using Robust.Shared.Audio.Components;
namespace OpenDreamClient.Audio;
-public sealed class DreamSoundChannel : IDisposable {
- public IClydeAudioSource Source { get; }
-
- public DreamSoundChannel(IClydeAudioSource source) {
- Source = source;
- }
+public sealed class DreamSoundChannel(AudioSystem audioSystem, (EntityUid Entity, AudioComponent Component) source) {
+ public readonly (EntityUid Entity, AudioComponent Component) Source = source;
public void Stop() {
- Source.StopPlaying();
- }
-
- public void Dispose() {
- Stop();
- Source.Dispose();
+ audioSystem.Stop(Source.Entity, Source.Component);
}
}
diff --git a/OpenDreamClient/Audio/DreamSoundEngine.cs b/OpenDreamClient/Audio/DreamSoundEngine.cs
index 703bd8ca35..8665d3f751 100644
--- a/OpenDreamClient/Audio/DreamSoundEngine.cs
+++ b/OpenDreamClient/Audio/DreamSoundEngine.cs
@@ -1,89 +1,103 @@
using OpenDreamClient.Resources;
using OpenDreamClient.Resources.ResourceTypes;
using OpenDreamShared.Network.Messages;
-using Robust.Client.Graphics;
+using Robust.Client.Audio;
using Robust.Shared.Audio;
using Robust.Shared.Network;
-namespace OpenDreamClient.Audio {
- public sealed class DreamSoundEngine : IDreamSoundEngine {
- private const int SoundChannelLimit = 1024;
+namespace OpenDreamClient.Audio;
- [Dependency] private readonly IDreamResourceManager _resourceManager = default!;
- [Dependency] private readonly ILogManager _logManager = default!;
- [Dependency] private readonly INetManager _netManager = default!;
+public sealed class DreamSoundEngine : IDreamSoundEngine {
+ private const int SoundChannelLimit = 1024;
- private ISawmill _sawmill = default!;
+ [Dependency] private readonly IDreamResourceManager _resourceManager = default!;
+ [Dependency] private readonly ILogManager _logManager = default!;
+ [Dependency] private readonly INetManager _netManager = default!;
+ [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
+ [Dependency] private readonly IAudioManager _audioManager = default!;
+ private AudioSystem? _audioSystem;
- private readonly DreamSoundChannel?[] _channels = new DreamSoundChannel[SoundChannelLimit];
+ private ISawmill _sawmill = default!;
- public void Initialize() {
- _sawmill = _logManager.GetSawmill("opendream.audio");
+ private readonly DreamSoundChannel?[] _channels = new DreamSoundChannel[SoundChannelLimit];
- _netManager.RegisterNetMessage(RxSound);
+ public void Initialize() {
+ _sawmill = _logManager.GetSawmill("opendream.audio");
- _netManager.Disconnect += DisconnectedFromServer;
- }
+ _netManager.RegisterNetMessage(RxSound);
- public void StopFinishedChannels() {
- for (int i = 0; i < SoundChannelLimit; i++) {
- if (_channels[i]?.Source.IsPlaying is false or null)
- StopChannel(i + 1);
- }
+ _netManager.Disconnect += DisconnectedFromServer;
+ }
+
+ public void StopFinishedChannels() {
+ for (int i = 0; i < SoundChannelLimit; i++) {
+ if (_channels[i]?.Source.Component.Playing is false or null)
+ StopChannel(i + 1);
}
+ }
- public void PlaySound(int channel, MsgSound.FormatType format, ResourceSound sound, float volume) {
- if (channel == 0) {
- //First available channel
- for (int i = 0; i < _channels.Length; i++) {
- if (_channels[i] == null) {
- channel = i + 1;
- break;
- }
- }
+ public void PlaySound(int channel, MsgSound.FormatType format, ResourceSound sound, float volume) {
+ if (_audioSystem == null)
+ _entitySystemManager.Resolve(ref _audioSystem);
- if (channel == 0) {
- _sawmill.Error("Failed to find a free audio channel to play a sound on");
- return;
+ if (channel == 0) {
+ //First available channel
+ for (int i = 0; i < _channels.Length; i++) {
+ if (_channels[i] == null) {
+ channel = i + 1;
+ break;
}
}
- StopChannel(channel);
-
- // convert from DM volume (0-100) to OpenAL volume (db)
- IClydeAudioSource? source = sound.Play(format, AudioParams.Default.WithVolume(20 * MathF.Log10(volume)));
- if (source == null)
+ if (channel == 0) {
+ _sawmill.Error("Failed to find a free audio channel to play a sound on");
return;
-
- _channels[channel - 1] = new DreamSoundChannel(source);
+ }
}
+ StopChannel(channel);
- public void StopChannel(int channel) {
- ref DreamSoundChannel? ch = ref _channels[channel - 1];
-
- ch?.Dispose();
- // This will null the corresponding index in the array.
- ch = null;
+ var stream = sound.GetStream(format, _audioManager);
+ if (stream == null) {
+ _sawmill.Error($"Failed to load audio ${sound}");
+ return;
}
- public void StopAllChannels() {
- for (int i = 0; i < SoundChannelLimit; i++) {
- StopChannel(i + 1);
- }
+ var db = 20 * MathF.Log10(volume); // convert from DM volume (0-100) to OpenAL volume (db)
+ var source = _audioSystem.PlayGlobal(stream, AudioParams.Default.WithVolume(db)); // TODO: Positional audio.
+ if (source == null) {
+ _sawmill.Error($"Failed to play audio ${sound}");
+ return;
}
- private void RxSound(MsgSound msg) {
- if (msg.ResourceId.HasValue) {
- _resourceManager.LoadResourceAsync(msg.ResourceId.Value,
- sound => PlaySound(msg.Channel, msg.Format!.Value, sound, msg.Volume / 100.0f));
- } else {
- StopChannel(msg.Channel);
- }
+ _channels[channel - 1] = new DreamSoundChannel(_audioSystem, source.Value);
+ }
+
+
+ public void StopChannel(int channel) {
+ ref DreamSoundChannel? ch = ref _channels[channel - 1];
+
+ ch?.Stop();
+ // This will null the corresponding index in the array.
+ ch = null;
+ }
+
+ public void StopAllChannels() {
+ for (int i = 0; i < SoundChannelLimit; i++) {
+ StopChannel(i + 1);
}
+ }
- private void DisconnectedFromServer(object? sender, NetDisconnectedArgs e) {
- StopAllChannels();
+ private void RxSound(MsgSound msg) {
+ if (msg.ResourceId.HasValue) {
+ _resourceManager.LoadResourceAsync(msg.ResourceId.Value,
+ sound => PlaySound(msg.Channel, msg.Format!.Value, sound, msg.Volume / 100.0f));
+ } else {
+ StopChannel(msg.Channel);
}
}
+
+ private void DisconnectedFromServer(object? sender, NetDisconnectedArgs e) {
+ StopAllChannels();
+ }
}
diff --git a/OpenDreamClient/DreamClientSystem.cs b/OpenDreamClient/DreamClientSystem.cs
index c6de6edfd6..029c7ca44b 100644
--- a/OpenDreamClient/DreamClientSystem.cs
+++ b/OpenDreamClient/DreamClientSystem.cs
@@ -2,7 +2,7 @@
using OpenDreamClient.Rendering;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
-using Robust.Client.Player;
+using Robust.Shared.Player;
namespace OpenDreamClient;
diff --git a/OpenDreamClient/OpenDreamClient.csproj b/OpenDreamClient/OpenDreamClient.csproj
index b741bea1c5..e06241556c 100644
--- a/OpenDreamClient/OpenDreamClient.csproj
+++ b/OpenDreamClient/OpenDreamClient.csproj
@@ -2,7 +2,7 @@
$(TargetFramework)
- 11
+ 12
false
false
..\bin\Content.Client\
diff --git a/OpenDreamClient/Rendering/DreamViewOverlay.cs b/OpenDreamClient/Rendering/DreamViewOverlay.cs
index ab2f4a514b..e24977df9a 100644
--- a/OpenDreamClient/Rendering/DreamViewOverlay.cs
+++ b/OpenDreamClient/Rendering/DreamViewOverlay.cs
@@ -68,6 +68,7 @@ internal sealed class DreamViewOverlay : Overlay {
// Defined here so it isn't recreated every frame
private ViewAlgorithm.Tile?[,]? _tileInfo;
+ private readonly HashSet _entities = new();
public DreamViewOverlay(TransformSystem transformSystem, EntityLookupSystem lookupSystem,
ClientAppearanceSystem appearanceSystem, ClientScreenOverlaySystem screenOverlaySystem, ClientImagesSystem clientImagesSystem) {
@@ -140,19 +141,18 @@ private void DrawAll(OverlayDrawArgs args, EntityUid eye, Vector2i viewportSize)
var worldHandle = args.WorldHandle;
- HashSet entities;
using (_prof.Group("lookup")) {
//TODO use a sprite tree.
//the scaling is to attempt to prevent pop-in, by rendering sprites that are *just* offscreen
- entities = _lookupSystem.GetEntitiesIntersecting(args.MapId, args.WorldAABB.Scale(1.2f), MapLookupFlags);
+ _lookupSystem.GetEntitiesIntersecting(args.MapId, args.WorldAABB.Scale(1.2f), _entities, MapLookupFlags);
}
var eyeTile = grid.GetTileRef(eyeTransform.MapPosition);
- var tiles = CalculateTileVisibility(grid, entities, eyeTile, seeVis);
+ var tiles = CalculateTileVisibility(grid, _entities, eyeTile, seeVis);
RefreshRenderTargets(args.WorldHandle, viewportSize);
- CollectVisibleSprites(tiles, grid, eyeTile, entities, seeVis, sight, args.WorldAABB);
+ CollectVisibleSprites(tiles, grid, eyeTile, _entities, seeVis, sight, args.WorldAABB);
ClearPlanes();
ProcessSprites(worldHandle, viewportSize, args.WorldAABB);
diff --git a/OpenDreamClient/Resources/ResourceTypes/ResourceSound.cs b/OpenDreamClient/Resources/ResourceTypes/ResourceSound.cs
index c8d36fe471..eb34c92da1 100644
--- a/OpenDreamClient/Resources/ResourceTypes/ResourceSound.cs
+++ b/OpenDreamClient/Resources/ResourceTypes/ResourceSound.cs
@@ -2,52 +2,28 @@
using JetBrains.Annotations;
using OpenDreamShared.Network.Messages;
using Robust.Client.Audio;
-using Robust.Client.Graphics;
-using Robust.Shared.Audio;
-namespace OpenDreamClient.Resources.ResourceTypes {
- [UsedImplicitly]
- public sealed class ResourceSound : DreamResource {
- private AudioStream? _stream;
+namespace OpenDreamClient.Resources.ResourceTypes;
- public ResourceSound(int id, byte[] data) : base(id, data) { }
-
- public IClydeAudioSource? Play(MsgSound.FormatType format, AudioParams audioParams) {
- LoadStream(format);
- if (_stream == null)
- return null;
-
- // TODO: Positional audio.
- var source = IoCManager.Resolve().CreateAudioSource(_stream);
-
- if (source != null) {
- source.SetGlobal();
- source.SetPitch(audioParams.PitchScale);
- source.SetVolume(audioParams.Volume);
- source.SetPlaybackPosition(audioParams.PlayOffsetSeconds);
- source.IsLooping = audioParams.Loop;
-
- source.StartPlaying();
- }
-
- return source;
- }
-
- private void LoadStream(MsgSound.FormatType format) {
- if (_stream != null)
- return;
+[UsedImplicitly]
+public sealed class ResourceSound(int id, byte[] data) : DreamResource(id, data) {
+ private AudioStream? _stream;
+ public AudioStream? GetStream(MsgSound.FormatType format, IAudioManager audioManager) {
+ if (_stream == null) {
switch (format) {
case MsgSound.FormatType.Ogg:
- _stream = IoCManager.Resolve().LoadAudioOggVorbis(new MemoryStream(Data));
+ _stream = audioManager.LoadAudioOggVorbis(new MemoryStream(Data));
break;
case MsgSound.FormatType.Wav:
- _stream = IoCManager.Resolve().LoadAudioWav(new MemoryStream(Data));
+ _stream = audioManager.LoadAudioWav(new MemoryStream(Data));
break;
default:
Logger.GetSawmill("opendream.audio").Fatal("Only *.ogg and *.wav audio files are supported.");
break;
}
}
+
+ return _stream;
}
}
diff --git a/OpenDreamPackageTool/OpenDreamPackageTool.csproj b/OpenDreamPackageTool/OpenDreamPackageTool.csproj
index 469182ada8..ac86916b7a 100644
--- a/OpenDreamPackageTool/OpenDreamPackageTool.csproj
+++ b/OpenDreamPackageTool/OpenDreamPackageTool.csproj
@@ -2,7 +2,7 @@
Exe
- net7.0
+ net8.0
enable
enable
diff --git a/OpenDreamPackaging/DreamPackaging.cs b/OpenDreamPackaging/DreamPackaging.cs
index b26dddae96..13004e1e97 100644
--- a/OpenDreamPackaging/DreamPackaging.cs
+++ b/OpenDreamPackaging/DreamPackaging.cs
@@ -19,11 +19,9 @@ public static async Task WriteResources(
var inputPass = graph.Input;
- await RobustClientPackaging.WriteContentAssemblies(
- inputPass,
+ await RobustClientPackaging.WriteClientResources(
contentDir,
- "Content.Client",
- new[] { "OpenDreamClient", "OpenDreamShared" },
+ inputPass,
cancel);
await RobustClientPackaging.WriteClientResources(contentDir, inputPass, cancel);
diff --git a/OpenDreamPackaging/OpenDreamPackaging.csproj b/OpenDreamPackaging/OpenDreamPackaging.csproj
index 5797b27310..34748e8ac1 100644
--- a/OpenDreamPackaging/OpenDreamPackaging.csproj
+++ b/OpenDreamPackaging/OpenDreamPackaging.csproj
@@ -2,7 +2,7 @@
Exe
- net7.0
+ net8.0
enable
enable
diff --git a/OpenDreamRuntime/DreamConnection.cs b/OpenDreamRuntime/DreamConnection.cs
index a750c9da83..8dcf8a6271 100644
--- a/OpenDreamRuntime/DreamConnection.cs
+++ b/OpenDreamRuntime/DreamConnection.cs
@@ -8,579 +8,572 @@
using OpenDreamShared.Dream;
using OpenDreamShared.Dream.Procs;
using OpenDreamShared.Network.Messages;
-using Robust.Server.GameObjects;
using Robust.Shared.Enums;
using Robust.Shared.Player;
-namespace OpenDreamRuntime {
- public sealed class DreamConnection {
- [Dependency] private readonly DreamManager _dreamManager = default!;
- [Dependency] private readonly DreamObjectTree _objectTree = default!;
- [Dependency] private readonly DreamResourceManager _resourceManager = default!;
- [Dependency] private readonly WalkManager _walkManager = default!;
- [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
-
- private readonly ServerScreenOverlaySystem? _screenOverlaySystem;
- private readonly ServerClientImagesSystem? _clientImagesSystem;
- private readonly ActorSystem? _actorSystem;
-
- [ViewVariables] private readonly Dictionary _availableVerbs = new();
- [ViewVariables] private readonly Dictionary> _statPanels = new();
- [ViewVariables] private bool _currentlyUpdatingStat;
-
- [ViewVariables] public ICommonSession? Session { get; private set; }
- [ViewVariables] public DreamObjectClient? Client { get; private set; }
- [ViewVariables] public DreamObjectMob? Mob {
- get => _mob;
- set {
- if (_mob != value) {
- var oldMob = _mob;
- _mob = value;
-
- if (oldMob != null) {
- oldMob.Key = null;
- oldMob.SpawnProc("Logout");
- oldMob.Connection = null;
- }
+namespace OpenDreamRuntime;
+
+public sealed class DreamConnection {
+ [Dependency] private readonly DreamManager _dreamManager = default!;
+ [Dependency] private readonly DreamObjectTree _objectTree = default!;
+ [Dependency] private readonly DreamResourceManager _resourceManager = default!;
+ [Dependency] private readonly WalkManager _walkManager = default!;
+ [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
+ [Dependency] private readonly ISharedPlayerManager _playerManager = default!;
+
+ private readonly ServerScreenOverlaySystem? _screenOverlaySystem;
+ private readonly ServerClientImagesSystem? _clientImagesSystem;
+
+ [ViewVariables] private readonly Dictionary _availableVerbs = new();
+ [ViewVariables] private readonly Dictionary> _statPanels = new();
+ [ViewVariables] private bool _currentlyUpdatingStat;
+
+ [ViewVariables] public ICommonSession? Session { get; private set; }
+ [ViewVariables] public DreamObjectClient? Client { get; private set; }
+ [ViewVariables] public DreamObjectMob? Mob {
+ get => _mob;
+ set {
+ if (_mob != value) {
+ var oldMob = _mob;
+ _mob = value;
+
+ if (oldMob != null) {
+ oldMob.Key = null;
+ oldMob.SpawnProc("Logout");
+ oldMob.Connection = null;
+ }
- StatObj = new(value);
- if (Eye != null && Eye == oldMob) {
- Eye = value;
- }
+ StatObj = new(value);
+ if (Eye != null && Eye == oldMob) {
+ Eye = value;
+ }
- if (_mob != null) {
- // If the mob is already owned by another player, kick them out
- if (_mob.Connection != null)
- _mob.Connection.Mob = null;
+ if (_mob != null) {
+ // If the mob is already owned by another player, kick them out
+ if (_mob.Connection != null)
+ _mob.Connection.Mob = null;
- _mob.Connection = this;
- _mob.Key = Session!.Name;
- _mob.SpawnProc("Login", usr: _mob);
- }
-
- UpdateAvailableVerbs();
+ _mob.Connection = this;
+ _mob.Key = Session!.Name;
+ _mob.SpawnProc("Login", usr: _mob);
}
+
+ UpdateAvailableVerbs();
}
}
+ }
- [ViewVariables]
- public DreamObjectMovable? Eye {
- get => _eye;
- set {
- _eye = value;
-
- if (_eye != null) {
- _actorSystem?.Attach(_eye.Entity, Session!);
- } else {
- _actorSystem?.Detach(Session!);
- }
- }
+ [ViewVariables]
+ public DreamObjectMovable? Eye {
+ get => _eye;
+ set {
+ _eye = value;
+ _playerManager.SetAttachedEntity(Session!, _eye?.Entity);
}
+ }
- [ViewVariables]
- public DreamValue StatObj { get; set; } // This can be just any DreamValue. Only atoms will function though.
+ [ViewVariables]
+ public DreamValue StatObj { get; set; } // This can be just any DreamValue. Only atoms will function though.
- [ViewVariables] private string? _outputStatPanel;
- [ViewVariables] private string _selectedStatPanel;
- [ViewVariables] private readonly Dictionary> _promptEvents = new();
- [ViewVariables] private int _nextPromptEvent = 1;
+ [ViewVariables] private string? _outputStatPanel;
+ [ViewVariables] private string _selectedStatPanel;
+ [ViewVariables] private readonly Dictionary> _promptEvents = new();
+ [ViewVariables] private int _nextPromptEvent = 1;
- private DreamObjectMob? _mob;
- private DreamObjectMovable? _eye;
+ private DreamObjectMob? _mob;
+ private DreamObjectMovable? _eye;
- private readonly ISawmill _sawmill = Logger.GetSawmill("opendream.connection");
+ private readonly ISawmill _sawmill = Logger.GetSawmill("opendream.connection");
- public string SelectedStatPanel {
- get => _selectedStatPanel;
- set {
- _selectedStatPanel = value;
+ public string SelectedStatPanel {
+ get => _selectedStatPanel;
+ set {
+ _selectedStatPanel = value;
- var msg = new MsgSelectStatPanel() { StatPanel = value };
- Session?.ConnectedClient.SendMessage(msg);
- }
+ var msg = new MsgSelectStatPanel() { StatPanel = value };
+ Session?.ConnectedClient.SendMessage(msg);
}
+ }
- public DreamConnection() {
- IoCManager.InjectDependencies(this);
+ public DreamConnection() {
+ IoCManager.InjectDependencies(this);
- _entitySystemManager.TryGetEntitySystem(out _screenOverlaySystem);
- _entitySystemManager.TryGetEntitySystem(out _clientImagesSystem);
- _entitySystemManager.TryGetEntitySystem(out _actorSystem);
- }
+ _entitySystemManager.TryGetEntitySystem(out _screenOverlaySystem);
+ _entitySystemManager.TryGetEntitySystem(out _clientImagesSystem);
+ }
- public void HandleConnection(ICommonSession session) {
- var client = new DreamObjectClient(_objectTree.Client.ObjectDefinition, this, _screenOverlaySystem, _clientImagesSystem);
+ public void HandleConnection(ICommonSession session) {
+ var client = new DreamObjectClient(_objectTree.Client.ObjectDefinition, this, _screenOverlaySystem, _clientImagesSystem);
- Session = session;
+ Session = session;
- Client = client;
- Client.InitSpawn(new());
+ Client = client;
+ Client.InitSpawn(new());
- SendClientInfoUpdate();
- }
+ SendClientInfoUpdate();
+ }
- public void HandleDisconnection() {
- if (Session == null || Client == null) // Already disconnected?
- return;
+ public void HandleDisconnection() {
+ if (Session == null || Client == null) // Already disconnected?
+ return;
- if (_mob != null) {
- // Don't null out the ckey here
- _mob.SpawnProc("Logout");
+ if (_mob != null) {
+ // Don't null out the ckey here
+ _mob.SpawnProc("Logout");
- if (_mob != null) { // Logout() may have removed our mob
- _mob.Connection = null;
- _mob = null;
- }
+ if (_mob != null) { // Logout() may have removed our mob
+ _mob.Connection = null;
+ _mob = null;
}
+ }
- Client.Delete();
- Client = null;
+ Client.Delete();
+ Client = null;
- Session = null;
- }
+ Session = null;
+ }
- public void UpdateAvailableVerbs() {
- _availableVerbs.Clear();
- var verbs = new List<(string, string, string)>();
-
- void AddVerbs(DreamObject src, IEnumerable adding) {
- foreach (DreamValue mobVerb in adding) {
- if (!mobVerb.TryGetValueAsProc(out var proc))
- continue;
-
- string verbName = proc.VerbName ?? proc.Name;
- string verbId = verbName.ToLowerInvariant().Replace(" ", "-"); // Case-insensitive, dashes instead of spaces
- if (_availableVerbs.ContainsKey(verbId)) {
- // BYOND will actually show the user two verbs with different capitalization/dashes, but they will both execute the same verb.
- // We make a warning and ignore the latter ones instead.
- _sawmill.Warning($"User \"{Session.Name}\" has multiple verb commands named \"{verbId}\", ignoring all but the first");
- continue;
- }
+ public void UpdateAvailableVerbs() {
+ _availableVerbs.Clear();
+ var verbs = new List<(string, string, string)>();
- _availableVerbs.Add(verbId, (src, proc));
+ void AddVerbs(DreamObject src, IEnumerable adding) {
+ foreach (DreamValue mobVerb in adding) {
+ if (!mobVerb.TryGetValueAsProc(out var proc))
+ continue;
- // Don't send invisible verbs.
- if (_mob != null && proc.Invisibility > _mob.SeeInvisible) {
- continue;
- }
+ string verbName = proc.VerbName ?? proc.Name;
+ string verbId = verbName.ToLowerInvariant().Replace(" ", "-"); // Case-insensitive, dashes instead of spaces
+ if (_availableVerbs.ContainsKey(verbId)) {
+ // BYOND will actually show the user two verbs with different capitalization/dashes, but they will both execute the same verb.
+ // We make a warning and ignore the latter ones instead.
+ _sawmill.Warning($"User \"{Session.Name}\" has multiple verb commands named \"{verbId}\", ignoring all but the first");
+ continue;
+ }
- // Don't send hidden verbs. Names starting with "." count as hidden.
- if ((proc.Attributes & ProcAttributes.Hidden) == ProcAttributes.Hidden ||
- verbName.StartsWith('.')) {
- continue;
- }
+ _availableVerbs.Add(verbId, (src, proc));
- string? category = proc.VerbCategory;
- // Explicitly null category is hidden from verb panels, "" category becomes the default_verb_category
- if (category == string.Empty) {
- // But if default_verb_category is null, we hide it from the verb panel
- Client.GetVariable("default_verb_category").TryGetValueAsString(out category);
- }
+ // Don't send invisible verbs.
+ if (_mob != null && proc.Invisibility > _mob.SeeInvisible) {
+ continue;
+ }
- // Null category is serialized as an empty string and treated as hidden
- verbs.Add((verbName, verbId, category ?? string.Empty));
+ // Don't send hidden verbs. Names starting with "." count as hidden.
+ if ((proc.Attributes & ProcAttributes.Hidden) == ProcAttributes.Hidden ||
+ verbName.StartsWith('.')) {
+ continue;
}
- }
- if (Client != null) {
- AddVerbs(Client, Client.Verbs.GetValues());
- }
+ string? category = proc.VerbCategory;
+ // Explicitly null category is hidden from verb panels, "" category becomes the default_verb_category
+ if (category == string.Empty) {
+ // But if default_verb_category is null, we hide it from the verb panel
+ Client.GetVariable("default_verb_category").TryGetValueAsString(out category);
+ }
- if (Mob != null) {
- AddVerbs(Mob, Mob.Verbs.GetValues());
+ // Null category is serialized as an empty string and treated as hidden
+ verbs.Add((verbName, verbId, category ?? string.Empty));
}
+ }
- var msg = new MsgUpdateAvailableVerbs() {
- AvailableVerbs = verbs.ToArray()
- };
+ if (Client != null) {
+ AddVerbs(Client, Client.Verbs.GetValues());
+ }
- Session?.ConnectedClient.SendMessage(msg);
+ if (Mob != null) {
+ AddVerbs(Mob, Mob.Verbs.GetValues());
}
- public void UpdateStat() {
- if (Session == null || Client == null || _currentlyUpdatingStat)
- return;
+ var msg = new MsgUpdateAvailableVerbs() {
+ AvailableVerbs = verbs.ToArray()
+ };
- _currentlyUpdatingStat = true;
- _statPanels.Clear();
+ Session?.ConnectedClient.SendMessage(msg);
+ }
- DreamThread.Run("Stat", async (state) => {
- try {
- var statProc = Client.GetProc("Stat");
+ public void UpdateStat() {
+ if (Session == null || Client == null || _currentlyUpdatingStat)
+ return;
- await state.Call(statProc, Client, Mob);
- if (Session.Status == SessionStatus.InGame) {
- var msg = new MsgUpdateStatPanels(_statPanels);
- Session.ConnectedClient.SendMessage(msg);
- }
+ _currentlyUpdatingStat = true;
+ _statPanels.Clear();
- return DreamValue.Null;
- } finally {
- _currentlyUpdatingStat = false;
- }
- });
- }
+ DreamThread.Run("Stat", async (state) => {
+ try {
+ var statProc = Client.GetProc("Stat");
- public void SendClientInfoUpdate() {
- MsgUpdateClientInfo msg = new() {
- View = Client!.View
- };
+ await state.Call(statProc, Client, Mob);
+ if (Session.Status == SessionStatus.InGame) {
+ var msg = new MsgUpdateStatPanels(_statPanels);
+ Session.ConnectedClient.SendMessage(msg);
+ }
- Session?.ConnectedClient.SendMessage(msg);
- }
+ return DreamValue.Null;
+ } finally {
+ _currentlyUpdatingStat = false;
+ }
+ });
+ }
- public void SetOutputStatPanel(string name) {
- if (!_statPanels.ContainsKey(name))
- _statPanels.Add(name, new());
+ public void SendClientInfoUpdate() {
+ MsgUpdateClientInfo msg = new() {
+ View = Client!.View
+ };
- _outputStatPanel = name;
- }
+ Session?.ConnectedClient.SendMessage(msg);
+ }
- public void AddStatPanelLine(string name, string value, string? atomRef) {
- if (_outputStatPanel == null || !_statPanels.ContainsKey(_outputStatPanel))
- SetOutputStatPanel("Stats");
+ public void SetOutputStatPanel(string name) {
+ if (!_statPanels.ContainsKey(name))
+ _statPanels.Add(name, new());
- _statPanels[_outputStatPanel].Add( (name, value, atomRef) );
- }
+ _outputStatPanel = name;
+ }
- public void HandleMsgSelectStatPanel(MsgSelectStatPanel message) {
- _selectedStatPanel = message.StatPanel;
- }
+ public void AddStatPanelLine(string name, string value, string? atomRef) {
+ if (_outputStatPanel == null || !_statPanels.ContainsKey(_outputStatPanel))
+ SetOutputStatPanel("Stats");
- public void HandleMsgPromptResponse(MsgPromptResponse message) {
- if (!_promptEvents.TryGetValue(message.PromptId, out var promptEvent)) {
- _sawmill.Warning($"{message.MsgChannel}: Received MsgPromptResponse for prompt {message.PromptId} which does not exist.");
- return;
- }
+ _statPanels[_outputStatPanel].Add( (name, value, atomRef) );
+ }
- DreamValue value = message.Type switch {
- DMValueType.Null => DreamValue.Null,
- DMValueType.Text or DMValueType.Message => new DreamValue((string)message.Value),
- DMValueType.Num => new DreamValue((float)message.Value),
- _ => throw new Exception("Invalid prompt response '" + message.Type + "'")
- };
+ public void HandleMsgSelectStatPanel(MsgSelectStatPanel message) {
+ _selectedStatPanel = message.StatPanel;
+ }
- promptEvent.Invoke(value);
- _promptEvents.Remove(message.PromptId);
+ public void HandleMsgPromptResponse(MsgPromptResponse message) {
+ if (!_promptEvents.TryGetValue(message.PromptId, out var promptEvent)) {
+ _sawmill.Warning($"{message.MsgChannel}: Received MsgPromptResponse for prompt {message.PromptId} which does not exist.");
+ return;
}
- public void HandleMsgTopic(MsgTopic pTopic) {
- DreamList hrefList = DreamProcNativeRoot.params2list(_objectTree, HttpUtility.UrlDecode(pTopic.Query));
- DreamValue srcRefValue = hrefList.GetValue(new DreamValue("src"));
- DreamValue src = DreamValue.Null;
+ DreamValue value = message.Type switch {
+ DMValueType.Null => DreamValue.Null,
+ DMValueType.Text or DMValueType.Message => new DreamValue((string)message.Value),
+ DMValueType.Num => new DreamValue((float)message.Value),
+ _ => throw new Exception("Invalid prompt response '" + message.Type + "'")
+ };
- if (srcRefValue.TryGetValueAsString(out var srcRef)) {
- src = _dreamManager.LocateRef(srcRef);
- }
+ promptEvent.Invoke(value);
+ _promptEvents.Remove(message.PromptId);
+ }
+
+ public void HandleMsgTopic(MsgTopic pTopic) {
+ DreamList hrefList = DreamProcNativeRoot.params2list(_objectTree, HttpUtility.UrlDecode(pTopic.Query));
+ DreamValue srcRefValue = hrefList.GetValue(new DreamValue("src"));
+ DreamValue src = DreamValue.Null;
- Client?.SpawnProc("Topic", usr: Mob, new(pTopic.Query), new(hrefList), src);
+ if (srcRefValue.TryGetValueAsString(out var srcRef)) {
+ src = _dreamManager.LocateRef(srcRef);
}
- public void OutputDreamValue(DreamValue value) {
- if (value.TryGetValueAsDreamObject(out var outputObject)) {
- ushort channel = (ushort)outputObject.GetVariable("channel").GetValueAsInteger();
- ushort volume = (ushort)outputObject.GetVariable("volume").GetValueAsInteger();
- DreamValue file = outputObject.GetVariable("file");
+ Client?.SpawnProc("Topic", usr: Mob, new(pTopic.Query), new(hrefList), src);
+ }
- var msg = new MsgSound() {
- Channel = channel,
- Volume = volume
- };
+ public void OutputDreamValue(DreamValue value) {
+ if (value.TryGetValueAsDreamObject(out var outputObject)) {
+ ushort channel = (ushort)outputObject.GetVariable("channel").GetValueAsInteger();
+ ushort volume = (ushort)outputObject.GetVariable("volume").GetValueAsInteger();
+ DreamValue file = outputObject.GetVariable("file");
- if (!file.TryGetValueAsDreamResource(out var soundResource)) {
- if (file.TryGetValueAsString(out var soundPath)) {
- soundResource = _resourceManager.LoadResource(soundPath);
- } else if (!file.IsNull) {
- throw new ArgumentException($"Cannot output {value}", nameof(value));
- }
- }
+ var msg = new MsgSound() {
+ Channel = channel,
+ Volume = volume
+ };
- msg.ResourceId = soundResource?.Id;
- if (soundResource?.ResourcePath is { } resourcePath) {
- if (resourcePath.EndsWith(".ogg"))
- msg.Format = MsgSound.FormatType.Ogg;
- else if (resourcePath.EndsWith(".wav"))
- msg.Format = MsgSound.FormatType.Wav;
- else
- throw new Exception($"Sound {value} is not a supported file type");
+ if (!file.TryGetValueAsDreamResource(out var soundResource)) {
+ if (file.TryGetValueAsString(out var soundPath)) {
+ soundResource = _resourceManager.LoadResource(soundPath);
+ } else if (!file.IsNull) {
+ throw new ArgumentException($"Cannot output {value}", nameof(value));
}
+ }
- Session?.ConnectedClient.SendMessage(msg);
- return;
+ msg.ResourceId = soundResource?.Id;
+ if (soundResource?.ResourcePath is { } resourcePath) {
+ if (resourcePath.EndsWith(".ogg"))
+ msg.Format = MsgSound.FormatType.Ogg;
+ else if (resourcePath.EndsWith(".wav"))
+ msg.Format = MsgSound.FormatType.Wav;
+ else
+ throw new Exception($"Sound {value} is not a supported file type");
}
- OutputControl(value.Stringify(), null);
+ Session?.ConnectedClient.SendMessage(msg);
+ return;
}
- public void OutputControl(string message, string? control) {
- var msg = new MsgOutput() {
- Value = message,
- Control = control
- };
+ OutputControl(value.Stringify(), null);
+ }
- Session?.ConnectedClient.SendMessage(msg);
- }
+ public void OutputControl(string message, string? control) {
+ var msg = new MsgOutput() {
+ Value = message,
+ Control = control
+ };
- public void HandleCommand(string fullCommand) {
- // TODO: Arguments are a little more complicated than "split by spaces"
- // e.g. strings can be passed
- string[] args = fullCommand.Split(' ', StringSplitOptions.TrimEntries);
- string command = args[0].ToLowerInvariant(); // Case-insensitive
-
- switch (command) {
- case ".north":
- case ".east":
- case ".south":
- case ".west":
- case ".northeast":
- case ".southeast":
- case ".southwest":
- case ".northwest":
- case ".center":
- string movementProc = command switch {
- ".north" => "North",
- ".east" => "East",
- ".south" => "South",
- ".west" => "West",
- ".northeast" => "Northeast",
- ".southeast" => "Southeast",
- ".southwest" => "Southwest",
- ".northwest" => "Northwest",
- ".center" => "Center",
- _ => throw new ArgumentOutOfRangeException()
- };
-
- if (Mob != null)
- _walkManager.StopWalks(Mob);
- Client?.SpawnProc(movementProc, Mob); break;
-
- default: {
- if (_availableVerbs.TryGetValue(command, out var value)) {
- (DreamObject verbSrc, DreamProc verb) = value;
-
- DreamThread.Run(fullCommand, async (state) => {
- DreamValue[] arguments;
- if (verb.ArgumentNames != null) {
- arguments = new DreamValue[verb.ArgumentNames.Count];
-
- // TODO: this should probably be done on the client, shouldn't it?
- if (args.Length == 1) { // No args given; prompt the client for them
- for (int i = 0; i < verb.ArgumentNames.Count; i++) {
- String argumentName = verb.ArgumentNames[i];
- DMValueType argumentType = verb.ArgumentTypes[i];
- DreamValue argumentValue = await Prompt(argumentType, title: String.Empty, // No settable title for verbs
- argumentName, defaultValue: String.Empty); // No default value for verbs
-
- arguments[i] = argumentValue;
- }
- } else { // Attempt to parse the given arguments
- for (int i = 0; i < verb.ArgumentNames.Count; i++) {
- DMValueType argumentType = verb.ArgumentTypes[i];
-
- if (argumentType == DMValueType.Text) {
- arguments[i] = new(args[i+1]);
- } else {
- _sawmill.Error($"Parsing verb args of type {argumentType} is unimplemented; ignoring command ({fullCommand})");
- return DreamValue.Null;
- }
- }
- }
- } else {
- arguments = Array.Empty();
- }
+ Session?.ConnectedClient.SendMessage(msg);
+ }
- await state.Call(verb, verbSrc, Mob, arguments);
- return DreamValue.Null;
- });
- }
+ public void HandleCommand(string fullCommand) {
+ // TODO: Arguments are a little more complicated than "split by spaces"
+ // e.g. strings can be passed
+ string[] args = fullCommand.Split(' ', StringSplitOptions.TrimEntries);
+ string command = args[0].ToLowerInvariant(); // Case-insensitive
+
+ switch (command) {
+ case ".north":
+ case ".east":
+ case ".south":
+ case ".west":
+ case ".northeast":
+ case ".southeast":
+ case ".southwest":
+ case ".northwest":
+ case ".center":
+ string movementProc = command switch {
+ ".north" => "North",
+ ".east" => "East",
+ ".south" => "South",
+ ".west" => "West",
+ ".northeast" => "Northeast",
+ ".southeast" => "Southeast",
+ ".southwest" => "Southwest",
+ ".northwest" => "Northwest",
+ ".center" => "Center",
+ _ => throw new ArgumentOutOfRangeException()
+ };
- break;
- }
- }
- }
+ if (Mob != null)
+ _walkManager.StopWalks(Mob);
+ Client?.SpawnProc(movementProc, Mob); break;
- public Task Prompt(DMValueType types, String title, String message, String defaultValue) {
- var task = MakePromptTask(out var promptId);
- var msg = new MsgPrompt() {
- PromptId = promptId,
- Title = title,
- Message = message,
- Types = types,
- DefaultValue = defaultValue
- };
+ default: {
+ if (_availableVerbs.TryGetValue(command, out var value)) {
+ (DreamObject verbSrc, DreamProc verb) = value;
- Session.ConnectedClient.SendMessage(msg);
- return task;
- }
+ DreamThread.Run(fullCommand, async (state) => {
+ DreamValue[] arguments;
+ if (verb.ArgumentNames != null) {
+ arguments = new DreamValue[verb.ArgumentNames.Count];
- public async Task PromptList(DMValueType types, DreamList list, string title, string message, DreamValue defaultValue) {
- List listValues = list.GetValues();
+ // TODO: this should probably be done on the client, shouldn't it?
+ if (args.Length == 1) { // No args given; prompt the client for them
+ for (int i = 0; i < verb.ArgumentNames.Count; i++) {
+ String argumentName = verb.ArgumentNames[i];
+ DMValueType argumentType = verb.ArgumentTypes[i];
+ DreamValue argumentValue = await Prompt(argumentType, title: String.Empty, // No settable title for verbs
+ argumentName, defaultValue: String.Empty); // No default value for verbs
- List promptValues = new(listValues.Count);
- for (int i = 0; i < listValues.Count; i++) {
- DreamValue value = listValues[i];
+ arguments[i] = argumentValue;
+ }
+ } else { // Attempt to parse the given arguments
+ for (int i = 0; i < verb.ArgumentNames.Count; i++) {
+ DMValueType argumentType = verb.ArgumentTypes[i];
+
+ if (argumentType == DMValueType.Text) {
+ arguments[i] = new(args[i+1]);
+ } else {
+ _sawmill.Error($"Parsing verb args of type {argumentType} is unimplemented; ignoring command ({fullCommand})");
+ return DreamValue.Null;
+ }
+ }
+ }
+ } else {
+ arguments = Array.Empty();
+ }
- if (types.HasFlag(DMValueType.Obj) && !value.TryGetValueAsDreamObject(out _))
- continue;
- if (types.HasFlag(DMValueType.Mob) && !value.TryGetValueAsDreamObject(out _))
- continue;
- if (types.HasFlag(DMValueType.Turf) && !value.TryGetValueAsDreamObject(out _))
- continue;
- if (types.HasFlag(DMValueType.Area) && !value.TryGetValueAsDreamObject(out _))
- continue;
+ await state.Call(verb, verbSrc, Mob, arguments);
+ return DreamValue.Null;
+ });
+ }
- promptValues.Add(value.Stringify());
+ break;
}
+ }
+ }
- if (promptValues.Count == 0)
- return DreamValue.Null;
+ public Task Prompt(DMValueType types, String title, String message, String defaultValue) {
+ var task = MakePromptTask(out var promptId);
+ var msg = new MsgPrompt() {
+ PromptId = promptId,
+ Title = title,
+ Message = message,
+ Types = types,
+ DefaultValue = defaultValue
+ };
+
+ Session.ConnectedClient.SendMessage(msg);
+ return task;
+ }
- var task = MakePromptTask(out var promptId);
- var msg = new MsgPromptList() {
- PromptId = promptId,
- Title = title,
- Message = message,
- CanCancel = (types & DMValueType.Null) == DMValueType.Null,
- DefaultValue = defaultValue.Stringify(),
- Values = promptValues.ToArray()
- };
+ public async Task PromptList(DMValueType types, DreamList list, string title, string message, DreamValue defaultValue) {
+ List listValues = list.GetValues();
- Session.ConnectedClient.SendMessage(msg);
+ List promptValues = new(listValues.Count);
+ for (int i = 0; i < listValues.Count; i++) {
+ DreamValue value = listValues[i];
- // The client returns the index of the selected item, this needs turned back into the DreamValue.
- var selectedIndex = await task;
- if (selectedIndex.TryGetValueAsInteger(out int index) && index < listValues.Count) {
- return listValues[index];
- }
+ if (types.HasFlag(DMValueType.Obj) && !value.TryGetValueAsDreamObject(out _))
+ continue;
+ if (types.HasFlag(DMValueType.Mob) && !value.TryGetValueAsDreamObject(out _))
+ continue;
+ if (types.HasFlag(DMValueType.Turf) && !value.TryGetValueAsDreamObject(out _))
+ continue;
+ if (types.HasFlag(DMValueType.Area) && !value.TryGetValueAsDreamObject(out _))
+ continue;
- // Client returned an invalid value.
- // Return the first value in the list, or null if cancellable
- return msg.CanCancel ? DreamValue.Null : listValues[0];
+ promptValues.Add(value.Stringify());
}
- public Task WinExists(string controlId) {
- var task = MakePromptTask(out var promptId);
- var msg = new MsgWinExists() {
- PromptId = promptId,
- ControlId = controlId
- };
+ if (promptValues.Count == 0)
+ return DreamValue.Null;
+
+ var task = MakePromptTask(out var promptId);
+ var msg = new MsgPromptList() {
+ PromptId = promptId,
+ Title = title,
+ Message = message,
+ CanCancel = (types & DMValueType.Null) == DMValueType.Null,
+ DefaultValue = defaultValue.Stringify(),
+ Values = promptValues.ToArray()
+ };
+
+ Session.ConnectedClient.SendMessage(msg);
+
+ // The client returns the index of the selected item, this needs turned back into the DreamValue.
+ var selectedIndex = await task;
+ if (selectedIndex.TryGetValueAsInteger(out int index) && index < listValues.Count) {
+ return listValues[index];
+ }
- Session.ConnectedClient.SendMessage(msg);
+ // Client returned an invalid value.
+ // Return the first value in the list, or null if cancellable
+ return msg.CanCancel ? DreamValue.Null : listValues[0];
+ }
- return task;
- }
+ public Task WinExists(string controlId) {
+ var task = MakePromptTask(out var promptId);
+ var msg = new MsgWinExists() {
+ PromptId = promptId,
+ ControlId = controlId
+ };
- public Task WinGet(string controlId, string queryValue) {
- var task = MakePromptTask(out var promptId);
- var msg = new MsgWinGet() {
- PromptId = promptId,
- ControlId = controlId,
- QueryValue = queryValue
- };
+ Session.ConnectedClient.SendMessage(msg);
- Session.ConnectedClient.SendMessage(msg);
+ return task;
+ }
- return task;
- }
+ public Task WinGet(string controlId, string queryValue) {
+ var task = MakePromptTask(out var promptId);
+ var msg = new MsgWinGet() {
+ PromptId = promptId,
+ ControlId = controlId,
+ QueryValue = queryValue
+ };
- public Task Alert(String title, String message, String button1, String button2, String button3) {
- var task = MakePromptTask(out var promptId);
- var msg = new MsgAlert() {
- PromptId = promptId,
- Title = title,
- Message = message,
- Button1 = button1,
- Button2 = button2,
- Button3 = button3
- };
+ Session.ConnectedClient.SendMessage(msg);
- Session.ConnectedClient.SendMessage(msg);
- return task;
- }
+ return task;
+ }
- private Task MakePromptTask(out int promptId) {
- TaskCompletionSource tcs = new();
- promptId = _nextPromptEvent++;
+ public Task Alert(String title, String message, String button1, String button2, String button3) {
+ var task = MakePromptTask(out var promptId);
+ var msg = new MsgAlert() {
+ PromptId = promptId,
+ Title = title,
+ Message = message,
+ Button1 = button1,
+ Button2 = button2,
+ Button3 = button3
+ };
+
+ Session.ConnectedClient.SendMessage(msg);
+ return task;
+ }
- _promptEvents.Add(promptId, response => {
- tcs.TrySetResult(response);
- });
+ private Task MakePromptTask(out int promptId) {
+ TaskCompletionSource tcs = new();
+ promptId = _nextPromptEvent++;
- return tcs.Task;
- }
+ _promptEvents.Add(promptId, response => {
+ tcs.TrySetResult(response);
+ });
- public void BrowseResource(DreamResource resource, string filename) {
- if (resource.ResourceData == null)
- return;
+ return tcs.Task;
+ }
- var msg = new MsgBrowseResource() {
- Filename = filename,
- Data = resource.ResourceData
- };
+ public void BrowseResource(DreamResource resource, string filename) {
+ if (resource.ResourceData == null)
+ return;
- Session?.ConnectedClient.SendMessage(msg);
- }
+ var msg = new MsgBrowseResource() {
+ Filename = filename,
+ Data = resource.ResourceData
+ };
- public void Browse(string? body, string? options) {
- string? window = null;
- Vector2i size = (480, 480);
+ Session?.ConnectedClient.SendMessage(msg);
+ }
- if (options != null) {
- foreach (string option in options.Split(',', ';', '&')) {
- string optionTrimmed = option.Trim();
+ public void Browse(string? body, string? options) {
+ string? window = null;
+ Vector2i size = (480, 480);
- if (optionTrimmed != string.Empty) {
- string[] optionSeparated = optionTrimmed.Split("=", 2);
- string key = optionSeparated[0];
- string value = optionSeparated[1];
+ if (options != null) {
+ foreach (string option in options.Split(',', ';', '&')) {
+ string optionTrimmed = option.Trim();
- if (key == "window") {
- window = value;
- } else if (key == "size") {
- string[] sizeSeparated = value.Split("x", 2);
+ if (optionTrimmed != string.Empty) {
+ string[] optionSeparated = optionTrimmed.Split("=", 2);
+ string key = optionSeparated[0];
+ string value = optionSeparated[1];
- size = (int.Parse(sizeSeparated[0]), int.Parse(sizeSeparated[1]));
- }
+ if (key == "window") {
+ window = value;
+ } else if (key == "size") {
+ string[] sizeSeparated = value.Split("x", 2);
+
+ size = (int.Parse(sizeSeparated[0]), int.Parse(sizeSeparated[1]));
}
}
}
-
- var msg = new MsgBrowse() {
- Size = size,
- Window = window,
- HtmlSource = body
- };
-
- Session?.ConnectedClient.SendMessage(msg);
}
- public void WinSet(string? controlId, string @params) {
- var msg = new MsgWinSet() {
- ControlId = controlId,
- Params = @params
- };
+ var msg = new MsgBrowse() {
+ Size = size,
+ Window = window,
+ HtmlSource = body
+ };
- Session?.ConnectedClient.SendMessage(msg);
- }
+ Session?.ConnectedClient.SendMessage(msg);
+ }
- public void WinClone(string controlId, string cloneId) {
- var msg = new MsgWinClone() { ControlId = controlId, CloneId = cloneId };
+ public void WinSet(string? controlId, string @params) {
+ var msg = new MsgWinSet() {
+ ControlId = controlId,
+ Params = @params
+ };
- Session?.ConnectedClient.SendMessage(msg);
- }
+ Session?.ConnectedClient.SendMessage(msg);
+ }
- ///
- /// Prompts the user to save a file to disk
- ///
- /// File to save
- /// Suggested name to save the file as
- public void SendFile(DreamResource file, string suggestedName) {
- var msg = new MsgFtp {
- ResourceId = file.Id,
- SuggestedName = suggestedName
- };
+ public void WinClone(string controlId, string cloneId) {
+ var msg = new MsgWinClone() { ControlId = controlId, CloneId = cloneId };
- Session?.ConnectedClient.SendMessage(msg);
- }
+ Session?.ConnectedClient.SendMessage(msg);
+ }
+
+ ///
+ /// Prompts the user to save a file to disk
+ ///
+ /// File to save
+ /// Suggested name to save the file as
+ public void SendFile(DreamResource file, string suggestedName) {
+ var msg = new MsgFtp {
+ ResourceId = file.Id,
+ SuggestedName = suggestedName
+ };
+
+ Session?.ConnectedClient.SendMessage(msg);
}
}
diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs
index eb17f69505..958a001d5d 100644
--- a/OpenDreamRuntime/Objects/Types/DreamList.cs
+++ b/OpenDreamRuntime/Objects/Types/DreamList.cs
@@ -753,7 +753,7 @@ public override void AddValue(DreamValue value) {
// TODO: Only override the entity's visibility if its parent atom is visible
if (entity != EntityUid.Invalid)
- _pvsOverrideSystem?.AddGlobalOverride(entity);
+ _pvsOverrideSystem?.AddGlobalOverride(_entityManager.GetNetEntity(entity));
_atomManager.UpdateAppearance(_atom, appearance => {
// Add even an invalid UID to keep this and _visContents in sync
diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs b/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs
index a24a19d891..0cdd2528fd 100644
--- a/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs
+++ b/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs
@@ -84,8 +84,8 @@ protected override bool TryGetVar(string varName, out DreamValue value) {
DreamList contents = ObjectTree.CreateList();
using (var childEnumerator = _transformComponent.ChildEnumerator) {
- while (childEnumerator.MoveNext(out EntityUid? child)) {
- if (!AtomManager.TryGetMovableFromEntity(child.Value, out var childAtom))
+ while (childEnumerator.MoveNext(out EntityUid child)) {
+ if (!AtomManager.TryGetMovableFromEntity(child, out var childAtom))
continue;
contents.AddValue(new DreamValue(childAtom));
diff --git a/OpenDreamRuntime/OpenDreamRuntime.csproj b/OpenDreamRuntime/OpenDreamRuntime.csproj
index 153b919dee..6c861cf1b5 100644
--- a/OpenDreamRuntime/OpenDreamRuntime.csproj
+++ b/OpenDreamRuntime/OpenDreamRuntime.csproj
@@ -1,6 +1,6 @@
- net7.0
+ net8.0
enable
true
Debug;Release;Tools
diff --git a/OpenDreamServer/OpenDreamServer.csproj b/OpenDreamServer/OpenDreamServer.csproj
index 5f14ea39d0..039555f069 100644
--- a/OpenDreamServer/OpenDreamServer.csproj
+++ b/OpenDreamServer/OpenDreamServer.csproj
@@ -2,7 +2,7 @@
$(TargetFramework)
- 11
+ 12
false
false
..\bin\Content.Server\
diff --git a/OpenDreamShared/OpenDreamShared.csproj b/OpenDreamShared/OpenDreamShared.csproj
index ccb9c13fc0..59d0a5476e 100644
--- a/OpenDreamShared/OpenDreamShared.csproj
+++ b/OpenDreamShared/OpenDreamShared.csproj
@@ -2,7 +2,7 @@
$(TargetFramework)
- 11
+ 12
false
false
../bin/Content.Shared
diff --git a/README.md b/README.md
index 85d2fd43ff..03a40d1626 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ The first step to building OpenDream is initializing the submodule for the game
To do this, simply run `git submodule update --init --recursive` in git bash and let it finish.
-**OpenDream requires .NET 7.** You can check your version by running `dotnet --version`. It should be at least `7.0.0`.
+**OpenDream requires .NET 8.** You can check your version by running `dotnet --version`. It should be at least `8.0.0`.
To build, one can use a C# compiler (such as MSBuild) to compile the various projects described in the solution.
diff --git a/RobustToolbox b/RobustToolbox
index f5874ea402..eb092e90ef 160000
--- a/RobustToolbox
+++ b/RobustToolbox
@@ -1 +1 @@
-Subproject commit f5874ea402430bb902a5d5d1f47953679d79d781
+Subproject commit eb092e90efc7ac4ae562bc46f9b760745a29e289
diff --git a/global.json b/global.json
index 22dfd864b4..3fea262b1b 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "7.0.0",
+ "version": "8.0.0",
"rollForward": "latestFeature"
}
}