Application.persistenDataPath not working anymore - c#

Since yesterday evening my Application.persistenDataPath is not working anymore on one place in code. I am using it 2 times. On the first place it works on the second it doesnt work anymore...
Maybe a hint for you is, that I saved json very often yesterday. Also I had infinite loops what maybe caused the Application Settings to Crash.
On Android Build it is working fine. (I get the right Path on both usages)
What I have tried below the code
Both in the same Class.
Unity 2021.3.2f
Using Google Firebase
Visual Studio 2022 Community Edition
This the code where it works:
I am saving a Photo to the given Path.
//then Save To Disk as PNG
byte[] bytes = avatarTexture.EncodeToPNG();
var dirPath = Application.persistentDataPath + "/Resources/User/";
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
if (File.Exists(dirPath + "avatar" + ".png"))
{
File.Delete(dirPath + "avatar" + ".png");
}
File.WriteAllBytes(dirPath + "avatar" + ".png", bytes);
saveUserDataAsJson(getUserToken(user.DisplayName, user.Email));
_warningLoginText.text = "";
_confirmLoginText.text = Translation.authManager_login_notification_loginSucceeded;
This is the code where it does not work:
I am saving a JSON File with User Information.
DataSnapshot snapshotYear = taskYear.Result;
_year = Convert.ToInt32(snapshotYear.Value);
FirebaseDatabaseUser tempUser = new FirebaseDatabaseUser(_token, _day, _month, _year);
var json = JsonConvert.SerializeObject(tempUser);
Debug.Log("Can see it in Console");
//After pressing Play Button in Unity
//Editor i get no Debug outprint here
Debug.Log(Application.persistentDataPath);
var folderPath = Application.persistentDataPath + "/Resources/User/";
Debug.Log("Can not see it in Console");
//HERE THE CODE STOPS AND NOTHING HAPPEN
//NO ERROR NO LOG NO FURTHER CODE WORKING
//HERE NOT WORKING ANYMORE
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
if (File.Exists(folderPath + "userdata" + ".json"))
{
File.Delete(folderPath + "userdata" + ".json");
}
File.WriteAllText(folderPath + "userdata" + ".json", json);
I have tried:
rebuild Project Settings
Print the Path in Console it was empty ""
Cleaned GI Cache
Restarted my PC
Cloned the project new
Tried using Application.dataPath (only for debugging tests, not to use it in my code) also not working in Unity Play Mode but on Android it points right to the APK.
First usage (Place in Code where it works) I get Path:
Computer: C:\Users\{user}\AppData\LocalLow\{company}\{app}\Resources\User\
Android: .\Android\data\com.{company}.{app}\...\Resources\User\
Second usage (Place in Code where does not work) I get Path:
Computer: ""
Android: ".\Android\data\com.{company}.{app}\...\Resources\User\"
I hope someone can explain the issue. I cannot solve the problem from myself. Need someone who has more background knowledge.

so the anwer was based on the Help of #derHugo :
Some of the Unity API is using only the Unity main thread. Application.persistentDataPath is one piece of this Unity API elements.
So the problem was, that in the first code snippet i was using the Unity Main Thread, but in the second code snippet i used a Background Thread. Was really easy to fix. Instead of using another thread, i used:
`<myasyncfunction>.ContinueWithOnMainThread(task => {....
//second code snippet
})`
Refactoring tips of this Code:
File.Exists can be removed, it is totally redundant. WriteAllText creates or overwrites the file by default anyway.
The Construction of data paths over and over again makes no sense. Better way is to create public static readonly paths and use them instead.
Hope the Solution of #derHugo can help someone else.
Have a nice Day if you read this :)

Related

Not able to communicate with "Stockfish-9-armv7" binary file

I am developing a Chess Game in Unity3D. I want to develop it for the Android platform. For AI I am using the Stockfish Chess Engine. I downloaded the Stockfish binary for Android named "Stockfish-9-armv7". I placed this binary file in my StreamingAssets folder so that it correctly goes into the targeted platform during build step. Everything works fine till here i.e. when I build my Unity project the file is placed in the correct location and I can very well see it.
Now in order for my AI to work I have to communicate with this binary file using UCI protocols. And so I wrote a C# script in my Unity project that creates a process to run the binary file and communicate with it. But this is not working.
However when I do the exact same thing for Windows i.e. using the windows binary version of Stockfish named "stockfish_9_x64.exe" and building it as a standalone application, things work perfectly and I am able to communicate with the engine via my C# code.
I researched it online but unable to find much resources and guidance. I found a similar post and reading through it led me conclude that maybe it has something to do with file permissions. The guy who asked this question actually solved the issue by writing these two lines of code:
string[] cmd = { "chmod", "744", Path.Combine(strToFolder, fileName) };
Java.Lang.Runtime.GetRuntime().Exec(cmd);
However he was using Xamarin and had access to Java Runtime library. I am using Unity and C# and I really don't know how to change the execute/run permission of this binary file and run it. In fact I don't even know whether this is the problem or not.
I just want to integrate stockfish into my Unity project with Android as the targeted platform. If anyone has any ideas, suggestions or if anyone has done this before, please guide me. Even if I am wrong from the start and my approach is buggy let me know that as well along with the corrected approach.
Given below is my code:
public class CommunicateWithEngine {
public static Process mProcess;
public static void Communicate()
{
// since the apk file is archived this code retreives the stockfish binary data and
// creates a copy of it in the persistantdatapath location.
string filepath = Application.persistentDataPath + "/" + "Stockfish-9-armv7";
if (!File.Exists(filepath))
{
WWW executable = new WWW("jar:file://" + Application.dataPath + "!/assets/" + "Stockfish-9-armv7");
while (!executable.isDone)
{
}
File.WriteAllBytes(filepath, executable.bytes);
}
// creating the process and communicating with the engine
mProcess = new Process();
ProcessStartInfo si = new ProcessStartInfo()
{
FileName = System.IO.Path.Combine(Application.persistentDataPath, "Stockfish-9-armv7"),
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true
};
mProcess.StartInfo = si;
mProcess.OutputDataReceived += new DataReceivedEventHandler(MProcess_OutputDataReceived);
mProcess.Start();
mProcess.BeginErrorReadLine();
mProcess.BeginOutputReadLine();
SendLine("uci");
SendLine("isready");
}
private static void SendLine(string command) {
mProcess.StandardInput.WriteLine(command);
mProcess.StandardInput.Flush();
}
private static void MProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
string text = e.Data;
Test.PrintStringToTheConsole(text);
}
}
For anyone struggling with the same problem as OP and me. After 12 hours searching and googling here is a solution. You can use executable stockfish binary, but you have to change extension for binary to .so and import it in unity in folder /Assets/Plugins/Android. After build you can find it on device in folder /data/data//lib. Here you can execute it and get input and output.

Reading only a certain part of the Filename in C#

I'm new to this Page just now but already got a Question to ask.
Ok, I'm right now on a bigger Project (for me atleast) for a Server Interface of round about 3 Minecraft Servers on one machine.
Now I got to the point where I no longer want to call the startfiles manually, so i created a function that creates a Savefile with the location of the "servers" startfile (it's a simple batchfile) called "(name_of_server)_SAVEFILE(number_of_server).txt".
And now i want the Program to show me (best on startup) how many Servers actually have been saved by asking for the number talked about earlier.
I also want to implement a lock so the filenumber already created can't be saved with another name, but that's a different story there.
I did it like this:
private void checkForServer_button_Click(object sender, EventArgs e)
{
if (File.Exists(#"C:\Users\" + system_user + #"\Desktop\savedServers\*1.txt") == true)
{
string server1_location = File.ReadAllText(#"C:\Users\" + system_user + #"\Desktop\savedServers\*_SAVEFILE1.txt");
checkForServer_response.Text = "There are Servers!";
onlyInfo.Clear();
onlyInfo.Text = "[CONSOLE] Found existing Server! Found at: " + server1_location;
}
}
onlyInfo is a RichTextBox used as a dummy output atm, might stay in the final version to show what the saved batchfiles look like.
Yes, so basically my code does nothing when I click the button.
And another Question, how do I set the "*" properly for this type of usage.
Thanks in advance!
File.Exists does not support wild characters in the file name. And neither does File.ReadAllText.
string[] files = System.IO.Directory.GetFiles(#"C:\Users\" + system_user +
#"\Desktop\savedServers\", "*1.txt");
if (files.Length > 0)
{
string server1_location = File.ReadAllText(files[0]);
...
}

saving files on IOS from a Unity 3D game

Ok, so I followed the tutorial at unity3d.com (located here)
Just so that you dont have to or want to watch the whole video , here is the save/load functions.
//This one saves/creates the file
void OnDisable()
{
BinaryFormatter binFormatter = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/savedgame.dat");
GameData data = new GameData();
data.coinAmount = coinAmount;
data.upgradeLevel = upgradeLevel;
binFormatter.Serialize(file, data);
file.Close();
}
//And this one loads it
void OnEnable()
{
if (File.Exists(Application.persistentDataPath + "/savedgame.dat"))
{
BinaryFormatter binFormatter = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/savedgame.dat",FileMode.Open);
GameData data = (GameData)binFormatter.Deserialize(file) ;
file.Close();
coinAmount = data.coinAmount;
upgradeLevel = data.upgradeLevel;
}
}
I'm using OnDisable/OnEnable so that my game autosaves every time the users exits. This works on a pc and a mac, but when I build it to the Ipad air2 and its like nothing happens. Xcode trows a exception that says unknown filename(-1).
Thanks in advance for the help
Upon further investigation on android it turns out that the file gets created and the game reads data from it. The problems is here :
data.coinAmount = coinAmount;
data.upgradeLevel = upgradeLevel;
For some reason this does not write the correct data when the app runs on Android(probably the same for IOS).
When I supply a hard coded value i.e : data.coinAmount = 100; the game then reads it correctly.
Upon even further investigation turns out that the code work when you call Application.quit but does not work when we "force close" the app i.e using the ipads front button. Need help with this
Ok, so turns out when we stop a Unity game with the home button on a mobile device the methods OnEnable/OnDisable dont seem to we called or working properly. So my solution to the problem is to migrate the save code to the OnApplicationPause(bool pauseStatus) method. After a few tests this seems to be called every time you press the home screen, thus making sure that the game autosaves.
However this only fixed the issue on android devices. Someone please tells me what happens when a IOS user presses the home button so I can freakin save my game when that happens.
Fixed the issue. After some debugging it turns out that ios and .Net JIT do not like each other. This code fixes the issue:
if (Application.platform == RuntimePlatform.IPhonePlayer)
{
System.Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes");
}
I recommend putting it in the Awake() method.

Can't Access a xml file at random by C# console application

I have a C# console application which creates, parses and deletes multiple xml files at runtime. The application used to run fine in Windows 2003 server with .Net 2.0.
Recently, the Application framework was upgraded to >net 4.0 and the Windows Server OS to Windows 2008 64-bit.
Since then, the application encounters the following exception at random:
Access to the path 'D:\Content\iSDC\GDCOasis\GATE_DATA\LOG\635125008068192773\635125008074911566\SOD\AllRespId.xml' is denied.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.File.Delete(String path)
at ProcessGateFile.SOD.saveFile(String psFile, String psXMLString, Boolean isNonAscii)
The code for the creation, parsing and deletion is as follows:
saveFile(tmpPath + "\\SOD\\AllRespId.xml", "<?xml version= \"1.0\" ?><XML>" + sbldDistinctResp.ToString() + "</XML>", isChinese);
//Save list of Distinct responsibilities for User
sbldDistinctResp.Remove(0, sbldDistinctResp.Length);
xmlCase.Load(tmpPath + "\\SOD\\AllRespId.xml");
arrResps.Clear();
//Start preparing Responsibility selection criteria
RespNodes = xmlCase.SelectNodes("//row");
sRespCriteria = "";
if (RespNodes.Count > 0)
{
foreach (XmlNode RespNode in RespNodes)
{
string RespName = RespNode.Attributes.GetNamedItem("RespId").Value.ToString();
if (!arrResps.Contains(RespName))
{
arrResps.Add(RespName);
}
}
for (int i = 0; i < arrResps.Count; i++)
{
sbldDistinctResp.Append("(#RespId = '" + arrResps[i].ToString() + "') or ");
}
sbldDistinctResp.Remove(sbldDistinctResp.Length - 4, 4);
sRespCriteria = sbldDistinctResp.ToString();
if (!sRespCriteria.Equals(""))
{
sRespCriteria = "(" + sRespCriteria + ")";
}
}
File.Delete(tmpPath + "\\SOD\\AllRespId.xml");
I repeat, the error is happening at random, i.e. it works at times and does not at other times during the same process.
Any idea what might be causing this and how to resolve?
Just a couple of observations:
Why are you saving and then immediately loading the file again? In fact, why do you even need to save this file - you already have all the information you need in the sbldDistinctResp variable to generate the XML you need to work with (as evidenced by the saveFile call at the start of the code) - couldn't you just make a copy of it, surround it with the same XML as you did during saveFile, and work with that?
"It happens randomly" is a very subjective observation :). You should profile this (run it 10,000 times in a loop for example) and record the pattern of errors. You may well be surprised that what seems random at first actually shows a clear pattern over a large number of runs. This may help you to make a connection between the problem and some other apparently unrelated event on the server; or it may confirm that it truly is random and therefore outside of your control.
If you really can't find the problem and you go with the idea of anti-virus, etc, then you could wrap the loading code in a try/catch and re-try a couple of times if you get the error. It's hacky but it would work, assuming you have accepted that the initial error is beyond your control.

Playing Audio with pipeline in Gstreamer (C#)

I've been struggling with GStreamer for a while because I can't find any C# examples/tutorials.
As far as I know, Gstreamer uses pipelines in order to decode and then be able to send, for instance a song, to the speakers, but I tried the following, which didn't work:
Gst.Element pipeline;
string path = #"some_path.mp3";
string command = "filesrc location=" + path + " ! oggdemux ! vorbisdec ! audioconvert ! gconfaudiosink";
pipeline = Gst.Parse.Launch(command);
pipeline.SetState(Gst.State.Playing);
However, it raises an exception in the Gst.Parse.Launch line
Does anyone know any good application example, and/or can actually post some code, so I can start getting used to the library? Also, if you can tell me what's wrong on the code above, I'd be thankful
Without further ado,
Regards
Just change your command string to "filesrc location=" + path + " ! decodebin2 ! gconfaudiosink", that should work.
On a side note you should use the gst-launch tool on the command line to check if your pipeline is working and to debug it. Also use gst-inspect to find which plugins are available on your system and what is their functionality.

Categories