[SHVDN3] NullReferenceException when calling Notification.Show from websocket eventhandler
-
Hey, i want to run a websocket server and recieve commands/messages made by some other client. The websockt server runs fine and i can connect to it but it seems like i can not execute anything GTA related inside the eventhandler and i dont know why or how to fix it. Help is apprechiated!
Error:
[01:32:26] [ERROR] Caught fatal unhandled exception: System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. bei SHVDN.ScriptDomain.ExecuteTask(IScriptTask task) bei SHVDN.NativeFunc.Invoke(UInt64 hash, UInt64* argPtr, Int32 argCount) bei GTA.Native.Function.Call(Hash hash, InputArgument argument0) bei GTA.UI.Notification.Show(String message, Boolean blinking) bei Cmod.WebsocketServer.OnWebsocketEvent(Object sender, WebSocketEventArg args) bei SimpleWebSocketServerLibrary.SimpleWebSocketHandler.WebSocketHandler.OnWebsocketEvent(Object sender, WebSocketEventArg arg) bei SimpleWebSocketServerLibrary.WSocketServer.WebSocketServer.<StartServerAsync>d__10.MoveNext() --- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde --- bei System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) bei SimpleWebSocketServerLibrary.SimpleWebSocketHandler.WebSocketHandler.<StartConnection>d__6.MoveNext() --- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde --- bei System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() bei System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) bei System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() bei System.Threading.ThreadPoolWorkQueue.Dispatch()
Code:
using System; using GTA; using GTA.Native; using System.Windows.Forms; using SimpleWebSocketServerLibrary; using System.Text; namespace Cmod { // Other code. public class WebsocketServer : Script { public SimpleWebSocketServer websocketServer; public WebsocketServer() { // this.KeyUp += onKeyUp; SimpleWebSocketServerSettings settings = new SimpleWebSocketServerSettings(); settings.port = 9091; websocketServer = new SimpleWebSocketServer(settings); websocketServer.WebsocketServerEvent += OnWebsocketEvent; websocketServer.StartServer(); GTA.UI.Notification.Show("WS STARTED"); } public static void OnWebsocketEvent(object sender, WebSocketEventArg args) { try { if (args.data != null && args.isText) { string received = Encoding.UTF8.GetString(args.data); // _WebsocketServer.SendTextMessage("Client: " + args.clientId + " on url: " + args.clientBaseUrl + ", says: " + received); GTA.UI.Notification.Show("MSG: " + received); } } catch (Exception) { throw; } } } }
-
@Slluxx Because the code executed on the socket event is not running on the main thread, and you can only call GTA functions from the main thread
-
@JustDancePC Ah i see, thanks! Is there a good/efficient way around that (eg can i
somehow execute code back on the main thread from within the socket thread)? Or is there nothing i can do about that?
-
@Slluxx Probably not the best solution, but what I do in these cases is use a static variable as a flag inside a function that runs on tick, that way I can check every frame if my GTA-related code should run:
private static bool _flag; private void MyEvent() { //Event triggered _flag = true; } private void OnTick() { if (_flag) { //Call GTA functions _flag = false; } }
-
@JustDancePC Yeah i guess that would work but as you said, its kind of jank. With websockets i would like to get 2 variables (socket & whatever has been transmitted). storing those inside classes to get them from a different thread by looking at it every tick is probably not smart. I have found synchronization context but didnt manage to make that work either.
-
I finally did it myself with the help of this SO thread.
Here is an example snippe, but PLEASE read the SO thread. This example always assumes to be in a different thread, which might or might not (always) be the case for you.
public class MyClass : Script { private Dispatcher _uiDispatcher = Dispatcher.CurrentDispatcher; public MyClass () { var thing = new SomeThing(); thing.runInDifferentThread(socket => { socket.OnMessage = message => { _uiDispatcher.Invoke(delegate () { OnMessage_Worker(socket, message); }); }; }); } public void OnMessage_Worker(IWebSocketConnection socket, string message){ GTA.UI.Notification.Show(message); socket.Send("OK"); } }
-
@Slluxx Oh, awesome 👌