Unity HostData array issues - unity3d

So my problem is that whenever i try and call the hostData array with a button it returns null, but I know its not null. I even put a debug.log calling the the first member of the HostData array in update() and get when hostData != null it returns every frame but whenever i call it from the button it says its null, i even used if(hostData == null) to verify it. What could possibly cause this?
private HostData[] hostData;
private bool refreshing = false;
private Text joinServerText;
private GameObject joinServerButton;
private GameObject startServerButton;
private GameObject refreshServerButton;
void Awake()
{
joinServerButton = GameObject.Find ("JoinServerButton");
joinServerText = GameObject.Find("JoinServerText").GetComponent<Text>();
startServerButton = GameObject.Find ("StartServerButton");
refreshServerButton = GameObject.Find ("RefreshServerButton");
}
void Start()
{
joinServerButton.SetActive (false);
}
void StartServer()
{
bool useNat = !Network.HavePublicAddress();
Network.InitializeServer(2,25000, !useNat);
MasterServer.RegisterHost(gameName,"Tutorial Game Name","This is a tutorial game");
}
void refreshHostList()
{
MasterServer.RequestHostList(gameName);
refreshing = true;
}
void Update()
{
if (refreshing)
{
if(MasterServer.PollHostList().Length > 0)
{
refreshing = false;
Debug.Log (MasterServer.PollHostList().Length);
hostData = MasterServer.PollHostList();
}
}
if(hostData != null)
{
joinServerButton.SetActive (true);
joinServerText.text = hostData[0].gameName;
Debug.Log ("hostData[0].gameName");
}
}
//Messages
void OnServerInitialized()
{
Debug.Log ("Server Initialized!");
}
void OnMasterServerEvent(MasterServerEvent mse)
{
if(mse == MasterServerEvent.RegistrationSucceeded)
{
Debug.Log ("Registered Server");
}
}
//UI
public void StartSeverButton()
{
Debug.Log ("Starting Server...");
StartServer();
}
public void RefreshHostsButton()
{
Debug.Log ("Refreshing Hosts...");
refreshHostList();
}
public void JoinServerButton()
{
//for(int i=0; i<hostData.Length; i++)
//{
if (hostData == null) <--------- My problem
{
Debug.Log("hostData is null");
//Debug.Log (hostData[0].gameName);
//Network.Connect (hostData[0]);
}
}
}

I usually solve problems of mysterious bad value assignment by replacing this:
private HostData[] hostData;
With this:
private HostData[] _hostData;
private HostData[] hostData { get { return _hostData; } set { _hostData=value; Debug.Log("Host data set. Is null?: "+(_hostData == null), this); } }
Run it, and you'll immediately see when the variable is being set to null. And maybe you'll discover that the problem isn't what you thought--for example, maybe it's never set, and there are actually multiple instances of that class, and the one that prints the host count isn't the same instance that has the null variable.

Related

MVVM AsyncExecute causing lag

AsyncExecute method causing lag in my treeview application when I am expanding a branch.
Important parts of my TreeView
public DirectoryItemViewModel(string fullPath, DirectoryItemType type, long size)
{
this.ExpandCommand = new AsyncCommand(Expand, CanExecute);
this.FullPath = fullPath;
this.Type = type;
this.Size = size;
this.ClearChildren();
}
public bool CanExecute()
{
return !isBusy;
}
public IAsyncCommand ExpandCommand { get; set; }
private async Task Expand()
{
isBusy = true;
if (this.Type == DirectoryItemType.File)
{
return;
}
List<Task<long>> tasks = new();
var children = DirectoryStructure.GetDirectoryContents(this.FullPath);
this.Children = new ObservableCollection<DirectoryItemViewModel>(
children.Select(content => new DirectoryItemViewModel(content.FullPath, content.Type, 0)));
//If I delete the remaining part of code in this method everything works fine,
in my idea it should output the folders without lag, and then start calculating their size in other threads, but it first lags for 1-2 sec, then output the content of the folder, and then start calculating.
foreach (var item in children)
{
if (item.Type == DirectoryItemType.Folder)
{
tasks.Add(Task.Run(() => GetDirectorySize(new DirectoryInfo(item.FullPath))));
}
}
var results = await Task.WhenAll(tasks);
for (int i = 0; i < results.Length; i++)
{
Children[i].Size = results[i];
}
isBusy = false;
}
My command Interface and class
public interface IAsyncCommand : ICommand
{
Task ExecuteAsync();
bool CanExecute();
}
public class AsyncCommand : IAsyncCommand
{
public event EventHandler CanExecuteChanged;
private bool _isExecuting;
private readonly Func<Task> _execute;
private readonly Func<bool> _canExecute;
public AsyncCommand(
Func<Task> execute,
Func<bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute()
{
return !_isExecuting && (_canExecute?.Invoke() ?? true);
}
public async Task ExecuteAsync()
{
if (CanExecute())
{
try
{
_isExecuting = true;
await _execute();
}
finally
{
_isExecuting = false;
}
}
RaiseCanExecuteChanged();
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
bool ICommand.CanExecute(object parameter)
{
return CanExecute();
}
void ICommand.Execute(object parameter)
{
//I suppose that here is the problem cause IDE is hinting me that I am not awaiting here, but I don't know how to change it if it is.
ExecuteAsync();
}
}

how to check if an object is not in the scene

I wanted this code to send a "no" when it doesn't find the object "Player_1" but when it finds it, it sends "yes" in the debug log but when I remove it simply
don't send "no"
{
public GameObject _objeto1;
public GameObject _objeto2;
public float _distancia1;
public
void Start()
{
_distancia1 = 0;
}
void Update()
{
_objeto1 = GameObject.Find("Player_1");
_distancia1 = Vector3.Distance(_objeto1.transform.position, _objeto2.transform.position);
if (_objeto1 == true)
{
Debug.Log("sim");
}
else
{
Debug.Log("nao");
}
}
}
Try this:
public GameObject _objeto1;
public GameObject _objeto2;
public float _distancia1;
void Start()
{
_distancia1 = 0;
}
void Update()
{
_objeto1 = GameObject.Find("Player_1");
if (_object1 != null)
{
_distancia1 = Vector3.Distance(_objeto1.transform.position, _objeto2.transform.position);
}
if (_objeto1 != null)
{
Debug.Log("sim");
}
else
{
Debug.Log("nao");
}
}
Thank you for question!
GameObject.Find() returns GameObject if it's exist on scene and returns "null" when there is no object. So you can use if statment to check if _objeto1 is null.
_objeto1 = GameObject.Find("Player_1");
if (_objeto1 == null)
{
Debug.Log("nao");
}
else
{
Debug.Log("Yes");
}
And don't use '_' for public fields. It's bad practice. You can use it for private fields.

Using admob rewarded ad in unity singleton pattern

I am using admob ads in my app and they are working fine. But when i try to do something after a ad close or reward earned call back my code breaks. Following is my adMob script
public class AdMobScript : MonoBehaviour
{
...
public event Action OnReviveRewardEarned;
public event Action OnReviveAdLoaded;
public event Action OnReviveAdClosed;
private void LoadReviveRewardedAd()
{
reviveRewardedAd = new RewardedAd(adReviveRewardedId);
reviveRewardedAd.OnAdLoaded += ReviveAdLoaded;
reviveRewardedAd.OnUserEarnedReward += ReviveEarnedReward;
reviveRewardedAd.OnAdClosed += ReviveAdClosed;
AdRequest request = new AdRequest.Builder().Build();
reviveRewardedAd.LoadAd(request);
}
private void ReviveAdClosed(object sender, EventArgs e)
{
LoadReviveRewardedAd();
if (isRewardErned)
{
isRewardErned = false;
OnReviveRewardEarned.Invoke();
}
else
OnReviveAdClosed.Invoke();
}
private void ReviveEarnedReward(object sender, Reward e)
{
isRewardErned = true;
}
private void ReviveAdLoaded(object sender, EventArgs e)
{
//reviveButton.interactable = true;
OnReviveAdLoaded.Invoke();
}
public void ShowAdToRevive()
{
if (reviveRewardedAd.IsLoaded())
{
reviveRewardedAd.Show();
}
}
...
}
In the callbacks i am calling my adManager script which is in term using adMob script. Here is the code for it.
public class AdManager : MonoBehaviour
{
...
private void Start() {
AdMobScript.instance.OnReviveAdClosed += ReviveAdClosed;
AdMobScript.instance.OnReviveAdLoaded += ReviveAdLoaded;
AdMobScript.instance.OnReviveRewardEarned += ReviveReward;
}
#region ReviveAds
private void ReviveReward() {
//game crash here
backButton.gameObject.SetActive(true);
reviveButton.gameObject.SetActive(false);
noThanksButton.gameObject.SetActive(false);
manager.Revive();
}
private void ReviveAdLoaded() {
reviveButton.interactable = true;
}
private void ReviveAdClosed() {
//game crash here
reviveButton.interactable = false;
}
public void ShowAdToRevive() {
AdMobScript.instance.ShowAdToRevive();
}
...
}
After either ad close or reward earned my game crashes (error log says
get_gameObject can only be called from the main thread
). There must be something i am doing wrong. Can anyone please point me to the right direction?
The reason for the problem - you trying to manipulate with MonoBehaviors, not in main thread.
Just write simple scheduler which calls the events in the Unity thread, like this:
Scheduler:
using System;
using UnityEngine;
public class Scheduler : MonoBehaviour
{
public static Scheduler instance;
public event Action secondTick = delegate { };
private float seconds = 0;
private void Awake()
{
instance = this;
}
private void Update()
{
seconds += Time.unscaledDeltaTime;
if (seconds >= 1.0f)
{
seconds -= 1.0f;
secondTick.Invoke();
}
}
}
Updated AdMobScript:
public class AdMobScript : MonoBehaviour
{
...
private bool onRewardEarnedCall = false;
private bool onRewardAdLoaded = false;
private bool onRewardAdClosed = false;
public event Action OnReviveRewardEarned;
public event Action OnReviveAdLoaded;
public event Action OnReviveAdClosed;
private void LoadReviveRewardedAd()
{
reviveRewardedAd = new RewardedAd(adReviveRewardedId);
reviveRewardedAd.OnAdLoaded += ReviveAdLoaded;
reviveRewardedAd.OnUserEarnedReward += ReviveEarnedReward;
reviveRewardedAd.OnAdClosed += ReviveAdClosed;
AdRequest request = new AdRequest.Builder().Build();
reviveRewardedAd.LoadAd(request);
Scheduler.instance.secondTick += OnSecondTick;
}
private void OnSecondTick()
{
if(onRewardAdClosed)
{
onRewardAdClosed = false;
OnReviveAdClosed.Invoke();
}
if(onRewardEarnedCall)
{
OnReviveRewardEarned.Invoke();
onRewardEarnedCall = false;
}
if(onRewardAdLoaded)
{
OnReviveAdLoaded.Invoke();
onRewardAdLoaded = false;
}
}
private void ReviveAdClosed(object sender, EventArgs e)
{
LoadReviveRewardedAd();
if (isRewardErned)
{
isRewardErned = false;
onRewardEarnedCall = true;
}
else
onRewardAdClosed = true;
}
private void ReviveEarnedReward(object sender, Reward e)
{
isRewardErned = true;
}
private void ReviveAdLoaded(object sender, EventArgs e)
{
//reviveButton.interactable = true;
onRewardAdLoaded = true;
}
public void ShowAdToRevive()
{
if (reviveRewardedAd.IsLoaded())
{
reviveRewardedAd.Show();
}
}
...
}
This is a very simple and not optimize solution but this will solve your problem.

Unity: Google Play Games crashes on login

I'm trying to save a variable using google play games cloud save. However it crashes when it signs in. I've definitely enabled it on the developer console. I never had this problem before I added the cloud save feature and it was just doing achievements and scoreboards. Also, when I'm not connected to the internet, it doesn't crash and locally saving the data works fine. Can any one help?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using GooglePlayGames.BasicApi.SavedGame;
using System.Text;
public class playgamesscript : MonoBehaviour {
public static playgamesscript Instance { get; private set; }
const string SAVE_NAME = "Test";
bool isSaving;
textEdit textEditScript;
control controlScript;
bool isCloudDataLoaded;
// Use this for initialization
void Start () {
Instance = this;
textEditScript = GameObject.FindGameObjectWithTag("UIControl").GetComponent<textEdit>();
controlScript = GameObject.FindGameObjectWithTag("Control").GetComponent<control>();
if (!PlayerPrefs.HasKey(SAVE_NAME))
PlayerPrefs.SetString(SAVE_NAME, "0");
if (!PlayerPrefs.HasKey("IsFirstTime"))
PlayerPrefs.SetInt("IsFirstTime", 1);
LoadLocal();
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder().EnableSavedGames().Build();
PlayGamesPlatform.InitializeInstance(config);
PlayGamesPlatform.Activate();
if (control.signInAttempt == false)
{
SignIn();
}
}
void SignIn()
{
control.signInAttempt = true;
Social.localUser.Authenticate(success => { LoadData(); });
}
#region Saved Games
string GameDataToString()
{
return control.Highscore.ToString();
}
void StringToGameData(string cloudData, string localData)
{
if (PlayerPrefs.GetInt("IsFirstTime") == 1){
PlayerPrefs.SetInt("IsFirstTime", 0);
if (int.Parse(cloudData) > int.Parse(localData)){
PlayerPrefs.SetString(SAVE_NAME, cloudData);
}
}
else if (int.Parse(localData) > int.Parse(cloudData))
{
control.Highscore = int.Parse(localData);
AddScoreToLoeaderBoard(textEdit.leaderboardStat, control.Highscore);
isCloudDataLoaded = true;
SaveData();
return;
}
control.Highscore = int.Parse(cloudData);
isCloudDataLoaded = true;
}
void StringToGameData (string localData)
{
control.Highscore = int.Parse(localData);
}
void LoadData()
{
if (Social.localUser.authenticated)
{
isSaving = false;
((PlayGamesPlatform)Social.Active).SavedGame.OpenWithManualConflictResolution(SAVE_NAME, DataSource.ReadCacheOrNetwork, true, ResolveConflict, OnSavedGameOpened);
}
else {
LoadLocal();
}
}
private void LoadLocal()
{
StringToGameData(PlayerPrefs.GetString(SAVE_NAME));
}
public void SaveData()
{
if (!isCloudDataLoaded)
{
SaveLocal();
return;
}
if (Social.localUser.authenticated)
{
isSaving = true;
((PlayGamesPlatform)Social.Active).SavedGame.OpenWithManualConflictResolution(SAVE_NAME, DataSource.ReadCacheOrNetwork, true, ResolveConflict, OnSavedGameOpened);
}
else
{
SaveLocal();
}
}
private void SaveLocal()
{
PlayerPrefs.SetString(SAVE_NAME, GameDataToString());
}
private void ResolveConflict(IConflictResolver resolver, ISavedGameMetadata original, byte[] originalData, ISavedGameMetadata unmerged, byte[] unmergedData)
{
if (originalData == null)
{
resolver.ChooseMetadata(unmerged);
} else if (unmergedData == null)
{
resolver.ChooseMetadata(original);
} else
{
string originalStr = Encoding.ASCII.GetString(originalData);
string unmergedStr = Encoding.ASCII.GetString(unmergedData);
int originalNum = int.Parse(originalStr);
int unmergedNum = int.Parse(unmergedStr);
if (originalNum > unmergedNum)
{
resolver.ChooseMetadata(original);
return;
} else if (unmergedNum> originalNum)
{
resolver.ChooseMetadata(unmerged);
}
resolver.ChooseMetadata(original);
}
}
private void OnSavedGameOpened(SavedGameRequestStatus status, ISavedGameMetadata game)
{
if (status == SavedGameRequestStatus.Success)
{
if (!isSaving)
{
LoadGame(game);
} else
{
SaveGame(game);
}
}
else
{
if (!isSaving)
{
LoadLocal();
}else
{
SaveLocal();
}
}
}
private void LoadGame(ISavedGameMetadata game)
{
((PlayGamesPlatform)Social.Active).SavedGame.ReadBinaryData(game, OnSavedGameDataRead);
}
private void SaveGame(ISavedGameMetadata game)
{
string stringToSave = GameDataToString();
PlayerPrefs.SetString(SAVE_NAME, stringToSave);
byte[] dataToSave = Encoding.ASCII.GetBytes(stringToSave);
SavedGameMetadataUpdate update = new SavedGameMetadataUpdate.Builder().Build();
((PlayGamesPlatform)Social.Active).SavedGame.CommitUpdate(game, update, dataToSave, OnSavedGameDataWritten);
}
private void OnSavedGameDataRead(SavedGameRequestStatus status, byte[] savedData)
{
if (status == SavedGameRequestStatus.Success)
{
string cloudDataString;
if (savedData.Length == 0)
{
cloudDataString = "0";
} else
cloudDataString = Encoding.ASCII.GetString(savedData);
string localDataString = PlayerPrefs.GetString(SAVE_NAME);
StringToGameData(cloudDataString, localDataString);
}
}
private void OnSavedGameDataWritten(SavedGameRequestStatus status, ISavedGameMetadata game)
{
}
#endregion /Saved Games
///
JNI DETECTED ERROR IN APPLICATION: can't call void com.google.android.gms.common.api.PendingResult.setResultCallback(com.google.android.gms.common.api.ResultCallback) on null object'

How to make HybridWebView go back when device back button is pressed

I have a Xamarin.Forms app with a HybridWebView and a HybridWebViewRenderer in the Droid project.
I am trying to make the web view navigate back when the device's back button is pressed.
It looks pretty simple if I was just using a Xamarin.Forms WebView within my page. I would just do it like this...
protected override bool OnBackButtonPressed()
{
_webView.GoBack();
return true;
}
But the HybridWebView does not have a GoBack() method.
In my Droid project, the only place where I have access to the WebView is in the HybridWebViewRenderer but I cannot listen for the OnBackButtonPressed event here.
Anyone know how I can make a HybridWebView navigate back when the device's back button is pressed?
Calling window.history.back(); from Javascript might be a dirty solution.
Also, calling the renderer's method from a PCL class should not be a problem.
The PCL class:
public class MyHybridWebView : HybridWebView
{
public event EventHandler<EventArgs> DoSomeNative;
public void CallNative()
{
DoSomeNative(this, EventArgs.Empty);
}
}
The renderer:
public class MyHybridWebViewRenderer : HybridWebViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
{
return;
}
(e.NewElement as MyHybridWebView).DoSomeNative += (sender, args) =>
{
//Do something
//Don't forget to unsubscribe in Dispose
};
}
}
My solution that is working are as follows:
HybridWebView that extends the View:
I created 2 event listeners and 1 bool property.
public event EventHandler<EventArgs> GoBackOnNativeEventListener;
public event EventHandler<EventArgs> CanGoBackOnNativeEventListener;
public bool _CanGoBack { get; set; }
Methods:
public bool GoBack()
{
GoBackOnNative();
return true;
}
private void GoBackOnNative()
{
GoBackOnNativeEventListener(this, EventArgs.Empty);
}
public bool CanGoBack()
{
CanGoBackOnNative();
return _CanGoBack;
}
private void CanGoBackOnNative()
{
CanGoBackOnNativeEventListener(this, EventArgs.Empty);
}
In iOS render
public class WebViewRender : HybridWebViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (Control != null && e.NewElement != null)
{
InitializeCommands((ClickableWebView)e.NewElement);
}
}
private void InitializeCommands(ClickableWebView element)
{
element.RefreshCommand = () =>
{
Control?.Reload();
};
element.GoBackCommand = () =>
{
var ctrl = Control;
if (ctrl == null)
return;
if (ctrl.CanGoBack)
ctrl.GoBack();
};
element.CanGoBackFunction = () =>
{
var ctrl = Control;
if (ctrl == null)
return false;
return ctrl.CanGoBack;
};
element.CanGoForwardFunction = () =>
{
var ctrl = Control;
if (ctrl == null)
return false;
return ctrl.CanGoForward;
};
element.GoFrowardCommand = () =>
{
var ctrl = Control;
if (ctrl == null)
return;
if (ctrl.CanGoForward)
ctrl.GoForward();
};
}
}
On renderer I bind the listener:
e.NewElement.GoBackOnNativeEventListener += (sender, args) =>
{
Control.GoBack();
};
e.NewElement.CanGoBackOnNativeEventListener += (sender, args) =>
{
_cangoback = Control.CanGoBack();
var hybridWebView = Element as HybridWebView;
hybridWebView._CanGoBack = _cangoback;
};
Finally on xaml.cs, on method OnBackButtonPressed(), I do like:
if (hybridWebView.CanGoBack())
{
hybridWebView.GoBack();
return true;
}
else
return base.OnBackButtonPressed();