diff --git a/ClassicalSharp/Game/Game.Properties.cs b/ClassicalSharp/Game/Game.Properties.cs
index 0a4871596..b587b8419 100644
--- a/ClassicalSharp/Game/Game.Properties.cs
+++ b/ClassicalSharp/Game/Game.Properties.cs
@@ -192,6 +192,7 @@ namespace ClassicalSharp {
internal EntryList AcceptedUrls = new EntryList( "acceptedurls.txt" );
internal EntryList DeniedUrls = new EntryList( "deniedurls.txt" );
internal EntryList ETags = new EntryList( "etags.txt" );
+ internal EntryList LastModified = new EntryList( "lastmodified.txt" );
/// Calculates the amount that the hotbar widget should be scaled by when rendered.
diff --git a/ClassicalSharp/Network/INetworkProcessor.cs b/ClassicalSharp/Network/INetworkProcessor.cs
index b20ffb545..55cfa6642 100644
--- a/ClassicalSharp/Network/INetworkProcessor.cs
+++ b/ClassicalSharp/Network/INetworkProcessor.cs
@@ -98,8 +98,8 @@ namespace ClassicalSharp {
void DownloadTexturePack( string url ) {
if( game.DeniedUrls.HasEntry( url ) ) return;
- DateTime lastModified = TextureCache.GetLastModifiedFromCache( url );
- string etag = TextureCache.GetETagFromCache( url, game.ETags );
+ DateTime lastModified = TextureCache.GetLastModified( url, game.LastModified );
+ string etag = TextureCache.GetETag( url, game.ETags );
if( url.Contains( ".zip" ) )
game.AsyncDownloader.DownloadData( url, true, "texturePack",
@@ -134,10 +134,12 @@ namespace ClassicalSharp {
game.Drawer2D.ConvertTo32Bpp( ref bmp );
}
if( !game.ChangeTerrainAtlas( bmp ) ) { bmp.Dispose(); return; }
- TextureCache.AddToCache( item.Url, bmp );
- TextureCache.AddETagToCache( item.Url, item.ETag, game.ETags );
+
+ TextureCache.Add( item.Url, bmp );
+ TextureCache.AddETag( item.Url, item.ETag, game.ETags );
+ TextureCache.AdddLastModified( item.Url, item.LastModified, game.LastModified );
} else {
- Bitmap bmp = TextureCache.GetBitmapFromCache( item.Url );
+ Bitmap bmp = TextureCache.GetBitmap( item.Url );
if( bmp == null ) { // e.g. 404 errors
ExtractDefault();
} else if( item.Url != game.World.TextureUrl ) {
@@ -154,10 +156,11 @@ namespace ClassicalSharp {
TexturePackExtractor extractor = new TexturePackExtractor();
extractor.Extract( (byte[])item.Data, game );
- TextureCache.AddToCache( item.Url, (byte[])item.Data );
- TextureCache.AddETagToCache( item.Url, item.ETag, game.ETags );
+ TextureCache.Add( item.Url, (byte[])item.Data );
+ TextureCache.AddETag( item.Url, item.ETag, game.ETags );
+ TextureCache.AdddLastModified( item.Url, item.LastModified, game.LastModified );
} else {
- byte[] data = TextureCache.GetDataFromCache( item.Url );
+ byte[] data = TextureCache.GetData( item.Url );
if( data == null ) { // e.g. 404 errors
ExtractDefault();
} else if( item.Url != game.World.TextureUrl ) {
diff --git a/ClassicalSharp/Network/Utils/AsyncDownloader.cs b/ClassicalSharp/Network/Utils/AsyncDownloader.cs
index 001321ad7..72072e163 100644
--- a/ClassicalSharp/Network/Utils/AsyncDownloader.cs
+++ b/ClassicalSharp/Network/Utils/AsyncDownloader.cs
@@ -27,9 +27,9 @@ namespace ClassicalSharp.Network {
public int CurrentItemProgress = -3;
public AsyncDownloader() { }
- public AsyncDownloader( string skinServer ) { Init( skinServer ); }
+ public AsyncDownloader( string skinServer ) { Init( skinServer ); }
public void Init( Game game ) { Init( game.skinServer ); }
-
+
void Init( string skinServer ) {
this.skinServer = skinServer;
WebRequest.DefaultWebProxy = null;
@@ -40,7 +40,7 @@ namespace ClassicalSharp.Network {
worker.Start();
}
- public void Ready( Game game ) { }
+ public void Ready( Game game ) { }
public void Reset( Game game ) {
lock( requestLocker )
requests.Clear();
@@ -56,49 +56,49 @@ namespace ClassicalSharp.Network {
string strippedSkinName = Utils.StripColours( skinName );
string url = Utils.IsUrlPrefix( skinName, 0 ) ? skinName :
skinServer + strippedSkinName + ".png";
- AddRequest( url, true, identifier, RequestType.Bitmap,
+ AddRequest( url, true, identifier, RequestType.Bitmap,
DateTime.MinValue , null);
}
/// Asynchronously downloads a bitmap image from the specified url.
public void DownloadImage( string url, bool priority, string identifier ) {
- AddRequest( url, priority, identifier, RequestType.Bitmap,
+ AddRequest( url, priority, identifier, RequestType.Bitmap,
DateTime.MinValue, null );
}
/// Asynchronously downloads a string from the specified url.
public void DownloadPage( string url, bool priority, string identifier ) {
- AddRequest( url, priority, identifier, RequestType.String,
+ AddRequest( url, priority, identifier, RequestType.String,
DateTime.MinValue, null );
}
/// Asynchronously downloads a byte array.
public void DownloadData( string url, bool priority, string identifier ) {
- AddRequest( url, priority, identifier, RequestType.ByteArray,
+ AddRequest( url, priority, identifier, RequestType.ByteArray,
DateTime.MinValue, null );
}
/// Asynchronously downloads a bitmap image.
- public void DownloadImage( string url, bool priority, string identifier,
+ public void DownloadImage( string url, bool priority, string identifier,
DateTime lastModified, string etag ) {
- AddRequest( url, priority, identifier, RequestType.Bitmap,
+ AddRequest( url, priority, identifier, RequestType.Bitmap,
lastModified, etag );
}
/// Asynchronously downloads a byte array.
- public void DownloadData( string url, bool priority, string identifier,
+ public void DownloadData( string url, bool priority, string identifier,
DateTime lastModified, string etag ) {
- AddRequest( url, priority, identifier, RequestType.ByteArray,
+ AddRequest( url, priority, identifier, RequestType.ByteArray,
lastModified, etag );
}
/// Asynchronously retrieves the content length of the body response.
public void RetrieveContentLength( string url, bool priority, string identifier ) {
- AddRequest( url, priority, identifier, RequestType.ContentLength,
+ AddRequest( url, priority, identifier, RequestType.ContentLength,
DateTime.MinValue, null );
}
- void AddRequest( string url, bool priority, string identifier,
+ void AddRequest( string url, bool priority, string identifier,
RequestType type, DateTime lastModified, string etag ) {
lock( requestLocker ) {
Request request = new Request( url, identifier, type, lastModified, etag );
@@ -197,11 +197,14 @@ namespace ClassicalSharp.Network {
object value = null;
HttpStatusCode status = HttpStatusCode.OK;
string etag = null;
+ DateTime lastModified = DateTime.MinValue;
try {
HttpWebRequest req = MakeRequest( request );
using( HttpWebResponse response = (HttpWebResponse)req.GetResponse() ) {
etag = response.Headers[HttpResponseHeader.ETag];
+ if( response.Headers[HttpResponseHeader.LastModified] != null )
+ lastModified = response.LastModified;
value = DownloadContent( request, response );
}
} catch( Exception ex ) {
@@ -220,7 +223,8 @@ namespace ClassicalSharp.Network {
lock( downloadedLocker ) {
DownloadedItem oldItem;
- DownloadedItem newItem = new DownloadedItem( value, request.TimeAdded, url, status, etag );
+ DownloadedItem newItem = new DownloadedItem( value, request.TimeAdded, url,
+ status, etag, lastModified );
if( downloaded.TryGetValue( request.Identifier, out oldItem ) ) {
if( oldItem.TimeAdded > newItem.TimeAdded ) {
@@ -325,7 +329,7 @@ namespace ClassicalSharp.Network {
/// ETag of the item most recently cached. (if any)
public string ETag;
- public Request( string url, string identifier, RequestType type,
+ public Request( string url, string identifier, RequestType type,
DateTime lastModified, string etag ) {
Url = url;
Identifier = identifier;
@@ -357,14 +361,19 @@ namespace ClassicalSharp.Network {
/// Unique identifier assigned by the server to this item.
public string ETag;
- public DownloadedItem( object data, DateTime timeAdded,
- string url, HttpStatusCode code, string etag ) {
+ /// Time the server indicates this item was last modified.
+ public DateTime LastModified;
+
+ public DownloadedItem( object data, DateTime timeAdded,
+ string url, HttpStatusCode code,
+ string etag, DateTime lastModified ) {
Data = data;
TimeAdded = timeAdded;
TimeDownloaded = DateTime.UtcNow;
Url = url;
ResponseCode = code;
ETag = etag;
+ LastModified = lastModified;
}
}
}
\ No newline at end of file
diff --git a/ClassicalSharp/TexturePack/TextureCache.cs b/ClassicalSharp/TexturePack/TextureCache.cs
index 3a0dc6b02..21d2a26fe 100644
--- a/ClassicalSharp/TexturePack/TextureCache.cs
+++ b/ClassicalSharp/TexturePack/TextureCache.cs
@@ -13,54 +13,76 @@ namespace ClassicalSharp.TexturePack {
/// Caches terrain atlases and texture packs to avoid making redundant downloads.
public static class TextureCache {
+ /// Gets whether the given url has data associated with it in the cache.
+ public static bool HasUrl( string url ) {
+ return File.Exists( MakePath( url ) );
+ }
+
/// Gets the bitmap associated with the url from the cache, returning null if the bitmap
/// for the url was not found in the cache or the bitmap in the cache was corrupted.
- public static Bitmap GetBitmapFromCache( string url ) {
+ public static Bitmap GetBitmap( string url ) {
string path = MakePath( url );
if( !File.Exists( path ) ) return null;
try {
return new Bitmap( path );
} catch( ArgumentException ex ) {
- ErrorHandler.LogError( "Cache.GetBitmapFromCache", ex );
+ ErrorHandler.LogError( "Cache.GetBitmap", ex );
return null;
} catch( IOException ex ) {
- ErrorHandler.LogError( "Cache.GetBitmapFromCache", ex );
+ ErrorHandler.LogError( "Cache.GetBitmap", ex );
return null;
}
}
- /// Gets the data associated with the url from the cache, returning null if the
+ /// Gets the data associated with the url from the cache, returning null if the
/// data for the url was not found in the cache.
- public static byte[] GetDataFromCache( string url ) {
+ public static byte[] GetData( string url ) {
string path = MakePath( url );
if( !File.Exists( path ) ) return null;
try {
return File.ReadAllBytes( path );
} catch( IOException ex ) {
- ErrorHandler.LogError( "Cache.GetDataFromCache", ex );
+ ErrorHandler.LogError( "Cache.GetData", ex );
return null;
}
}
- /// Gets the time the data associated with the url from the cache was last modified,
+ /// Gets the time the data associated with the url from the cache was last modified,
/// returning DateTime.MinValue if data for the url was not found in the cache.
- public static DateTime GetLastModifiedFromCache( string url ) {
- string path = MakePath( url );
- if( !File.Exists( path ) )
- return DateTime.MinValue;
+ public static DateTime GetLastModified( string url, EntryList tags ) {
+ string entry = GetFromTags( url, tags );
+ long ticks = 0;
+ if( entry != null && long.TryParse( entry, out ticks ) )
+ return new DateTime( ticks, DateTimeKind.Utc );
+ string path = MakePath( url );
+ if( !File.Exists( path ) ) return DateTime.MinValue;
return File.GetLastWriteTimeUtc( path );
}
-
- /// Gets whether the given url has a bitmap associated with it in the cache.
- public static bool IsInCache( string url ) {
- return File.Exists( MakePath( url ) );
+
+ public static string GetETag( string url, EntryList tags ) {
+ return GetFromTags( url, tags );
}
+ static string GetFromTags( string url, EntryList tags ) {
+ string crc32 = CRC32( url );
+
+ for( int i = 0; i < tags.Entries.Count; i++ ) {
+ string entry = tags.Entries[i];
+ if( !entry.StartsWith( crc32 ) ) continue;
+
+ int sepIndex = entry.IndexOf( ' ' );
+ if( sepIndex == -1 ) continue;
+ return entry.Substring( sepIndex + 1 );
+ }
+ return null;
+ }
+
+
/// Adds the url and the bitmap associated with it to the cache.
- public static void AddToCache( string url, Bitmap bmp ) {
+ public static void Add( string url, Bitmap bmp ) {
string path = MakePath( url );
try {
string basePath = PathIO.Combine( Program.AppDirectory, Folder );
@@ -75,7 +97,7 @@ namespace ClassicalSharp.TexturePack {
}
/// Adds the url and the data associated with it to the cache.
- public static void AddToCache( string url, byte[] data ) {
+ public static void Add( string url, byte[] data ) {
string path = MakePath( url );
try {
string basePath = PathIO.Combine( Program.AppDirectory, Folder );
@@ -87,52 +109,49 @@ namespace ClassicalSharp.TexturePack {
ErrorHandler.LogError( "Cache.AddToCache", ex );
}
}
-
- public static string GetETagFromCache( string url, EntryList tags ) {
- byte[] utf8 = Encoding.UTF8.GetBytes( url );
- string crc32 = CRC32( utf8 ).ToString();
-
- for( int i = 0; i < tags.Entries.Count; i++ ) {
- string entry = tags.Entries[i];
- if( !entry.StartsWith( crc32 ) ) continue;
-
- int sepIndex = entry.IndexOf( ' ' );
- if( sepIndex == -1 ) continue;
- return entry.Substring( sepIndex + 1 );
- }
- return null;
+
+ public static void AddETag( string url, string etag, EntryList tags ) {
+ if( etag == null ) return;
+ AddToTags( url, etag, tags );
}
- public static void AddETagToCache( string url, string etag, EntryList tags ) {
- if( etag == null ) return;
- byte[] utf8 = Encoding.UTF8.GetBytes( url );
- string crc32 = CRC32( utf8 ).ToString();
-
+ public static void AdddLastModified( string url, DateTime lastModified, EntryList tags ) {
+ if( lastModified == DateTime.MinValue ) return;
+ string data = lastModified.ToUniversalTime().Ticks.ToString();
+ AddToTags( url, data, tags );
+ }
+
+ static void AddToTags( string url, string data, EntryList tags ) {
+ string crc32 = CRC32( url );
for( int i = 0; i < tags.Entries.Count; i++ ) {
if( !tags.Entries[i].StartsWith( crc32 ) ) continue;
- tags.Entries[i] = crc32 + " " + etag;
+ tags.Entries[i] = crc32 + " " + data;
tags.Save(); return;
}
- tags.AddEntry( crc32 + " " + etag );
+ tags.AddEntry( crc32 + " " + data );
}
+
const string Folder = "texturecache";
static string MakePath( string url ) {
- byte[] utf8 = Encoding.UTF8.GetBytes( url );
- uint crc32 = CRC32( utf8 );
+ string crc32 = CRC32( url );
string basePath = PathIO.Combine( Program.AppDirectory, Folder );
- return PathIO.Combine( basePath, crc32.ToString() );
+ return PathIO.Combine( basePath, crc32 );
}
- static uint CRC32( byte[] data ) {
+ static string CRC32( string url ) {
+ byte[] data = Encoding.UTF8.GetBytes( url );
uint crc = 0xffffffffU;
+
for( int i = 0; i < data.Length; i++ ) {
crc ^= data[i];
for( int j = 0; j < 8; j++ )
crc = (crc >> 1) ^ (crc & 1) * 0xEDB88320;
}
- return crc ^ 0xffffffffU;
+
+ uint result = crc ^ 0xffffffffU;
+ return result.ToString();
}
}
}