There are two buttons in my program,
I get the file with bin extension from the address given with the get button. The operation was successful. When I press the test button, it says "test" on the console.
That is all.
However, when I press the get button, I cannot press the test button until the get request is completed. This is my problem.
What I want is that the get request continues in the background and I can click on other buttons.
What would you recommend me to do?
Get Button function :
public void get()
{
StartCoroutine(Get());
}
private IEnumerator Get()
{
string Url = "http://my/local/server/file.bin";
Debug.Log(Url);
using (UnityWebRequest unityWebRequest = UnityWebRequest.Get(Url))
{
yield return unityWebRequest.SendWebRequest();
if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
{
Debug.Log("error");
}
else
{
byte[] results = unityWebRequest.downloadHandler.data;
Debug.Log("Received: " + unityWebRequest.downloadHandler.text);
Debug.Log("Size: " + results.Length ); }
}
}
and simple test button :
public void print()
{
Debug.Log("test");
}
As I said, both work separately. What I want is that the Test button is not blocked while doing the getting file from HTTP server.
(btw file size is 100mb)
Just glancing through the documentation, it looks like the lockup may be happening when you call:
byte[] results = unityWebRequest.downloadHandler.data;
The getter on data for the default downloadHandler appears to run a protected method to actually download the content. That download runs on the main thread by default, and is likely what's locking up your UI.
You could confirm this by adding log statements:
Debug.Log("Before get data");
byte[] results = unityWebRequest.downloadHandler.data;
Debug.Log("After get data");
and checking if there's a substantial delay between the two log statements in which the UI is frozen.
If that's the case, it looks like you should be able to attach a custom downloadHandler to run on a background thread and not block the UI thread.
DownloadHandler.GetData
DownloadHandler
To avoid using a custom downloadHandler, you could insert the following
while (!unityWebRequest.downloadHandler.isDone)
yield return null;
byte[] results = unityWebRequest.downloadHandler.data;
The problem may be the Debug.Log you are trying to do. You can delete this line below and try it.
Debug.Log ("Received:" + unityWebRequest.downloadHandler.text);
Related
private fun shareOperation(file: File) {
val uri = Uri.fromFile(file)
val storage = FirebaseStorage.getInstance()
val pdfRef = storage.reference.child("pdf/${uri.lastPathSegment}")
pdfRef.putFile(uri).addOnFailureListener { e ->
Log.e(TAG, "Couldn't share " + e.message)
}.addOnCompleteListener{
it.addOnCompleteListener {
pdfRef.downloadUrl.addOnSuccessListener { e ->
run {
link = e.toString()
Log.i(TAG,link!!) // Here i get the link to file in firebase storage
}
}
}
}
// Here link gets null
}
i was expecting somehow i can get the link to the file and can use it for sharing intent
You are performing an asynchronous call to upload the file, that is correct since any UI blocking action must be performed in background. The variable link will be null until the run code is executed in the background thread.
You need to code inside the run block whatever you want to happen when the link is available.
BTW looks weird what you are doing with the nested addOnCompleteListener, there should be an easier way to code that. You should probably spend time learning how to code with listeners and background threads.
My situation is the following:
I developed a game, where people can save their progress via google cloud. I'm releasing big content-updates, resulting in many people returning to my game at the same time, trying to get their savegame.
This overload causing my customers to get stucked in the savegame-loading process - not able to start playing with their progress.
Here's an updated 4 days screenshot of the cloud-api-dashboard
(And here's the old "12 hours" screenshot of the cloud-api-dashboard)
more informations about the Project:
The game keeps using the "save in cloud"-function in the background on some stages of the game to provide players with the functionality to play on two diffrent devices.
I'm using Unity 2019.3.9f1 and the Asset "Easy Mobile Pro 2.17.3" for the Game-Service-Feature.
The "Google Play Games Plugin" has the version "0.10.12" and can be found on github
more informations about the Cloud-Situation:
The OAuth "user type" is "External" (and can't be changed)
The OAuth user cap display shows "0/100" for the user-cap
And The OAuth rate limits is displaying this for the token-grant-rate (highest "Average Token Grant Rate" is 3,33 of 10.000 as limit)
All used quotas are within the limit. The project reaches
1/10 of "queries per day" (1.000.000.000 max) and
1/2 of "queries per 100 sec" (20.000 max).
more informations about the Error-Trace in the cloud-API:
On my search for a better Error-Log I tried to find “Cloud Logging”-tools in the “Google Cloud Platform”-console. But every section i tried won’t display anything:
“Logging” (Operations tool) is empty
“Cloud Logging API” says: “no data available for the selected time frame.”
“Cloud Debugger API” says: “no data available for the selected time frame.”
I can't find a more detailed variant of the errors as this (the "Metrics"-Section in the "Google Drive API"):
Is there anything I miss to get a better insight?
more informations about the Core-Code
As I mentioned, I’m using “EasyMobilePro”, so I have one “SaveGame”-Var and 8 calls for google and apple as well. I already contacted their support: They assured me that those calls are unchangeable & kind of rock solid (so it can’t be caused from their code) and I should try to contact google if the problem is not on my side.
The 5 calls from EasyMobile for cloudsave are:
bool “GameServices.IsInitialized()”
void “GameServices.OpenWithAutomaticConflictResolution”
void “GameServices.WriteSavedGameData”
void “GameServices.ReadSavedGameData”
void “GameServices.DeleteSavedGame”
The 3 calls from EasyMobile for cloud-login are:
void “GameServices.Init()”
delegate “GameServices.UserLoginSucceeded”
delegate “GameServices.UserLoginFailed”
The Process, that causes the Issue:
I call “GameService.Init()”, the user logs in (no problem)
On that “LoginSuccess”-Callback I call my Function “HandleFirstCloudOpening”:
//This Method is Called, after the player Pressed "Save/ Load" on the StartScreen
//The button is disabled imidiately (and will be re-enabled if an error/fail happens)
public void TryCallUserLogin() {
if (!IsLoginInit) {
EasyMobile.GameServices.UserLoginFailed += HandleLoginFail;
EasyMobile.GameServices.UserLoginSucceeded += HandleFirstCloudOpening;
IsLoginInit = true;
}
if (!IsGameServiceInitialized) {
EasyMobile.GameServices.Init();
} else { //This "else" is only be called, if the "Init" was successfull, but the player don't have a connected savegame
HandleFirstCloudOpening();
}
}
private void HandleLoginFail() {
//(...) Show ErrorPopup, let the player try to login again
}
private void HandleFirstCloudOpening() {
if (currentSaveState != CloudSaveState.NONE) {
CloudStateConflictDebug(CloudSaveState.OPENING);
return;
}
currentSaveState = CloudSaveState.OPENING;
EasyMobile.GameServices.SavedGames.OpenWithAutomaticConflictResolution(cloudSaveNameReference, UseFirstTimeOpenedSavedGame);
}
private void UseFirstTimeOpenedSavedGame(EasyMobile.SavedGame _savedGame, string _error) {
currentSaveState = CloudSaveState.NONE;
if (string.IsNullOrEmpty(_error)) {
cloudSaveGame = _savedGame;
ReadDataFromCloud(cloudSaveGame);
} else {
ErrorPopupWithCloseButton("cloud_open", "failed with error: " + _error);
}
}
private void ReadDataFromCloud(EasyMobile.SavedGame _savedGame) {
if (_savedGame.IsOpen) {
currentSaveState = CloudSaveState.LOADING;
EasyMobile.GameServices.SavedGames.ReadSavedGameData(_savedGame, UseSucessfullLoadedCloudSaveGame);
} else { //backup function if the fresh-opened savegame is "closed" for some reason (can happen later while "saving" ingame)
HandleFirstCloudOpening();
}
}
private void UseSucessfullLoadedCloudSaveGame(EasyMobile.SavedGame _game, byte[] _cloudData, string error) {
if (!string.IsNullOrEmpty(error)) {
ErrorPopupWithCloseButton("cloud_read", "Reading saved game data failed: " + error);
return;
}
if (_cloudData.Length > 0) {
//A function, that converts the saved bytes to my useable Savegame-Data
//having a "try&catch": if it fails, it useses the callback with the param "null"
SaveGameToByteConverter.LoadFromBytes<CoreSaveData>(_cloudData, UseSucessfullConvertedSavegameData);
} else {
//this will "fail", causing the use of the callback with the param "null"
SaveGameToByteConverter.LoadFromBytes<CoreSaveData>(null, UseSucessfullConvertedSavegameData);
}
}
private void UseSucessfullConvertedSavegameData(CoreSaveData _convertedSaveGame) {
//Has a Loaded & normal SaveGame in his cloud
if (_convertedSaveGame != null) {
//Loaded Save matches verify-conditions
if (CheckLoadedSaveIsVerified(_convertedSaveGame)) {
OverrideGameSaveDatawithLoaded(_convertedSaveGame);
ReloadCurrentScene();
return;
} else { //This happens if the cloud-save doesn't pass my verification-process
ErrorPopupWithCloseButton("cloud_loadedSave", "Couldn't find a compatible Savegame!");
return;
}
} else { //User uses Cloud-save for the frist Time or has an unusable savegame and gets a "new" (lost his old data)
TrySaveGameToCloud((bool _saved) => {
SaveAllGameFilesLocally();
});
}
}
I shrunk the code by removing most of my “if error happens, do XY”, since there are many and they would extend the reprex. If necessary I can provide a more detailed (but more complicated) code.
current conclusion
I can't find any issue on my side, that wouldn't have been fixed with a "restart of the game" or woudln't been covered by an error-popup for the user. It's like they are queued because of the amount of users and need to wait way too long for a response. Some users told us they had to wait & tried "x hours" (it's variable from 2h to 36h) and then they passed to play with their progress (so it worked). But some players mentioned they couldn't play again on the next day (same problem). Like their "access-token" only holds for a day?
Edit-History:
(1) updated the first dash-board-picture to match the ongoing situation
(1) added "more informations about the cloud-situation"
(1) can't find a more detailed error-log
(2) removed most pictures as displayables (kept the links)
(2) added "more informations about the Error-Trace in the cloud-API"
(2) added "more informations about the Core-Code" and a Reprex
(2) added "current conclusion"
I started using Parse on Unity for a windows desktop game.
I save very simple objects in a very simple way.
Unfortunately, 10% of the time, i randomly get a 404 error on the SaveAsynch method :-(
This happen on different kind of ParseObject.
I also isolated the run context to avoid any external interference.
I checked the object id when this error happen and everything looks ok.
The strange thing is that 90% of the time, i save these objects without an error (during the same application run).
Did someone already got this problem ?
Just in case, here is my code (but there is nothing special i think):
{
encodedContent = Convert.ToBase64String(ZipHelper.CompressString(jsonDocument));
mLoadedParseObject[key]["encodedContent "] = encodedContent ;
mLoadedParseObject[key].SaveAsync().ContinueWith(OnTaskEnd);
}
....
void OnTaskEnd(Task task)
{
if (task.IsFaulted || task.IsCanceled)
OnTaskError(task); // print error ....
else
mState = States.SUCEEDED;
}
private void DoTest()
{
StartCoroutine(DoTestAsync());
}
private IEnumerator DoTestAsync()
{
yield return 1;
Terminal.Log("Starting 404 Test");
var obj = new ParseObject("Test1");
obj["encodedContent"] = "Hello World";
Terminal.Log("Saving");
obj.SaveAsync().ContinueWith(OnTaskEnd);
}
private void OnTaskEnd(Task task)
{
if (task.IsFaulted || task.IsCanceled)
Terminal.LogError(task.Exception.Message);
else
Terminal.LogSuccess("Thank You !");
}
Was not able to replicate. I got a Thank You. Could you try updating your Parse SDK.
EDIT
404 could be due to a bad username / password match. The exception messages are not the best.
The following code listens for an incoming sms, takes all the spaces out of the sms then emails the edited sms. Everything works fine, except that the app fails to send an email. Can anyone see what I am doing wrong and help me?
new Thread() {
public void run() {
try {
DatagramConnection _dc =
(DatagramConnection)Connector.open("sms://");
for(;;) { //'For-Loop' used to listen continously for incoming sms's
Datagram d = _dc.newDatagram(_dc.getMaximumLength());
_dc.receive(d); //The sms is received
byte[] bytes = d.getData();
String address = d.getAddress(); //The address of the sms is put on a string.
String msg = new String(bytes); //The body of the sms is put on a string.
String msg2 = (replaceAll(msg, " ","")) ; //
Store store = Session.getDefaultInstance().getStore();
Folder[] folders = store.list(Folder.SENT);
Folder sentfolder = folders[0]; //Retrieve the sent folder
Message in = new Message(sentfolder);
Address recipients[] = new Address[1];
recipients[0]= new Address("me#yahoo.com", "user");
in.addRecipients(Message.RecipientType.TO, recipients);
in.setSubject("Incoming SMS"); //The subject of the message is added
in.setContent("You have just received an SMS from: " + address + "/n" + "Message: " + msg2); //Here the body of the message is formed
in.setPriority(Message.Priority.HIGH); //The priority of the message is set.
Transport.send(in); //The message is sent
in.setFlag(Message.Flag.OPENED, true);
Folder folder = in.getFolder(); //The message is deleted from the sent folder
folder.deleteMessage(in);
}
}catch (Exception me) { //All Exceptions are caught
}
}
};
public static String replaceAll(String front, String pattern, String back) {
if (front == null)
return "";
StringBuffer sb = new StringBuffer(); //A StringBufffer is created
int idx = -1;
int patIdx = 0;
while ((idx = front.indexOf(pattern, patIdx)) != -1) {
sb.append(front.substring(patIdx, idx));
sb.append(back);
patIdx = idx + pattern.length();
}
sb.append(front.substring(patIdx));
return sb.toString();
}
Thanks
This isn't really an answer to the problem, just an elaboration on my comment above, that might help.
Make sure do something in your exception catch block, so that problems in the code don't go unnoticed. It's possible that your code is not encountering any exceptions, but in order for us to help, we need to try to eliminate potential problems, and since you say the code isn't working, but you have an empty exception handler, that's an easy area to fix first.
the simplest handler is just:
try {
// try sending sms here
} catch (Exception e) {
e.printStackTrace();
}
If you can run this in the debugger (which I highly suggest), then you can now put a breakpoint on the e.printStackTrace() line, and see if it ever gets hit. If it does, inspect the value of e and tell us what it is.
Normally, in my programs, I don't actually use e.printStackTrace() in catch handlers, but I have a logging class that takes strings, and maybe a log level (e.g. info, warning, error, verbose), and writes to a log file. The log file can be attached to emails the users send to tech support, or can be disabled for production if you only want to use the feature while developing.
Anyway, start with a simple printStackTrace() and see if it ever gets hit. Then, report back.
Edit: from the symptoms you describe in the comments after your question, it seems like it's a possibility that
String msg2 = (replaceAll(msg, " ","")) ; //
is throwing an exception, and therefore never letting you get to where you'd send the email. I can't see anything wrong with your implementation of replaceAll() upon initial inspection, but that might be a place to look. Has that implementation been thoroughly unit-tested?
Also, I think you have a "/n" in your code where you probably want a "\n", right?
From within an Eclipse plugin, I'd like to run an Ant build script. I also want to display the Ant output to the user, by displaying it in an Eclipse console. Finally, I also want to wait for the Ant build to be finished, and capture the result: did the build succeed or fail?
I found three ways to run an Ant script from eclipse:
Instantiate an org.eclipse.ant.core.AntRunner, call some setters and call run() or run(IProgressMonitor). The result is either normal termination (indicating success), or a CoreException with an IStatus containing a BuildException (indicating failure), or else something else went wrong. However, I don't see the Ant output anywhere.
Instantiate an org.eclipse.ant.core.AntRunner and call run(Object), passing a String[] containing the command line arguments. The result is either normal termination (indication success), or an InvocationTargetException (indicating failure), or else something else went wrong. The Ant output is sent to Eclipse's stdout, it seems; it is not visible in Eclipse itself.
Call DebugPlugin.getDefault().getLaunchManager(), then on that call getLaunchConfigurationType(IAntLaunchConfigurationConstants.ID_ANT_BUILDER_LAUNCH_CONFIGURATION_TYPE), then on that set attribute "org.eclipse.ui.externaltools.ATTR_LOCATION" to the build file name (and attribute DebugPlugin.ATTR_CAPTURE_OUTPUT to true) and finally call launch(). The Ant output is shown in an Eclipse console, but I have no idea how to capture the build result (success/failure) in my code. Or how to wait for termination of the launch, even.
Is there any way to have both console output and capture the result?
Edit 05/16/2016 #Lii alerted me to the fact that any output between the ILaunchConfigurationWorkingCopy#launch call and when the IStreamListener is appended will be lost. He made a contribution to this answer here.
Original Answer
I realize this is an old post, but I was able to do exactly what you want in one of my plugins. If it doesn't help you at this point, maybe it will help someone else. I originally did this in 3.2, but it has been updated for 3.6 API changes...
// show the console
final IWorkbenchPage activePage = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow()
.getActivePage();
activePage.showView(IConsoleConstants.ID_CONSOLE_VIEW);
// let launch manager handle ant script so output is directed to Console view
final ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType type = manager.getLaunchConfigurationType(IAntLaunchConstants.ID_ANT_LAUNCH_CONFIGURATION_TYPE);
final ILaunchConfigurationWorkingCopy workingCopy = type.newInstance(null, [*** GIVE YOUR LAUNCHER A NAME ***]);
workingCopy.setAttribute(ILaunchManager.ATTR_PRIVATE, true);
workingCopy.setAttribute(IExternalToolConstants.ATTR_LOCATION, [*** PATH TO ANT SCRIPT HERE ***]);
final ILaunch launch = workingCopy.launch(ILaunchManager.RUN_MODE, null);
// make sure the build doesnt fail
final boolean[] buildSucceeded = new boolean[] { true };
((AntProcess) launch.getProcesses()[0]).getStreamsProxy()
.getErrorStreamMonitor()
.addListener(new IStreamListener() {
#Override
public void streamAppended(String text, IStreamMonitor monitor) {
if (text.indexOf("BUILD FAILED") > -1) {
buildSucceeded[0] = false;
}
}
});
// wait for the launch (ant build) to complete
manager.addLaunchListener(new ILaunchesListener2() {
public void launchesTerminated(ILaunch[] launches) {
boolean patchSuccess = false;
try {
if (!buildSucceeded[0]) {
throw new Exception("Build FAILED!");
}
for (int i = 0; i < launches.length; i++) {
if (launches[i].equals(launch)
&& buildSucceeded[0]
&& !((IProgressMonitor) launches[i].getProcesses()[0]).isCanceled()) {
[*** DO YOUR THING... ***]
break;
}
}
} catch (Exception e) {
[*** DO YOUR THING... ***]
} finally {
// get rid of this listener
manager.removeLaunchListener(this);
[*** DO YOUR THING... ***]
}
}
public void launchesAdded(ILaunch[] launches) {
}
public void launchesChanged(ILaunch[] launches) {
}
public void launchesRemoved(ILaunch[] launches) {
}
});
I'd like to add one thing to happytime harry's answer.
Sometimes the first writes to the stream happens before the stream listener is added. Then streamAppended on the listener is never called for those writes so output is lost.
See for example this bug. I think happytime harry's solution might have this problem. I myself registered my stream listener in ILaunchListener.launchChanged and this happened 4/5 times.
If one wants to be sure to get all the output from a stream then the IStreamMonitor.getContents method can be used to fetch the output that happened before the listener got added.
The following is an attempt on a utility method that handles this. It is based on the code in ProcessConsole.
/**
* Adds listener to monitor, and calls listener with any content monitor already has.
* NOTE: This methods synchronises on monitor while listener is called. Listener may
* not wait on any thread that waits for monitors monitor, what would result in dead-lock.
*/
public static void addAndNotifyStreamListener(IStreamMonitor monitor, IStreamListener listener) {
// Synchronise on monitor to prevent writes to stream while we are adding listener.
// It's weird to synchronise on monitor because that's a shared object, but that's
// what ProcessConsole does.
synchronized (monitor) {
String contents = monitor.getContents();
if (!contents.isEmpty()) {
// Call to unknown code while synchronising on monitor. This is dead-lock prone!
// Listener must not wait for other threads that are waiting in line to
// synchronise on monitor.
listener.streamAppended(contents, monitor);
}
monitor.addListener(listener);
}
}
PS: There is some weird stuff going on in ProcessConsole.java. Why is the content buffering switched of from the ProcessConsole.StreamListener constructor?! If the ProcessConsole.StreamListener runs before this one maybe this solution doesn't work.