diff --git a/OpenDreamClient/OpenDreamClient.csproj b/OpenDreamClient/OpenDreamClient.csproj index 0421e82885..190148bd90 100644 --- a/OpenDreamClient/OpenDreamClient.csproj +++ b/OpenDreamClient/OpenDreamClient.csproj @@ -17,6 +17,7 @@ + diff --git a/OpenDreamClient/Resources/DreamResourceManager.cs b/OpenDreamClient/Resources/DreamResourceManager.cs index a88576c145..e5a197eb8d 100644 --- a/OpenDreamClient/Resources/DreamResourceManager.cs +++ b/OpenDreamClient/Resources/DreamResourceManager.cs @@ -5,6 +5,8 @@ using Robust.Shared.ContentPack; using Robust.Shared.Network; using Robust.Shared.Utility; +using System.Linq; +using SpaceWizards.Sodium; namespace OpenDreamClient.Resources; @@ -69,21 +71,32 @@ private void EnsureCacheDirectory() { } private void RxBrowseResource(MsgBrowseResource message) { - _sawmill.Debug($"Received cache check for {message.Filename}"); + _sawmill.Debug($"Received cache check for {message.Filename} hash: {BitConverter.ToString(message.DataHash)}"); EnsureCacheDirectory(); - if(_resourceManager.UserData.Exists(GetCacheFilePath(message.Filename))){ //TODO CHECK HASH + if(_resourceManager.UserData.Exists(GetCacheFilePath(message.Filename)) && GetFileHash(GetCacheFilePath(message.Filename)).SequenceEqual(message.DataHash)){ _sawmill.Debug($"Cache hit for {message.Filename}"); } else { if(_activeBrowseRscRequests.Contains(message.Filename)) //we've already requested it, don't need to do it again return; - _sawmill.Debug($"Cache miss for {message.Filename}, requesting from server."); + if (_resourceManager.UserData.Exists(GetCacheFilePath(message.Filename))) { + _sawmill.Debug($"Cache hit for {message.Filename} but hashes did not match (hash: {BitConverter.ToString(GetFileHash(GetCacheFilePath(message.Filename)))}). Re-requesting!"); + _resourceManager.UserData.Delete(GetCacheFilePath(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 byte[] GetFileHash(ResPath path) { + var stream = _resourceManager.UserData.OpenRead(path); + Span filebytes = new(new byte[stream.Length]); + stream.ReadToEnd(filebytes); + return CryptoGenericHashBlake2B.Hash(32, filebytes, ReadOnlySpan.Empty); + } + private void RxBrowseResourceResponse(MsgBrowseResourceResponse message) { - if(_activeBrowseRscRequests.Contains(message.Filename)) { + if (_activeBrowseRscRequests.Contains(message.Filename)) { _activeBrowseRscRequests.Remove(message.Filename); EnsureCacheDirectory(); CreateCacheFile(message.Filename, message.Data); @@ -227,7 +240,7 @@ public bool EnsureCacheFile(string filename, int timeoutSeconds = 5) { 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 somebody called browse() without browse_rsc() first."); + _sawmill.Error($"Cache was ensured for a file ({filename}) that does not exist in cache and is not requested. Probably somebody called browse() without browse_rsc() first."); return false; } } diff --git a/OpenDreamRuntime/DreamConnection.cs b/OpenDreamRuntime/DreamConnection.cs index 6e0a446a74..fc17ccc9b3 100644 --- a/OpenDreamRuntime/DreamConnection.cs +++ b/OpenDreamRuntime/DreamConnection.cs @@ -9,6 +9,7 @@ using OpenDreamShared.Network.Messages; using Robust.Shared.Enums; using Robust.Shared.Player; +using SpaceWizards.Sodium; namespace OpenDreamRuntime; @@ -373,7 +374,7 @@ public void BrowseResource(DreamResource resource, string filename) { var msg = new MsgBrowseResource() { Filename = filename, - DataHash = resource.ResourceData.Length //TODO: make a quick hash that can work clientside too + DataHash = CryptoGenericHashBlake2B.Hash(32, resource.ResourceData!, ReadOnlySpan.Empty) }; _permittedBrowseRscFiles[filename] = resource; @@ -384,7 +385,7 @@ 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 + 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); diff --git a/OpenDreamRuntime/OpenDreamRuntime.csproj b/OpenDreamRuntime/OpenDreamRuntime.csproj index 9d1bf2678e..9bde5a58e9 100644 --- a/OpenDreamRuntime/OpenDreamRuntime.csproj +++ b/OpenDreamRuntime/OpenDreamRuntime.csproj @@ -16,6 +16,7 @@ + diff --git a/OpenDreamShared/Network/Messages/MsgBrowseResource.cs b/OpenDreamShared/Network/Messages/MsgBrowseResource.cs index b2ca8e2870..e774b87981 100644 --- a/OpenDreamShared/Network/Messages/MsgBrowseResource.cs +++ b/OpenDreamShared/Network/Messages/MsgBrowseResource.cs @@ -8,15 +8,15 @@ public sealed class MsgBrowseResource : NetMessage { public override MsgGroups MsgGroup => MsgGroups.EntityEvent; public string Filename = string.Empty; - public int DataHash; + public byte[] DataHash = []; public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { Filename = buffer.ReadString(); - DataHash = buffer.ReadVariableInt32(); + DataHash = buffer.ReadBytes(32); } public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) { buffer.Write(Filename); - buffer.WriteVariableInt32(DataHash); + buffer.Write(DataHash); } }