I am trying to implement push notifications using signalR hubs. I have a sample code, which when I run, I get an error saying
JavaScript runtime error: 'Rx' is undefined
This error comes in the dynamic signalr/hubs file.
I have added all the necessary Javascript references i.e., jquery, signalR and signalr/hubs.
What am i missing ?
My code looks something like this:
Global.asax file has this
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs("~/signalr");
}
My Hub is defined like this
[HubName("HealthCheck")]
public class MyConnectionClass : Hub
{
public static List<string> messages = new List<string>();
public void GetServiceState()
{
Clients.updateMessages(messages);
}
public void UpdateServiceState()
{
messages.Add(DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"));
Clients.updateMessages(messages);
}
And my client in javascript like this
$(function () {
// creates a proxy to the health check hub
var healthCheckHub = $.connection.healthCheck;
// handles the callback sent from the server
healthCheckHub.updateMessages = function (data) {
$("li").remove();
$.each(data, function () {
$('#messages').append('<li>' + this + '</li>');
});
};
$("#trigger").click(function () {
healthCheckHub.server.updateServiceState();
});
// Start the connection and request current state
$.connection.hub.start(function () {
healthCheckHub.server.getServiceState();
});
});
Also I have added all the necessary js references in the client.
I have picked this sample from here
Is this enough or am i missing something ?
Thanks
Given that the error is about Rx not being defined I'm wondering if you may have installed the wrong NuGet package. It sounds like you installed SignalR.Reactive instead.
I think this is the package you really want. (Note that this is a prerelease version and I think the package names changed quite recently so it's possible the tutorial you're following is slightly out of date, which may have been what's caused the confusion.)
Related
I'm using mvvmcross version 6.4.1 to develop an app for IOS, Android, and WPF.
I've searched all over for my to use plugins. There seems to be no code examples. The documentation said to install the nuget in both my core and ui application projects. Which I did. Is there any special IOC registration/setup/or loading that needs to be done before I can use the plugin and how do I go about using the plugin? Do they get injected in the constructor or Do I have to manually pull them from the IOC container or new () them up.
I've installed nuget for the File plugin into my WPF UI and Core project. I added the IMvxFileStore to one of my core project's service constructor thinking it automagically gets added to the DI container, but it doesn't seem to get injected.
namespace My.Core.Project.Services
{
public class SomeService : ISomeService
{
private IMvxFileStore mvxFileStore;
public SomeService(IMvxFileStore mvxFileStore)
{
this.mvxFileStore = mvxFileStore;
}
public string SomeMethod(string somePath)
{
mvxFileStore.TryReadTextFile(somePath, out string content);
return content;
}
}
}
App.xaml.cs
using MvvmCross.Core;
using MvvmCross.Platforms.Wpf.Views;
...
public partial class App : MvxApplicatin
{
protected override void RegisterSetup()
{
this.RegisterSetupType<Setup<Core.App>>();
}
}
App.cs
using MvvmCross;
using MvvmCross.ViewModels;
using My.Core.Project.Services;
public class App: MvxApplication
{
public override void Initialize()
{
Mvx.IocProvider.RegisterType<ISomeService, SomeService>();
RegisterCustomAppStart<AppStart>();
}
}
AppStart.cs
using MvvmCross.Exceptions;
using MvvmCross.Navigation;
using MvvmCross.ViewModels;
using My.Core.Project.ViewModels;
using System;
using System.Threading.Tasks;
....
public class AppStart : MvxAppStart
{
public AppStart(IMvxApplication application, IMvxNavigationService navigationService) : base(application, navigationService)
{}
public override Task NavigateToFirstViewModel(object hint = null)
{
try {
return NavigationService.Navigate<FirstPageViewModel>();
} catch {
throw e.MvxWrap("Some error message {0}", typeof(FirstPageViewModel).Name);
}
}
}
Setup.cs in WPF project
using MvvmCross;
using MvvmCross.Base;
using MvvmCross.Platforms.Wpf.Core;
using MvvmCross.Plugin.File;
using MvvmCross.Plugin.Json;
using MvvmCross.ViewModels;
using My.Wpf.Project.Services;
...
public class Setup<T> : MvxWpfSetup
{
public Setup() : base() {}
protected override IMvxApplication CreateApp()
{
return new Core.App();
}
protected override void InitializeFirstChange()
{
base.InitializeFirstChange();
Mvx.IocProvider.RegisterType<ISomeWpfSpecificService>(() => new SomeWpfSpecificService());
}
protected override void InitializeLastChange()
{
base.InitializeLastChange();
}
}
I'm expecting my service to load but instead, I get the error message
MvxIoCResolveException: Failed to resolve parameter for parameter mvxJsonConverter of type IMvxJsonConverter
NOTE: I get the same error message for both File and Json plugin, The plugin that gets listed first in the constructor gets the error message when the app trys to load.
Am I properly using or loading the plugin?
UPDATE: I manually registered the Plugins in the UI Setup.cs and it is working but I am not sure if this is the proper way to do it.
WPF UI project Setup.cs
using MvvmCross;
using MvvmCross.Base;
using MvvmCross.Platforms.Wpf.Core;
using MvvmCross.Plugin.File;
using MvvmCross.Plugin.Json;
using MvvmCross.ViewModels;
using My.Wpf.Project.Services;
...
public class Setup<T> : MvxWpfSetup
{
public Setup() : base() {}
protected override IMvxApplication CreateApp()
{
return new Core.App();
}
protected override void InitializeFirstChange()
{
base.InitializeFirstChange();
Mvx.IocProvider.RegisterType<ISomeWpfSpecificService>(() => new SomeWpfSpecificService());
Mvx.IoCProvider.RegisterType<IMvxFileStore, MvxFileStoreBase>();
Mvx.IoCProvider.RegisterType<IMvxJsonConverter, MvxJsonConverter>();
}
protected override void InitializeLastChange()
{
base.InitializeLastChange();
}
}
Yes you are using the plugin properly and I think that for now your solution to manually register your plugin is viable.
The root of the problem is located in the MvxSetup class. This class contains the method LoadPlugins which is responsible for loading the MvvmCross plugins which are referenced by your UI project. This is how LoadPlugins determines what plugins to load:
Get all assemblies that have been loaded into the execution context of the application domain.
Find types within these assemblies which contain the MvxPluginAttribute.
Now the problem occurs in step 1. In a .NET framework project, by default, your referenced assemblies won't be loaded into the execution context until you actually use them in your code. So if you don't use something from your MvvmCross.Plugin.File reference in your UI project it won't be loaded into your execution context and it won't be found in step 1 and thus it won't be registered by LoadPlugins. (good read: when does a .NET assembly Dependency get loaded)
One way I have tested this is by doing this:
protected override void InitializeFirstChance()
{
// Because a type of the MvvmCross.Plugin.File.Platforms.Wpf reference is
// used here the assembly will now get loaded in the execution context
var throwaway = typeof(Plugin);
base.InitializeFirstChance();
}
With the above code you don't have to manually register the Plugin.
There has been a pull request to fix this in the MvvmCross framework but this has been reverted later since it caused problems on other platforms.
In other platforms the plugin assemblies will get loaded into the execution context without any tricks so I would say updating the MvvmCross documentation stating you have to register your plugin manually for WPF would be useful for other developers in the future.
I want to keep network code separate from my game logic. Not only do I need to do that to be able to share game logic between single and multiplayer game modes, I also want it because of the Separation Of Concerns thing.
My current approach is to generate the code for my network related classes in such a way that there is an online and an offline version. I do this using T4 templates.
The resulting classes look like this:
Standalone/Singleplayer version:
// T4 GENERATED CODE
// Head (Singleplayer version)
class StandaloneHelloWorld : MonoBehaviour, IHelloWorld
{
private string name;
public void SayHello()
{
SayHelloInternal();
}
// Body
public string Name
{
get { return name; }
set { name = value; }
}
void SayHelloInternal()
{
Debug.Log(Name + ": Hello World");
}
}
Multiplayer version:
// T4 GENERATED CODE
// Head (Multiplayer version)
class NetworkedHelloWorld : NetworkBehaviour, IHelloWorld
{
[SyncVar]
private string name;
public void SayHello()
{
CmdSayHello();
}
[Command]
void CmdSayHello()
{
RpcSayHello();
}
[ClientRpc]
void RpcSayHello()
{
SayHelloInternal();
}
// Body
public string Name
{
get { return name; }
set { name = value; }
}
void SayHelloInternal()
{
Debug.Log(Name + ": Hello World");
}
}
They both share an interface to hide the implementation from the callers:
interface IHelloWorld
{
string Name { get; set; }
void SayHello();
}
So as you can see, both implementations use the same body, sharing most of the code, while the entry points depend on the implementation being networked or not. Also note that the two implementations inherit different base classes.
Advantages:
Singleplayer code has no dependencies towards networked code and vice versa
No duplicate code (none that has to be maintained manually at least)
Disadvantages:
Support for interfaces in Unity is limited. I would not be able to reference scene instances of IHelloWorld from inside the Editor.
Having to maintain separate Prefabs for singleplayer/multiplayer game modes
Having to meddle with T4/code generation
Do you know of better ways to deal with this? How did you solve this problem?
You could structure the code in an event-based fashion. This will allow systems to register to events they're interested in. This naturally separates the logic from the network code.
As an example, let's say you want to fire a projectile.
You can fire it by calling:
new Event(EventType.FireProjectile, pos, dir, template)
You can then register systems that are interested in this event:
CollisionSystem.Register(EventType.FireProjectile, (e) => {
CollisionSystem.AddCollider(e.template.bounds);
});
AudioSystem.Register(EventType.FireProjectile, (e) => {
AudioSystem.PlaySound("Woosh");
});
AISystem.Register(EventType.FireProjectile, (e) => {
AISystem.AlertAtPosition(e.pos);
});
What's cool is next you can register this event to the NetworkSystem that will serialize it, move it across the net, deserialize it, and fire it off on the client's machine. So as far as the client is concerned this event was called locally.
NetworkSystem.Register(EventType.FireProjectile, (e) => {
NetworkSystem.Broadcast(e, Channel.Reliable);
});
This is pretty great, except that you'll soon realize that this will cause an infinite loop of events. As you send a FireProjectile event to the other client, they catch it and fire it. Instantly their NetworkSystem catches it and fires it over the net.
To fix this you need two events for every action – a request: FireProjectile, and response: ProjectileFired.
I've worked with a codebase like this for a personal project a while ago. It's in C++, but if you're interested you can read more here. Notice how the server and the client are registering to certain events, which they will forward across.
I'm working on a Framework for a Minecraft Server and I'm running into a constant error while retrieving player data from a MongoDB database. I have both a proxy plugin and a spigot plugin that the framework is shaded into.
For the proxy plugin, I can get and store the player data in my PlayerCache (A map that assigns UUIDs to PlayerData objects), but however, for the spigot plugin, I cannot. It returns a null pointer, saying that the data is not found in the cache, even though I am sure I called the cacheData method.
I believe what is going on is that I'm not allowing enough time for the database to get the information and cache it.
Spigot code:
#EventHandler
public void onLogin(PlayerLoginEvent event)
{
PlayerCache.getInstance().cachePlayer(event.getPlayer().getUniqueId());
}
#EventHandler
public void onJoin(PlayerJoinEvent event)
{
event.setJoinMessage(null);
event.getPlayer().setDisplayName(PlayerCache.getInstance().getCachedRank(
event.getPlayer().getUniqueId()
).getColor() + event.getPlayer().getName()); //Throws a null pointer.
}
PlayerCache:
public void cachePlayer(UUID uuid)
{
PlayerData PD = PlayerManager.getInstance().getPlayerData(uuid);
this.cache.put(uuid, PD);
}
public PlayerRank getCachedRank(UUID uuid)
{
return this.cache.get(uuid).getRank();
}
Hope someone can help.
I am in the process of converting a home grown logging system to NLog and am wondering if there is a way to add an event to a Logger or otherwise support a mechanism where when I log a message I can get a callback with the final formatted message and the LogLevel. I currently use something like this to send server messages back to a connected client.
Thx
This is an MCVE of what I was talking about in the comments. Create a target that accepts some callback functions:
[Target("MyFirst")]
public sealed class MyFirstTarget : TargetWithLayout
{
private readonly Action<string>[] _callbacks;
public MyFirstTarget(params Action<string>[] callbacks)
{
_callbacks = callbacks;
}
protected override void Write(LogEventInfo logEvent)
{
foreach (var callback in _callbacks)
{
callback(logEvent.FormattedMessage);
}
}
}
Configure NLog to use the target. I do this programmatically since the callbacks are passed in the constructor. You can also configure the target in the NLog.config, but your target will need to be a singleton then so you can register the callbacks in code.
class Program
{
public static void Main()
{
LogManager.Configuration.AddTarget("MyFirst", new MyFirstTarget(s => Debug.WriteLine(s)));
var logger = LogManager.GetCurrentClassLogger();
logger.Debug("test");
}
}
With no other NLog configuration (copy this code into an empty project and add the NLog nuget package), this will emit a message to your debug window.
I am trying to implement an API (SCORM API) using GWT.
The client code expects an API object with methods like Initialize(), getLastError() and so on...
I tried to implement this api as an Java Object, but i see that the compiled names are changed and cannot be used directly by client code.
I see that gwt-exporter can do the trick (http://code.google.com/p/gwt-exporter/) but i would like to know how to do it using pure gwt and jsni.
As the API is expected as a object, named API_1484_11 attached to the window object, not an function, , i don't see how to use the $entry() idiom.
Here is my current, failing, code:
public final class SCORMApi {
protected SCORMApi() {}
public void Initialize(){
GWT.log("** INITIALIZE CALLED **");
}
public static void create(){
bind(new SCORMApi());
}
public static native void bind(SCORMApi api) /*-{
$wnd.API_1484_11 = api;
}-*/;
}
So, in this context, my question is:
How can i get javascript calls (e.g. window.API_1484_11.Initialize() ) to reach my java gwt code?
You're on the right lines with your bind method. But you haven't understood how to call Java methods from within JSNI. This is how you do it in the case of your Initialize method:
public static native void bind(SCORMApi api) /*-{
$wnd.API_1484_11 = {
initialize: function() {
$entry( api.#com.yourpackage.name.SCORMApi::Initialize()() );
}
};
}-*/;
The blogs Getting To Really Know GWT parts 1 and 2 are required reading on this subject.