提交帧同步案例

This commit is contained in:
PC-20230316NUNE\Administrator
2024-01-26 19:15:07 +08:00
parent 3a345ab966
commit 68c4d5e811
3928 changed files with 463020 additions and 1 deletions

View File

@@ -0,0 +1,195 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BestHTTP;
namespace BestHTTP.Examples
{
public sealed class AssetBundleSample : MonoBehaviour
{
/// <summary>
/// The url of the resource to download
/// </summary>
private Uri URI = new Uri(GUIHelper.BaseURL + "/AssetBundles/WebGL/demobundle.assetbundle");
#region Private Fields
/// <summary>
/// Debug status text
/// </summary>
string status = "Waiting for user interaction";
/// <summary>
/// The downloaded and cached AssetBundle
/// </summary>
AssetBundle cachedBundle;
/// <summary>
/// The loaded texture from the AssetBundle
/// </summary>
Texture2D texture;
/// <summary>
/// A flag that indicates that we are processing the request/bundle to hide the "Start Download" button.
/// </summary>
bool downloading;
#endregion
#region Unity Events
void OnGUI()
{
GUIHelper.DrawArea(GUIHelper.ClientArea, true, () =>
{
GUILayout.Label("Status: " + status);
// Draw the texture from the downloaded bundle
if (texture != null)
GUILayout.Box(texture, GUILayout.MaxHeight(256));
if (!downloading && GUILayout.Button("Start Download"))
{
UnloadBundle();
StartCoroutine(DownloadAssetBundle());
}
});
}
void OnDestroy()
{
UnloadBundle();
}
#endregion
#region Private Helper Functions
IEnumerator DownloadAssetBundle()
{
downloading = true;
// Create and send our request
var request = new HTTPRequest(URI).Send();
status = "Download started";
// Wait while it's finishes and add some fancy dots to display something while the user waits for it.
// A simple "yield return StartCoroutine(request);" would do the job too.
while (request.State < HTTPRequestStates.Finished)
{
yield return new WaitForSeconds(0.1f);
status += ".";
}
// Check the outcome of our request.
switch (request.State)
{
// The request finished without any problem.
case HTTPRequestStates.Finished:
if (request.Response.IsSuccess)
{
#if !BESTHTTP_DISABLE_CACHING
status = string.Format("AssetBundle downloaded! Loaded from local cache: {0}", request.Response.IsFromCache.ToString());
#else
status = "AssetBundle downloaded!";
#endif
// Start creating the downloaded asset bundle
AssetBundleCreateRequest async =
#if UNITY_5_3_OR_NEWER
AssetBundle.LoadFromMemoryAsync(request.Response.Data);
#else
AssetBundle.CreateFromMemory(request.Response.Data);
#endif
// wait for it
yield return async;
// And process the bundle
yield return StartCoroutine(ProcessAssetBundle(async.assetBundle));
}
else
{
status = string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
request.Response.StatusCode,
request.Response.Message,
request.Response.DataAsText);
Debug.LogWarning(status);
}
break;
// The request finished with an unexpected error. The request's Exception property may contain more info about the error.
case HTTPRequestStates.Error:
status = "Request Finished with Error! " + (request.Exception != null ? (request.Exception.Message + "\n" + request.Exception.StackTrace) : "No Exception");
Debug.LogError(status);
break;
// The request aborted, initiated by the user.
case HTTPRequestStates.Aborted:
status = "Request Aborted!";
Debug.LogWarning(status);
break;
// Connecting to the server is timed out.
case HTTPRequestStates.ConnectionTimedOut:
status = "Connection Timed Out!";
Debug.LogError(status);
break;
// The request didn't finished in the given time.
case HTTPRequestStates.TimedOut:
status = "Processing the request Timed Out!";
Debug.LogError(status);
break;
}
downloading = false;
}
/// <summary>
/// In this function we can do whatever we want with the freshly downloaded bundle.
/// In this example we will cache it for later use, and we will load a texture from it.
/// </summary>
IEnumerator ProcessAssetBundle(AssetBundle bundle)
{
if (bundle == null)
yield break;
// Save the bundle for future use
cachedBundle = bundle;
// Start loading the asset from the bundle
var asyncAsset =
#if UNITY_5_1 || UNITY_5_2 || UNITY_5_3_OR_NEWER
cachedBundle.LoadAssetAsync("9443182_orig", typeof(Texture2D));
#else
cachedBundle.LoadAsync("9443182_orig", typeof(Texture2D));
#endif
// wait til load
yield return asyncAsset;
// get the texture
texture = asyncAsset.asset as Texture2D;
}
void UnloadBundle()
{
if (cachedBundle != null)
{
cachedBundle.Unload(true);
cachedBundle = null;
}
}
#endregion
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 03eab9c6191c5cd4c9613084e817bd29
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,244 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using BestHTTP;
namespace BestHTTP.Examples
{
public sealed class LargeFileDownloadSample : MonoBehaviour
{
/// <summary>
/// The url of the resource to download
/// </summary>
const string URL = "https://uk3.testmy.net/dl-102400";
#region Private Fields
/// <summary>
/// Cached request to be able to abort it
/// </summary>
HTTPRequest request;
/// <summary>
/// Debug status of the request
/// </summary>
string status = string.Empty;
/// <summary>
/// Download(processing) progress. Its range is between [0..1]
/// </summary>
float progress;
/// <summary>
/// The fragment size that we will set to the request
/// </summary>
int fragmentSize = HTTPResponse.MinBufferSize;
#endregion
#region Unity Events
void Awake()
{
// If we have a non-finished download, set the progress to the value where we left it
if (PlayerPrefs.HasKey("DownloadLength"))
progress = PlayerPrefs.GetInt("DownloadProgress") / (float)PlayerPrefs.GetInt("DownloadLength");
}
void OnDestroy()
{
// Stop the download if we are leaving this example
if (request != null && request.State < HTTPRequestStates.Finished)
{
request.OnProgress = null;
request.Callback = null;
request.Abort();
}
}
void OnGUI()
{
GUIHelper.DrawArea(GUIHelper.ClientArea, true, () =>
{
// Draw the current status
GUILayout.Label("Request status: " + status);
GUILayout.Space(5);
// Draw the current progress
GUILayout.Label(string.Format("Progress: {0:P2} of {1:N0}Mb", progress, PlayerPrefs.GetInt("DownloadLength") / 1048576 /*1 Mb*/));
GUILayout.HorizontalSlider(progress, 0, 1);
GUILayout.Space(50);
if (request == null)
{
// Draw a slider to be able to change the fragment size
GUILayout.Label(string.Format("Desired Fragment Size: {0:N} KBytes", fragmentSize / 1024f));
fragmentSize = (int)GUILayout.HorizontalSlider(fragmentSize, HTTPResponse.MinBufferSize, 10 * 1024 * 1024);
GUILayout.Space(5);
string buttonStr = PlayerPrefs.HasKey("DownloadProgress") ? "Continue Download" : "Start Download";
if (GUILayout.Button(buttonStr))
StreamLargeFileTest();
}
else if (request.State == HTTPRequestStates.Processing && GUILayout.Button("Abort Download"))
{
// Simulate a connection lost
request.Abort();
}
});
}
#endregion
#region Private Helper Functions
// Calling this function again when the "DownloadProgress" key in the PlayerPrefs present will
// continue the download
void StreamLargeFileTest()
{
request = new HTTPRequest(new Uri(URL), (req, resp) =>
{
switch (req.State)
{
// The request is currently processed. With UseStreaming == true, we can get the streamed fragments here
case HTTPRequestStates.Processing:
// Set the DownloadLength, so we can display the progress
if (!PlayerPrefs.HasKey("DownloadLength"))
{
string value = resp.GetFirstHeaderValue("content-length");
if (!string.IsNullOrEmpty(value))
PlayerPrefs.SetInt("DownloadLength", int.Parse(value));
}
// Get the fragments, and save them
ProcessFragments(resp.GetStreamedFragments());
status = "Processing";
break;
// The request finished without any problem.
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
{
// Save any remaining fragments
ProcessFragments(resp.GetStreamedFragments());
// Completely finished
if (resp.IsStreamingFinished)
{
status = "Streaming finished!";
// We are done, delete the progress key
PlayerPrefs.DeleteKey("DownloadProgress");
PlayerPrefs.Save();
request = null;
}
else
status = "Processing";
}
else
{
status = string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText);
Debug.LogWarning(status);
request = null;
}
break;
// The request finished with an unexpected error. The request's Exception property may contain more info about the error.
case HTTPRequestStates.Error:
status = "Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception");
Debug.LogError(status);
request = null;
break;
// The request aborted, initiated by the user.
case HTTPRequestStates.Aborted:
status = "Request Aborted!";
Debug.LogWarning(status);
request = null;
break;
// Connecting to the server is timed out.
case HTTPRequestStates.ConnectionTimedOut:
status = "Connection Timed Out!";
Debug.LogError(status);
request = null;
break;
// The request didn't finished in the given time.
case HTTPRequestStates.TimedOut:
status = "Processing the request Timed Out!";
Debug.LogError(status);
request = null;
break;
}
});
// Are there any progress, that we can continue?
if (PlayerPrefs.HasKey("DownloadProgress"))
// Set the range header
request.SetRangeHeader(PlayerPrefs.GetInt("DownloadProgress"));
else
// This is a new request
PlayerPrefs.SetInt("DownloadProgress", 0);
#if !BESTHTTP_DISABLE_CACHING
// If we are writing our own file set it true(disable), so don't duplicate it on the file-system
request.DisableCache = true;
#endif
// We want to access the downloaded bytes while we are still downloading
request.UseStreaming = true;
// Set a reasonable high fragment size. Here it is 5 megabytes.
request.StreamFragmentSize = fragmentSize;
// Start Processing the request
request.Send();
}
/// <summary>
/// In this function we can do whatever we want with the downloaded bytes. In this sample we will do nothing, just set the metadata to display progress.
/// </summary>
void ProcessFragments(List<byte[]> fragments)
{
if (fragments != null && fragments.Count > 0)
{
/*string dir = "TODO!";
string filename = "TODO!";
using (System.IO.FileStream fs = new System.IO.FileStream(System.IO.Path.Combine(dir, filename), System.IO.FileMode.Append))
for (int i = 0; i < fragments.Count; ++i)
fs.Write(fragments[i], 0, fragments[i].Length);*/
for (int i = 0; i < fragments.Count; ++i)
{
// Save how many bytes we wrote successfully
int downloaded = PlayerPrefs.GetInt("DownloadProgress") + fragments[i].Length;
PlayerPrefs.SetInt("DownloadProgress", downloaded);
}
PlayerPrefs.Save();
// Set the progress to the actually processed bytes
progress = PlayerPrefs.GetInt("DownloadProgress") / (float)PlayerPrefs.GetInt("DownloadLength");
}
}
#endregion
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 972fc2301f87c9e46bfb5523f2bc5090
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using BestHTTP;
namespace BestHTTP.Examples
{
public sealed class TextureDownloadSample : MonoBehaviour
{
/// <summary>
/// The URL of the server that will serve the image resources
/// </summary>
string BaseURL = GUIHelper.BaseURL + "/images/Demo/";
#region Private Fields
/// <summary>
/// The downloadable images
/// </summary>
string[] Images = new string[9] { "One.png", "Two.png", "Three.png", "Four.png", "Five.png", "Six.png", "Seven.png", "Eight.png", "Nine.png" };
/// <summary>
/// The downloaded images will be stored as textures in this array
/// </summary>
Texture2D[] Textures = new Texture2D[9];
#if !BESTHTTP_DISABLE_CACHING
/// <summary>
/// True if all images are loaded from the local cache instead of the server
/// </summary>
bool allDownloadedFromLocalCache;
#endif
/// <summary>
/// How many sent requests are finished
/// </summary>
int finishedCount;
/// <summary>
/// GUI scroll position
/// </summary>
Vector2 scrollPos;
#endregion
#region Unity Events
void Awake()
{
// Set a well observable value
// This is how many concurrent requests can be made to a server
HTTPManager.MaxConnectionPerServer = 1;
// Create placeholder textures
for (int i = 0; i < Images.Length; ++i)
Textures[i] = new Texture2D(100, 150);
}
void OnDestroy()
{
// Set back to its defualt value.
HTTPManager.MaxConnectionPerServer = 4;
}
void OnGUI()
{
GUIHelper.DrawArea(GUIHelper.ClientArea, true, () =>
{
scrollPos = GUILayout.BeginScrollView(scrollPos);
// Draw out the textures
GUILayout.SelectionGrid(0, Textures, 3);
#if !BESTHTTP_DISABLE_CACHING
if (finishedCount == Images.Length && allDownloadedFromLocalCache)
GUIHelper.DrawCenteredText("All images loaded from the local cache!");
#endif
GUILayout.FlexibleSpace();
GUILayout.BeginHorizontal();
GUILayout.Label("Max Connection/Server: ", GUILayout.Width(150));
GUILayout.Label(HTTPManager.MaxConnectionPerServer.ToString(), GUILayout.Width(20));
HTTPManager.MaxConnectionPerServer = (byte)GUILayout.HorizontalSlider(HTTPManager.MaxConnectionPerServer, 1, 10);
GUILayout.EndHorizontal();
if (GUILayout.Button("Start Download"))
DownloadImages();
GUILayout.EndScrollView();
});
}
#endregion
#region Private Helper Functions
void DownloadImages()
{
// Set these metadatas to its initial values
#if !BESTHTTP_DISABLE_CACHING
allDownloadedFromLocalCache = true;
#endif
finishedCount = 0;
for (int i = 0; i < Images.Length; ++i)
{
// Set a blank placeholder texture, overriding previously downloaded texture
Textures[i] = new Texture2D(100, 150);
// Construct the request
var request = new HTTPRequest(new Uri(BaseURL + Images[i]), ImageDownloaded);
// Set the Tag property, we can use it as a general storage bound to the request
request.Tag = Textures[i];
// Send out the request
request.Send();
}
}
/// <summary>
/// Callback function of the image download http requests
/// </summary>
void ImageDownloaded(HTTPRequest req, HTTPResponse resp)
{
// Increase the finished count regardless of the state of our request
finishedCount++;
switch (req.State)
{
// The request finished without any problem.
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
{
// Get the Texture from the Tag property
Texture2D tex = req.Tag as Texture2D;
// Load the texture
tex.LoadImage(resp.Data);
#if !BESTHTTP_DISABLE_CACHING
// Update the cache-info variable
allDownloadedFromLocalCache = allDownloadedFromLocalCache && resp.IsFromCache;
#endif
}
else
{
Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText));
}
break;
// The request finished with an unexpected error. The request's Exception property may contain more info about the error.
case HTTPRequestStates.Error:
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
break;
// The request aborted, initiated by the user.
case HTTPRequestStates.Aborted:
Debug.LogWarning("Request Aborted!");
break;
// Connecting to the server is timed out.
case HTTPRequestStates.ConnectionTimedOut:
Debug.LogError("Connection Timed Out!");
break;
// The request didn't finished in the given time.
case HTTPRequestStates.TimedOut:
Debug.LogError("Processing the request Timed Out!");
break;
}
}
#endregion
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: af6ffe2bbf96d2b49ba3ef0713511d45
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,222 @@
using BestHTTP;
using System;
using System.IO;
using System.Threading;
namespace BestHTTP.Examples
{
public sealed class UploadStream : Stream
{
#region Private Fields
/// <summary>
/// Buffer for reads
/// </summary>
MemoryStream ReadBuffer = new MemoryStream();
/// <summary>
/// Buffer for writes
/// </summary>
MemoryStream WriteBuffer = new MemoryStream();
/// <summary>
/// Indicates that we will not write more data to this stream
/// </summary>
bool noMoreData;
/// <summary>
/// For thread synchronization
/// </summary>
AutoResetEvent ARE = new AutoResetEvent(false);
/// <summary>
/// For thread synchronization
/// </summary>
object locker = new object();
#endregion
#region Properties
/// <summary>
/// Name of this stream for easier debugging
/// </summary>
public string Name { get; private set; }
/// <summary>
/// true if we are read all data from the read buffer
/// </summary>
private bool IsReadBufferEmpty { get { lock (locker) return ReadBuffer.Position == ReadBuffer.Length; } }
#endregion
#region Constructors
public UploadStream(string name)
: this()
{
this.Name = name;
}
public UploadStream()
{
this.ReadBuffer = new MemoryStream();
this.WriteBuffer = new MemoryStream();
this.Name = string.Empty;
}
#endregion
#region Stream Implementation
public override int Read(byte[] buffer, int offset, int count)
{
// We will not push more data to the write buffer
if (noMoreData)
{
// No data left in the read buffer
if (ReadBuffer.Position == ReadBuffer.Length)
{
// Is there any data in the write buffer? If so, switch the buffers
if (WriteBuffer.Length > 0)
SwitchBuffers();
else
{
HTTPManager.Logger.Information("UploadStream", string.Format("{0} - Read - End Of Stream", this.Name));
return -1;
}
}
else
return ReadBuffer.Read(buffer, offset, count);
}
// There are no more data in the read buffer? Wait for it.
if (IsReadBufferEmpty)
{
ARE.WaitOne();
lock (locker)
if (IsReadBufferEmpty && WriteBuffer.Length > 0)
SwitchBuffers();
}
int read = -1;
lock (locker)
read = ReadBuffer.Read(buffer, offset, count);
return read;
}
public override void Write(byte[] buffer, int offset, int count)
{
if (noMoreData)
throw new System.ArgumentException("noMoreData already set!");
lock (locker)
{
WriteBuffer.Write(buffer, offset, count);
SwitchBuffers();
}
ARE.Set();
}
public override void Flush()
{
Finish();
}
#endregion
#region Dispose Implementation
protected override void Dispose(bool disposing)
{
if (disposing)
{
HTTPManager.Logger.Information("UploadStream", string.Format("{0} - Dispose", this.Name));
ReadBuffer.Dispose();
ReadBuffer = null;
WriteBuffer.Dispose();
WriteBuffer = null;
#if NETFX_CORE
ARE.Dispose();
#else
ARE.Close();
#endif
ARE = null;
}
base.Dispose(disposing);
}
#endregion
#region Helper Functions
public void Finish()
{
if (noMoreData)
throw new System.ArgumentException("noMoreData already set!");
HTTPManager.Logger.Information("UploadStream", string.Format("{0} - Finish", this.Name));
noMoreData = true;
ARE.Set();
}
private bool SwitchBuffers()
{
// Switch the buffers only when all data are consumed from our read buffer
lock (locker)
{
if (ReadBuffer.Position == ReadBuffer.Length)
{
// This buffer will be the read buffer, we need to seek back to the beginning
WriteBuffer.Seek(0, SeekOrigin.Begin);
// This will be the write buffer, set the length to zero
ReadBuffer.SetLength(0);
// switch the two buffers
MemoryStream tmp = WriteBuffer;
WriteBuffer = ReadBuffer;
ReadBuffer = tmp;
return true;
}
}
return false;
}
#endregion
#region Not Implemented Functions and Properties
public override bool CanRead { get { throw new NotImplementedException(); } }
public override bool CanSeek { get { throw new NotImplementedException(); } }
public override bool CanWrite { get { throw new NotImplementedException(); } }
public override long Length { get { throw new NotImplementedException(); } }
public override long Position { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
#endregion
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a2ab021301a7baf45a80b5a46275a140
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: