Video Games Development

Client-Server Communication Socket-Based in Videogames – Basic Concepts

Client-Server Communication Socket-Based in Videogames – Basic Concepts

What is a Client-Server Communication

Client-server is a request-response remote communication model that involves processes requesting services from other processes which offer these services via the network.
The processes offering services by executing certain tasks following remote process requests are known as servers. In general, the servers receive requests from remote processes, execute the tasks associated with these services, and dispatch responses back to the requesting entities. Example of services include database information retrieval and updates, file system access services, and dedicated user-application tasks.
The process that contact the servers and request them to perform services is known as Client

In general, Client processes manage user-interfaces, validate data entered by users, dispatch requests to servers, collect servers’ responses, and/or display the information received.

Client-Server Communication in Videogames

In the fantastic world of videogames development we find:

Client
It is the videogame, installed in your PC, Mobile Device or Web Player, generally:
– manage the user input
– manage local data as scores, lives etc…
– collect data from a database installed inside a Server and manage it inside the videogame

Server
It is the computer that stores (using for example MySQL) accounts, scores and others usefull datas for the gamer
If you play a MMORPG (Massive(ly) Multiplayer Online Role-Playing Game, you will see that the Server manages a lot af data and thousands of users in the same time.

Low Data Management

If you have to manage few users or only username and scores value, the server will not work hard, it is enough a communication like that:

Client (Videogame) -> PHP POST REQUEST -> Server with PHP+MySQL offers a service

High Data Management

You are in trouble, there are thousands of users that sends thousands of PHP POST in the same time.
Using the previus scheme we have:

Client1 (Videogame) -> PHP POST -> Server with PHP+MySQL offers a service
Client2 (Videogame) -> PHP POST -> Server with PHP+MySQL offers a service
Client3 (Videogame) -> PHP POST -> Server with PHP+MySQL offers a service
Client4 (Videogame) -> PHP POST -> Server with PHP+MySQL offers a service
Client5 (Videogame) -> PHP POST -> Server with PHP+MySQL offers a service
Client6 (Videogame) -> PHP POST -> Server with PHP+MySQL offers a service
Client7 (Videogame) -> PHP POST -> Server with PHP+MySQL offers a service
Client8 (Videogame) -> PHP POST -> Server with PHP+MySQL offers a service
Client9 (Videogame) -> PHP POST -> Server with PHP+MySQL offers a service
Client1000000 (Videogame) -> PHP POST REQUEST -> Server with PHP+MySQL offers a service …-> CRASH OF THE SYSTEM!!!

The solution are:

1. Use of SSD (Solid State Drive) instead ad traditional Hard Driver
2. Use of one or more dedicated servers, not sharing IP or low-end hosting solution admitted!
3. Use of Client-Server Communication Socket-Based

The Client-Server Communication Socket-Based is:

Client1 (Videogame) -> PHP POST -> | |
Client2 (Videogame) -> PHP POST -> | |
Client3 (Videogame) -> PHP POST -> | |
Client4 (Videogame) -> PHP POST -> | |
Client5 (Videogame) -> PHP POST -> | Socket manage REQUEST | -> Server with PHP+MySQL offers a service
Client6 (Videogame) -> PHP POST -> | (Multithreaded) |
Client7 (Videogame) -> PHP POST -> | |
Client8 (Videogame) -> PHP POST -> | |
Client9 (Videogame) -> PHP POST -> | |

With WebSockets you can transfer as much data as you like without incurring the overhead associated with traditional HTTP requests. Data is transferred through a WebSocket as messages, each of which consists of one or more frames containing the data you are sending (the payload).

To run a WebSocket you need a DEDICATED IP, no SHARED IP are allowed, because you will setup your own IP and listening ports.

A popular software to create a WebSocket is Node.js (http://nodejs.org/).

WebSoscket inside a SHARED IP

You can run Node.js in a shared host using CGI-Node (http://www.cgi-node.org/).
‘CGI-Node’ is a CGI adaptor for node.js that mimmicks the Node.js http library.

Low Latency Client-Server and Server-Client Connections

Around 2005, AJAX started to make the web feel more dynamic. Still, all HTTP communication was steered by the client, which required user interaction or periodic polling to load new data from the server.

One of the most common hacks to create the illusion of a server initiated connection is called long polling. With long polling, the client opens an HTTP connection to the server which keeps it open until sending response. Long polling and the other techniques work quite well. You use them every day in applications such as GMail chat.

However, all of these work-arounds share one problem: They carry the overhead of HTTP, which doesn’t make them well suited for low latency applications. Think multiplayer first person shooter games in the browser or any other online game with a realtime component.

The WebSocket (http://dev.w3.org/html5/websockets/) specification defines an API establishing “socket” connections between a web browser and a server. In plain words: There is an persistent connection between the client and the server and both parties can start sending data at any time over a single TCP channel.

WebSocket give you a bidirectional communication technology for web apps for:
IE 10 or over
Firefox 33 or over
Chrome 31 or over
Safari 7.1 or over
Opera 26 or over
iOS Safari 7.1 or over
Android Browser 4.4 or over
Chrome for Android 39 or over

What it means single TCP Channel?

The Transmission Control Protocol (TCP) is one of the core protocols of the Internet protocol suite (IP), and is so common that the entire suite is often called TCP/IP.

IP works by exchanging pieces of information called packets. A packet is a sequence of bytes and consists of a header followed by a body. The header describes the packet’s source, destination and control information. The body contains the data IP is transmitting. The size of one IP packet is 65535 bytes (63.99902 Kbyte or 0.0625 Mbyte).

Due to network congestion, traffic load balancing, or other unpredictable network behavior, IP packets can be lost, duplicated, or delivered out of order. TCP detects these problems, requests retransmission of lost data, rearranges out-of-order data, and even helps minimize network congestion to reduce the occurrence of the other problems.

TCP is utilized extensively by many of the Internet’s most popular applications, including the World Wide Web (WWW), E-mail, File Transfer Protocol, Secure Shell, peer-to-peer file sharing, and some streaming media applications.
TCP is optimized for accurate delivery rather than timely delivery, and therefore, TCP sometimes incurs relatively long delays (on the order of seconds) while waiting for out-of-order messages or retransmissions of lost messages. It is not particularly suitable for real-time applications such as Voice over IP. For such applications, protocols like the Real-time Transport Protocol (RTP) running over the User Datagram Protocol (UDP) are usually recommended instead.

Ready to use solutions

Setup a server is very expensive, sometimes the budget and the time we can spend to develop is not enought to do that.
In the market there are some ready to use platforms to build multiplayer games.

Photon Cloud

https://www.exitgames.com

Photon Cloud is available globally to guarantee low latency and shortest round-trip times for your multiplayer games worldwide.
Distributed worldwide Photon Cloud scales seamless and automatically even for tens of thousands of concurrent users.

Customers: from indie to AAA

Support:

– Unity3D (check Unity3D Asset Store also)
– iOS
– Android
– Win8
– Unreal4
– Cocos
– HTML5
– Flash
– Marmalade
– Corona

– Realtime Multiplayer
– Turn Based Games
– Chat

App Warp

http://appwarp.shephertz.com/

Powerful, yet simple to use cloud platform for massively multiplayer realtime games

Customers: from indie to AAA

Support:

– Unity3D
– iOS
– Android
– Win8
– Unreal4
– Cocos
– HTML5
– Flash
– Marmalade
– Corona
– J2me SDK
– andengine
– Starlink SDK
– Fixel SDK
– Sencha SDK

Game Cloud

http://game-cloud.org/

Using our advanced technology we reduce the traffic to the player allowing large number of players to connect even on slow (mobile) networks without lagging

Support:

– Unity3D (Build for Unity3D integrated into Unity Editor Tools)
– Win 8
– Android
– Mac OS
– Linux
– Playstation
– XBox 360
– Wii
– iOS
– Browsers

Gamooga

http://www.gamooga.com/

State-of-art realtime backend for your apps/games that guarantees under 1ms response time. None of our competitiors can beat us here!

Support:

– Unity3D
– HTML5
– iOS
– Android
– Falsh
– Marmalade

Gamespark

http://www.gamesparks.com/

GameSparks ensures you keep your costs to a minimum whether they be intial set-up or recurring fees

Support:

– Unity3D
– Unreal 4
– iOS
– Android
– Falsh
– Marmalade

My official website: http://www.lucedigitale.com

References:

– Server Side Score
http://www.lucedigitale.com/blog/unity-3d-server-side-highscores-js-programming/

– Advanced Network Programming – Principles and Techniques (By Bogdan Ciubotaru, Gabriel-Miro Muntean)

– http://www.html5rocks.com/en/tutorials/websockets/basics/ (By Malte Ubl and Eiji Kitamura)

– http://caniuse.com/#feat=websockets

By |Video Games Development|Commenti disabilitati su Client-Server Communication Socket-Based in Videogames – Basic Concepts

Unity3D Tutorials – Multiplayer Introduction

Unity3D Tutorials – Multiplayer Introduction

In this article I discuss how to create a simple multiplayer game using a Windows Home Network and 2 PC.
First we need some basic concept about Network and Networking.

Network Computing – Basic Concepts

A Network of PC
To create a network we need 2 or more PC connected with a Router using a LAN cable.

The simplest network is:

PC1 <-> Router <-> PC2

The router is a networking device that forwards data packets between computer networks.

Network Software Setup

If you use Windows7 is very simple setup a Home Network, go to Start button> ‘Control Panel’ and typing homegroup in the search box, and then clicking HomeGroup. After that Windows7 assigns a dynamic IP adress to every PC connected on the network.

An IP adress is a adress that uniquely identifies that individual host maschine inside the network.
An IP adress can be:
– Dynamic, the router assigns automatically a different IP number to every PC connected
– Static, you can manually assign the IP number to your PC

The final resul will be:

PC1(IP 192.168.1.10) <-> Router <-> PC2(IP 192.168.1.11)

IP Private – IP Public & NAT

The most common form of network translation involves a large private network using addresses in a private range (10.0.0.0 to 10.255.255.255, 172.16.0.0 to 172.31.255.255, or 192.168.0 0 to 192.168.255.255). The private addressing scheme works well for computers that only have to access resources inside the network, like workstations needing access to file servers and printers. Routers inside the private network can route traffic between private addresses with no trouble.

However, to access resources outside the network, like the Internet, these computers have to have a public address in order for responses to their requests to return to them. This is where NAT (Network Address Translation) comes into play.
A workstation inside a network makes a request to a computer on the Internet. Routers within the network recognize that the request is not for a resource inside the network, so they send the request to the firewall. The firewall sees the request from the computer with the internal IP. It then makes the same request to the Internet using its own public address, and returns the response from the Internet resource to the computer inside the private network. From the perspective of the resource on the Internet, it is sending information to the address of the firewall. From the perspective of the workstation, it appears that communication is directly with the site on the Internet. When NAT is used in this way, all users inside the private network access the Internet have the same public IP address when they use the Internet. That means only one public addresses is needed for hundreds or even thousands of users.

The scheme is:

PC1(IP Private) | <->
PC2(IP Private) | <->
PC2(IP Private) | <-> Router <-> Firewall (NAT) assign one IP Public <-> Internet
PC2(IP Private) | <->
PC2(IP Private) | <->

Ports

In computer networking, a port is an application-specific or process-specific software construct serving as a communications endpoint in a computer’s host operating system. The purpose of ports is to uniquely identify different applications or processes running on a single computer and thereby enable them to share a single physical connection to a packet-switched network like the Internet. In the context of the Internet Protocol, a port is associated with an IP address of the host, as well as the type of protocol used for communication.

An example for the use of ports is the Internet mail system. A server used for sending and receiving email generally needs two services. The first service is used to transport email to and from other servers. This is accomplished with the Simple Mail Transfer Protocol (SMTP). The SMTP service application usually listens on TCP port 25 for incoming requests. The second service is usually either the Post Office Protocol (POP) or the Internet Message Access Protocol (IMAP) which is used by e-mail client applications on user’s personal computers to fetch (recuperare) email messages from the server. The POP service listens on TCP port number 110.

The well-known ports (also known as system ports) are those from 0 through 1023.

Very popular are:

20 & 21: File Transfer Protocol (FTP)
22: Secure Shell (SSH)
23: Telnet remote login service
25: Simple Mail Transfer Protocol (SMTP)
53: Domain Name System (DNS) service
80: Hypertext Transfer Protocol (HTTP) used in the World Wide Web
110: Post Office Protocol (POP3)
119: Network News Transfer Protocol (NNTP)
143: Internet Message Access Protocol (IMAP)
161: Simple Network Management Protocol (SNMP)
194: Internet Relay Chat (IRC)
443: HTTP Secure (HTTPS)
465: SMTP Secure (SMTPS)

The registered ports are those from 1024 through 49151.

The scheme is:

External application <-> Router IP/Port <-> destination in network IP/Port

Port 25001 is default port for Unity3D game engine networking, UDP protocol.

Game Network approches

There are two common and proven approaches to structuring a network game which are known as Authoritative Server and Non-Authoritative Server.

The authoritative server approach requires the server to perform all world simulation, application of game rules and processing of input from the player clients.

A non-authoritative server does not control the outcome of every user input. The clients themselves process user input and game logic locally, then send the result of any determined actions to the server. The server then synchronizes all actions with the world state. This is easier to implement from a design perspective, as the server really just relays messages between the clients and does no extra processing beyond what the clients do.

Methods of Network Communication

How clients and servers can talk to each other?

There are two relevant methods: Remote Procedure Calls and State Synchronization. It is not uncommon to use both methods at different points in any particular game.

Remote Procedure Calls (RPCs) are used to invoke functions on other computers across the network, although the “network” can also mean the message channel between the client and server when they are both running on the same computer. Clients can send RPCs to the server, and the server can send RPCs to one or more clients. Most commonly, they are used for actions that happen infrequently. For example, if a client flips a switch to open a door, it can send an RPC to the server telling it that the door has been opened. The server can then send another RPC to all clients, invoking their local functions to open that same door. They are used for managing and executing individual events.

State Synchronization is used to share data that is constantly changing. The best example of this would be a player’s position in an action game. The player is always moving, running around, jumping, etc. All the other players on the network, even the ones that are not controlling this player locally, need to know where he is and what he is doing. By constantly relaying data about this player’s position, the game can accurately represent that position to the other players.

Minimizing Network Bandwidth

When working with State Synchronization across multiple clients, you don’t necessarily need to synchronize every single detail in order to make objects appear synchronized. For example, when synchronizing a character avatar you only need to send its position and rotation between clients.

For example, you know that assets like textures and meshes exist on all installations and they usually don’t change, so they will never have to be synchronized.

Bear in mind that you can use a single RPC call with a level name to make all clients load the entire specified level and add their own networked elements automatically. Structuring your game to make each client as self-sufficient as possible will result in reduced bandwidth.

Server Physical Location

The physical location and performance of the server itself can greatly affect the playability of a game running on it. Clients which are located a continent away from the server may experience a great deal of lag.

Unity 3D – State Synchronization

For this Unity3D tutorial I am going to work with a simple Home Network created with Windows7.

1. Create a scene with:
– Main Camera
– Cube, Pos XYZ 0,0.5,0 – Scale xYZ 1,1,1
– Plane, Pos XYZ 0,0,0 – Scale xYZ 10,10,10
– Directional Light
– Empty Object, name it ‘NetworkController’

2. Attach to the Cube, Movement.js


#pragma strict
 
function Start () {
 
}
 
function Update () {
 
 var horiz : float = Input.GetAxis("Horizontal"); // get AD buttons input
 var vert : float = Input.GetAxis("Vertical");    // get WS buttons input
 transform.Translate(Vector3(horiz,0,vert));      // move along X -> AD and Z -> WS
}

3. Attach to NetworkController, NetworkManagerScript.js


#pragma strict

var btnX:float; // Buttons Setup
var btnY:float;
var btnW:float;
var btnH:float;

function Start () {
	btnX = Screen.width * 0.05; // Buttons Setup
	btnY = Screen.width * 0.05; // Dimension based on Screen.width
	btnW = Screen.width * 0.2;
	btnH = Screen.width * 0.2;

}

function startServer() {
        // This machine will be the sever
        // Initialize the server, number of allowed connection 32, listenport 25001, NOT have a public IP Adress
	Network.InitializeServer(32,25001, !Network.HavePublicAddress);
}

// Called when Network.InitializeServer was invoked and has completed.
function OnServerInitialized() {
	Debug.Log("Server Initialized");
}

function OnGUI() {
	if(GUI.Button(Rect(btnX, btnY,btnW,btnH),"Start Server")){
	Debug.Log("Starting Server");
	startServer();
	}
	
	if(GUI.Button(Rect(btnX, btnY*1.2+btnH,btnW,btnH),"Refresh Hosts")){
	Debug.Log("Refreshing");
	
	}
}

function Update () {

}

Play, in the console you will receive the message ‘Sever Initialized’
Good! Now my Unity3D application is the Server.

Next, improve NetworkManagerScript.js


#pragma strict

var gameName: String = "MyFirstNetGame"; 

private var btnX:float; // Buttons Setup
private var btnY:float;
private var btnW:float;
private var btnH:float;

function Start () {
	btnX = Screen.width * 0.05; // Buttons Setup
	btnY = Screen.width * 0.05; // Dimension based on Screen.width
	btnW = Screen.width * 0.2;
	btnH = Screen.width * 0.2;

}

function startServer() {
        // This machine will be the Server
        // Initialize the server, number of allowed connection 32, listenport 25001, NOT have a public IP Adress
	Network.InitializeServer(32,25001, !Network.HavePublicAddress);
	
	// Register this server on the master server 
	// The Master Server is a meeting place that puts game instances in touch with the player clients who want to connect to them. 
	// Each individual running game instance provides a Game Type to the Master Server
	//(gameTypeName: string, gameName: string, comment: string = "")
	MasterServer.RegisterHost(gameName, "My Game Name", "This is a comment");
}

// Called when Network.InitializeServer was invoked and has completed.
function OnServerInitialized() {
	Debug.Log("Server Initialized");
}

// Called on clients or servers when reporting events from the MasterServer
function OnMasterServerEvent(mse:MasterServerEvent){
        // check if registration is ok
	if (mse == MasterServerEvent.RegistrationSucceeded){
	Debug.Log("Registrered Server!");
	}
}

function OnGUI() {
	if(GUI.Button(Rect(btnX, btnY,btnW,btnH),"Start Server")){
	Debug.Log("Starting Server");
	startServer();
	}
	
	if(GUI.Button(Rect(btnX, btnY*1.2+btnH,btnW,btnH),"Refresh Hosts")){
	Debug.Log("Refreshing");
	
	}
}

function Update () {

}

Play it, you will get in console “Server Initialized -> Registrered Server!”

For italian people: come funziona?

1. Network.InitializeServer(32,25001, !Network.HavePublicAddress);
Inizializza l’applicazione Server attraverso il network, identifica IP, la porta, il numero massimo di connessioni ammesse.

2. MasterServer.RegisterHost(gameName, “My Game Name”, “This is a comment”);
Il MasterServer è il punto di incontro tra i vari Clients, quindi registriamo presso il MasterServer l’applicazione con un identificativo di tipo, nome e un commento.

Next, improve NetworkManagerScript.js


#pragma strict

var gameName: String = "MyFirstNetGame"; 
private var refreshing: boolean;
private var hostData: HostData[];

private var btnX:float; // Buttons Setup
private var btnY:float;
private var btnW:float;
private var btnH:float;

function Start () {
	btnX = Screen.width * 0.05; // Buttons Setup
	btnY = Screen.width * 0.05; // Dimension based on Screen.width
	btnW = Screen.width * 0.2;
	btnH = Screen.width * 0.2;

}

function startServer() {
    // This machine will be the Server
    // Initialize the server, number of allowed connection 32, listenport 25001, NOT have a public IP Adress
	Network.InitializeServer(32,25001, !Network.HavePublicAddress);
	
	// Register this server on the master server 
	// The Master Server is a meeting place that puts game instances in touch with the player clients who want to connect to them. 
	// Each individual running game instance provides a Game Type to the Master Server
	//(gameTypeName: string, gameName: string, comment: string = "")
	MasterServer.RegisterHost(gameName, "My Game Name", "This is a comment");
} // END startServer

function refreshHostList() {
    // check the number of hosts registered in MasterServer
    // controlla il numero di applicazioni registrate in MasterServer con 'gameName'
    // la registrazione al MasterServer può richiedere dai 3 ai 6 secondi
	MasterServer.RequestHostList(gameName);
	refreshing = true;
} // END refreshHostList

// Called when Network.InitializeServer was invoked and has completed.
function OnServerInitialized() {
	Debug.Log("Server Initialized");
}// END OnServerInitialized

// Called on clients or servers when reporting events from the MasterServer
function OnMasterServerEvent(mse:MasterServerEvent){
    // check if registration is ok
	if (mse == MasterServerEvent.RegistrationSucceeded){
	Debug.Log("Registrated Server!");
	}
}

// GUI Graphic User Interface
function OnGUI() {
//  if your peer type is NOT client AND is NOT server, cioè se non è già connnesso
// questo if fa scomparire i bottoni una volta cliccato su 'Start Server'
if (!Network.isClient && !Network.isServer){
	if(GUI.Button(Rect(btnX, btnY,btnW,btnH),"Start Server")){
	Debug.Log("Starting Server");
	startServer();
	}
	
	if(GUI.Button(Rect(btnX, btnY*1.2+btnH,btnW,btnH),"Refresh Hosts")){
	Debug.Log("Refreshing");
	refreshHostList();
	}
	
	// se esistono dei gameName registrati su MasterServer
	if (hostData){
		// visualizza un array di bottoni con tutti i gameName registrati come MasterServer
		for(var i:int = 0; i<hostData.length; i++){
		    // se viene premuto il bottone
			if (GUI.Button(Rect(btnX*1.5 + btnW, btnY*1.2 + (btnH * i),btnW*3, btnH*0.5), hostData[i].gameName)){
				// Connect to the specified host (ip or domain name) and server port
				// Example: Network.Connect("127.0.0.1", 25000)
				Network.Connect(hostData[i]);
			}
		}
	}
}	
}// END OnGUI

function Update () {
    // se refreshing è true, cioè ho cliccato sul bottone 'Refresh Hosts'
	if (refreshing) {
		// se c'è almeno una registrazione
		if (MasterServer.PollHostList().Length > 0)  {
		// disabilita il refreshing per evitare che continui ad eseguirlo ad ogni ciclo Update
		refreshing = false;
		Debug.Log(MasterServer.PollHostList().Length);
		// array con tutte le registrazioni al MasterServer
		hostData = MasterServer.PollHostList();
		}	
	}// END Refreshing

}// END Update

For italia people: come funziona?

1. Inseriamo in un array – private var hostData: HostData[] – tutte le connessioni registrare nel MasterServer
2. Con un ciclo for le visualizzo utilizzando una grafica composta di bottoni – (var i:int = 0; iTestBuild.exe

1. MAIN TOP MENU> Edit> Project Settings> Player> Inspector> Settings for PC Mac LinuX Standalone> Run in Background: check

L’applicazione deve essere attiva in background perchè deve continuare ad inviare dati al MasterServer anche quando non lo stiamo utilizzando.
Se così non fosse ogni volta che usciamo dal gioco, lasciandolo attivo nella barra di windows, la connessione con il MasterServer andrebbe persa.
Con l’applicazione settata per funzionare in background invece posso lasciare il gioco attivo, utilizzare un’altra applicazione e tornare al gioco senza dover di nuovo effettuare la procedura di registrazione al MasterServer.

2. MAIN TOP MENU> File> Buil Settings…> PC Mac Linux Standalone> Build, TestBuild.exe

a. Run the standalone build, click ‘Start Server’, the buttons will disapper
b. Play inside Unity3D the Project> click ‘Refresh Hostst’, you will see the registered host, yeah it works!

For italian people: come funziona?
a. Creo un buil stand alone e l’avvio, questa crea il MasterServer e si registra presso il MasterServer come client
b. Avvio da Unity3D il progetto, cliccando su’Refresh Hosts’ osservo che effettivamente la build standalone funziona ed è reistrata presso il MasterServer.
c. Posso provare a chiedere TestBuild.exe e riavviare il progetto su Unity3D, cliccare su ‘Refresh Hosts’, nessun applicazione risulterà registrata a MasterServer

Network View Component

1. Hierarchy> select Cube (our player)> Inspector > ‘Add Component’> Miscellaneous> Network View:

– State Syncronization: Reliable Delta Compressed
The difference between the last state and the current state will be sent, if nothing has changed nothing will be sent.

– Observed: Cube(Transform)
The Component data that will be sent across the network

‘Network Views’ are the gateway to creating networked multiplayer games in Unity.

New TestBuild.exe

1. Save the scene and the project
2. Build and run the standalone exe, press ‘Start Server’, all buttons disappear.
3. Play inside Unity3D the Project> click ‘Refresh Hosts’, the button ‘Tutorial Game Name’ appears, press it to starl this client, all buttons disappear.
4. Jump to standalone exe, move the Cube using the arrow key. In the Unity Game window you will see the cube goes all around!

For italian people: come funziona?

1. Primo eseguibile: premendo ‘Start Server’ questa applicazione funzionerà da Server e in più si registrerà come client
2. Secondo eseguibile: facendo un refresh degli hosts si visualizza il Server attualmente registrato (il primo eseguibile)
3. Secondo eseguibile: facendo click sul nome del Server si registra il secondo eseguibile come client
Se si tenta di far partire il server dal secondo eseguibile si riceverà la risposta che il server è già stato avviato.

The Player is mine!

A this point in time both players can move the same cube… mmm… it sounds strange…

Let’s take a look to the new Movement.js


#pragma strict
 
function Start () {
 
}// END Start()
 
function Update () {
 
 var horiz : float = Input.GetAxis("Horizontal"); // get AD buttons input
 var vert : float = Input.GetAxis("Vertical");    // get WS buttons input
 
 // Solo il server, cioè quello che parte per primo potrà spostare il cubo
 // Object instanted by me
 if (networkView.isMine){
 		transform.Translate(Vector3(horiz,0,vert));      // move along X -> AD and Z -> WS
	}
}// END Update()

For italian people: come funziona?
1. Il primo eseguibile che viene avviato genera l’oggetto e diventa di sua proprietà – networkView.isMine –
2. Solo se l’oggetto è di mia proprietà puoi muoverlo
3. Il secondo eseguibile può solo assistere al movimento del cubo senza poter inviare dei comandi

Spawn Player

1. Add to NetworkManagerScript.js the code:


...

var playerPrefab: GameObject;
var spawnObject: Transform;

...

function spawnPlayer() {
	Network.Instantiate(playerPrefab, spawnObject.position, Quaternion.identity, 0);
}

// Called when Network.InitializeServer was invoked and has completed.
function OnServerInitialized() {
	Debug.Log("Server Initialized");
	spawnPlayer();
}// END OnServerInitialized

function OnConnectedToServer() {
	spawnPlayer();
}

...

2. DRAG ANG DROP the Cube from Hierarchy to Project, it will become a Prefab

3. Hierarchy> select Cube and press CANC on the keyboard

4. MAIN TOP MENU> GameObject> Create Empty, name it SpawnPoint, position it in the point where the Cube will appear.

5a. DRAG AND DROP Hierarchy> SpawnPoint over NetworkController> NetworkManagerScript> var spawnObject
5b. DRAG AND DROP Project> Cube over NetworkController> NetworkManagerScript> var playerPrefab

6. Add to NetworkManagerScript.js the code:


...

 if (networkView.isMine){
 		transform.Translate(Vector3(horiz,0,vert));      // move along X -> AD and Z -> WS
	} else {
	enabled = false;
	}
}// END Update()

...

For italian people: come funziona?
1. Trasformo il Cubo che rappresenta il nostro player in un Prefab e poi lo cancello dalla scena rimuovendolo da Hierarchy
2. Aggiungo un oggetto vuoto per segnare la posizione di generazione del Cubo
3. Con la modifica effettuata al codice ogni client genera il proprio Cubo, mi devo ricordare di assegnare in Inspector il Prefab che rappresenta il player e l’oggetto Empty che rappresenta la posizione in cui il player sarà generato.
5. In comune resta solo l’ambientazione costituita dal piano e dalla luce
6a. spawnPlayer() viene richiamata all’inizializzazione del server – OnServerInitialized() – per l’eseguibile server
6b. spawnPlayer() viene richiamata all’inizializzazione del client – OnConnectedToServer() – per l’eseguibile client
7. Se l’oggetto non è mio, disabilitalo – enabled = false; –

Unity 3D – RPC

0. Project> select the Cube> Add Component> Physic> Rigid Body> check ‘Is Kinematic’
NOTE: Without Rigid Body Component collisions will not be detected!

1. MAIN TOP MENU> GameObject> 3D Object> Sphere> Inspector> Collider> check ‘Is Trigger’, move it at Position X=-3 Y=0 Z=0

2. Attach to the Sphere, SphereScript.js


#pragma strict

function Start () {
}

function Update () {
}

function OnTriggerEnter (other : Collider) {
		Debug.Log("Collision detected!");
		renderer.material.color = Color(1,0,0,1);
}// END OnTriggerEnter

3. Play, ‘Start the Server’, move the Cube to collide with the Sphere, the Sphere will change color to red.

4. Hierarchy> select the Sphere> Inspector> ‘Add Component’> Miscellaneus> Network View:
– State Syncronization: Off, it is ideal for RPC
– Observed: None

Change SphereScript.js as:


function OnTriggerEnter () {
	Debug.Log("Collision detected!");	
	var newCol:Vector3 = Vector3(1,0,0);
	// Is sends the function SetColor(), to everyone and adds to the buffer
	networkView.RPC("SetColor",RPCMode.AllBuffered, newCol);
}// END OnTriggerEnter

// RPC Tag
@RPC
function SetColor(newColor:Vector3) {
	renderer.material.color = Color(newColor.x,newColor.y,newColor.z,1);
}// END SetColor()

For italian peolple: come funziona?
1. Non abbiamo bisogno di controllare costantemente il colore della sfera con una comunicazione di tipo Inspector> NetworkView> StateSyncronization, che viene disabilitata.
2. Operiamo il cambio di colore solo se c’è la collisione, quindi selezioniamo un metodo RPC
3. – networkView.RPC(“SetColor”,RPCMode.AllBuffered, newCol); – segnala a tutti i client connessi di avviare la funzione – SetColor() – che cambia il colore ad ogni sfera in ogni client connesso.
4. In definitiva posso avviare i 2 eseguibili, ogni giocatore muoverà il suo player (Cube) e il primo che arriva alla sfera gli farà cambiare colore in rosso.

Final Overview

Now a final overview:

Hierarchy:

– Main Camera
– Sphere (it is the goal to reach)
– Plane
– Directional Light
– NetworkController (Empty)
– SpawnPoint (Empty, give the PosXYZ to spawn Cube)

Project:

– Cube (prefab, It is our Player)
– Scene
– Scripts

Particular Components List:

– NetworkController (Empty)
— NetworkManagerScript.js to manage Server and Client

– Cube (prefab, It is our Player)
— Rigid Body – is kinematic
— Box Collider
— MovementScript,js to move the cube
— NetworkView – State Syncronization Reliable Delta Compressed

– Sphere (it is the goal to reach)
— Sphere Collider – is Trigger
— SphereScript.js to cahnge its color
— NetworkView – StateSyncronization None becuase it use RPC communication inside SphereScript.js

The final scripts:

Movement.js


#pragma strict
 
function Start () {
 
}// END Start()
 
function Update () {
 
 var horiz : float = Input.GetAxis("Horizontal"); // get AD buttons input
 var vert : float = Input.GetAxis("Vertical");    // get WS buttons input
 
 // Solo il server, cioè quello che parte per primo potrà spostare il cubo
 // Object instanted by me
 if (networkView.isMine){
 		transform.Translate(Vector3(horiz,0,vert));      // move along X -> AD and Z -> WS
	} else {
	enabled = false;
	}
}// END Update()

NetworkManagerScript.js


#pragma strict

var playerPrefab: GameObject;
var spawnObject: Transform;

var gameName: String = "MyFirstNetGame"; 

private var refreshing: boolean;
private var hostData: HostData[];

private var btnX:float; // Buttons Setup
private var btnY:float;
private var btnW:float;
private var btnH:float;

function Start () {
	btnX = Screen.width * 0.05; // Buttons Setup
	btnY = Screen.width * 0.05; // Dimension based on Screen.width
	btnW = Screen.width * 0.2;
	btnH = Screen.width * 0.2;

}

function startServer() {
    // This machine will be the Server
    // Initialize the server, number of allowed connection 32, listenport 25001, NOT have a public IP Adress
	Network.InitializeServer(32,25001, !Network.HavePublicAddress);
	
	// Register this server on the master server 
	// The Master Server is a meeting place that puts game instances in touch with the player clients who want to connect to them. 
	// Each individual running game instance provides a Game Type to the Master Server
	//(gameTypeName: string, gameName: string, comment: string = "")
	MasterServer.RegisterHost(gameName, "My Game Name", "This is a comment");
} // END startServer

function refreshHostList() {
    // check the number of hosts registered in MasterServer
    // controlla il numero di applicazioni registrate in MasterServer con 'gameName'
    // la registrazione al MasterServer può richiedere dai 3 ai 6 secondi
	MasterServer.RequestHostList(gameName);
	refreshing = true;
} // END refreshHostList

function spawnPlayer() {
	Network.Instantiate(playerPrefab, spawnObject.position, Quaternion.identity, 0);
}

// Called when Network.InitializeServer was invoked and has completed.
function OnServerInitialized() {
	Debug.Log("Server Initialized");
	spawnPlayer();
}// END OnServerInitialized

function OnConnectedToServer() {
	spawnPlayer();
}

// Called on clients or servers when reporting events from the MasterServer
function OnMasterServerEvent(mse:MasterServerEvent){
    // check if registration is ok
	if (mse == MasterServerEvent.RegistrationSucceeded){
	Debug.Log("Registrated Server!");
	}
}

// GUI Graphic User Interface
function OnGUI() {
//  if your peer type is NOT client AND is NOT server, cioè se non è già connesso
// questo if fa scomparire i bottoni una volta cliccato su 'Start Server'
if (!Network.isClient && !Network.isServer){
	if(GUI.Button(Rect(btnX, btnY,btnW,btnH),"Start Server")){
	Debug.Log("Starting Server");
	startServer();
	}
	
	if(GUI.Button(Rect(btnX, btnY*1.2+btnH,btnW,btnH),"Refresh Hosts")){
	Debug.Log("Refreshing");
	refreshHostList();
	}
	
	// se esistono dei gameName registrati su MasterServer
	if (hostData){
		// visualizza un array di bottoni con tutti i gameName registrati come MasterServer
		for(var i:int = 0; i<hostData.length; i++){
		    // se viene premuto il bottone
			if (GUI.Button(Rect(btnX*1.5 + btnW, btnY*1.2 + (btnH * i),btnW*3, btnH*0.5), hostData[i].gameName)){
				// Connect to the specified host (ip or domain name) and server port
				// Example: Network.Connect("127.0.0.1", 25000)
				Network.Connect(hostData[i]);
			}
		}
	}
}	
}// END OnGUI

function Update () {
    // se refreshing è true, cioè ho cliccato sul bottone 'Refresh Hosts'
	if (refreshing) {
		// se c'è almeno una registrazione
		if (MasterServer.PollHostList().Length > 0)  {
		// disabilita il refreshing per evitare che continui ad eseguirlo ad ogni ciclo Update
		refreshing = false;
		Debug.Log(MasterServer.PollHostList().Length);
		// array con tutte le registrazioni al MasterServer
		hostData = MasterServer.PollHostList();
		}	
	}// END Refreshing

}// END Update

SphereScript.js


function OnTriggerEnter () {
	Debug.Log("Collision detected!");	
	var newCol:Vector3 = Vector3(1,0,0);
	// Is sends the function SetColor(), to everyone and adds to the buffer
	networkView.RPC("SetColor",RPCMode.AllBuffered, newCol);
}// END OnTriggerEnter

// RPC Tag
@RPC
function SetColor(newColor:Vector3) {
	renderer.material.color = Color(newColor.x,newColor.y,newColor.z,1);
}// END SetColor()

That’s all folks!

NOTE: You will notice a small delay between the input and the actual movement, this is because the position is updated after the new data is received. All we can do is predict what is going to happen based on the old data.

One method to predict the next position is by taking the velocity into account. A more accurate end position can be calculated by adding the velocity multiplied by the delay.

See the lesson 2 about Multiplayer – Latency Compensating Methods at:
http://www.lucedigitale.com/blog/unity3d-tutorials-multiplayer-latency-compensating-methods/

My official website: http://www.lucedigitale.com

References:

– http://docs.unity3d.com/Manual/net-HighLevelOverview.html
– http://www.inetdaemon.com/tutorials/internet/ip/whatis_ip_network.shtml
– http://whatismyipaddress.com/nat
– http://vimeo.com/33996023#
– http://www.paladinstudios.com/2013/07/10/how-to-create-an-online-multiplayer-game-with-unity/

Extra resources:

– http://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking
– http://developer.valvesoftware.com/wiki/Lag_Compensation
– http://developer.valvesoftware.com/wiki/Working_With_Prediction
– http://www.gamasutra.com/resource_guide/20020916/lambright_01.htm

By |Unity3D, Video Games Development|Commenti disabilitati su Unity3D Tutorials – Multiplayer Introduction

Unity3D – Tutorials – Encrypt MD5

To send data over internet, as scores or player names, the best practice is encrypt data to prevent sniffing.
Sniffing allows individuals to capture data as it is transmitted over a network.

The MD5 message-digest algorithm is a widely used cryptographic hash function producing a 128-bit (16-byte) hash value, typically expressed in text format as a 32 digit hexadecimal number. MD5 has been utilized in a wide variety of cryptographic applications, and is also commonly used to verify data integrity.
MD5 was designed by Ron Rivest in 1991 to replace an earlier hash function, MD4.The source code in RFC 1321 contains a “by attribution” RSA license.

Unity3D

Create in Unity3D:
1. Main Camera
2. Empty GameObject, name it GameController, attach to it the next script:

MD5Function.js:


#pragma strict

var myString : String = "Andrea"; // my string
var myStringMd5 : String;         // my encrypted string

// Button to encript START
function OnGUI () {
    if (GUI.Button (Rect (10,10,550,100), "String: " + myString + " " + "Ecrypted: " + myStringMd5)) {
        // send datas to function to encrypt
        myStringMd5 = Md5Sum(myString);
    }
} // END OnGUI
// Button to encript END 
 
// MD5 Encrytpt START ############################################################### 
// Server side note: the output is the same of the PHP function - md5($myString) -
static function Md5Sum(strToEncrypt: String)
{
	var encoding = System.Text.UTF8Encoding();
	var bytes = encoding.GetBytes(strToEncrypt);
 
	// encrypt bytes
	var md5 = System.Security.Cryptography.MD5CryptoServiceProvider();
	var hashBytes:byte[] = md5.ComputeHash(bytes);
 
	// Convert the encrypted bytes back to a string (base 16)
	var hashString = "";
 
	for (var i = 0; i < hashBytes.Length; i++)
	{
		hashString += System.Convert.ToString(hashBytes[i], 16).PadLeft(2, "0"[0]);
	}
 
	return hashString.PadLeft(32, "0"[0]);
}// End Md5Sum
// MD5 Encrypt END #####################################################################

The result is: 28f719c89ef7f33ce2e178490676b5ab

Server Side – PHP

We can verify the result using the PHP code:


<?php
$str = 'Andrea';

if (md5($str) === '28f719c89ef7f33ce2e178490676b5ab') {
    echo "Yes! He is Andrea!";
}
?>

My official website: http://www.lucedigitale.com

Original Article: http://wiki.unity3d.com/index.php?title=MD5

By |Unity3D, Video Games Development|Commenti disabilitati su Unity3D – Tutorials – Encrypt MD5

Unity 3D – Server Side Highscores – JS Programming

Unity 3D – Server Side Highscores – JS Programming

How to manage server side highscores with Unity3D, PHP and MySQL.

Create a Data Base

Blue Host users:

CPanel> Database Tools> MySQL databases>

– Create a Database:
name -> game_scores
rules-> Collation

– Create a User

– Assign User to Database

CREATE TABLE ‘scores’

Blue Host users:

CPanel> phpMyAdmin> Enter with username and password

LEFT COLUMN> you will see your new empty Database.

A Database is a data structure with tables, every table has rows and columns.

TOP LABELS> SQL, here we can write the next SQL Query:


CREATE TABLE 'scores' (
   'id' INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
   'name' VARCHAR(15) NOT NULL DEFAULT 'anonymous',
   'score' INT(10) UNSIGNED NOT NULL DEFAULT '0'
)
TYPE=MyISAM;

If you get an SQL Syntax error, replace TYPE=MyISAM; by ENGINE=MyISAM; as TYPE is deprecated.

If you get others syntax errors you can: phpMyAdmin> LEFT COLUMN> Crea Tabella +> Create the table

We will create:

id
– it is our index
– it is an integer number from 0 to 9999999999
– it is UNSIGNED, it means that can’t be negative (a SIGNED integer can hold both positive and negative numbers)
– it can’t be NULL (NULL value is different from zero, it means no value)
– it have an autoincrement of 1 unit
– it is our primary key, the primary key of a relational table uniquely identifies each record in the table.

name
– it is the nickname of our player
– it can be a string of 15 characters
– it can’t be NULL
– the default value will be ‘anonymous’, if the user does not type a nickname

score
– it is the score value
– it is an integer number from 0 to 9999999999
– it is UNSIGNED, it means that can’t be negative (a SIGNED integer can hold both positive and negative numbers)
– it can’t be NULL (NULL value is different from zero, it means no value)
– the default value is zero

addscore.php

Create into your server the script:


<?php 
        // Create connection
        // Statement: mysqli_connect(host,username,password,dbname)
        // NOTICE: se lo script è installato nello stesso server del Data Base, host->localhost 
        $db = mysql_connect('mysql_host', 'mysql_user', 'mysql_password') or die('Could not connect: ' . mysql_error()); 
        mysql_select_db('my_dbname') or die('Could not select database');
  
        // Strings must be escaped to prevent SQL injection attack. 
        $name = mysql_real_escape_string($_GET['name'], $db); 
        $score = mysql_real_escape_string($_GET['score'], $db);  
  
        // Send variables for the MySQL database class. 
        $query = "INSERT INTO scores VALUES (NULL, '$name', '$score');"; 
        $result = mysql_query($query) or die('Query failed: ' . mysql_error());     
?>

For italian people: come funziona?

1. Apro una connessione con il database fornendo i dati di accesso – ‘mysql_host’, ‘mysql_user’, ‘mysql_password’) –

2. Se la connessione è andata a buon fine non viene restituito alcun messaggio, se fallisce – die(‘Could not select database’) – viene restituito un messaggio di errore.

3. Con il comando – $_GET – ricevo i dati POST inviati dal gioco che vengono immagazzinati nelle variabili $name, $score

4. Per evitare l’hacking del database – mysql_real_escape_string() – non aggiunge le sequenze di escape a % ed a _.
In questo modo i malintenzionati non potranno operare ‘SQL injection’ inserendo codice maligno all’interno di una query SQ.

5. Inserisce nella tabella ‘scores’ i valori ‘$name’ e ‘$score’

6. Se fallisce restituisce il messaggio ‘Query failed’.

display.php

This script will take the top 5 scores from the MySQL Database, Unity3D will read the render of this script and put it into a GUIText.


<?php
 
    // To change the total number of row you need only change LIMIT 5
    // Example: to see the top ten set LIMIT 10
 
    // Connect to database
    // NOTICE: se lo script è installato nello stesso server del Data Base, mysql_host->localhost 
    $database = mysql_connect('mysql_host', 'mysql_user', 'mysql_password') or die('Could not connect: ' . mysql_error());
    mysql_select_db('my_dbname') or die('Could not select database');
  
    // Send a query, order the records in descending 
    $query = "SELECT * FROM scores ORDER BY score DESC LIMIT 5";
    // Store the result inside a variable or If fails send an error message
    $result = mysql_query($query) or die('Query failed: ' . mysql_error());
  
    // How many rows there are inside the result
    $num_results = mysql_num_rows($result);  
   
    // Loop 5 times, remember some line above that LIMIT 5
    for($i = 0; $i < $num_results; $i++)
    {
         $row = mysql_fetch_array($result);
         // Render the result in text, questo viene letto poi da Unity3D come testo e renderizzato su un GUIText
         echo $row['name'] . "\t" . $row['score'] . "\n";
    }
?>

Now, we are going to populate the database to test our .php scripts.
1. phpMyAdmin> SQL label> write


/* Insert New Records in a table */ 
INSERT INTO scores (name, score)
VALUES ('Maria', '1260');

2. BOTTOM RIGHT> press ‘Esegui’ button
3. Insert 10 records and try display.php

XML – crossdomain.xml

Unity3D can send a WWW Request to a server only if it have a cross domain policy.
The crossdomain.xml file is a cross-domain policy file, it grants at the game the permission to talk to server, even if the game is not hosted inside it.

Upload the next file to the root of your web server, for BlueHost user put it into www. folder

crossdomain.xml:


<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy>

Blue Host users: inside crossdomain.xml if you have secure=”true” only requests from HTTPS will be allowed, secure=”false” mean requests from both HTTP and HTTPS are allowed.


<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*" secure="false"/>
</cross-domain-policy>

For italian people: come funziona?
Il server deve essere settato per poter dialogare con applicazioni esterne, intese come applicazioni non installate direttamente all’interno del server stesso.
Il caso più ecclatante è quello di un gioco installato su Smartphone che deve inviare dei dati ad un server.
Anche un webgame che funziona su Unity Player è di fatto installato in locale sul PC del giocatore, nel momento in cui invia i punteggi al server viene visto dal servizio di hosting come un’applicazione esterna.

Potremo vedere alcuni esempio di sintassi per capire meglio.
Nel file sotto garantiamo il dialogo dalle richieste provenienti solo da domini specifici.


<?xml version="1.0"?>
<cross-domain-policy>
    <allow-access-from domain="*.mycompany.com" />
    <allow-access-from domain="*.speedtest.net" />
</cross-domain-policy>

Unity – Display Scores – JS

Open Unity 3D and create:
– Main Camera -> name it ‘Main Camera’
– GUI Text -> name it ‘Scores-Text’
– Empty Object -> name it ‘GameController’
– JS Script -> name it ‘HSController.js’, attach it to ‘GameController’


#pragma strict

var scoreText : GUIText; // Assign into Inspector the GUI Text you have created

// CHANGE THIS VALUE WITH YOUR ADDRESS
var urlDisplay = "http://www.lucedigitale.com/testgames/display.php";
  
function Start() {
    getScores(); // get and display the scores into GUIText scoreText
}
// Get Score START ###################################################################  
// Get the scores from the MySQL DB to display in a GUIText.
function getScores() {
    // First a loading message
    scoreText.text = "Loading Scores";
    // Start a download of the given URL
    var wwwDisplay : WWW = new WWW (urlDisplay);
    // Wait for download to complete
    yield wwwDisplay;
    // if it can't load the URL
    if(wwwDisplay.error) {
        // Write in the console: There was an error getting the high score: Could not resolve host: xxx; No data record of requested type
        print("There was an error getting the high score: " + wwwDisplay.error);
        // Display an error message
        scoreText.text = "No Data Record";
    } else {
        // This is a GUIText that will display the scores in game
        scoreText.text = wwwDisplay.text; 
    }
}
// Get Score END ###################################################################  

The result will be:

Maria 2999
Giovanna 1787
Arianna 87
Antonio 24
Erica 12

Unity – Display and Write Scores – JS

Ok, now we are going to add functions to write scores


#pragma strict

var scoreText : GUIText; // Assign into Inspector the GUI Text you have created

// CHANGE THIS VALUE WITH YOUR ADDRESS TO GET BEST SCORES
var urlDisplay = "http://www.lucedigitale.com/testgames/display.php";
// CHANGE THIS VALUE WITH YOUR ADDRESS TO WRITE SCORES
var urlAddScores = "http://www.lucedigitale.com/testgames/addscore.php";

// CHANGE THIS VALUES WITH YOUR OWN
// this vars are private because we won't put data from Inspector, public will cause a refresh error 
private var playerName : String = "Andrea"; // name of the player
private var playerScore : int = 56565656; // the players' score
  
function Start() {
    getScores(); // get and display the scores into GUIText scoreText
}
// Get Score START ###################################################################  
// Get the scores from the MySQL DB to display in a GUIText.
function getScores() {
    // First a loading message
    scoreText.text = "Loading Scores";
    // Start a download of the given URL
    var wwwDisplay : WWW = new WWW (urlDisplay);
    // Wait for download to complete
    yield wwwDisplay;
    // if it can't load the URL
    if(wwwDisplay.error) {
        // Write in the console: There was an error getting the high score: Could not resolve host: xxx; No data record of requested type
        print("There was an error getting the high score: " + wwwDisplay.error);
        // Display an error message
        scoreText.text = "No Data Record";
    } else {
        // This is a GUIText that will display the scores in game
        scoreText.text = wwwDisplay.text; 
    }
} // END getScores()
// Get Score END ###################################################################  

// Write Score START ###############################################################

// Button to send scores START
function OnGUI () {
    if (GUI.Button (Rect (10,10,350,100), "Send player name and Scores:" + playerName + " " + playerScore)) {
        // send datas to function to POST scores
        postScore(playerName, playerScore);
    }
} // END OnGUI
// Button to send scores END 

function postScore(name, score) {
     // Debug code to verify datas on console
     Debug.Log("Name " + name + " Score " + score);
     
    //This connects to a server side php script that will add the name and score to a MySQL DB.
    // Supply it with a string representing the players name and the players score.
    // Once it will generate an error message of Implicit Downcast, no problem it will work well
    var postData : String = urlAddScores + "?name=" + WWW.EscapeURL(name) + "&score=" + score;
    // Debug Code
    Debug.Log(postData);
    // It sends: http://www.lucedigitale.com/testgames/addscore.php?name=Andrea&score=123321
    // notare che il nome delle variabili in POST devono essere uguali a quelli in GET di addscore.php
    var wwwPostScore : WWW = new WWW (postData);
    // Wait until the post is done
    yield wwwPostScore; 
    // If it can't give an errore message
    if(wwwPostScore.error) {
        // Debug code
        print("There was an error posting the high score: " + wwwPostScore.error);
        // Error message for the player
        scoreText.text = "I can't record data";
    }
}// END postScore()

// Write Score END ################################################################# 

addscore.php – update existing player

Now I can improve my .php script, updating existing players and closing db connection.


<?php 
        // Create connection
        // Statement: mysqli_connect(host,username,password,dbname)
        // NOTICE: se lo script è installato nello stesso server del Data Base, host->localhost 
        $db = mysql_connect('mysql_host', 'mysql_user', 'mysql_password') or die('Could not connect: ' . mysql_error()); 
        mysql_select_db('mysql_dbname') or die('Could not select database');
  
        // Strings must be escaped to prevent SQL injection attack. 
        $name = mysql_real_escape_string($_GET['name'], $db); 
        $score = mysql_real_escape_string($_GET['score'], $db);  
  
        // Check if the name already exists
		$checkname = mysql_query("SELECT 1 FROM scores WHERE name='$name' LIMIT 1");
		// if exists
		if (mysql_fetch_row($checkname)) {
		        // Update the existing name with new score
			// AGGIORNA db_name SETTA il valore di score dove name è uguale a quello ottenuto con GET
			$queryupdate = "UPDATE scores SET score=$score WHERE name='$name'"; 	
			$resultupdate = mysql_query($queryupdate) or die('Query failed: ' . mysql_error()); 		
		// if not exists
		} else {
		        // Insert a new name and a new score 
			$query = "INSERT INTO scores VALUES (NULL, '$name', '$score');"; 
			$result = mysql_query($query) or die('Query failed: ' . mysql_error()); 
		}
		
		// Close the connection with the database
		mysqli_close($db); 
        echo "<br>Great! Connection Closed!"; 
?>

addscore.php – update existing player – write only best score

I will write in the database only if new score is better than the older one.


<?php 
        // Create connection
        // Statement: mysqli_connect(host,username,password,dbname)
        // NOTICE: se lo script è installato nello stesso server del Data Base, host->localhost 
        $db = mysql_connect('mysql_host', 'mysql_user', 'mysql_password') or die('Could not connect: ' . mysql_error()); 
        mysql_select_db('mysql_dbname') or die('Could not select database');
  
        // Strings must be escaped to prevent SQL injection attack. 
        $name = mysql_real_escape_string($_GET['name'], $db); 
        $score = mysql_real_escape_string($_GET['score'], $db);  
		
		// Check if the name already exists 
		$checkname = mysql_query("SELECT 1 FROM scores WHERE name='$name' LIMIT 1");
		
		// ------------------------------------------------
		// if exists --------------------------------------
		//-------------------------------------------------
		if (mysql_fetch_row($checkname)) {	
		echo "Vecchio giocatore";// Debug Code
		echo "<br>";// Debug Code
	        echo "Punteggio arrivato dal gioco: ".$score;// Debug Code
		echo "<br>";// Debug Code
                // Check score from database
		$checkscore = mysql_query("SELECT score FROM scores WHERE name='$name'");
		$checkscorerow = mysql_fetch_array($checkscore);
		echo "Punteggio ottenuto dal database: ".$checkscorerow['score'];// Debug Code
		
				// if the new score are better than old one
				if ($score > $checkscorerow['score']){
					echo "<br>Great! New personal record";
					
					// Update the existing name with new score
					// AGGIORNA db_name SETTA il valore di score dove name è uguale a quello ottenuto con GET
					$queryupdate = "UPDATE scores SET score=$score WHERE name='$name'";     
					$resultupdate = mysql_query($queryupdate) or die('Query failed: ' . mysql_error());
					
					mysqli_close($db); // Close the connection with the database
					echo "<br>Connection Closed!"; 
					break; // stop the execution of the script
				} else {
					echo "<br>Bad! Are you tired?";
					mysqli_close($db); // Close the connection with the database
					echo "<br>Connection Closed!"; 
					break; // stop the execution of the script
				}	
         
		// ------------------------------------------------
		// if not exists ----------------------------------
		// ------------------------------------------------
		} else {
			    echo "Nuovo giocatore";// Debug Code
		        // Insert a new name and a new score 
				$query = "INSERT INTO scores VALUES (NULL, '$name', '$score');"; 
				$result = mysql_query($query) or die('Query failed: ' . mysql_error()); 
		}		
		
		mysqli_close($db); // Close the connection with the database
        echo "<br>Connection Closed!"; 
?>

Ok, now we can add an MD5 encryption.

MD5 Encryption – HSController.js


#pragma strict

var scoreText : GUIText; // Assign into Inspector the GUI Text you have created

// CHANGE THIS VALUE WITH YOUR ADDRESS TO GET BEST SCORES
var urlDisplay = "http://www.lucedigitale.com/testgames/display.php";
// CHANGE THIS VALUE WITH YOUR ADDRESS TO WRITE SCORES
var urlAddScores = "http://www.lucedigitale.com/testgames/addscore.php";

// CHANGE THIS VALUES WITH YOUR OWN
// this vars are private because we won't put data from Inspector, public will cause a refresh error 
private var playerName : String = "AndreaTonin"; // name of the player
private var playerScore : int = 800; // the players' score
private var secretKey : String = "MyKey"; // the secret key to improve encryption  

private var secretSum : String; // the sum of playerName+playerScore+secretKey
private var secretSumMd5 : String; // the MD5 sum of: playerName+playerScore+secretKey
  
function Start() {
    getScores(); // get and display the scores into GUIText scoreText
    
    // calculate MD5 Key
    secretSum = playerName + playerScore + secretKey;
    secretSumMd5 = Md5Sum(secretSum);
}
// Get Score START ###################################################################  
// Get the scores from the MySQL DB to display in a GUIText.
function getScores() {
    // First a loading message
    scoreText.text = "Loading Scores";
    // Start a download of the given URL
    var wwwDisplay : WWW = new WWW (urlDisplay);
    // Wait for download to complete
    yield wwwDisplay;
    // if it can't load the URL
    if(wwwDisplay.error) {
        // Write in the console: There was an error getting the high score: Could not resolve host: xxx; No data record of requested type
        print("There was an error getting the high score: " + wwwDisplay.error);
        // Display an error message
        scoreText.text = "No Data Record";
    } else {
        // This is a GUIText that will display the scores in game
        scoreText.text = wwwDisplay.text; 
    }
} // END getScores()
// Get Score END ###################################################################  

// Write Score START ###############################################################
// Button to send scores START
function OnGUI () {
    if (GUI.Button (Rect (10,10,650,100), "SEND name score md5:" + playerName + " " + playerScore + " " + secretSumMd5)) {
        // send datas to function to POST scores
        postScore(playerName, playerScore,secretSumMd5);
    }
} // END OnGUI
// Button to send scores END 

function postScore(name, score, md5key) {
     // Debug code to verify datas on console
     Debug.Log("Name " + name + " Score " + score + " MD5Key " + md5key);
     
    //This connects to a server side php script that will add the name and score to a MySQL DB.
    // Supply it with a string representing the players name and the players score.
    // Once it will generate an error message of Implicit Downcast, no problem it will work well
    var postData : String = urlAddScores + "?name=" + WWW.EscapeURL(name) + "&score=" + score + "&md5key=" + md5key;
    // Debug Code
    Debug.Log(postData);
    // It sends: http://www.lucedigitale.com/testgames/addscore.php?name=Andrea&score=123321
    // notare che il nome delle variabili in POST devono essere uguali a quelli in GET di addscore.php
    var wwwPostScore : WWW = new WWW (postData);
    // Wait until the post is done
    yield wwwPostScore; 
    // If it can't give an errore message
    if(wwwPostScore.error) {
        // Debug code
        print("There was an error posting the high score: " + wwwPostScore.error);
        // Error message for the player
        scoreText.text = "I can't record data";
    }
}// END postScore()
// Write Score END ################################################################# 

// MD5 Encrytpt START ############################################################### 
// Server side note: the output is the same of the PHP function - md5($myString) -
static function Md5Sum(strToEncrypt: String)
{
	var encoding = System.Text.UTF8Encoding();
	var bytes = encoding.GetBytes(strToEncrypt);
 
	// encrypt bytes
	var md5 = System.Security.Cryptography.MD5CryptoServiceProvider();
	var hashBytes:byte[] = md5.ComputeHash(bytes);
 
	// Convert the encrypted bytes back to a string (base 16)
	var hashString = "";
 
	for (var i = 0; i < hashBytes.Length; i++)
	{
		hashString += System.Convert.ToString(hashBytes[i], 16).PadLeft(2, "0"[0]);
	}
 
	return hashString.PadLeft(32, "0"[0]);
}// End Md5Sum
// MD5 Encrypt END #####################################################################

MD5 Encryption – addscore.php


<?php 
        // Create connection
        // Statement: mysqli_connect(host,username,password,dbname)
        // NOTICE: se lo script è installato nello stesso server del Data Base, host->localhost 
        $db = mysql_connect('localhost', 'lucedigi_testgam', '3dmaster1508%A') or die('Could not connect: ' . mysql_error()); 
        mysql_select_db('lucedigi_testgames') or die('Could not select database');
  
        // GET post data from Unity3D
		// Strings must be escaped to prevent SQL injection attack. 
        $name = mysql_real_escape_string($_GET['name'], $db); 
        $score = mysql_real_escape_string($_GET['score'], $db);  
		$md5key = mysql_real_escape_string($_GET['md5key'], $db); 
		
		$secretKey = "MyKey"; // It is the same Unity3D posts
		$secretSum = $name.$score.$secretKey;
 
        // ------------------------------------------------
		// if MD5 Key is right ----------------------------
		//-------------------------------------------------
	if (md5($secretSum) === $md5key) {
        echo "Yes! It is the right MD5, let's write on the database";
		
		// Check if the name already exists 
		$checkname = mysql_query("SELECT 1 FROM scores WHERE name='$name' LIMIT 1");
		
		// ------------------------------------------------
		// if exists --------------------------------------
		//-------------------------------------------------
		if (mysql_fetch_row($checkname)) {	
		echo "<br>Old Player";// Debug Code
		echo "<br>";// Debug Code
	    echo "Punteggio arrivato dal gioco: ".$score;// Debug Code
		echo "<br>";// Debug Code
		$checkscore = mysql_query("SELECT score FROM scores WHERE name='$name'");
		$checkscorerow = mysql_fetch_array($checkscore);
		echo "Punteggio ottenuto dal database: ".$checkscorerow['score'];// Debug Code
		
				// if the new score are better than old one
				if ($score > $checkscorerow['score']){
					echo "<br>Great! New personal record";
					
					// Update the existing name with new score
					// AGGIORNA db_name SETTA il valore di score dove name è uguale a quello ottenuto con GET
					$queryupdate = "UPDATE scores SET score=$score WHERE name='$name'";     
					$resultupdate = mysql_query($queryupdate) or die('Query failed: ' . mysql_error());
					
					mysqli_close($db); // Close the connection with the database
					echo "<br>Connection Closed!"; 
					break; // stop the execution of the script
				} else {
					echo "<br>Bad! Are you tired?";
					mysqli_close($db); // Close the connection with the database
					echo "<br>Connection Closed!"; 
					break; // stop the execution of the script
				}	
         
		// ------------------------------------------------
		// if not exists ----------------------------------
		// ------------------------------------------------
		} else {
			    echo "Nuovo giocatore";// Debug Code
		        // Insert a new name and a new score 
				$query = "INSERT INTO scores VALUES (NULL, '$name', '$score');"; 
				$result = mysql_query($query) or die('Query failed: ' . mysql_error()); 
		}		
		
		mysqli_close($db); // Close the connection with the database
        echo "<br>Connection Closed!"; 
		
	} else {
		// Debug Code
		echo "Bad MD5! Who are you?";
		echo "<br>Data received: ".$name." ".$score." ".$md5key;
		echo "<br>MD5 calcolato dal server: ".md5($secretSum);
		break;
	}
?>

My official website: http://www.lucedigitale.com
Original article: http://wiki.unity3d.com/index.php?title=Server_Side_Highscores

By |Unity3D, Video Games Development|Commenti disabilitati su Unity 3D – Server Side Highscores – JS Programming

Faceshift Studio – Basic Tutorial with XBOX360 Kinect

Faceshift Studio – Basic Tutorial with XBOX360 Kinect

Faceshift Studio (http://www.faceshift.com/) is a face-mocap software.
It analyzes the face motions of an actor, and describes them as a mixture of basic expressions, plus head orientation and gaze. This description is then used to animate virtual characters for use in movie or game production.

Faceshift Studio is really fantastic and it is used by some of most creative companies in the world as Industrial Light and Magic, Dreamworks, Disney, Sega. It works well with Autodesk 3DS Max and Unity3D.

To use Faceshift Studio you need a RGBD cameras (Red-Green-Blue-Deph).

Officially it runs with:
– PrimeSense Carmine 1.09
– Asus Xtion Live Pro
– Microsoft Kinect (it is the Kinetic sensor for PC)

Unofficially it might work with XBOX360 Kinetic sensor.

Ok let’s start!

XBOX360 sensor installation

1. Plug your sensor

2. From Microsoft website download and install KinectSDK-v1.8-Setup.exe

3. and install KinectDeveloperToolkit-v1.8.0-Setup.exe

XBOX360 Sensor testing

1. Go to Programs/Microsoft SDKs/Kinect/Developer Toolkit 1.8/bin/

– KinectExplorer-D2D.exe to move the bar

– FaceTrackingBasics-WPF.exe

NOTICE: You have to stay 80 cm from the sensor for face tracking

Faceshift capture and export

1. Start Faceshift> select MS Kinect Sensor Plugin

2. LEFT COLUMN> Tracking

3. You have to stay 80 cm from the sensor for face tracking

4. REC button to record

5. REC button to stop

6. Tracking> select the clip (it turns to blue)> Refine (this operation will remove glitches)

7. File> Save> to save the project in .fsp format

8. File> Export> to export in mocap data or animated .fbx file

Import into 3DS MAX

File> Import> .fbx

First expand the timeline, we will find:

– Mesh
– Head Bones
– Morph Targets

By |3D Graphic, 3DS Max, Video Games Development|Commenti disabilitati su Faceshift Studio – Basic Tutorial with XBOX360 Kinect