using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using Badumna.DataTypes;
using Badumna.SpatialEntities;
using Badumna;
using Badumna.Arbitration;
using UnityArbitrationEvents;
using Dei;
/// <summary>
/// The NetworkInitialization class is used to initialize the Badumna network library.
/// Configuration options are specified in the constructor.
///
/// The Badumna Network initialization is called in Awake() function (Awake
/// is called when the script instance is loaded, is called only once
/// during the lifetime of the script instance)
///
/// Start is called after Awake function of each script has been called (see
/// Unity ScriptReference help for further information about Start() function)
/// Initiate the localAvatar and register this instance class to current scene
///
/// update the Badumna by calling ProcessNetworkState everytime FixedUpdate
/// function is called (more information about ProcessNetworkState can be seen
/// in http://www.badumna.com/badumna/api/)
/// </summary>
public class NetworkInitialization : MonoBehaviour
{
public GameObject[] ListOfAvatars; // List of possible avatar objects
public Boolean allowChangeLevel = false; // by default is false
public IArbitrator Arbitrator;
public GUIScoreScript GUIScore;
private Dictionary<BadumnaId, RemoteAvatar> mRemoteEntities = new Dictionary<BadumnaId, RemoteAvatar>();
protected NetworkScene mNetworkScene;
private string mNetworkSceneName = "Demo6"; //"Unity-Badumna Demo World";
private LocalAvatar mLocalAvatar;
private int mLocalAvatarId = 0;
private Boolean mIsRegistered = false;
private Boolean mIsChangeLevel = false;
private DeiTokenSupplier mTokenSupplier;
/// <summary>
/// Initializes a new instance of the <see cref="NetworkInitialisation"/> class.
/// </summary>
public NetworkInitialization()
{
// The configuration options must be specified before a call to NetworkFacade.Instance.Initialize.
// By default Badumna will look for a file called 'NetworkConfig.xml' (included in the package)
// which specifies the configuration options to use.
// In general it is better to use an xml file for configuration because a re-compilation is not required
// when experimenting with options, however, the security restrictions for the Unity web player
// do not allow access to the file system, so we use programatically specified configuration options when
// the file cannot be read.
try
{
NetworkFacade.ConfigureFrom("NetworkConfig.xml");
}
catch (Exception)
{
// If the NetworkConfigFile.xml file could not be loaded then we try setting the options programatically
ConfigurationOptions badumnaConfigOptions = new ConfigurationOptions();
// The type of discovery mechanism to use.
// Badumna requires the knowledge of one existing peer to connect with in
// order to join the network. The address of any other peer can be 'discovered' through
// two main mechanisms ;
// - NameServers (a web service dedicated to the recording of recent peers) and
// - SeedPeers (peers that are 'always' running)
// If no mechanism is specified then peers can still discovery each other through UDP broadcast,
// but only when they are on the same local network.
badumnaConfigOptions.DiscoveryType = DiscoveryType.SeedPeer;
// The source address of the discovery mechanism.
// For the NameServer mechanism this specifies the web service's url (eg. http://www.badumna.com/Login/)
// For the SeedPeer mechanism this is comma seperated list of the public addresses of the seed peers.
badumnaConfigOptions.DiscoverySource = "secure.badumna.com:21255";
// The port used for discovery of peers via UDP broadcast.
// 0 disables the use of broadcast
badumnaConfigOptions.BroadcastPort = 34524;
// The arbitration server
//badumnaConfigOptions.ArbitrationServer = "128.250.76.95:27248";
badumnaConfigOptions.ArbitrationServer = "secure.badumna.com:27248";
// For futher configurations operations see https://www.badumna.com/badumna/api/
NetworkFacade.ConfigureFrom(badumnaConfigOptions);
}
}
#region MonoBehaviour members
// Three steps to initialize and join a scene in Badumna :
// Step 1 : Initialize the Network by calling NetworkFacade.Instance.Initialize() (make sure the
// network is not initialized already by calling the function NetworkFacade.Instance.IsOnline)
//
// Step 2 : If the initialisation process is successful, then Authenticate the user by calling
// NetworkFacade.Instance.Login(). The default Login() overload is used only for testing, it DOES NOT
// validate user credentials in any way and issues a default commonly known certificate resulting
// in a totally unsecure network (any one can join and pretend to be anyone else). For more information
// see the api documentation for ITokenSupplied to provide your own authentication service or Dei for
// a ready to use service.
//
// Step 3 : Ensure that login was successful then join the scene by calling NetworkFacade.
// Instance.JoinScene(string SceneName, CreateSpatialReplica createEntityDelegate,
// RemoveSpatialReplica removeEntityDelegate)
// - createEntityDelegate function will be called when there is remote entity joining the scene
// and its position is within the interest sphere radius.
// - removeEntityDelegate function will be called when a remote entity
// leaves the scene or its position moves outside the interest sphere radius.
void Awake()
{
DontDestroyOnLoad(this); // Don't Destroy this object when a new level is loaded
// Check whether the network has been initiated yet
if (!NetworkFacade.Instance.IsInitialized)
{
// Initialize the Badumna network library
NetworkFacade.Instance.Initialize();
// Find the gui login script to get the users login and password
GameObject guiLogin;
GUILoginScript login;
if (guiLogin = GameObject.Find("GuiLogin"))
{
login = (GUILoginScript)guiLogin.GetComponent(typeof(GUILoginScript));
this.mNetworkSceneName = login.SceneName; // Record the scene chose by the user
this.mTokenSupplier = login.TokenSupplier;
NetworkFacade.Instance.Login(this.mTokenSupplier); // Login to Badumna using token supplier
}
else
{
NetworkFacade.Instance.Login();
}
}
if (NetworkFacade.Instance.IsLoggedIn)
{
// Join the chosen scene.
// Scenes are identified by a name (a string) which should be unique.
// All entities within a scene will see each other, but not any entities
// in other scenes.
this.mNetworkScene = NetworkFacade.Instance.JoinScene(this.mNetworkSceneName, this.CreateEntity, this.RemoveEntity);
Debug.Log(NetworkFacade.Instance.GetNetworkStatus().ToString());
}
else
{
Debug.LogError("Login error");
return;
}
// Additional code for arbitration server
this.Arbitrator = NetworkFacade.Instance.GetArbitrator();
this.Arbitrator.ServerEvent = delegate(byte[] message) { this.HandleArbitrationEvent(message); };
this.Arbitrator.Connect();
}
// Initialise the local avatar/player after the network initialisation processes has completed and then
// register the local avatar in the current scene by calling
// NetworkScene.RegisterEntity(ISpatialOriginal entity, uint entityType)
//
// Note: - Awake() function is always called before any Start function in Unity
// - See http://www.badumna.com/badumna/api/clas ... scene.html
// for more details information regarding RegisterEntity function.
//
void Start()
{
if (!this.CreateLocalPlayer())
{
Debug.LogError("Failed to create local avatar.");
return;
}
this.InitiateChatInterface();
}
// NetworkFacade.Instance.ProcessNetworkState() is called everytime the FixedUpdate function
// is called. ProcessNetworkState performs processing of network events that need to be processed,
// the application, eg. handling updates and other messages, add/removing entities and processing
// chat messages. This method must be called regularly to ensure timely processsing of these events.
//
// Note : - for more information regarding ProcessNetworkState(), please refer
// to http://www.badumna.com/badumna/api/1.3/badumna
// - before calling the ProcessNetworkState make sure that the Network is Initialized
// successfully. An exception will be thrown otherwise.
//
void FixedUpdate()
{
if (NetworkFacade.Instance.IsInitialized)
{
NetworkFacade.Instance.ProcessNetworkState();
if (mLocalAvatar != null)
{
this.ArbitrationEventUpdate();
}
}
// set allowChangeLevel into true when the game have more than one level, and it is possible
// to change level under certain circumstances
if(this.allowChangeLevel)
{
UnityEngine.Vector3 position = new UnityEngine.Vector3(this.mLocalAvatar.Position.X,
this.mLocalAvatar.Position.Y,this.mLocalAvatar.Position.Z);
string nextScene = ChangingScene.changeScene(position, this.mNetworkSceneName);
if (!this.mNetworkSceneName.Equals(nextScene))
{
this.mNetworkSceneName = nextScene;
this.mIsChangeLevel = true;
Application.LoadLevel(nextScene);
}
}
}
// this function is called after the level is loaded, in this case we need to register the current
// local avatar to the corresponding scene
void OnLevelWasLoaded(int level)
{
if(this.mLocalAvatar != null)
{
Debug.Log("Level:" + level);
// Check the status of the badumna network before joining a new scene
if (!NetworkFacade.Instance.IsInitialized)
{
NetworkFacade.Instance.Initialize();
if (this.mTokenSupplier != null)
{
NetworkFacade.Instance.Login(this.mTokenSupplier);
}
else
{
NetworkFacade.Instance.Login();
}
}
else if (!NetworkFacade.Instance.IsLoggedIn)
{
if (this.mTokenSupplier != null)
{
NetworkFacade.Instance.Login(this.mTokenSupplier);
}
else
{
NetworkFacade.Instance.Login();
}
}
if(NetworkFacade.Instance.IsLoggedIn)
{
this.mNetworkScene = NetworkFacade.Instance.JoinScene(this.mNetworkSceneName, this.CreateEntity, this.RemoveEntity);
}
// register local player into current scene
uint entityType = (uint)(PlayerType) Enum.Parse(typeof(PlayerType), this.mLocalAvatar.LocalAvatarTag,true);
this.mNetworkScene.RegisterEntity(this.mLocalAvatar, entityType);
this.mIsRegistered = true;
this.mIsChangeLevel = false;
Debug.Log(NetworkFacade.Instance.GetNetworkStatus().ToString());
}
}
void OnApplicationQuit()
{
// Note: OnApplicationQuit is called before the OnDisable function is called on any other behaviours,
// therefore, this is a good place to implement any code which is required to be called prior to
// individual OnDisable functions.
}
// Three steps to unregister the local entity (local player) from the scene and shut down Badumna
// Step 1: Unregister the local entity by calling NetworkScene.UnregisterEntity(ISpatialOriginal entity).
// When this function is called, other peers will receive notification of the entities departure via the
// their RemoveEntity method.
// Step 2: After successfully unregistering the entity call NetworkFacade.Instance.Logout to leave
// the network
// Step 3: Then shutdown the Badumna library by calling the NetworkFacade.Instance.Shutdown()
void OnDisable()
{
if (this.mIsRegistered)
{
// Send the event message to arbitration server before goes offline
this.SendEvent(new OfflineEvent());
this.mNetworkScene.UnregisterEntity(this.mLocalAvatar);
this.mNetworkScene.Leave();
this.mIsRegistered = false;
// when leave scene, clear the remote entities container
this.mRemoteEntities.Clear();
}
// call this two functions when exiting not when move to other level
if (NetworkFacade.Instance.IsLoggedIn && !this.mIsChangeLevel)
{
NetworkFacade.Instance.Logout();
}
if (NetworkFacade.Instance.IsInitialized && !this.mIsChangeLevel)
{
NetworkFacade.Instance.Shutdown();
}
}
#endregion
#region NetworkScene members
// This function will be called from ProccessNetworkState()
// when there is another entity nearby, i.e. within the local avatars visible region.
// It creates a remote entity (i.e. remote avatar) and stores it in the list of remote entities.
private ISpatialReplica CreateEntity(NetworkScene scene, BadumnaId entityId, uint entityType)
{
PlayerType playerType = (PlayerType)entityType;
GameObject remotePlayerObject = this.FindPlayerObject(playerType.ToString());
RemoteAvatar remoteAvatar;
if (remotePlayerObject != null)
{
remotePlayerObject.AddComponent(typeof(SyncAnimation));
remotePlayerObject.AddComponent(typeof(RemoteAvatar));
remoteAvatar = (RemoteAvatar)remotePlayerObject.GetComponent(typeof(RemoteAvatar));
if (remoteAvatar != null)
{
// The network guid should be set to the given guid
remoteAvatar.Guid = entityId;
// Note: Change the keyword if necessary, match with the remote avatars tag
// in this example, all the remote avatars tagname start with "Remote"
remoteAvatar.SetAvatarToUse(remotePlayerObject);
// Add the remote avatar to mRemoteEntities
this.mRemoteEntities.Add(entityId, remoteAvatar);
ISpatialReplica spatialReplica = remoteAvatar as ISpatialReplica;
return spatialReplica;
}
}
return null;
}
// RemoveEntity will be called if a remote entity leaves the scene (i.e. when the other
// peer called UnregisterEntity function).
private void RemoveEntity(NetworkScene scene, ISpatialReplica replica)
{
RemoteAvatar remoteAvatar = (RemoteAvatar)replica;
if (this.mRemoteEntities.TryGetValue(remoteAvatar.Guid, out remoteAvatar))
{
remoteAvatar.DestroyRemoteAvatar();
this.mRemoteEntities.Remove(remoteAvatar.Guid);
}
}
#endregion
#region ArbitrationEvent Handler
private void ArbitrationEventUpdate()
{
this.SendArbitrationPosition();
// TODO: added into event handler
if (this.mLocalAvatar.ArbitratorManager.QueueClientEvents.Count != 0)
{
ClientEvent clientEvent = this.mLocalAvatar.ArbitratorManager.QueueClientEvents.Dequeue();
this.SendEvent(clientEvent);
}
}
private void HandleArbitrationEvent(byte[] message)
{
Debug.Log("Handler is called");
ServerEvent serverEvent = (ServerEvent)ServerEventSet.Instance.Deserialize(message);
if (serverEvent is ReplyEvent)
{
Debug.Log("Reply event is received");
}
if (serverEvent is GetPunchEvent)
{
// TODO: do something when the avatar get punch
Debug.Log("GetHit");
this.mLocalAvatar.IncomingEvent(serverEvent);
}
if (serverEvent is AliveEvent)
{
Debug.Log("Back to live");
this.mLocalAvatar.IncomingEvent(serverEvent);
}
if (serverEvent is ScoreEvent)
{
Debug.Log("Score: " + ((ScoreEvent)serverEvent).Score);
if (GUIScore != null)
{
GUIScore.TextMessages += "Score: " + ((ScoreEvent)serverEvent).Score + "\n";
}
}
// TODO: do something if there is any event received from the arbitration server
}
private void SendEvent(ClientEvent clientEvent)
{
this.Arbitrator.SendEvent(ClientEventSet.Instance.Serialize(clientEvent));
}
private DateTime mLastSendTime;
private Badumna.DataTypes.Vector3 mLastPosition;
private void SendArbitrationPosition()
{
DateTime now = DateTime.Now;
if ((now - this.mLastSendTime).TotalMilliseconds < 200 ||
this.mLastPosition == this.mLocalAvatar.Position)
{
return;
}
this.mLastPosition = this.mLocalAvatar.Position;
this.mLastSendTime = now;
this.SendEvent(new MoveEvent(this.mLocalAvatar.Position,this.mLocalAvatarId));
}
#endregion
private GameObject FindPlayerObject(string objectTag)
{
GameObject existingObject = null;
for (int i = 0; i < this.ListOfAvatars.Length; i++)
{
if (this.ListOfAvatars[i].CompareTag(objectTag))
{
existingObject = this.ListOfAvatars[i];
break;
}
}
if (existingObject != null)
{
return (GameObject)GameObject.Instantiate(existingObject, transform.position, transform.rotation);
}
Debug.LogWarning(String.Format("Player with tag:{0} is not found", objectTag));
return null;
}
private bool CreateLocalPlayer()
{
try
{
String playerType = PlayerType.SmallLerpz.ToString();
String playerName = "SmallLerpz";
GameObject guiLogin;
GUILoginScript login;
if (guiLogin = GameObject.Find("GuiLogin"))
{
login = (GUILoginScript)guiLogin.GetComponent(typeof(GUILoginScript));
playerName = login.Username;
playerType = login.PlayerType;
}
GameObject playerObject = null;
if (playerType != null)
{
playerObject = this.FindPlayerObject(playerType);
if (playerObject != null)
{
// set all the components required
playerObject.AddComponent(typeof(CharacterController));
playerObject.AddComponent(typeof(ThirdPersonController));
playerObject.AddComponent(typeof(ThirdPersonSimpleAnimation));
playerObject.AddComponent(typeof(AnimationHandler));
playerObject.AddComponent(typeof(CameraFollowerScript));
playerObject.AddComponent(typeof(ArbitratorManager));
playerObject.AddComponent(typeof(LocalAvatar));
CharacterController controller = (CharacterController)playerObject.GetComponent(typeof(CharacterController));
controller.radius = 0.4f;
controller.center = new UnityEngine.Vector3(0, 1.1f, 0);
this.mLocalAvatar = (LocalAvatar)playerObject.GetComponent(typeof(LocalAvatar));
if (this.mLocalAvatar != null)
{
// Set the game object used by the local avatar, and give the returned entity
// type to the RegisterEntity() method. This type id will be passed to the
// CreateEntity() mewthod on remote peers when this avatar's replica is instantiated.
uint entityType = this.mLocalAvatar.SetAvatarToUse(playerObject, playerName,this.mLocalAvatarId);
if (entityType > 0)
{
this.mNetworkScene.RegisterEntity(this.mLocalAvatar, entityType);
this.mIsRegistered = true;
return true;
}
}
}
}
}
catch (Exception e)
{
Debug.LogError(e);
return false;
}
return false;
}
private void InitiateChatInterface()
{
GameObject parentObject = (GameObject)transform.gameObject;
parentObject.AddComponent(typeof(GUIChatScript));
GUIChatScript chatScript = (GUIChatScript)transform.GetComponent(typeof(GUIChatScript));
chatScript.LocalAvatar = this.mLocalAvatar;
}
}