Unity Multiplayer: Standalone client - unity3d

I'm looking into Unity multiplayer support. From all docs it seems like the main model is for a game to be capable of being both the server and the client, and the same binary used for both.
Would it be possible to make a game where the client and the server are two different binaries: client being more lightweight and only doing the client part, while server doing heavy lifting of handling the open world/gameplay/state etc.?
As a simplified example imagine a huge world populated by characters, and the client is a mobile app that only needs to display their health/stats and render their avatar. While on the server those characters live a complex life in a large environment.

You could use something like SmartFoxServer (supports unity3d) for the server operations completely independent of the client side logic. This is going to be C# Unity on the client and Java for SmartFoxServer. It is pretty easy to configure extensions, manage rooms, lobby, user events, chats etc in the server and get the events on the client side. You can build a complete MMO system and run it on mobile too.

So I believe I found a way, at least it's working for me.
What I need is possible using NetworkClient and NetworkServer classes. So now I have two separate projects, server and client.
Server has a script which is pretty much:
public class Server : MonoBehaviour {
public Text text;
public class HelloMessage : MessageBase
{
public string helloText;
}
void Start () {
NetworkServer.Listen(4444);
NetworkServer.RegisterHandler(333, onHelloMessage);
}
public void onHelloMessage(NetworkMessage msg)
{
text.text = msg.ReadMessage<HelloMessage>().helloText;
}
}
This listens for messages on port 4444.
Then the client side is like this:
public class NetworkManager : MonoBehaviour {
NetworkClient client;
public class HelloMessage : MessageBase
{
public string helloText;
}
// Use this for initialization
void Start () {
client = new NetworkClient();
client.Connect("127.0.0.1", 4444);
}
public void SendNetworkMessage()
{
HelloMessage msg = new HelloMessage();
msg.helloText = "Hello";
client.Send(333, msg);
}
}
Now on the server side we can hook up text to a label and on the client side SendNetworkMessage to a button and we can send messages from client to appear on the server.
Now just need to define a protocol and off we go.

Related

how to display a new created object on both devices in Unity networking?

I am working on a network game with Unity3D network. I am new to network programming.
Let's say I created objects and attached them to the player (either on server or client). I need to keep displaying those objects on another device. I've seen many materials, but I haven't found a solution yet.
It seems NetworkServer.Spawn is a way to do it. Is that right and any other better way? Give some advice to this novice^^ Thanks.
If you're using NetworkServer.Spawn you need to register your prefab in your NetworkManager.
If you're not expecting anyone new to join your server you can also use Command and ClientRPC that will spawn this prefab and parent it to correct game object on every client.
Only server/host can spawn network-synched GameObjects.
// put this component on a prefab you wish to spawn
class MyNetworkedObject : NetworkBehaviour
{
[SyncVar]
public int myHealth;
void Start()
{
Debug.Log("My health is: " + myHealth);
}
}
// put this component on an empty GameObject somewhere in the scene and check "Server Only" on its NetworkIdentity
class MySpawner : NetworkBehaviour
{
public MyNetworkedObject myPrefab;
void OnValidate()
{
if (GetComponent<NetworkIdentity>().serverOnly == false)
Debug.LogWarning("This component wants NetworkIdentity to have \"Server Only\" checked (for educational purposes, not an actual requirement).");
}
public void SpawnObject(int withHealth)
{
if (!isServer)
throw new Exception("Only server may call this! It wouldn't have any effect on client.");
MyNetworkedObject newObject = Instantiate(myPrefab);
// setup newObject here, before you spawn it
newObject.myHealth = withHealth;
NetworkServer.Spawn(newObject.gameObject);
}
}
As Fiffe already mentioned, the prefab must be registered inside your NetworkManager for it to be spawnable. Also, all networked objects must have a NetworkIdentity component.
Once the object is spawned by the server, server will tell all connected clients to spawn the object with current values of any SyncVars, and also on any clients that might connect later. If SyncVars change on the server, server will tell all connected clients to change the value accordingly.
If you want clients to invoke the spawning of an object, the easiest way is to go with Local Authority:
// put this component on the player prefab that you already assigned to NetworkManager.PlayerPrefab
class MyNetworkedPlayerObject : NetworkBehaviour
{
void OnValidate()
{
if (GetComponent<NetworkIdentity>().localPlayerAuthority == false)
Debug.LogWarning("This component requires NetworkIdentity to have \"Local Player Authority\" checked.");
}
public void RequestSpawnObject(int withHealth)
{
if (!hasAuthority)
throw new Exception("Only the player owned by this client may call me.");
// the following call will magically execute on the server, even though it was called on the client
CmdSpawnObject(withHealth);
}
[Command]
private void CmdSpawnObject(int withHealth)
{
// this code will always run on the server
FindObjectOfType<MySpawner>().SpawnObject(withHealth);
}
}
Each client can have one or more networked objects with Local Player Authority. Each networked object is either owned by server, or by exactly one client that has Local Player Authority for that object. Client may invoke Commands only on objects that that client has Local Player Authority on. Commands are invoked by the client, but executed on the server. This execution will happen asynchronously, client can not know when the server will execute it. Thus, Commands can have parameters, but no return value.
Also:
If you want all clients to execute a method on a networked object, server can invoke [ClientRPC] methods on any networked object, which will then execute on the clients. If a client connects later, it will not invoke rpc calls that happened before the client connected.
Even clients with Local Player Authority can not change SyncVars. If they do, server and other clients will not receive the changes. Only server can change SyncVars and have it synchronized to the clients
Transform is a special case. Objects can have a NetworkTransform component which allows owning client (or server) to control the position and rotation of that object and have it synched to server and other clients. Same with NetworkAnimator.
If you want to decide at runtime whether the server or the client have authority over an object, you must have a prefab for either case, one with "Local Player Authority" checked, one without. Server can never be owner of an object that has "Local Player Authority" checked (but local client on a host can).
Clients can send messages to the server and vice versa.
I hope this gives you a short overview.

Force ASP .Net Web API to block HTTP requests with Error (403)

I would like to configure my project to not allow http requests with the following restrictions:
It must be a global restriction for all APIs (via web.config, script in the installer, etc.)
It must be hard coded(not pressing "Require SSL" on the APP in the IIS)
No "redirect"- just return error (403)
my ideal option would be to configure "Require SSL" by a script which runs in the installer.
This can be accomplished by writing a simple ActionFilter that inspects the request and responds when the scheme is not set to ssl. A very minimal implementation may look something like:
public class RequireHttpsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.Request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
}
}
}
To make this apply everywhere, you'll likely want to register it as a global filter in the WebAPI configuration when your application is bootstrapping. That would look something like:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new RequireHttpsAttribute());
// ... More configuration ...
}
}
If you search the web a bit, you can find many examples of similar filters with more robust logic that may better meet your needs.

CometD Subscription Listeners

I’m having a problem processing Subscription Requests from Clients and carrying out some processing based on the request. I’d like to be able to invoke a method and carry out some processing when an incoming subscription request is received on the Server. I’ve had a look at the following CometD documentation and tried the example outlined in “Subscription Configuration Support” but I’m not having much luck.
http://www.cometd.org/documentation/2.x/cometd-java/server/services/annotated
I’ve already created the Bayeux Server using a Spring Bean and I’m able to publish data to other channel names I’ve created on the Server side. Any help or additional info. on the topic would be appreciated!
The code example I’m using:
#Service("CometDSubscriptionListener")
public class CometDSubscriptionListener {
private final String channel = "/subscription";
private static final Logger logger = Logger.getLogger(CometDSubscriptionListener.class);
private Heartbeat heartbeat;
#Inject
private BayeuxServer bayeuxserver;
#Session
private ServerSession sender;
public CometDSubscriptionListener(BayeuxServer bayeuxserver){
logger.info("CometDSubscriptionListener constructor called");
}
#Subscription(channel)
public void processClientRequest(Message message)
{
logger.info("Received request from client for channel " + channel);
PublishData();
}
Have a look at the documentation for annotated services, and also to the CometD concepts.
If I read your question correctly, you want to be able to perform some logic when clients subscribe to a channel, not when messages arrive to that channel.
You're confusing the meaning of the #Subscription annotation, so read the links above that should clarify its semantic.
To do what I understood you want to do it, you need this:
#Service
public class CometDSubscriptionListener
{
...
#Listener(Channel.META_SUBSCRIBE)
public void processSubscription(ServerSession remote, ServerMessage message)
{
// What channel the client wants to subscribe to ?
String channel = (String)message.get(Message.SUBSCRIPTION_FIELD);
// Do your logic here
}
}

Proper way of using MVVM Light Messenger

What is the proper way to use Messenger class ?
I know it can be used for ViewModels/Views communications, but is it a good approach to use it in for a technical/business service layer ?
For example, a logging/navigation service registers for some messages in the constructors and is aware when these messages occurs in the app. The sender (ViewModel ou Service) does not reference the service interface but only messenger for sending messages. Here is a sample service :
using System;
using System.Windows;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using App.Service.Interfaces;
using GalaSoft.MvvmLight.Messaging;
namespace App.Service
{
public class NavigationService : INavigationService
{
private PhoneApplicationFrame _mainFrame;
public event NavigatingCancelEventHandler Navigating;
public NavigationService()
{
Messenger.Default.Register<NotificationMessage<Uri>>(this, m => { this.NavigateTo(m.Content); });
}
public void NavigateTo(Uri pageUri)
{
if (EnsureMainFrame())
{
_mainFrame.Navigate(pageUri);
}
}
public void GoBack()
{
if (EnsureMainFrame()
&& _mainFrame.CanGoBack)
{
_mainFrame.GoBack();
}
}
private bool EnsureMainFrame()
{
if (_mainFrame != null)
{
return true;
}
_mainFrame = Application.Current.RootVisual as PhoneApplicationFrame;
if (_mainFrame != null)
{
// Could be null if the app runs inside a design tool
_mainFrame.Navigating += (s, e) =>
{
if (Navigating != null)
{
Navigating(s, e);
}
};
return true;
}
return false;
}
}
}
For me, the main use of a messenger is because it allows for communication between viewModels. Lets say you have a viewmodel that is used to provide business logic to a search function and 3 viewmodels on your page/window that want to process the search to show output, the messenger would be the ideal way to do this in a loosely-bound way.
The viewmodel that gets the search data would simply send a "search" message that would be consumed by anything that was currently registered to consume the message.
The benefits here are:
easy communication between viewmodels without each viewmodel having to know about each other
I can swap out the producer without affecting a consumer.
I can add more message consumers with little effort.
It keeps the viewmodels simple
Edit:
So, what about services?
ViewModels are all about how to present data to the UI. They take your data and shape it into something that can be presented to your View. ViewModels get their data from services.
A service provides the data and/or business logic to the ViewModel. The services job is to service business model requests. If a service needs to communicate/use other services to do its job these should be injected into the service using dependency injection. Services would not normally communicate with each other using a messenger. The messenger is very much about horizontal communication at the viewmodel level.
One thing I have seen done is to use a messenger as a mediator, where instead of injecting the service directly into a viewmodel the messenger is injected into the viewmodel instead. The viewmodel subscribes to an event and receives events containing models from the event. This is great if you're receiving a steady flow of updates or you're receiving updates from multiple services that you want to merge into a single stream.
Using a messenger instead of injecting a service when you're doing request/response type requests doesn't make any sense as you'll have to write more code to do this that you'd have to write just injecting the service directly and it makes the code hard to read.
Looking at your code, above. Imagine if you had to write an event for each method on there (Navigate, CanNavigate, GoBack, GoForward, etc). You'd end up with a lot of messages. Your code would also be harder to follow.

Spring DefaultMessageListenerContainer/SimpleMessageListenerContainer (JMS/AMQP) Annotation configuration

So I'm working on a project where many teams are using common services and following a common architecture. One of the services in use is messaging, currently JMS with ActiveMQ. Pretty much all teams are required to follow a strict set of rules for creating and sending messages, namely, everything is pub-subscribe and the messages that are sent are somewhat like the following:
public class WorkDTO {
private String type;
private String subtype;
private String category;
private String jsonPayload; // converted custom Java object
}
The 'jsonPayload' comes from a base class that all teams extend from so it has common attributes.
So basically in JMS, everyone is always sending the same kind of message, but to different ActiveMQ Topics. When the message (WorkDTO) is sent via JMS, first it is converted into a JSON object then it is sent in a TextMessage.
Whenever a team wishes to create a subscriber for a topic, they create a DefaultMessageListenerContainer and configure it appropriately to receive messages (We are using Java-based Spring configuration). Basically every DefaultMessageListenerContainer that a team defines is pretty much the same except for maybe the destination from which to receive messages and the message handler.
I was wondering how anyone would approach further abstracting the messaging configuration via annotations in such a case? Meaning, since everyone is pretty much required to follow the same requirements, could something like the following be useful:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Listener {
String destination();
boolean durable() default false;
long receiveTimeout() default -1; // -1 use JMS default
String defaultListenerMethod() default "handleMessage";
// more config details here
}
#Listener(destination="PX.Foo", durable=true)
public class FooListener {
private ObjectMapper mapper = new ObjectMapper(); // converts JSON Strings to Java Classes
public void handleMessage(TextMessage message){
String text = message.getText();
WorkDTO dto = mapper.readValue(text, WorkDto.class);
String payload = dto.getPayload();
String type = dto.getType();
String subType = dto.getSubType();
String category = dto.getCategory();
}
}
Of course I left out the part on how to configure the DefaultMessageListenerContainer by use of the #Listener annotation. I started looking into a BeanFactoryPostProcessor to create the necessary classes and add them to the application context, but I don't know how to do all that.
The reason I ask the question is that we are switching to AMQP/RabbitMQ from JMS/ActiveMQ and would like to abstract the messaging configuration even further by use of annotations. I know AMQP is not like JMS so the configuration details would be slightly different. I don't believe we will be switching from AMQP to something else.
Here teams only need to know the name of the destination and whether they want to make their subscription durable.
This is just something that popped into my head just recently. Any thoughts on this?
I don't want to do something overly complicated though so the other alternative is to create a convenience method that returns a pre-configured DefaultMessageListenerContainer given a destination and a message handler:
#Configuration
public class MyConfig{
#Autowired
private MessageConfigFactory configFactory;
#Bean
public DefaultMessageListenerContainer fooListenerContainer(){
return configFactory.getListenerContainer("PX.Foo", new FooListener(), true);
}
}
class MessageConfigFactory {
public DefaultMessageListenerContainer getListener(String destination, Object listener, boolean durable) {
DefaultMessageListenerContainer l = new DefaultMessageListenerContainer();
// configuration details here
return l;
}
}