Having Issues Using WebClient to Download Files In Unity - c#

So I am having a big issue when coding for Unity. I am very new to C# and so have used examples I have found online to make this code. My only issue is there are no errors popping up but it won't download the file properly.
I am using this code so that it will be easy for my users to import unitypackages they would use often. I have a button that works as intended, it shows download and then changes to import if the file exists. However, if I click it when it says download it will instantly say "Download Complete" and the file won't show up for a few minutes. When it finally does the file is 0KB in size.
I really need help figured out why my file isn't downloading properly. I am super stumped.
This code is the script for the WebClient.
using UnityEngine;
using System.IO;
using System.Net;
using System;
using System.ComponentModel;
using UnityEditor;
namespace SentinelsSDK
{
public class SentinelsSDK_ImportManager
{
private static string localPath = "Assets/VRCSDK/Dependencies/VRChat/Imports/";
private static string localDownloadPath = "Assets/VRCSDK/Dependencies/VRChat/Imports/";
private static string urlStart = "https://www.sentinels.xyz/uploads/2/0/9/0/20909832/";
public static void DownloadAndImportAssetFromServer(string assetName)
{
if (File.Exists(localDownloadPath + assetName))
{
sentLog(assetName + " exists. Importing it..");
importDownloadedAsset(assetName);
}
else
{
sentLog(assetName + " does not exist. Starting download..");
downloadFile(assetName);
}
}
private static void downloadFile(string assetName)
{
WebClient w = new WebClient();
w.Headers.Set(HttpRequestHeader.UserAgent, "Webkit Gecko wHTTPS (Keep Alive 55)");
w.QueryString.Add("assetName", assetName);
w.DownloadFileCompleted += fileDownloadCompleted;
w.DownloadProgressChanged += fileDownloadProgress;
string url = urlStart + assetName;
w.DownloadFileAsync(new Uri(url), localDownloadPath + assetName);
}
private static void fileDownloadCompleted(object sender, AsyncCompletedEventArgs e)
{
string assetName = ((WebClient)(sender)).QueryString["assetName"];
sentLog("Download of file " + assetName + " completed!");
}
private static void fileDownloadProgress(object sender, DownloadProgressChangedEventArgs e)
{
sentLog("Progress is at " + e.ProgressPercentage.ToString() + "%");
}
private static void sentLog(string message)
{
Debug.Log("[SentinelsSDK] AssetDownloader: " + message);
}
public static void importAsset(string assetName)
{
AssetDatabase.ImportPackage(localPath + assetName, true);
}
public static void importDownloadedAsset(string assetName)
{
AssetDatabase.ImportPackage(localDownloadPath + assetName, true);
}
}
}
This code is the button calling the download from my other script.
using SentinelsSDK;
...
private static string localDownloadPath = "Assets/VRCSDK/Dependencies/VRChat/Imports/";
...
GUILayout.BeginHorizontal();
if (GUILayout.Button((File.Exists(localDownloadPath + "poiyomitoon.unitypackage") ? "Import" : "Download") + " - Poiyomi Toon"))
{
SentinelsSDK_ImportManager.DownloadAndImportAssetFromServer("poiyomitoon.unitypackage");
}
GUILayout.EndHorizontal();

So I figured out my issue, but I am not sure why it was an issue. Apparently putting "https://" for the urlstart instead of "http://" broke it, despite you being able to download the file normally regardless of the protocol used. If someone can help me figure out why that is I would be grateful!

Related

Setup C# TFTP server using GSF.NET library

I am trying to setup a TFTP server in C#. I am using this library https://www.gridprotectionalliance.org/NightlyBuilds/GridSolutionsFramework/Help/html/N_GSF_Net_TFtp.htm.
using GSF.Net.TFtp;
using System.Net;
IPAddress ip = IPAddress.Parse("192.168.1.102");
TFtpServer tftpserver = new TFtpServer(ip);
tftpserver.Start();
When I run that code the program exits inmediately. It seems I need to listen to an event to do something.
D:\TFTPtest\TFTPtest\bin\Release\net6.0\TFTPtest.exe (process 17884) exited with code 0.
Press any key to close this window . . .
The API says:
public delegate void TFtpServerEventHandler(
ITFtpTransfer transfer,
EndPoint client
)
The API don't give examples on how to do that.
An example of how to do that, how to setup the eventhandler, would be great.
PS: I am a beginner in C#.
I figured out how to handle the events:
using GSF.Net.TFtp;
using System.Net;
namespace Tftp.Net.SampleServer
{
class Program
{
private static String? ServerDirectory;
static void Main(string[] args) {
ServerDirectory = "D:\\Prgramacion\\Tftproot";
TFtpServer tftpserver = new TFtpServer();
Console.WriteLine(tftpserver.ToString());
tftpserver.OnReadRequest += new TFtpServerEventHandler(server_OnReadRequest);
tftpserver.Start();
Console.Read();
}
static void server_OnReadRequest(ITFtpTransfer transfer, EndPoint client)
{
Console.WriteLine("Reading...");
String path = Path.Combine(ServerDirectory, transfer.Filename);
FileInfo file = new FileInfo(path);
//Is the file within the server directory?
if (!file.FullName.StartsWith(ServerDirectory, StringComparison.InvariantCultureIgnoreCase))
{
CancelTransfer(transfer, TFtpErrorPacket.AccessViolation);
}
else if (!file.Exists)
{
CancelTransfer(transfer, TFtpErrorPacket.FileNotFound);
}
else
{
OutputTransferStatus(transfer, "Accepting request from " + client);
StartTransfer(transfer, new FileStream(file.FullName, FileMode.Open, FileAccess.Read));
}
}
private static void StartTransfer(ITFtpTransfer transfer, Stream stream)
{
transfer.OnProgress += new TFtpProgressHandler(transfer_OnProgress);
transfer.Start(stream);
}
static void transfer_OnProgress(ITFtpTransfer transfer, TFtpTransferProgress progress)
{
OutputTransferStatus(transfer, "Progress " + progress);
}
private static void CancelTransfer(ITFtpTransfer transfer, TFtpErrorPacket reason)
{
OutputTransferStatus(transfer, "Cancelling transfer: " + reason.ErrorMessage);
transfer.Cancel(reason);
}
static void transfer_OnError(ITFtpTransfer transfer, TftpTransferError error)
{
OutputTransferStatus(transfer, "Error: " + error);
}
static void transfer_OnFinished(ITFtpTransfer transfer)
{
OutputTransferStatus(transfer, "Finished");
}
static void transfer_OnProgress(ITFtpTransfer transfer, TftpTransferProgress progress)
{
OutputTransferStatus(transfer, "Progress " + progress);
}
private static void OutputTransferStatus(ITFtpTransfer transfer, string message)
{
Console.WriteLine("[" + transfer.Filename + "] " + message);
}
}
}
This was based in the example of another library:
https://github.com/Callisto82/tftp.net/blob/master/Tftp.Net.SampleServer/Program.cs
Edit: I added the full code for reading a file from a TFTP Client.

Assets\mqtthandler.cs(59,26): error CS7069: Reference to type 'IPAddress' claims it is defined in 'System', but it could not be found

I made a MQTT Client using the M2MQTT Library in Unity for Microsoft Hololens but i can't build the Unity project with error "Assets\mqtthandler.cs(59,26): error CS7069: Reference to type 'IPAddress' claims it is defined in 'System', but it could not be found".
I need to build it to deploy it to Hololens Emulator.
In my script mqtthandler.cs the brokerHostname in my code somehow induces this error when trying to create an MQTTClient. Visual Studio sees no error in this script, only when trying to build my project in Unity i geht this error in the Unity console.
If i run my project in Unity itself i can successfully receive mqtt messages.
I've already reimported all assets, cleared Unity cache, regenerated the M2MQTT.NET.dll.
I am using Unity 2018.3 and Visual Studio 2019.
My code is:
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;
using UnityEngine;
// including the M2Mqtt Library
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;
using uPLibrary.Networking.M2Mqtt.Exceptions;
using System.Net;
using System.Net.Sockets;
public class mqtthandler : MonoBehaviour
{
private MqttClient client;
// The connection information
public string brokerHostname = "127.0.0.1";
public int brokerPort = 1883;
public string userName = "test";
public string password = "test";
public static string messageOutput;
public static string topicOutput;
//public TextAsset certificate;
// listen on all the Topic
static string subTopic = "#";
// Start is called before the first frame update
void Start()
{
if (brokerHostname != null && userName != null && password !=null)
{
Debug.Log("connecting to " + brokerHostname + ":" + brokerPort);
Connect();
client.MqttMsgPublishReceived += client_MqttMsgPublishReceived;
byte[] qosLevels = { MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE };
client.Subscribe(new string[] { subTopic }, qosLevels);
}
}
// Update is called once per frame
void Update()
{
}
private void Connect()
{
Debug.Log("about to connect on '" + brokerHostname + "'");
// Forming a certificate based on a TextAsset
/*X509Certificate cert = new X509Certificate();
cert.Import(certificate.bytes);
Debug.Log("Using the certificate '" + cert + "'");*/
client = new MqttClient(brokerHostname/*, brokerPort, false, null, true, cert, null, MqttSslProtocols.TLSv1_0, MyRemoteCertificateValidationCallback*/);
string clientId = Guid.NewGuid().ToString();
Debug.Log("About to connect using '" + userName + "' / '" + password + "'");
try
{
client.Connect(clientId, userName, password);
}
catch (Exception e)
{
Debug.LogError("Connection error: " + e);
}
}
void client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
{
string msg = System.Text.Encoding.UTF8.GetString(e.Message);
Debug.Log("Received message from " + e.Topic + " : " + msg);
messageOutput = msg;
topicOutput = e.Topic;
}
private void Publish(string _topic, string msg)
{
client.Publish(
_topic, Encoding.UTF8.GetBytes(msg),
MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE, false);
}
}
Problem Solved: I had to build the M2MQTT.WinRT.dll and import it into the Assets folder as well. Now i can successfully build the Unity Project.

Unable to start Windows Service if reference is used

I have create a windows service. when i try to start the service, i recieve the error
Windows could not start the Auto Process service on Local Computer.
Error 1053: The service did not respond to the start or control request in a timely fashion.
This is my code
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Timers;
using Drone.Engine; //
namespace Auto
{
public partial class Service1 : ServiceBase
{
static string month = DateTime.Now.ToString("MMM");
private Timer timer = new Timer();
private Dictionary<string, bool> processStatus = new Dictionary<string, bool>();
private Executor worker = new Executor(); //
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
processStatus.Add("start", false);
processStatus.Add("stop", false);
System.Threading.Thread.Sleep(10000);
WriteToFile("Service Started at " + DateTime.Now);
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = 2000;
timer.Enabled = true;
}
private void OnElapsedTime(object sender, ElapsedEventArgs e)
{
foreach (var process in processStatus.Where(v => v.Value == false))
{
WriteToFile(process.Key + " Sync Started at " + DateTime.Now);
ChangeProcessStatus(process.Key, true);
worker.Execute(Executor.sender.Navision, process.Key); //
ChangeProcessStatus(process.Key, false);
WriteToFile(process.Key + " Sync Finished at " + DateTime.Now);
}
}
protected override void OnStop()
{
WriteToFile("Service stopped at " + DateTime.Now);
}
private void ChangeProcessStatus(string key, bool status)
{
processStatus[key] = status;
}
public void WriteToFile(string Message)
{
string path = AppDomain.CurrentDomain.BaseDirectory + "\\data\\logs";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\data\\logs\\ServiceLog_" + DateTime.Now.Date.Day + " " + month + ".log";
if (!File.Exists(filepath))
{
using (StreamWriter sw = File.CreateText(filepath))
{
sw.WriteLine(Message);
}
}
else
{
using (StreamWriter sw = File.AppendText(filepath))
{
sw.WriteLine(Message);
}
}
}
}
}
But if i remove the lines
using Drone.Engine;
The service is starting successfully. Why am i unable to use it in the service?
The dll is present in the same directory. I have used the release build for installing. Still this issue.
Previously i was using filepaths like
filePath=#"data\log";
This was working fine in my application. But when i reference it to my windows service, i was getting System.IO.DirectoryNotFoundException error.
I changed the filepath to
filepath=AppDomain.CurrentDomain.BaseDirectory + "\\data\\logs";
It worked for both my service and application.

Where is the Unity IAP package located?

I'm currently working on an editor script that will ease my transition between freemium and paid versions of my game.I would like to manually import the .unitypackage file that gets imported when I click the import button under Services -> In-App Purchasing.
I am aware of the function AssetDatabase.ImportAsset(path) but I need the path of the package first.
Thanks in advance!
When you enable IAP and click Import, the following will happen:
1.Unity will generate a random file name with
FileUtil.GetUniqueTempPathInProject()
2.A full path will be constructed like this:
<ProjectName>Temp\FileUtil.GetUniqueTempPathInProject()
3.Unity will then add .unitypackage to the end of that random file name.
Now, you have something like:
<ProjectName>Temp\FileUtil.GetUniqueTempPathInProject()+".unitypackage";
4.IAP package will then be downloaded and stored to path from #3.
Illustration of what it looks like:
5.The file is then copied to
<ProjectName>Temp\TarGZ
It is saved with the file name generated from #2. No .unitypackage at the end like #3.
UnityEngine.Cloud.Purchasing
6.After that, Unity imports it with AssetDatabase.ImportPackage not AssetDatabase.ImportAsset(path,false) as mentioned in your question.
Now you can see where Unity IAP package is located but there are just few problems in what you are trying to do:
1.For the IAP package to be present, the import button from the Services Tab must be clicked. I am sure you want this to be automated.
2.The file name is not static therefore making the path hard to retrieve. As you can see from the image above, there are other files in this folder too. We don't know which one is the AIP Package.
3.When you re-start Unity, the temp folder will be deleted. So the downloaded IAP package will be lost.
The best way to get the Unity IAP package is to download it directly from Unity's server then save it to your preferred location. The code below will download the IAP package and store it at: <ProjectName>/UnityEngine.Cloud.Purchasing.unitypackage
It will also import it and then, enable it. For AIP to work, Analytics must be enabled too. This code will also do that.
I tried to hide the AIP url so that it won't be abused and Unity won't have change it.
If you want to remake this into something else, the two most important functions to look at are downloadAndInstallAIP() and deleteAndDisableAIP().
AIPDownloader Code:
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using UnityEditor;
using UnityEditor.Analytics;
using UnityEditor.Purchasing;
using UnityEngine;
[ExecuteInEditMode]
public class AIPDownloader : MonoBehaviour
{
static string projectDirectory = Environment.CurrentDirectory;
static string aipFileName = "UnityEngine.Cloud.Purchasing.unitypackage";
static string etagName = "UnityEngine.Cloud.PurchasingETAG.text";
static string aipfullPath = "";
static string eTagfullPath = "";
static EditorApplication.CallbackFunction doneEvent;
[MenuItem("AIP/Enable AIP")]
public static void downloadAndInstallAIP()
{
//Make AIP fullpath
aipfullPath = null;
aipfullPath = Path.Combine(projectDirectory, aipFileName);
//Make AIP Etag fullpath
eTagfullPath = null;
eTagfullPath = Path.Combine(projectDirectory, etagName);
/*If the AIP File already exist at <ProjectName>/UnityEngine.Cloud.Purchasing.unitypackage,
* there is no need to re-download it.
Re-import the package
*/
if (File.Exists(aipfullPath))
{
Debug.Log("AIP Package already exist. There is no need to re-download it");
if (saveETag(null, true))
{
importAIP(aipfullPath);
return;
}
}
string[] uLink = {
"aHR0cHM=",
"Oi8vcHVibGljLWNkbg==",
"LmNsb3Vk",
"LnVuaXR5M2Q=",
"LmNvbQ==",
"L1VuaXR5RW5naW5l",
"LkNsb3Vk",
"LlB1cmNoYXNpbmc=",
"LnVuaXR5cGFja2FnZQ=="
};
prepare(uLink);
try
{
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(ValidateRemoteCertificate);
WebClient client = new WebClient();
client.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDoneDownloading);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);
client.DownloadFileAsync(new Uri(calc(uLink)), aipfullPath);
}
catch (Exception e)
{
Debug.LogError("Error: " + e.Message);
}
}
[MenuItem("AIP/Disable AIP")]
public static void deleteAndDisableAIP()
{
FileUtil.DeleteFileOrDirectory("Assets/Plugins/UnityPurchasing");
//Disable AIP
PurchasingSettings.enabled = false;
//Disable Analytics
AnalyticsSettings.enabled = false;
}
private static bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
{
return true;
}
public bool isAIPEnabled()
{
return PurchasingSettings.enabled;
}
private static bool saveETag(WebClient client, bool alreadyDownloadedAIP = false)
{
string contents = "";
if (alreadyDownloadedAIP)
{
//Load Etag from file
try
{
contents = File.ReadAllText(eTagfullPath);
return _saveEtag(contents, alreadyDownloadedAIP);
}
catch (Exception e)
{
Debug.LogWarning("File does not exist!: " + e.Message);
}
return false; //Failed
}
else
{
//Load Etag from downloaded WebClient
contents = client.ResponseHeaders.Get("ETag");
return _saveEtag(contents, alreadyDownloadedAIP);
}
}
static bool _saveEtag(string contents, bool alreadyDownloadedAIP = false)
{
if (contents != null)
{
try
{
//Save if not downloaded
if (!alreadyDownloadedAIP)
{
Directory.CreateDirectory(Path.GetDirectoryName(eTagfullPath));
File.WriteAllText(eTagfullPath, contents);
}
//Save to the etag to AIP directory
Directory.CreateDirectory(Path.GetDirectoryName("Assets/Plugins/UnityPurchasing/ETag"));
File.WriteAllText("Assets/Plugins/UnityPurchasing/ETag", contents);
return true;//Success
}
catch (Exception e)
{
Debug.LogWarning("Failed to write to file: " + e.Message);
return false; //Failed
}
}
else
{
return false; //Failed
}
}
public static void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
Debug.Log("Downloading: " + e.ProgressPercentage);
}
public static void OnDoneDownloading(object sender, AsyncCompletedEventArgs args)
{
WebClient wc = (WebClient)sender;
if (wc == null || args.Error != null)
{
Debug.Log("Failed to Download AIP!");
return;
}
Debug.Log("In Download Thread. Done Downloading");
saveETag(wc, false);
doneEvent = null;
doneEvent = new EditorApplication.CallbackFunction(AfterDownLoading);
//Add doneEvent function to call back!
EditorApplication.update = (EditorApplication.CallbackFunction)Delegate.Combine(EditorApplication.update, doneEvent);
}
static void AfterDownLoading()
{
//Remove doneEvent function from call back!
EditorApplication.update = (EditorApplication.CallbackFunction)Delegate.Remove(EditorApplication.update, doneEvent);
Debug.Log("Back to Main Thread. Done Downloading!");
importAIP(aipfullPath);
}
//Import or Install AIP
public static void importAIP(string path)
{
AssetDatabase.ImportPackage(path, false);
Debug.Log("Done Importing AIP package");
//Enable Analytics
AnalyticsSettings.enabled = true;
//Enable AIP
PurchasingSettings.enabled = true;
}
private static void prepare(string[] uLink)
{
for (int i = 5; i < uLink.Length; i++)
{
byte[] textAsBytes = System.Convert.FromBase64String(uLink[i]);
uLink[i] = Encoding.UTF8.GetString(textAsBytes);
}
for (int i = 0; i < uLink.Length; i++)
{
byte[] textAsBytes = System.Convert.FromBase64String(uLink[i]);
uLink[i] = Encoding.UTF8.GetString(textAsBytes);
if (i == 4)
{
break;
}
}
}
private static string calc(string[] uLink)
{
return string.Join("", uLink);
}
}

C# - FTP Upload Using UploadFileAsync With Progress on Console

I'm new to C# and I wanted to do an FTP upload with progress showing on console. My code is as below from some help from MSDN examples.
using System;
using System.Net;
using System.IO;
using System.Threading.Tasks;
namespace FTPUpload
{
class FTPClient
{
public void Upload()
{
string localFile = #"D:\Applications\python-3.3.0.msi";
string destPath = "ftp://127.0.0.1/python-3.3.0.msi";
if (File.Exists(localFile))
{
Console.WriteLine(localFile + " exists!");
}
else
{
Console.WriteLine(localFile + " doesn't exists!");
}
using (WebClient client = new WebClient())
{
client.Credentials = new NetworkCredential("Anonymous", "Anonymous");
client.UploadProgressChanged += new UploadProgressChangedEventHandler(UploadProgressCallback);
client.UploadFileCompleted += new UploadFileCompletedEventHandler(UploadCompleted);
client.UploadFileAsync(new Uri(destPath), localFile);
Console.WriteLine("Upload started");
}
}
public void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e)
{
Console.WriteLine(e.TotalBytesToSend);
}
public void UploadCompleted(object sender, UploadFileCompletedEventArgs e)
{
Console.WriteLine(e.Result);
}
public static void Main(string[] args)
{
FTPClient ftpClient = new FTPClient();
ftpClient.Upload();
}
}
}
I think I have not written it properly and I tried to search in Google can't find any examples with respect to this.
Kindly point me in right direction.
I have seen this but not able to get the point
File is not uploaded to FTP server on some machines when using WebClient.UploadFileAsync
Thanks!

Categories