Attempting to load AssetReference that has already been loaded - c#

When I start the main scene and test a new character it shows this error why?
Attempting to load AssetReference that has already been loaded. Handle is exposed through getter OperationHandle
UnityEngine.AddressableAssets.AssetReference:LoadAssetAsync<UnityEngine.GameObject> ()
TrackManager/<SpawnFromAssetReference>d__104:MoveNext () (at Assets/Scripts/Tracks/TrackManager.cs:565)
UnityEngine.MonoBehaviour:StartCoroutine (System.Collections.IEnumerator)
TrackManager:SpawnObstacle (TrackSegment) (at Assets/Scripts/Tracks/TrackManager.cs:556)
TrackManager/<SpawnNewSegment>d__102:MoveNext () (at Assets/Scripts/Tracks/TrackManager.cs:538)
UnityEngine.SetupCoroutine:InvokeMoveNext (System.Collections.IEnumerator,intptr)
my code :
if (m_SafeSegementLeft <= 0)
{
SpawnObstacle(newSegment);
}
else
m_SafeSegementLeft -= 1;
m_Segments.Add(newSegment);
if (newSegmentCreated != null) newSegmentCreated.Invoke(newSegment);
}
public void SpawnObstacle(TrackSegment segment)
{
if (segment.possibleObstacles.Length != 0)
{
for (int i = 0; i < segment.obstaclePositions.Length; ++i)
{
AssetReference assetRef = segment.possibleObstacles[Random.Range(0, segment.possibleObstacles.Length)];
StartCoroutine(SpawnFromAssetReference(assetRef, segment, i));
}
}
StartCoroutine(SpawnCoinAndPowerup(segment));
}
private IEnumerator SpawnFromAssetReference(AssetReference reference, TrackSegment segment, int posIndex)
{
AsyncOperationHandle op = reference.LoadAssetAsync<GameObject>();
yield return op;
GameObject obj = op.Result as GameObject;
if (obj != null)
{
Obstacle obstacle = obj.GetComponent<Obstacle>();
if (obstacle != null)
yield return obstacle.Spawn(segment, segment.obstaclePositions[posIndex]);
}
}
It says i have error in line 565 which is AsyncOperationHandle op = reference.LoadAssetAsync<GameObject>();
What is the error here?

The error message sounds quite self-explanatory: You try to load the same addressable twice.
As said in
AssetReference assetRef = segment.possibleObstacles[Random.Range(0, segment.possibleObstacles.Length)];
you pick a random entry from available addressables. However, nothing here prevents that you get casually the same element twice.
I would rather either keep track of which ones already are loaded
Dictionary<AssetReference, GameObject> loadedAssets = new Dictionary<AssetReference, GameObject>();
and then do
private IEnumerator SpawnFromAssetReference(AssetReference reference, TrackSegment segment, int posIndex)
{
if(!loadedAssets.TryGetValue(reference, out var obj || !obj)
{
loadedAssets.Add(reference, null);
AsyncOperationHandle op = reference.LoadAssetAsync<GameObject>();
yield return op;
obj = op.Result;
loadedAssets[reference] = obj;
}
if(!obj.TryGetComponent<Obstacle>(out var obstacle))
{
Debug.LogError($"No {nameof(Obstacle)} component on loaded object!");
yield break;
}
yield return obstacle.Spawn(segment, segment.obstaclePositions[posIndex]);
}
And then of course whenever you Release one of the loaded assets you also want to
loadedAssets.Remove(reference);
Or depending on your use case and needs load them all and then start your app if you are going to spawn them more often anyway.

Following derHugo approach of storing values I came up with this to always have the addressable result ready to be returned as a gameobject but prevent it to be loaded more than once, addressables also has a 'address.LoadAssetAsync().WaitForCompletion()' method but in my case when loading many things it gets too laggy.
private async Task<T> GetNewInstance<T>(AssetReferenceGameObject address) where T : MonoBehaviour
{
if (!loadedAssetsTask.ContainsKey(address))
{
LoadNewAddress<T>(address);
}
var asset = await loadedAssetsTask[address];
var newGameObject = Instantiate(asset);
var component = newGameObject.GetComponent<T>();
return component;
}
private void LoadNewAddress<T>(AssetReferenceGameObject address) where T : MonoBehaviour
{
var loadedAssetTask = address.LoadAssetAsync().Task;
loadedAssetsTask.Add(address, loadedAssetTask);
}

A little bit late, but after some dig up I found the explaination from Unity_Bill:
AssetReference.LoadAssetAsync() is a helper we've provided that you in
no way need to use when loading an AssetReference.
Addressables.LoadAssetAsync(AssetReference) will work just as well. If
you do the latter, the async operation handle is returned to you and
you are in charge of it. You can call that as many times as you want,
and get as many handles as you want. Each will be ref-counted.
If you choose to use the AssetReference.LoadAssetAsync() helper, the
asset reference itself will hold on to the handle. This enabled the
helper method AssetReference.ReleaseAsset(). Prior to 1.15.1, if you
called load twice, the first handle would simply get stomped. If you
happened to keep up with it, great, but if not, it was lost forever.
So, in short, AssetReference.LoadAssetAsync() is a convenience helper
that really only works in the most simple case. If you are doing
anything beyond the simple, and are keeping up with the handles
yourself, just use Addr.Load... If I were starting addressables over I
likely wouldn't have the helpers at all, requiring the
Addressables.Load..(AssetRef) be used instead.
TLDR: Use Addressables.LoadAssetAsync(AssetReference) instead of AssetReference.LoadAssetAsync
You can read more here: https://forum.unity.com/threads/1-15-1-assetreference-not-allow-loadassetasync-twice.959910/

Related

Why does my app crash when a button is clicked?

I am trying to make a friends function into my Unity game. Each friend will have their own line with their name and a few buttons (challenge, about, etc.).
I have a friend row prefab and I instantiate it into the parent list for each friend.
It works just fine, until I click the challenge button, which whould call a method that takes in two parameters: the UId of the friend, and their username (two strings).
I am using Firebase Realtime Database for database.
void RetrieveFriendList(object sender, ValueChangedEventArgs args) {
foreach(Transform childTransform in listParent.GetComponentInChildren<Transform>()) {
GameObject.Destroy(childTransform.gameObject);
}
friends.Clear();
foreach (DataSnapshot s in args.Snapshot.Children) {
friends.Add(s.Key);
GameObject newRow = Instantiate(friendRowPrefab);
newRow.transform.Find("Deny").gameObject.GetComponent<Button>().onClick.RemoveAllListeners();
newRow.transform.Find("Challenge").gameObject.GetComponent<Button>().onClick.RemoveAllListeners();
newRow.transform.SetParent(listParent.transform);
newRow.transform.localScale = new Vector3(1f, 1f, 1f);
newRow.transform.Find("Text_Name").gameObject.GetComponent<TMPro.TMP_Text>().text = s.Child("username").Value.ToString();
string retrievedStatus = s.Child("type").Value.ToString();
if (retrievedStatus == "sent") {
newRow.transform.Find("Status").gameObject.GetComponent<TMPro.TMP_Text>().text = "Friend request sent";
} else if (retrievedStatus == "request") {
newRow.transform.Find("Status").gameObject.GetComponent<TMPro.TMP_Text>().text = "Incoming friend request";
newRow.transform.Find("Accept").gameObject.SetActive(true);
newRow.transform.Find("Deny").gameObject.SetActive(true);
newRow.transform.Find("Accept").gameObject.GetComponent<Button>().onClick.AddListener(delegate { AcceptFriendRequest(s.Key); });
newRow.transform.Find("Deny").gameObject.GetComponent<Button>().onClick.AddListener(delegate { DenyFriendRequest(s.Key); });
} else if (retrievedStatus == "friends") {
newRow.transform.Find("Challenge").gameObject.SetActive(true);
Debug.Log(s.Key + " - " + s.Child("username").Value.ToString());
newRow.transform.Find("Challenge").gameObject.GetComponent<Button>().onClick.AddListener(delegate { ChallengeFriend(s.Key, s.Child("username").Value.ToString()); }); //this is the line that causes the crash
newRow.transform.Find("About").gameObject.SetActive(true);
newRow.transform.Find("Status").gameObject.SetActive(false);
}
}
FirebaseDatabase.DefaultInstance.GetReference("users").Child(auth.CurrentUser.UserId).Child("friends").ValueChanged -= RetrieveFriendList;
}
What's likely happening is that the underlying C++ representation of your database snapshot is being cleaned up before your button accesses it. See this bug.
The easiest thing to do would be to find this line:
newRow.transform.Find("Challenge").gameObject.GetComponent<Button>().onClick.AddListener(delegate { ChallengeFriend(s.Key, s.Child("username").Value.ToString()); }); //this is the line that causes the crash
and turn it into something like:
var challengeKey = s.Key;
var challengeUsername = s.Child("username").Value.ToString();
newRow.transform.Find("Challenge").gameObject.GetComponent<Button>().onClick.AddListener(delegate { ChallengeFriend(challengeKey, challengeUsername); });
This way you retrieve the values you need (key and username) at the time of the callback rather than in the context of the button click (at some arbitrary point in the future after this function has returned). If you still get a crash, you may have to .Clone or .CopyTo the data -- but I believe that once an object is retrieved from the underlying snapshot it should be a full on C# object obeying the expected C# GC rules.
You also may experience null reference exceptions if the snapshot hits a local cache first -- so make sure you have null checks around everything (generally a good practice whenever you're hitting the web).

Unexpected behavior when using Parallel.Foreach

I have this code:
// positions is a List<Position>
Parallel.ForEach(positions, (position) =>
{
DeterminePostPieceIsVisited(position, postPieces);
});
private void DeterminePostPieceIsVisited(Position position, IEnumerable<Postpieces> postPieces)
{
foreach (var postPiece in postPieces)
{
if (postPiece.Deliverd)
continue;
var distanceToClosestPosition = postPiece.GPS.Distance(position.GPS);
postPiece.Deliverd = distanceToClosestPosition.HasValue && IsInRadius(distanceToClosestPosition.Value);
}
}
}
I know that 50 post pieces must have the property Deliverd set to true. But, when running this code, I get changing results. Sometimes I get 44, when I run it another time I get 47. The results are per execution different.
When I run this code using a plain foreach-loop I get the expected result. So I know my implementation of the method DeterminePostPieceIsVisited is correct.
Could someone explain to me why using the Parallel foreach gives me different results each time I execute this code?
You've already, I think, tried to avoid a race, but there is still one - if two threads are examining the same postPiece at the same time, they may both observe that Deliverd (sic) is false, and then both assess whether it's been delivered to position (a distinct value for each thread) and both attempt to set a value for Deliverd - and often, I would guess, one of them will be trying to set it to false. Simple fix:
private void DeterminePostPieceIsVisited(Position position, IEnumerable<Postpieces> postPieces)
{
foreach (var postPiece in postPieces)
{
if (postPiece.Deliverd)
continue;
var distanceToClosestPosition = postPiece.GPS.Distance(position.GPS);
var delivered = distanceToClosestPosition.HasValue && IsInRadius(distanceToClosestPosition.Value);
if(delivered)
postPiece.Deliverd = true;
}
}
Also, by the way:
When I run this code using a plain foreach-loop I get the expected result. So I know my implementation of the method DeterminePostPieceIsVisited is correct.
The correct thing to state is would be "I know my implementation is correct for single threaded access" - what you hadn't established is that the method was safe for calling from multiple threads.
I have solved my issue with ConcurrentBag<T>. Here's what I use now:
var concurrentPostPiecesList = new ConcurrentBag<Postpiece>(postPieces);
Parallel.ForEach(positions, (position) =>
{
DeterminePostPieceIsVisited(position, concurrentPostPiecesList);
});
private void DeterminePostPieceIsVisited(Position position, ConcurrentBag<Postpieces> postPieces)
{
foreach (var postPiece in postPieces)
{
if (postPiece.Deliverd)
continue;
var distanceToClosestPosition = postPiece.GPS.Distance(position.GPS);
postPiece.Deliverd = distanceToClosestPosition.HasValue && IsInRadius(distanceToClosestPosition.Value);
}
}

GET Request dont save data

I'm trying to save some data from a GET request. I use StartCoroutine to request and I use Lambda expression for save the data.
My Code is this:
Using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour {
// Use this for initialization
public void Start () {
string url1 = "http://localhost/virtualTV/query/?risorsa=";
string ciao = "http://desktop-pqb3a65:8080/marmotta/resource/ef299b79-35f2-4942-a33b-7e4d7b7cbfb5";
url1 = url1 + ciao;
WWW www1 = new WWW(url1);
var main=new JSONObject(JSONObject.Type.OBJECT);
var final= new JSONObject(JSONObject.Type.OBJECT);;
StartCoroutine(firstParsing((value)=>{main = value;
final= main.Copy();
Debug.Log(main);
}));
Debug.Log(final);
}
public IEnumerator firstParsing( System.Action<JSONObject> callback)
{
string url2 = "http://localhost/virtualTV/FirstQuery/?risorsa=";
string ciao = "http://desktop-pqb3a65:8080/marmotta/resource/ef299b79-35f2-4942-a33b-7e4d7b7cbfb5";
url2 = url2 + ciao;
WWW www2 = new WWW(url2);
yield return www2;
string json = www2.text;
//Parsing del json con creazione di un array
var firstjson = new JSONObject(json);
var tempVideo = new JSONObject(JSONObject.Type.OBJECT);
var array2 = new JSONObject(JSONObject.Type.OBJECT);
tempVideo.AddField ("id", firstjson.GetField ("id"));
tempVideo.AddField ("type", firstjson.GetField ("type"));
tempVideo.AddField ("url", firstjson.GetField ("url"));
array2.Add (tempVideo);
yield return array2;
callback (array2);
Debug.Log ("First Run" + array2);
}
When I try to use FINAL after the command,
final=main.copy()
it is empty. Can you help me to save the value in the variable final? Thanks all.
A coroutine's execution is spread across many frames. When a coroutine encounters a yield return statement, it returns to the calling method, which finishes executing, till the task finishes.
In your case, the Debug.Log(final) statement in Start executes as soon as yield return www2; in firstParsing is executed. The callback hasn't been called yet which is why final is empty.
To be able to access the value in final after it has been assigned outside the callback function, you will have to set a bool which is set to true after final is assigned in the callback. Something like this:
StartCoroutine(firstParsing((value)=>{main = value;
final= main.Copy();
Debug.Log(main);
isFinalAssigned = true;
}));
// In another method
if(isFinalAssigned)
{
// Access final
}
You will have to note that the above if statement is useful only in a method that is called periodically like Update. If you're accessing final in a method that is called only once (like OnEnable) you will have to wait for final to be assigned. You can use another coroutine for this task like
IEnumerator DoSomethingWithFinal()
{
while(!isFinalAssigned)
yield return null; // Wait for next frame
// Do something with final
}
The easiest way out is to consume (access) final in your callback.
EDIT2: From your comments, you can do something like the following. You will have to use coroutines, because blocking the main game thread is not a good idea.
private JSONObject final = null; // Make final a field
Wherever you use final, you have two options.
Use a null check if(final == null) return; This can be impractical.
Wait for final to be assigned in a coroutine and do something as a callback. This is the only way you can do what you want cleanly.
Look below for the implementation.
// Calls callback after final has been assigned
IEnumerator WaitForFinal(System.Action callback)
{
while(final == null)
yield return null; // Wait for next frame
callback();
}
// This whole method depends on final.
// This should be similar to your method set up if you have
// good coding standards (not very long methods, each method does only 1 thing)
void MethodThatUsesFinal()
{
if (final == null)
{
// Waits till final is assigned and calls this method again
StartCoroutine(WaitForFinal(MethodThatUsesFinal));
return;
}
// use final
}

Waiting for a frame to load using Selenium

I have seen many posts on handling switching between frames in Selenium but they all seem to reference the Java 'ExpectedConditions' library for the below method.
ExpectedConditions.frameToBeAvailableAndSwitchToIt
I was wondering if there is any C# implementation anywhere or if anyone has any such work around?
Cheers
There isn't a direct equivalent in the C# bindings but it's very easy to do this yourself.
Remember that Selenium is open source so let's dig out the source code. Here is the Java ExpectedConditions and here is the C# set.
So what's the Java version doing? Well, not a lot I tell you.
try {
return driver.switchTo().frame(frameLocator);
} catch (NoSuchFrameException e) {
return null;
}
All it's doing is attempting to switch to the frame you tell it to, and providing it was successful (as in, there was no exception in attempting to do that), then it's assumed it can carry on.
So, all you'll need to do is do the same thing in C#, so something like (not compiled):
public static Func<IWebDriver, bool> WaitUntilFrameLoadedAndSwitchToIt(By byToFindFrame)
{
return (driver) =>
{
try
{
return driver.SwitchTo().Frame(driver.FindElement(byToFindFrame));
}
catch (Exception)
{
return null;
}
return true;
};
}
As in, keep the same concept: try to find the frame and switch to it, any exceptions then we return null and force the caller (usually a WebDriverWait instance) to iterate through again. Returning true will tell the caller that we are happy we can move on.
All the waiting & expected conditions classes live in the OpenQA.Selenium.Support.UI namespace which lives in the WebDriver.Support.dll assembly.
These answers are old and I had the same issue. I was able to use SeleniumExtras.WaitHelpers.ExpectedConditions from nuget to achieve this easily.
//wait for 10 seconds max for the frame
WebDriverWaitwait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.FrameToBeAvailableAndSwitchToIt(By.Id("FRAMEID")));
I have just committed such a ugly code. Dropping here for the future!
protected void SwitchToFrame(int iframe = 1)
{
var driver = GetWebDriver();
driver.SwitchTo().DefaultContent();
bool done = false, timeout = false;
int counter = 0;
do
{
counter++;
try
{
driver.SwitchTo().Frame(iframe);
done = true;
}
catch (OpenQA.Selenium.NoSuchFrameException)
{
if (counter <= Constants.GLOBAL_MAX_WAIT_SEC)
{
Wait(1);
continue;
}
else timeout = true;
}
} while (!done && !timeout);
if (timeout) throw new OpenQA.Selenium.NoSuchFrameException(iframe.ToString());
}

I want to create a global array in order to listen for events

I have a coroutine that is invoked several times on my scene.
IEnumerator Save_assets(string file, int i, string name){
var filename = Path.GetFileName(file);
docPath = Application.streamingAssetsPath+"/files/";
var temp_name = docPath+filename;
downloaded_asset = false;
if(!Directory.Exists(docPath)){
Directory.CreateDirectory(docPath);
}
if (!System.IO.File.Exists(temp_name)){
WWW www = new WWW(file);
yield return www;
//Save the image
System.IO.File.WriteAllBytes(temp_name, www.bytes);
}
/* I really would like to have a sort of listener here doing something like:
//pseudocode
while(global.file != true){ //while not done
yield return null;
}
*/
downloaded_asset = true;
finished = false;
tries = 0;
go = GameObject.Find(name);
go.gameObject.BroadcastMessage("paint_file", temp_name);
}
Once paint_file has been invoked the Update function on that very class
is constantly looking for a certain condition to happen, let's say it's "done = true;"
void paint_file(file){
[...]//code
}
void Update () {
var done = true;//let's pretend there's no code here, just done = true
if(done){
Debug.Log("Finished: "+paint_file._filename);
}
}
I have no idea how to set this var global.file = done,
any help would be greatly appreciated
Avoid ever using globals ( statics or singletons ) use direct callbacks instead or hook stuff up with component/object references
Also avoid Find, and broadcastMessage ideally as they are slow :-)
instead have:
var SomeComponentType myReferenceToThatComponent;
on Awake(){
if(myReferenceToThatComponent==null){
myReferenceToThatComponent = GetComponent<SomeComponentType>();
}
}
Once you have a reference to a component you can access any public items on it eg:
myReferenceToThatComponent.done = true;
myReferenceToThatComponent.SomeMethod( true );
benefit is you can set that reference in the Editor, but if you don't set it leaving it null, it goes and tries to find it, and it only finds it once, any further use will uses the cached result and not have to use an expensive search again.
Your www should be surrounded with a 'using' as is the common practice to make sure it garbage collects when done
using ( WWW www = new WWW( url, form ) ) {
yield return www;
}
The myriad of approaches to finding and getting components and objects is beyond the scope of this answer I think
Just remember Unity is preferably Component based which is quite different to the typical OOP Inheritance/Interface style of C# coding.
-
Way to structure Coroutines to run one after another waiting for one to complete:
void Start() {
StartCoroutine( BaseRoutine() );
}
IEnumerator BaseRoutine() {
for ( int i = 0; i < 10; i++ ) {
yield return StartCoroutine( ChildRoutine( i ) );
}
}
IEnumerator ChildRoutine( int i ) {
Debug.Log( "ChildRoutine number: " + i );
yield return new WaitForSeconds( 1 ); // Put your yield return WWW here for example
}

Categories