Skip to content

Commit

Permalink
make browse_rsc respect cache
Browse files Browse the repository at this point in the history
  • Loading branch information
amylizzle committed May 27, 2024
1 parent e6fb5fa commit 7bc30d4
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 23 deletions.
5 changes: 4 additions & 1 deletion OpenDreamClient/Interface/Controls/ControlBrowser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ private void RequestHandler(IRequestHandlerContext context) {
Stream stream;
HttpStatusCode status;
var path = new ResPath(newUri.AbsolutePath);
try {
if(!_dreamResource.EnsureCacheFile(newUri.AbsolutePath)) {
stream = Stream.Null;
status = HttpStatusCode.NotFound;
} else try {
stream = _resourceManager.UserData.OpenRead(_dreamResource.GetCacheFilePath(newUri.AbsolutePath));
status = HttpStatusCode.OK;
} catch (FileNotFoundException) {
Expand Down
51 changes: 48 additions & 3 deletions OpenDreamClient/Resources/DreamResourceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Network;
using Robust.Shared.Timing;
using Robust.Shared.Utility;

namespace OpenDreamClient.Resources {
Expand All @@ -23,6 +22,7 @@ public interface IDreamResourceManager {
/// <typeparam name="T">The type of resource to load as.</typeparam>
void LoadResourceAsync<T>(int resourceId, Action<T> onLoadCallback) where T : DreamResource;
ResPath GetCacheFilePath(string filename);
public bool EnsureCacheFile(string filename, int timeoutSeconds = 5);
}

internal sealed class DreamResourceManager : IDreamResourceManager {
Expand All @@ -38,11 +38,14 @@ internal sealed class DreamResourceManager : IDreamResourceManager {

private ISawmill _sawmill = default!;

private readonly HashSet<string> _activeBrowseRscRequests = new();

public void Initialize() {
_sawmill = Logger.GetSawmill("opendream.res");
InitCacheDirectory();

_netManager.RegisterNetMessage<MsgBrowseResource>(RxBrowseResource);
_netManager.RegisterNetMessage<MsgBrowseResourceResponse>(RxBrowseResourceResponse);
_netManager.RegisterNetMessage<MsgRequestResource>();
_netManager.RegisterNetMessage<MsgResource>(RxResource);
}
Expand All @@ -64,7 +67,24 @@ private void InitCacheDirectory() {
}

private void RxBrowseResource(MsgBrowseResource message) {
CreateCacheFile(message.Filename, message.Data);
_sawmill.Debug($"Received cache check for {message.Filename}");
if(_resourceManager.UserData.Exists(GetCacheFilePath(message.Filename))){ //TODO CHECK HASH
_sawmill.Debug($"Cache hit for {message.Filename}");
return;

Check warning

Code scanning / InspectCode

Redundant control flow jump statement Warning

Redundant control flow jump statement
} else {
_sawmill.Debug($"Cache miss for {message.Filename}, requesting from server.");
_activeBrowseRscRequests.Add(message.Filename);
_netManager.ServerChannel?.SendMessage(new MsgBrowseResourceRequest(){ Filename = message.Filename});
}
}

private void RxBrowseResourceResponse(MsgBrowseResourceResponse message) {
if(_activeBrowseRscRequests.Contains(message.Filename)) {
_activeBrowseRscRequests.Remove(message.Filename);
CreateCacheFile(message.Filename, message.Data);
} else {
_sawmill.Error($"Recieved a browse_rsc response for a file we didn't ask for: {message.Filename}");
}
}

private void RxResource(MsgResource message) {
Expand Down Expand Up @@ -121,7 +141,7 @@ public void LoadResourceAsync<T>(int resourceId, Action<T> onLoadCallback) where
_netManager.ClientSendMessage(msg);

var timeout = _cfg.GetCVar(OpenDreamCVars.DownloadTimeout);
Timer.Spawn(TimeSpan.FromSeconds(timeout), () => {
Robust.Shared.Timing.Timer.Spawn(TimeSpan.FromSeconds(timeout), () => {
if (_loadingResources.ContainsKey(resourceId)) {
_sawmill.Warning(
$"Resource id {resourceId} was requested, but is still not received {timeout} seconds later.");
Expand Down Expand Up @@ -163,6 +183,31 @@ public ResPath CreateCacheFile(string filename, byte[] data)
return new ResPath(filename);
}

/// <summary>
/// Blocking check for the existence of a cached file from `browse_rsc()`. Returns true when the file is ready, or returns false if the file is not ready within timeoutSeconds.
/// </summary>
/// <param name="filename">filepath of the cached resource (eg `./foo.png`)</param>
/// <param name="timeoutSeconds">how long to block for while waiting for the resource. Default 5 seconds.</param>
/// <returns></returns>
public bool EnsureCacheFile(string filename, int timeoutSeconds = 5) {
var actualPath = GetCacheFilePath(filename);
if(_resourceManager.UserData.Exists(actualPath)) {
return true;
} else {
if(_activeBrowseRscRequests.Contains(actualPath.Filename)) {
//block until the file arrives for like 5 seconds, then give up
DateTime thresholdTime = DateTime.Now.AddSeconds(timeoutSeconds);
while(!_resourceManager.UserData.Exists(actualPath) && DateTime.Now < thresholdTime) {
_netManager.ProcessPackets(); //todo this should be sleep
}
return _resourceManager.UserData.Exists(actualPath);
} else {
_sawmill.Error("Cache was ensured for a file that does not exist in cache and is not requested. Probably someobody called browse() without browse_rsc() first.");
return false;
}
}
}

private DreamResource? GetCachedResource(int resourceId) {
_resourceCache.TryGetValue(resourceId, out var cached);

Expand Down
21 changes: 18 additions & 3 deletions OpenDreamRuntime/DreamConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public DreamObjectMovable? Eye {
[ViewVariables] private string _selectedStatPanel;
[ViewVariables] private readonly Dictionary<int, Action<DreamValue>> _promptEvents = new();
[ViewVariables] private int _nextPromptEvent = 1;

private readonly Dictionary<string, DreamResource> _permittedBrowseRscFiles = new();
private DreamObjectMob? _mob;
private DreamObjectMovable? _eye;

Expand Down Expand Up @@ -399,10 +399,25 @@ public void BrowseResource(DreamResource resource, string filename) {

var msg = new MsgBrowseResource() {
Filename = filename,
Data = resource.ResourceData
DataHash = resource.ResourceData.Length //TODO: make a quick hash that can work clientside too
};
_permittedBrowseRscFiles.Add(filename, resource);

Session?.Channel.SendMessage(msg);
}

public void HandleBrowseResourceRequest(string filename) {
if(_permittedBrowseRscFiles.TryGetValue(filename, out var dreamResource)) {
var msg = new MsgBrowseResourceResponse() {
Filename = filename,
Data = dreamResource.ResourceData! //honestly if this is null, something mega fucked up has happened and we should error hard
};
_permittedBrowseRscFiles.Remove(filename);
Session?.Channel.SendMessage(msg);
} else {
_sawmill.Error($"Client({Session}) requested a browse_rsc file they had not been permitted to request ({filename}).");
}

Session?.ConnectedClient.SendMessage(msg);
}

public void Browse(string? body, string? options) {
Expand Down
7 changes: 7 additions & 0 deletions OpenDreamRuntime/DreamManager.Connections.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ private void InitializeConnectionManager() {
_netManager.RegisterNetMessage<MsgPromptList>();
_netManager.RegisterNetMessage<MsgPromptResponse>(RxPromptResponse);
_netManager.RegisterNetMessage<MsgBrowseResource>();
_netManager.RegisterNetMessage<MsgBrowseResourceRequest>(RxBrowseResourceRequest);
_netManager.RegisterNetMessage<MsgBrowseResourceResponse>();
_netManager.RegisterNetMessage<MsgBrowse>();
_netManager.RegisterNetMessage<MsgTopic>(RxTopic);
_netManager.RegisterNetMessage<MsgWinSet>();
Expand Down Expand Up @@ -224,6 +226,11 @@ private void RxAckLoadInterface(MsgAckLoadInterface message) {
_playerManager.JoinGame(player);
}

private void RxBrowseResourceRequest(MsgBrowseResourceRequest message) {
var connection = ConnectionForChannel(message.MsgChannel);
connection.HandleBrowseResourceRequest(message.Filename);
}

private DreamConnection ConnectionForChannel(INetChannel channel) {
return _connections[_playerManager.GetSessionByChannel(channel).UserId];
}
Expand Down
29 changes: 13 additions & 16 deletions OpenDreamShared/Network/Messages/MsgBrowseResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,21 @@
using Robust.Shared.Network;
using Robust.Shared.Serialization;

namespace OpenDreamShared.Network.Messages {
public sealed class MsgBrowseResource : NetMessage {
// TODO: Browse should be on its own channel or something.
public override MsgGroups MsgGroup => MsgGroups.EntityEvent;
namespace OpenDreamShared.Network.Messages;
public sealed class MsgBrowseResource : NetMessage {
// TODO: Browse should be on its own channel or something.
public override MsgGroups MsgGroup => MsgGroups.EntityEvent;

public string Filename = String.Empty;
public byte[] Data = Array.Empty<byte>();
public string Filename = String.Empty;

Check notice

Code scanning / InspectCode

Replace built-in type reference with a CLR type name or a keyword in static member access expressions Note

Built-in type reference is inconsistent with code style settings
public int DataHash;

public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) {
Filename = buffer.ReadString();
var bytes = buffer.ReadVariableInt32();
Data = buffer.ReadBytes(bytes);
}
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) {
Filename = buffer.ReadString();
DataHash = buffer.ReadVariableInt32();
}

public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) {
buffer.Write(Filename);
buffer.WriteVariableInt32(Data.Length);
buffer.Write(Data);
}
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) {
buffer.Write(Filename);
buffer.WriteVariableInt32(DataHash);
}
}
20 changes: 20 additions & 0 deletions OpenDreamShared/Network/Messages/MsgBrowseResourceRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using Lidgren.Network;
using Robust.Shared.Network;
using Robust.Shared.Serialization;

namespace OpenDreamShared.Network.Messages;
public sealed class MsgBrowseResourceRequest : NetMessage {
// TODO: Browse should be on its own channel or something.
public override MsgGroups MsgGroup => MsgGroups.EntityEvent;

public string Filename = String.Empty;

Check notice

Code scanning / InspectCode

Replace built-in type reference with a CLR type name or a keyword in static member access expressions Note

Built-in type reference is inconsistent with code style settings

public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) {
Filename = buffer.ReadString();
}

public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) {
buffer.Write(Filename);
}
}
25 changes: 25 additions & 0 deletions OpenDreamShared/Network/Messages/MsgBrowseResourceResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using Lidgren.Network;
using Robust.Shared.Network;
using Robust.Shared.Serialization;

namespace OpenDreamShared.Network.Messages;
public sealed class MsgBrowseResourceResponse : NetMessage {
// TODO: Browse should be on its own channel or something.
public override MsgGroups MsgGroup => MsgGroups.EntityEvent;

public string Filename = String.Empty;

Check notice

Code scanning / InspectCode

Replace built-in type reference with a CLR type name or a keyword in static member access expressions Note

Built-in type reference is inconsistent with code style settings
public byte[] Data = Array.Empty<byte>();

Check notice

Code scanning / InspectCode

Use collection expression syntax Note

Use collection expression

public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) {
Filename = buffer.ReadString();
var bytes = buffer.ReadVariableInt32();
Data = buffer.ReadBytes(bytes);
}

public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) {
buffer.Write(Filename);
buffer.WriteVariableInt32(Data.Length);
buffer.Write(Data);
}
}
3 changes: 3 additions & 0 deletions TestGame/code.dm
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
usr << "menus: [json_encode(winget(usr, null, "menus"))]"
usr << "macros: [json_encode(winget(usr, null, "macros"))]"

verb/browse_rsc_test()
usr << browse_rsc('icons/mob.dmi', "mobicon.png")
usr << browse("<p><img src=mobicon.png></p>Oh look, it's you!","window=honk")

verb/rotate()
for(var/i in 1 to 8)
Expand Down

0 comments on commit 7bc30d4

Please sign in to comment.