I have a gameObject "plane" that isn't controlled by the clients. it gets spawned with an audio source that plays a clip when the host clicks a certain button. i would like the sound to be heard by the clients, i tried using rpc but i can't seem to be able to send them.
i keep getting the error: Found no behaviour for incoming [ClientRpc:InvokeRpcRpc_SendSoundIDToServer] on plane (UnityEngine.GameObject), the server and client should have the same NetworkBehaviour instances.
It's been driving me crazy for more than a day, i would really apreciate some help.
Here's my code:
using UnityEngine;
using UnityEngine.Networking;
[RequireComponent(typeof(AudioSource))]
[RequireComponent(typeof(NetworkIdentity))]
public class SpoolUp : NetworkBehaviour
{
private AudioSource source;
public AudioClip[] clips;
public bool start;
void Start()
{
clips = Resources.LoadAll("Audio");
source = GetComponent();
source.playOnAwake = false;
}
public void Spool()
{
start = true;
if (isServer)
PlaySound(0);
}
public void PlaySound(int id)
{
if (id >= 0 && id < clips.Length)
{
RpcPlaySound(id);
}
}
[ClientRpc]
void RpcPlaySound(int id)
{
source.PlayOneShot(clips[id]);
}
PS: I also get the following warning: ClientRpc [ClientRpc:InvokeRpcRpcPlaySound] handler not found [netId=4]
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I've been trying to get Application Level Pause and Resume similar to an activity's onPause and onResume. I know there's no API that has this functionality.
I try to follow this post: http://curioustechizen.blogspot.com/2012/12/android-application-level-pause-and.html
But I've had no luck so far.
Has anyone been able to achieve this? What paradigm did you use?
Let me know if you need me to paste some code into this question.
Thanks for the help
Another solution to the problem would be to just keep track of the count of onStart() and onStop() calls from every activity. Example:
First, create a class to hold the counts:
public class ActiveActivitiesTracker {
private static int sActiveActivities = 0;
public static void activityStarted()
{
if( sActiveActivities == 0 )
{
// TODO: Here is presumably "application level" resume
}
sActiveActivities++;
}
public static void activityStopped()
{
sActiveActivities--;
if( sActiveActivities == 0 )
{
// TODO: Here is presumably "application level" pause
}
}
}
Then in every activity, simply call the activityStarted() and activityStopped() methods:
#Override
public void onStart() {
super.onStart();
ActiveActivitiesTracker.activityStarted();
}
#Override
public void onStop() {
super.onStop();
ActiveActivitiesTracker.activityStopped();
}
I had the same problem. My aim was to lock the App, if the user abandons it. A simple aim, which i thought would be easy to implement. But all the solutions I found were way to complex. So I came to a simple solution: A time based lock.
Basically it works like this:
Start countdown to lock app in onPause
Stop countdown in onResume
If onResume is not called in time, change to locked
Therefor I created a small little class:
public class ApplicationLock {
private static final String TAG = ApplicationLock.class.getSimpleName();
private static final int LOCK_TIME = 1000; //lock after a second
private static boolean lock = true; //default is locked
private static Handler handler = new Handler();
private static Runnable runnable = new Runnable() {
#Override
public void run() {
lock = true;
Log.i("ActivityTracker", "App locked");
}
};
public static boolean activityStarted()
{
handler.removeCallbacks(runnable);
if(lock)
{
Log.i(TAG, "App resumed - LOCKED");
return true;
}else{
Log.i(TAG, "App resumed - NOT LOCKED");
return false;
}
}
public static void activityStopped()
{
handler.postDelayed(runnable, LOCK_TIME);
Log.i(TAG, "App paused - Starting countdown");
}
Just call activityStopped() in your activities onPause() and activityStarted() in onResume(). Check the result of activityStarted(). If it returns true, lock your app. If the orientation of the app is changed, onResume will be called very quickly after onPause, so the app will not lock.
This solution might not fit every scenario, but in my case it was the best solution. Additionally you can change the countdown, to increase the user experience (The user pressed a wrong button and returns to the app in a few seconds, no need to lock the app). Hope this is useful to someone else.
I have done something very similar to this in an app which used a service that provided GPS functions by several activities. The idea was to only have the service there when one of the activities that used it is visible, and not there when none are visible. In your case, every activity would hook into a service, and you will know when the entire application was paused or resumed by hooking into the service's onCreate() and onDestroy() methods.
Here is a stripped-down example:
Components needed (these could probably be placed into a utility class if you want to reuse them, or I just had them for each activity class):
private boolean mAppActiveServiceBound = false;
private AppActiveService mAppActiveService = null;
private ServiceConnection mAppActiveConnection = new ServiceConnection() {
public void onServiceConnected( ComponentName className, IBinder service ) {
mAppActiveService = ( (AppActiveService.AppActiveBinder) service ).getService();
}
public void onServiceDisconnected( ComponentName className ) {
mAppActiveService = null;
}
};
Then in your onStart() and onStop() methods for each activity:
#Override
public void onStart() {
super.onStart();
mAppActiveServiceBound = bindService( new Intent( this, AppActiveService.class ), mAppActiveConnection, Context.BIND_AUTO_CREATE );
}
#Override
public void onStop() {
super.onStop();
if( mAppActiveServiceBound ) {
unbindService( mAppActiveConnection );
mAppActiveServiceBound = false;
}
}
And finally, the service itself:
public class AppActiveService extends Service {
// Receives interactions from clients:
private final IBinder mBinder = new AppActiveBinder();
/**
* Provides a handle to the bound service.
*/
public class AppActiveBinder extends Binder {
AppActiveService getService() {
return AppActiveService.this;
}
}
#Override
public void onCreate(){
// TODO: Here is presumably "application level" resume
}
#Override
public void onDestroy(){
// TODO: Here is presumably "application level" pause
}
}
I'am playing around with WebSocketServlet (tomcat) and I have some question about doing it properly without race condition problems.
I have an instance variable (so non thread-safe) that will keep track of all the websocket connections
HashMap<String,MyServers> myNonThreadSafeVariable = HashMap<String,MyServers>
This is what the HashMap will contain (roughly...)
private final class MyServers extends MessageInbound {
final Set<MyClients> clients = new CopyOnWriteArraySet<MyClients>();
private String serverName;
#Override
protected void onOpen(WsOutbound outbound) {}
#Override
protected void onClose(WsOutbound outbound) {}
#Override
protected void onText(WsOutbound outbound) {}
}
private final class Clients extends MessageInbound {
private int clientID;
#Override
protected void onOpen(WsOutbound outbound) {}
#Override
protected void onClose(WsOutbound outbound) {}
#Override
protected void onText(WsOutbound outbound) {}
}
So now.. during my servlet life time, I am looping through myNonThreadSafeVariable and then maybe also will loop through myNonThreadSafeVariable.clients and then maybe also modified or add a clients or server etc...
For example when a server connect, in his onOpen there will be something like
myNonThreadSafeVariable.put(key,this);
or When a client connects in his onOpen (quit concern about this one)
server = myNonThreadSafeVariable,get(key);
sever.clients.add(this);
Or sometimes when I have to ping all the clients of all the servers:
for (Entry<String, MyServers> entry : myNonThreadSafeVariable.entrySet()) {
MyServers server = entry.getValue();
server.sendMessage("ping||");
for (MyClients member : entry.getValue().clients) {
client.sendMessage("")
}
}
So If I undertand correctly as myNonThreadSafeVariable is global so will myNonThreadSafeVariable.clients etc..
So my question is what is a good practice to avoid race condition in this scenario ?
Using mutex and synchronized on them when access ether the myNonThreadSafeVariable and myNonThreadSafeVariable.clients ? Or should I avoid using an instance variable at all ? But how ?
thanks !
You could use a ReadWriteLock: you block readers and writers when writing, you block only writers when reading:
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
...
writeLock.lock();
try
{
myNonThreadSafeVariable.put(key,this);
}
finally
{
writeLock.unlock();
}
...
writeLock.lock();
try
{
server = myNonThreadSafeVariable,get(key);
sever.clients.add(this);
}
finally
{
writeLock.unlock();
}
...
readLock.lock();
try
{
for (Entry<String, MyServers> entry : myNonThreadSafeVariable.entrySet())
{
MyServers server = entry.getValue();
server.sendMessage("ping||");
for (MyClients member : entry.getValue().clients)
{
client.sendMessage("")
}
}
}
finally
{
readLock.unlock();
}
Moreover if you want to avoid the read lock you can copy the whole collection and scan the copy, letting the possibility for the original collection to be changed while notifying.
i'm still very new at c#, threads and forms. i'm writing a small data acquistion program. it has two threads: the main ui thread and a sensor polling/logging/charting thread. when the user clicks the "start-logging" button, it it continuously polls the sensors (over a virtual COM port), writes the response to a file, updates the main form with some basic polling stats (how many pollings per second). if the user has clicked a "monitor" button, it opens a charting form and the polling thread invokes a methods that that adds the sensors values to the chart.
i have a version of this program that works very well but i found that if i have multiple charts open (so that i can view multiple sensors in realtime), the chart updates become sporadic or stop and only the window with the focus updates smoothly. (the comm port is only 56kbaud so it's not like the polling is being swamped with data.)
so i got the "bright" idea to make charting threads, thinking this would provide multiple UI loops and would produce nice smooth charting on multiple chart forms. below is simplified code; e.g. here, the charting thread is started with the polling thread instead of when the user clicks the "monitor" button. it compiles, but when it runs, i get a cross-reference error at the point when the update_chart method is called.
seems i have a fundamental misunderstanding of several things about threads and control ownership. the chart was made in the "charting" thread, but when the "polling" thread invokes the update_chart method, the code shows that update_chart methods is being run by the "main_ui" thread. i'm open to any suggestions/advise that'll give me smooth charting and stats updates. thanks.
namespace WindowsFormsApplication1
{
public partial class Main_Form : Form
{
delegate void UpdateUIStatsDelegate(string update);
UpdateUIStatsDelegate update_stats_delegate;
static BackgroundWorker polling_thread = new BackgroundWorker();
static BackgroundWorker charting_thread = new BackgroundWorker();
public static Chart_Form chart_form = new Chart_Form();
public Main_Form()
{
Thread.CurrentThread.Name = "main_ui";
update_stats_delegate = new UpdateUIStatsDelegate(update_stats);
polling_thread.DoWork += polling_thread_DoWork;
charting_thread.DoWork += charting_thread_start;
}
private void start_logging_Click(object sender, EventArgs e)
{
start_polling_thread();
start_charting_thread();
}
private void start_polling_thread()
{
polling_thread.RunWorkerAsync();
}
private void polling_thread_DoWork(object sender, DoWorkEventArgs e)
{
string sensor_values;
Thread.CurrentThread.Name = "polling";
while (true)
{
sensor_values = poll_the_sensors_and_collect_the_responses();
log_to_file(sensor_values);
// BeginInvoke(chart_form.update_chart_delegate, new object[] { sensor_values });
chart_form.BeginInvoke(chart_form.update_chart_delegate, new object[] { sensor_values });
pps = compute_polling_performance();
BeginInvoke(update_stats_delegate, new object[] { pps.ToString("00") });
}
}
private void update_stats(string stat)
{
string tn = Thread.CurrentThread.Name;
// this says "main_ui", but i don't get a cross-reference error
pollings_per_second.Text = stat;
}
private void start_charting_thread()
{
charting_thread.RunWorkerAsync();
}
private void charting_thread_start(object sender, DoWorkEventArgs e)
{
Thread.CurrentThread.Name = "charting";
Chart_Form chart_form = new Chart_Form();
chart_form.Show();
while (charting_is_active) { }
}
}
public partial class Chart_Form : Form
{
public delegate void UpdateChartDelegate(string sensor_values);
public UpdateChartDelegate update_chart_delegate;
public Chart_Form()
{
string tn = Thread.CurrentThread.Name;
update_chart_delegate = new UpdateChartDelegate(update_chart);
this.Text = "a realtime plot of sensor values";
}
private void update_chart(string sensor_values)
{
string tn = Thread.CurrentThread.Name;
// this says "main_ui" and i get a cross reference error; set below.
int x = extract_x_value(sensor_values);
int y = extract_y_value(sensor_values);
chart1.Series[X_AXIS].Points.AddY(x); // <<--- i get a cross-reference runtime error here...
chart1.Series[Y_AXIS].Points.AddY(y);
}
}
}
i started to work with .net remoting, read myself through tutorials and explanations, saw now at least three examples on the web and they looked all similar to my code. i can't find the reason for the error I get. (RemotingException was unhandled "Attempted to call a method declared on type 'System.IFormattable' on an object which exposes 'HES.MyProcess'.")
I tried to fix this for six hours now, unsuccessfully looking up the internet, reading through lots of pages...
Maybe you guys can help me out ?
MarshalByRefObject deriving class looks like:
public class MyProcess : MarshalByRefObject, IMyProcess
{
//public System.Diagnostics.Process process {get; set;}
public MyProcess()
{
// TODO: Complete member initialization
// this.process = Process.GetCurrentProcess();
}
public string GetProcessId()
{ Console.WriteLine("I'm on..");
return "test";
// return this.process.Id;
}
}
My interface loooks like this:
interface IMyProcess
{
string GetProcessId();
}
My server looks like this:
namespace HES
{
public class HES_Starter
{
public static void Main(string[] args)
{
// using TCP protocol
TcpChannel channel = new TcpChannel(_port);
//second value is for security settings
ChannelServices.RegisterChannel(channel, false);
Console.WriteLine("HES Server here... on PID: " + Process.GetCurrentProcess().Id);
//Type, objectUri to access the object remotely, mode
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(HES.MyProcess), "HESProcess",
WellKnownObjectMode.Singleton);
Console.ReadLine();
}
}
}
and finally my client like that:
namespace Service_Provider
{
public class Program
{
public static void Main(string[] args)
{
TcpChannel channel = new TcpChannel();
//second value is for security settings
ChannelServices.RegisterChannel(channel, false);
Console.WriteLine("HES Client here...");
IMyProcess remoteProcess = (IMyProcess)Activator.GetObject(
typeof(IMyProcess), "tcp://localhost:8050/HESProcess");
Console.WriteLine(remoteProcess);
Console.WriteLine(remoteProcess.GetProcessId());
Console.ReadLine();
}
}
}
does anybody have a clue what i'm doing wrong ?
I mean from the exception I can see that the client knows that the object is an remote object in the 'HES' namespace. And in debug I can see that the object
remoteProcess = {System.Runtime.Remoting.Proxies.__TransparentProxy}
is a proxy...
I don't know what i'm doing wrong here.
I develop simple j2me bluetooth client and have problem with bluetooth device search.
Function startInquiry nothing found.
Client : nokia 5220
Server : my pc with bluetooth adapter
All bluetooth devices is on.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
import javax.microedition.midlet.*;
import javax.bluetooth.*;
import java.util.Vector;
import javax.microedition.lcdui.*;
/**
* #author Администратор
*/
public class Midlet extends MIDlet implements DiscoveryListener
{
private static Vector vecDevices=new Vector();
private static String connectionURL=null;
private LocalDevice localDevice;
private DiscoveryAgent agent;
private RemoteDevice remoteDevice;
private RemoteDevice[] devList;
private Display display;
private Form form;
public void startApp() {
display = Display.getDisplay(this);
form = new Form( "Client" );
try {
localDevice = LocalDevice.getLocalDevice();
} catch( BluetoothStateException e ) {
e.printStackTrace();
}
form.append("Address: "+localDevice.getBluetoothAddress()+"\n\n");
form.append("Name: "+localDevice.getFriendlyName()+"\n\n");
try {
agent = localDevice.getLocalDevice().getDiscoveryAgent();
form.append("Starting device inquiry... \n\n");
boolean si = agent.startInquiry(DiscoveryAgent.GIAC, this);
if ( si ) {
form.append("true");
} else {
form.append("false");
}
} catch( BluetoothStateException e ) {
}
int deviceCount = vecDevices.size();
if(deviceCount <= 0){
form.append("No Devices Found .");
}
else{
//print bluetooth device addresses and names in the format [ No. address (name) ]
form.append("Bluetooth Devices: ");
for (int i = 0; i < deviceCount; i++) {
remoteDevice=(RemoteDevice)vecDevices.elementAt(i);
form.append( remoteDevice.getBluetoothAddress() );
}
}
display.setCurrent(form);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
//add the device to the vector
if(!vecDevices.contains(btDevice)){
vecDevices.addElement(btDevice);
}
}
public void inquiryCompleted(int discType)
{
}
//implement this method since services are not being discovered
public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
if(servRecord!=null && servRecord.length>0){
connectionURL=servRecord[0].getConnectionURL(0,false);
}
}
//implement this method since services are not being discovered
public void serviceSearchCompleted(int transID, int respCode) {
}
}
Not sure what the exact problem is, but you definitely don't want to be doing this in your midlet's startApp() method. This is a system lifecycle method, and should return quickly, but scanning for bluetooth devices will block it for a long time. Your startApp() method is tying up the device's resources which it could need for doing the actual scanning!
Refactor, so your device scanning is done in a new thread, then see what happens.
You seem to have misunderstood how the Bluetooth API works. The startInquiry method only starts the device discovery process and returns immediately afterwards, leaving the discovery running in the background. When devices are discovered, you get a callback of the deviceDiscovered method for each of them, and when the discovery process has completed, you get a callback of the inquiryCompleted method. So you need to move the accessing of the vecDevices member and the form manipulation from startApp to inquiryCompleted to be able to actually show the discovered information.
You say all devices are on - but also check if all devices are discoverable.
I've made this mistake before myself!
Lookup the method LocalDevice.setDiscoverable() if you want to toggle between modes programatically.