Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make browse_rsc() respect cache #1814

Merged
merged 4 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions OpenDreamClient/Interface/Controls/ControlBrowser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,16 +113,21 @@ private void RequestHandler(IRequestHandlerContext context) {
Stream stream;
HttpStatusCode status;
var path = new ResPath(newUri.AbsolutePath);
try {
stream = _resourceManager.UserData.OpenRead(_dreamResource.GetCacheFilePath(newUri.AbsolutePath));
status = HttpStatusCode.OK;
} catch (FileNotFoundException) {
if(!_dreamResource.EnsureCacheFile(newUri.AbsolutePath)) {
stream = Stream.Null;
status = HttpStatusCode.NotFound;
} catch (Exception e) {
_sawmill.Error($"Exception while loading file from {newUri}:\n{e}");
stream = Stream.Null;
status = HttpStatusCode.InternalServerError;
} else {
try {
stream = _resourceManager.UserData.OpenRead(_dreamResource.GetCacheFilePath(newUri.AbsolutePath));
status = HttpStatusCode.OK;
} catch (FileNotFoundException) {
stream = Stream.Null;
status = HttpStatusCode.NotFound;
} catch (Exception e) {
_sawmill.Error($"Exception while loading file from {newUri}:\n{e}");
stream = Stream.Null;
status = HttpStatusCode.InternalServerError;
}
}

if (!FileExtensionMimeTypes.TryGetValue(path.Extension, out var mimeType))
Expand Down
50 changes: 47 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,23 @@ 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}");
} 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 +140,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 +182,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
32 changes: 14 additions & 18 deletions OpenDreamShared/Network/Messages/MsgBrowseResource.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
using System;
using Lidgren.Network;
using Lidgren.Network;
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;
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);
}
}
19 changes: 19 additions & 0 deletions OpenDreamShared/Network/Messages/MsgBrowseResourceRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
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;

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

public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) {
buffer.Write(Filename);
}
}
24 changes: 24 additions & 0 deletions OpenDreamShared/Network/Messages/MsgBrowseResourceResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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;
public byte[] Data = [];

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
Loading