I'm developing a multiplayer online application with unity3D, and I'm using networkLobbyManager, I managed to create a match and allow the players to join the match by running matchMaker.JoinMatch so I have all the players in "Lobby Scene" but I did not understand how we can join them in "play scene".
(I have found that we can move to play scene only if all players are ready, but I do not understand how I can put them all ready). my code :
public InputField impF;
public InputField impF1;
public Dropdown d;
List<MatchInfoSnapshot> m;
MatchInfoSnapshot ma;
public int n = 1;
// Use this for initialization
public void enableMatch()
{
Debug.Log("# MMStart ");
this.StartMatchMaker();
}
public override void OnStartHost()
{
base.OnStartHost();
Debug.Log("the game has created");
}
public void crereMatch()
{
string nom = impF.text;
Debug.Log(nom);
MMCreateMateches(nom);
n = 2;
}
public void findMtches()
{
MMListMateches();
}
public void joinMatch()
{
Debug.Log(d.options[d.value].text);
foreach (var match in this.matches)
{
Debug.Log("dans la boucle des matches");
Debug.Log(match.name);
if ((d.options[d.value].text).Equals(match.name))
{
//on récupere
ma = match;
Debug.Log("dans le if " + ma.name);
break;
}
}
// this.f
m = this.matches;
ma = m.Find(x => (x.name).Equals(ma.name));
Debug.Log("apres recuperation de la liste" + ma.name);
MMjoinMateches(ma);
}
void MMListMateches()
{
Debug.Log("# MMListMateches ");
this.matchMaker.ListMatches(0, 20, "", true, 0, 0, OnMatchList);
}
public override void OnMatchList(bool success, string extendedInfo, List<MatchInfoSnapshot> matchList)
{
Debug.Log("# OnMatchList ");
base.OnMatchList(success, extendedInfo, matchList);
if (!success)
{
Debug.Log("liste failed " + extendedInfo);
}
else
{
if (matches.Count > 0)
{ //les matches en cours > 0 ms on doit le joiner
Debug.Log("succesfully listed match 1 er match :" + matchList[0]);
Debug.Log(matchList[0].name);
foreach (var match in this.matches)
{
Debug.Log("dans la boucle");
Debug.Log(match.name);
Debug.Log(" wééééééééééééééééééé ");
}
d.ClearOptions();
foreach (var match in this.matches)
{
Debug.Log(match.name);
// impF1.text = match.name;
List<string> m_DropOptions = new List<string> { match.name };
d.AddOptions(m_DropOptions);
}
// MMjoinMateches(matchList[0]);
}
else
{
Debug.Log("no mateches");
// MMCreateMateches();
}
}
}
void MMjoinMateches(MatchInfoSnapshot firstMatch)
{
Debug.Log("# MMjoinMateches ");
this.matchMaker.JoinMatch(firstMatch.networkId, "", "", "", 0, 0, OnMatchJoined);
}
public override void OnMatchJoined(bool success, string extendedInfo, MatchInfo matchInfo)
{
Debug.Log("# OnMatchJoined ");
base.OnMatchJoined(success, extendedInfo, matchInfo);
if (!success)
{
Debug.Log("failed to join match " + extendedInfo);
impF1.text = "failed";
}
else
{
Debug.Log("succesfuly joined match " + matchInfo.networkId);
impF1.text = "succes";
this.StartClient(matchInfo);
}
}
void MMCreateMateches(string nom)
{
Debug.Log("# MMCreateMateches ");
Debug.Log(nom);
this.matchMaker.CreateMatch(nom, 15, true, "", "", "", 0, 0, OnMatchCreate);
}
public override void OnMatchCreate(bool success, string extendedInfo, MatchInfo matchInfo)
{
Debug.Log("# OnMatchCreate ");
base.OnMatchCreate(success, extendedInfo, matchInfo);
if (!success)
{
Debug.Log("failed to create match " + extendedInfo);
}
else
{
Debug.Log("succesfuly created match " + matchInfo.networkId);
OnStartHost();
}
}
public override void OnLobbyServerPlayersReady()
{
base.OnLobbyServerPlayersReady();
}
Watch this tutorial by Holistic 3D on how to setup Unity Network Lobby (Free in the Asset Store). https://www.youtube.com/watch?v=jklWlm5v21k
In the settings, you determine Max Players needed to enter a room. You can set it to '1' so anyone can enter at anytime.
Change Max Players from 4 to 1 See Link
Related
I am making online multiplayer game with Darkrift2 and with Unity.
I deeply know how to make games but i am new at online part.
I made the game succesfully with LAN.
I done the server side.
But just the game that opened with server can connect.
I am putting the server to my pc and go and open the game in another pc.
It can't connect.
I could definitely make an obvious mistake but IPV4 ADDRESS IS SAME AS PC'S IPV4 ADDRESS AND PORT NO IS SAME ON EVERY PC.
Also there is no firewall ban, Game is opening in another pc's.
By the way probably you don't have to read the whole code, Just the connection part.
Client
NetworkManager.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DarkRift.Client.Unity;
using DarkRift;
using Tags;
using DarkRift.Client;
using UnityEngine.InputSystem;
using System.Net;
using System.Linq;
public class NetworkManager : MonoBehaviour
{
IPAddress IPv4;
public UnityClient client;
public GameObject Bug;
public Transform[] PlayerBegin;
public Light DirLight;
public List<GameObject> Bugs;
private void Awake()
{
IPv4 = Dns.GetHostEntry(Dns.GetHostName())
.AddressList.First(
f => f.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
Debug.Log(IPv4);
client.Connect(IPv4, client.Port, true);
client.MessageReceived += OnMessageReceived;
//DontDestroyOnLoad(gameObject);
}
private void OnDestroy()
{
client.MessageReceived -= OnMessageReceived;
}
public void Interaction(byte Condition)//legdeathi de içine koyunca sorun yaratır mı? sanırım hayır
{
using (DarkRiftWriter writer = DarkRiftWriter.Create())
{
writer.Write(client.ID);
writer.Write(Condition);
using (Message message = Message.Create((ushort)Models.Tags0.InteractionType, writer))
{
client.SendMessage(message, SendMode.Reliable);
}
}
}
public void PosRot(Vector3 Pos, Quaternion Rot, bool ThereIsPos)
{
XYZ Position=null;
if (ThereIsPos)
{
Position = new XYZ();
Position.X = Pos.x;
Position.Y = Pos.y;
Position.Z = Pos.z;
}
XYZ Rotation = new XYZ();
Rotation.X = Rot.x;
Rotation.Y = Rot.y;
Rotation.Z = Rot.z;
float RotW = Rot.w;
using (DarkRiftWriter writer = DarkRiftWriter.Create())
{
writer.Write(client.ID);
var tag = Models.Tags0.Rot;
if (ThereIsPos)
{
tag = Models.Tags0.PosRot;
writer.Write(Position);
}
writer.Write(Rotation);
writer.Write(RotW);
using (Message message = Message.Create((ushort)tag, writer))
{
client.SendMessage(message, SendMode.Unreliable);
}
}
}
public int PlayerId;
public ushort PlayerCount;
private void OnMessageReceived(object sender, MessageReceivedEventArgs e)
{
using (Message message = e.GetMessage())
{
using (DarkRiftReader reader = message.GetReader())
{
if (message.Tag == (ushort)Models.Tags0.InteractionType)
{
ushort Id = reader.ReadUInt16();
GameObject tarantula = Bugs[Id].transform.GetChild(0).gameObject;
byte Interaction = reader.ReadByte();
Spider s = tarantula.GetComponent<Spider>();
switch (Interaction)
{
case 0: s.Attack(); break;
case 1:
s.DefenceGetBug();
break;
case 2:
s.DefenceOff();
break;
case 3:
s.Death();
break;
case 4:
s.LegsHealthDown();
break;
default:
s.LegHealthDown(s.Legs[Interaction - 5], 3);
break;
}
}
byte ThereIsPos=0;
if(message.Tag == (ushort)Models.Tags0.PosRot)
{
ThereIsPos = 1;
}
else if(message.Tag == (ushort)Models.Tags0.Rot)
{
ThereIsPos = 2;
}
if (ThereIsPos != 0)
{
ushort Id = reader.ReadUInt16();
GameObject tarantula = Bugs[Id].transform.GetChild(0).gameObject;
if (ThereIsPos == 1)
{
XYZ Position = reader.ReadSerializable<XYZ>();
tarantula.transform.position = new Vector3(Position.X, Position.Y, Position.Z);
}
XYZ Rotation = reader.ReadSerializable<XYZ>();
float RotW = reader.ReadSingle();
tarantula.transform.rotation = new Quaternion(Rotation.X, Rotation.Y, Rotation.Z, RotW);
}
if (message.Tag == (ushort)Models.Tags0.NewPlayer)
{
PlayerCount = (ushort)(reader.ReadUInt16() + 1);
//Debug.Log("PlayerCount : " + PlayerCount);
PlayerId = Bugs.Count;//Player No
//Debug.Log("PlayerId : " + PlayerId);
while (PlayerId < PlayerCount)
{
Debug.Log("Player Spawn" + PlayerId);
GameObject bug = Instantiate(Bug, PlayerBegin[PlayerId].position, Quaternion.identity, null);
bug.name = "Bug" + PlayerId;
Spider spider;
spider = bug.GetComponentInChildren<Spider>();
spider.nm = this;
spider.DirLight = DirLight;
//spider.transform.position = PlayerBegin[PlayerId].position;
//PosChange(spider.gameObject.transform.position);
if (client.ID != PlayerId)
{
//spider.enabled = false;
bug.transform.Find("Camera").gameObject.SetActive(false);
bug.transform.Find("CM FreeLook1").gameObject.SetActive(false);
bug.transform.Find("Camera Late").gameObject.SetActive(false);
bug.transform.Find("Canvas").gameObject.SetActive(false);
bug.GetComponentInChildren<PlayerInput>().enabled = false;
}
Bugs.Add(bug);
PlayerId++;
}
}
}
}
}
}
Server code .Net (only the main class):
using System;
using DarkRift.Server;
using DarkRift;
using Tags;
namespace Bug_Wars_Online
{
public class BugWarsOnline : Plugin
{
public override bool ThreadSafe => false;
public override Version Version => new Version(1, 0, 0);
public BugWarsOnline(PluginLoadData pluginLoadData) : base(pluginLoadData)
{
ClientManager.ClientConnected += OnClientConnected;
ClientManager.ClientDisconnected += OnClientDisconnected;
}
private void OnClientConnected(object sender, ClientConnectedEventArgs e)
{
Console.WriteLine("Connected");
e.Client.MessageReceived += OnMessageReceived;
using (DarkRiftWriter writer = DarkRiftWriter.Create())
{
writer.Write(e.Client.ID);
writer.Write(0);
using (Message message = Message.Create((ushort)Models.Tags0.NewPlayer, writer))
{
foreach (IClient client in ClientManager.GetAllClients())
{
//if (client.ID != e.Client.ID)
{
client.SendMessage(message, SendMode.Reliable);
Console.WriteLine("Player" + client.ID + " Connected");
Console.WriteLine("ClientManager.Count" + ClientManager.Count);
}
}
}
}
}
private void OnClientDisconnected(object sender, ClientDisconnectedEventArgs e)
{
Console.WriteLine("Disconnected");
//destroy et!
}
private void OnMessageReceived(object sender, MessageReceivedEventArgs e)
{
using (Message message = e.GetMessage())
{
if (message.Tag == (ushort)Models.Tags0.PosRot|| message.Tag == (ushort)Models.Tags0.Rot)
{
foreach (IClient client in ClientManager.GetAllClients())
{
if (client.ID != e.Client.ID)
{
client.SendMessage(message, SendMode.Unreliable);
}
}
}
if (message.Tag == (ushort)Models.Tags0.InteractionType)
{
foreach (IClient client in ClientManager.GetAllClients())
{
if (client.ID != e.Client.ID)
{
client.SendMessage(message, SendMode.Reliable);
}
}
}
}
}
}
}
Okay I should have enter the ipv4 address of server’s pc.
Using Unity 5.4 and FB SDK 7.9 I want to use the onclick button functionality to run the method Share. This method has many parameters, so I need to create the onclick functionality in code and not in the inspector. I am getting the error unexpected symbol link parameter expecting '.' What is going on? How can I fix this?
public class FBScript : MonoBehaviour {
public Button PostBtn;
void Start() {
Button btn = PostBtn.GetComponent<Button>();
btn.onClick.AddListener(delegate {
Share(string linkParameter, string namerParameter, string captionParameter, string descriptionParameter, string pictureParameter, string redirectParameter);
});
}
void Awake () {
FB.Init(SetInit, OnHideUnity);
}
void SetInit () {
if (FB.IsLoggedIn) {
Debug.Log("FB is Logged In");
} else {
Debug.Log("FB is not Logged In");
}
}
void OnHideUnity(bool isGameShown) {
if (!isGameShown) {
Time.timeScale = 0;
} else {
Time.timeScale = 1;
}
}
public void FBLogin() {
List<string> permissions = new List<string>();
permissions.Add("public_profile");
FB.LogInWithReadPermissions(permissions, AuthCallBack);
}
void AuthCallBack(ILoginResult result) {
if (result.Error != null) {
Debug.Log(result.Error);
} else {
if (FB.IsLoggedIn) {
Debug.Log("FB is Logged In");
} else {
Debug.Log("FB is not Logged In");
}
}
}
private const string FACEBOOK_APP_ID = "999999999999999";
private const string FACEBOOK_URL = "http://www.facebook.com/dialog/feed";
public void Share(string linkParameter, string namerParameter, string captionParameter, string descriptionParameter, string pictureParameter, string redirectParameter) {
Application.OpenURL(FACEBOOK_URL + "?app_id=" + FACEBOOK_APP_ID +
"&link=" + WWW.EscapeURL(linkParameter) +
"&name=" + WWW.EscapeURL(namerParameter) +
"&caption=" + WWW.EscapeURL(captionParameter) +
"&description=" + WWW.EscapeURL(descriptionParameter) +
"&picture=" + WWW.EscapeURL(pictureParameter) +
"&redirect_url=" + WWW.EscapeURL(redirectParameter));
}
}
The AutoCompleteTextField seems to work exactly as intended until I start backspacing in the TextField. I am not sure what the difference is, but if I type in something like "123 M" then I get values that start with "123 M". If I backspace and delete the M leaving "123 " in the field, the list changes, but it does not scroll to the top of the list.
I should note that everything works fine on the simulator and that I am experiencing this behavior when running a debug build on my iPhone.
EDIT: So this does not only seem to happen when backspacing. This image shows the results I have when typing in an address key by key. In any of the pictures where the list isn't viewable or is clipped, I am able to drag down on the list to get it to then display properly. I have not tried this on an Android device.
EDIT2:
public class CodenameOneTest {
private Form current;
private Resources theme;
private WaitingClass w;
private String[] properties = {"1 MAIN STREET", "123 E MAIN STREET", "12 EASTER ROAD", "24 MAIN STREET"};
public void init(Object context) {
theme = UIManager.initFirstTheme("/theme");
// Enable Toolbar on all Forms by default
Toolbar.setGlobalToolbar(true);
}
public void start() {
if(current != null) {
current.show();
return;
}
Form form = new Form("AutoCompleteTextField");
form.setLayout(new BorderLayout());
final DefaultListModel<String> options = new DefaultListModel<>();
AutoCompleteTextField ac = new AutoCompleteTextField(options) {
protected boolean filter(String text) {
if(text.length() == 0) {
options.removeAll();
return false;
}
String[] l = searchLocations(text);
if(l == null || l.length == 0) {
return false;
}
options.removeAll();
for(String s : l) {
options.addItem(s);
}
return true;
};
};
Container container = new Container(BoxLayout.y());
container.setScrollableY(true); // If you comment this out then the field works fine
container.add(ac);
form.addComponent(BorderLayout.CENTER, container);
form.show();
}
String[] searchLocations(String text) {
try {
if(text.length() > 0) {
if(w != null) {
w.actionPerformed(null);
}
w = new WaitingClass();
String[] properties = getProperties(text);
if(Display.getInstance().isEdt()) {
Display.getInstance().invokeAndBlock(w);
}
else {
w.run();
}
return properties;
}
}
catch(Exception e) {
Log.e(e);
}
return null;
}
private String[] getProperties(String text) {
List<String> returnList = new ArrayList<>();
List<String> propertyList = Arrays.asList(properties);
for(String property : propertyList) {
if(property.startsWith(text)) {
returnList.add(property);
}
}
w.actionPerformed(null);
return returnList.toArray(new String[returnList.size()]);
}
class WaitingClass implements Runnable, ActionListener<ActionEvent> {
private boolean finishedWaiting;
public void run() {
while(!finishedWaiting) {
try {
Thread.sleep(30);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
}
}
public void actionPerformed(ActionEvent e) {
finishedWaiting = true;
return;
}
}
public void stop() {
current = Display.getInstance().getCurrent();
if(current instanceof Dialog) {
((Dialog)current).dispose();
current = Display.getInstance().getCurrent();
}
}
public void destroy() {
}
}
I used this code on an iPhone 4s:
public void start() {
if(current != null){
current.show();
return;
}
Form hi = new Form("AutoComplete", new BorderLayout());
if(apiKey == null) {
hi.add(new SpanLabel("This demo requires a valid google API key to be set in the constant apiKey, "
+ "you can get this key for the webservice (not the native key) by following the instructions here: "
+ "https://developers.google.com/places/web-service/get-api-key"));
hi.getToolbar().addCommandToRightBar("Get Key", null, e -> Display.getInstance().execute("https://developers.google.com/places/web-service/get-api-key"));
hi.show();
return;
}
Container box = new Container(new BoxLayout(BoxLayout.Y_AXIS));
box.setScrollableY(true);
for(int iter = 0 ; iter < 30 ; iter++) {
box.add(createAutoComplete());
}
hi.add(BorderLayout.CENTER, box);
hi.show();
}
private AutoCompleteTextField createAutoComplete() {
final DefaultListModel<String> options = new DefaultListModel<>();
AutoCompleteTextField ac = new AutoCompleteTextField(options) {
#Override
protected boolean filter(String text) {
if(text.length() == 0) {
return false;
}
String[] l = searchLocations(text);
if(l == null || l.length == 0) {
return false;
}
options.removeAll();
for(String s : l) {
options.addItem(s);
}
return true;
}
};
ac.setMinimumElementsShownInPopup(5);
return ac;
}
String[] searchLocations(String text) {
try {
if(text.length() > 0) {
ConnectionRequest r = new ConnectionRequest();
r.setPost(false);
r.setUrl("https://maps.googleapis.com/maps/api/place/autocomplete/json");
r.addArgument("key", apiKey);
r.addArgument("input", text);
NetworkManager.getInstance().addToQueueAndWait(r);
Map<String,Object> result = new JSONParser().parseJSON(new InputStreamReader(new ByteArrayInputStream(r.getResponseData()), "UTF-8"));
String[] res = Result.fromContent(result).getAsStringArray("//description");
return res;
}
} catch(Exception err) {
Log.e(err);
}
return null;
}
I was able to create this issue but not the issue you describe.
I get the following log during on-device debugging
Error:
*** [Play Games Plugin DLL] ERROR: Cloud load failed with status code 7
Basically the OnStateLoaded() callback function always returns the boolean success = false and I can't figure out the reason why.
All that the plugin debugging logs mention is "Cloud load failed with status code 7".
According to the android doc, 7 is a generic "developer error", see https://developer.android.com/reference/com/google/android/gms/appstate/AppStateStatusCodes.html#STATUS_DEVELOPER_ERROR
I tried a quick sample and everything worked ok. Here are my steps:
Created a new game in the play console
(https://play.google.com/apps/publish)
Made sure Saved Games is set
to ON
Linked an Android Application Remembering the application ID
(the number after the title) and the package ID
Created a new project in Unity
Added the play games plugin (Assets/Import Package.../Custom
Package)
Set the application ID (Google Play Games/Android Setup...)
Switched the platform to Android (File/Build Settings...)
Set the player settings (bundle identifier and the keystore info)
Added a new script component to the camera
Saved everything and hit build and run.
Here are the contents:
using UnityEngine;
using System.Collections;
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using System;
public class SaveSample : MonoBehaviour {
System.Action<bool> mAuthCallback;
GameData slot0;
void Start () {
slot0 = new GameData(0,"waiting for login....");
mAuthCallback = (bool success) => {
if (success) {
Debug.Log("Authentication was successful!");
slot0.Data =" loading....";
slot0.LoadState();
}
else {
Debug.LogWarning("Authentication failed!");
}
};
// make Play Games the default social implementation
PlayGamesPlatform.Activate();
// enable debug logs
PlayGamesPlatform.DebugLogEnabled = true;
//Login explicitly for this sample, usually this would be silent
PlayGamesPlatform.Instance.Authenticate(mAuthCallback, false);
}
protected void OnGUI() {
Screen.fullScreen = true;
int buttonHeight = Screen.height / 20;
int buttonWidth = Screen.width / 5;
GUI.skin.label.fontSize = 60;
GUI.skin.button.fontSize = 60;
Rect statusRect = new Rect(10,20,Screen.width,100);
Rect dataRect = new Rect( 10, 150, Screen.width,100);
Rect b1Rect = new Rect(10, 400, buttonWidth, buttonHeight);
Rect b2Rect = new Rect(b1Rect.x + 20 + buttonWidth,
b1Rect.y, buttonWidth, buttonHeight);
if(!Social.localUser.authenticated) {
if(GUI.Button(b1Rect, "Signin")) {
Social.localUser.Authenticate(mAuthCallback);
}
}
else {
// logged in, so show the load button and the contents of the saved data.
if(GUI.Button(b1Rect, "Load")) {
slot0.LoadState();
}
GUI.Label(dataRect, slot0.Data);
}
if(GUI.Button(b2Rect, "Save")) {
// just save a string, incrementing the number on the end.
int idx = slot0.Data.IndexOf("_");
if (idx > 0) {
int val = Convert.ToInt32(slot0.Data.Substring(idx+1));
val++;
slot0.Data = "Save_" + val;
}
else {
slot0.Data = "Save_0";
}
slot0.SaveState();
}
GUI.Label(statusRect, slot0.State);
}
// Class to handle save/load callbacks.
public class GameData : OnStateLoadedListener {
int slot;
string data;
string state;
public GameData(int slot, string data) {
this.slot = slot;
this.data = data;
this.state = "Initialized, modified";
}
public void LoadState() {
this.state += ", loading";
((PlayGamesPlatform)Social.Active).LoadState(0, this);
}
public void SaveState() {
byte[] bytes = new byte[data.Length * sizeof(char)];
System.Buffer.BlockCopy(data.ToCharArray(), 0, bytes, 0, bytes.Length);
this.state += ", saving";
((PlayGamesPlatform) Social.Active).UpdateState(slot, bytes, this);
}
public void OnStateLoaded(bool success, int slot, byte[] data) {
if (success) {
Debug.Log ("Save game slot : " + slot + " loaded: " + data);
if (data != null) {
char[] chars = new char[data.Length / sizeof(char)];
System.Buffer.BlockCopy(data, 0, chars, 0, data.Length);
this.data = new string(chars);
this.state = "loaded";
} else {
Debug.Log ("Saved data is null");
this.data = "";
this.state = "loaded, but empty";
}
} else {
// handle failure
Debug.LogWarning ("Save game slot : " + slot + " failed!: ");
this.data = "";
this.state = "loading failed!";
}
}
public byte[] OnStateConflict(int slot, byte[] local, byte[] server) {
// resolve conflict and return a byte[] representing the
// resolved state.
Debug.LogWarning("Conflict in saved data!");
state = "conflicted";
// merge or resolve using app specific logic, here
byte[] resolved = local.Length > server.Length ? local : server;
char[] chars = new char[resolved.Length / sizeof(char)];
System.Buffer.BlockCopy(resolved, 0, chars, 0, resolved.Length);
this.data = new string(chars);
return resolved;
}
public void OnStateSaved(bool success, int slot) {
Debug.Log ("Save game slot : " + slot + " success: " + success);
state = "saved";
}
public string Data {
get {
return data;
}
set {
data = value;
state += ", modified";
}
}
public int Slot {
get {
return slot;
}
}
public string State {
get {
return state;
}
}
}
}
The error code 7 is because the Cloud Save API has been deprecated and is only currently accessible to existing games that have used the API. The Unity plugin version 0.9.11 has been updated to use the SavedGames API.
I tried a quick sample and everything worked ok. Here are my steps:
Created a new game in the play console
(https://play.google.com/apps/publish)
Made sure Saved Games is set to ON
Linked an Android Application Remembering the application ID
(the number after the title) and the package ID Created a new
project in Unity
Added the play games plugin (Assets/Import
Package.../Custom Package)
Set the application ID (Google Play
Games/Android Setup...)
Switched the platform to Android (File/Build
Settings...)
Set the player settings (bundle identifier and the
keystore info)
Added a new script component to the camera Saved
everything and hit build and run.
Here is my script:
using UnityEngine;
using System.Collections;
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using System;
using GooglePlayGames.BasicApi.SavedGame;
public class SaveSample : MonoBehaviour {
System.Action<bool> mAuthCallback;
GameData slot0;
bool mSaving;
private Texture2D mScreenImage;
// Use this for initialization
void Start () {
slot0 = new GameData("New game");
mAuthCallback = (bool success) => {
if (success) {
Debug.Log("Authentication was successful!");
slot0.State = "Click load or save";
}
else {
Debug.LogWarning("Authentication failed!");
}
};
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder()
.EnableSavedGames()
.Build();
PlayGamesPlatform.InitializeInstance(config);
// Activate the Play Games platform. This will make it the default
// implementation of Social.Active
PlayGamesPlatform.Activate();
// enable debug logs (note: we do this because this is a sample; on your production
// app, you probably don't want this turned on by default, as it will fill the user's
// logs with debug info).
PlayGamesPlatform.DebugLogEnabled = true;
//Login explicitly for this sample, usually this would be silent
PlayGamesPlatform.Instance.Authenticate(mAuthCallback, false);
}
public void CaptureScreenshot() {
mScreenImage = new Texture2D(Screen.width, Screen.height);
mScreenImage.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
mScreenImage.Apply();
}
protected virtual void OnGUI() {
Screen.fullScreen = true;
int buttonHeight = Screen.height / 20;
int buttonWidth = Screen.width / 5;
GUI.skin.label.fontSize = 60;
GUI.skin.button.fontSize = 60;
Rect statusRect = new Rect(10,20,Screen.width,200);
Rect dataRect = new Rect( 10, 250, Screen.width,100);
Rect b1Rect = new Rect(10, 800, buttonWidth, buttonHeight);
Rect b2Rect = new Rect(b1Rect.x + 20 + buttonWidth, b1Rect.y, buttonWidth, buttonHeight);
if(!Social.localUser.authenticated) {
if(GUI.Button(b1Rect, "Signin")) {
Social.localUser.Authenticate(mAuthCallback);
}
}
else {
if(GUI.Button(b1Rect, "Load")) {
mSaving = false;
((PlayGamesPlatform)Social.Active).SavedGame.ShowSelectSavedGameUI("Select game to load",
4,false,false,SavedGameSelected);
}
GUI.Label(dataRect, slot0.Data);
}
if(GUI.Button(b2Rect, "Save")) {
int idx = slot0.Data.IndexOf("_");
if (idx > 0) {
int val = Convert.ToInt32(slot0.Data.Substring(idx+1));
val++;
slot0.Data = "Save_" + val;
}
else {
slot0.Data = "Save_0";
}
mSaving = true;
CaptureScreenshot();
((PlayGamesPlatform)Social.Active).SavedGame.ShowSelectSavedGameUI("Save game progress",
4,true,true,SavedGameSelected);
}
GUI.Label(statusRect, slot0.State);
}
public void SavedGameSelected(SelectUIStatus status, ISavedGameMetadata game) {
if (status == SelectUIStatus.SavedGameSelected) {
string filename = game.Filename;
Debug.Log("opening saved game: " + game);
if(mSaving && (filename == null || filename.Length == 0)) {
filename = "save" + DateTime.Now.ToBinary();
}
if (mSaving) {
slot0.State = "Saving to " + filename;
}
else {
slot0.State = "Loading from " + filename;
}
//open the data.
((PlayGamesPlatform)Social.Active).SavedGame.OpenWithAutomaticConflictResolution(filename,
DataSource.ReadCacheOrNetwork,
ConflictResolutionStrategy.UseLongestPlaytime,
SavedGameOpened);
} else {
Debug.LogWarning("Error selecting save game: " + status);
}
}
public void SavedGameOpened(SavedGameRequestStatus status, ISavedGameMetadata game) {
if(status == SavedGameRequestStatus.Success) {
if( mSaving) {
slot0.State = "Opened, now writing";
byte[] pngData = (mScreenImage!=null) ?mScreenImage.EncodeToPNG():null;
Debug.Log("Saving to " + game);
byte[] data = slot0.ToBytes();
TimeSpan playedTime = slot0.TotalPlayingTime;
SavedGameMetadataUpdate.Builder builder = new
SavedGameMetadataUpdate.Builder()
.WithUpdatedPlayedTime(playedTime)
.WithUpdatedDescription("Saved Game at " + DateTime.Now);
if (pngData != null) {
Debug.Log("Save image of len " + pngData.Length);
builder = builder.WithUpdatedPngCoverImage(pngData);
}
else {
Debug.Log ("No image avail");
}
SavedGameMetadataUpdate updatedMetadata = builder.Build();
((PlayGamesPlatform)Social.Active).SavedGame.CommitUpdate(game,updatedMetadata,data,SavedGameWritten);
} else {
slot0.State = "Opened, reading...";
((PlayGamesPlatform)Social.Active).SavedGame.ReadBinaryData(game,SavedGameLoaded);
}
} else {
Debug.LogWarning("Error opening game: " + status);
}
}
public void SavedGameLoaded(SavedGameRequestStatus status, byte[] data) {
if (status == SavedGameRequestStatus.Success) {
Debug.Log("SaveGameLoaded, success=" + status);
slot0 = GameData.FromBytes(data);
} else {
Debug.LogWarning("Error reading game: " + status);
}
}
public void SavedGameWritten(SavedGameRequestStatus status, ISavedGameMetadata game) {
if(status == SavedGameRequestStatus.Success) {
Debug.Log ("Game " + game.Description + " written");
slot0.State = "Saved!";
} else {
Debug.LogWarning("Error saving game: " + status);
}
}
public class GameData {
private TimeSpan mPlayingTime;
private DateTime mLoadedTime;
string mData;
string mState;
static readonly string HEADER = "GDv1";
public GameData(string data) {
mData = data;
mState = "Initialized, modified";
mPlayingTime = new TimeSpan();
mLoadedTime = DateTime.Now;
}
public TimeSpan TotalPlayingTime {
get {
TimeSpan delta = DateTime.Now.Subtract(mLoadedTime);
return mPlayingTime.Add(delta);
}
}
public override string ToString () {
string s = HEADER + ":" + mData;
s += ":" + TotalPlayingTime.TotalMilliseconds;
return s;
}
public byte[] ToBytes() {
return System.Text.ASCIIEncoding.Default.GetBytes(ToString());
}
public static GameData FromBytes (byte[] bytes) {
return FromString(System.Text.ASCIIEncoding.Default.GetString(bytes));
}
public static GameData FromString (string s) {
GameData gd = new GameData("initializing from string");
string[] p = s.Split(new char[] { ':' });
if (!p[0].StartsWith(HEADER)) {
Debug.LogError("Failed to parse game data from: " + s);
return gd;
}
gd.mData = p[1];
double val = Double.Parse(p[2]);
gd.mPlayingTime = TimeSpan.FromMilliseconds(val>0f?val:0f);
gd.mLoadedTime = DateTime.Now;
gd.mState = "Loaded successfully";
return gd;
}
public string Data {
get {
return mData;
}
set {
mData = value;
mState += ", modified";
}
}
public string State {
get {
return mState;
}
set {
mState = value;
}
}
}
}
I am using below class to enable stream management("urn:xmpp:sm:3") in our ejabberd server(we have latest version of ejabberd). But when I send the Enable packet to server it says Service Unavailable(503). But when I use "yaxim" it works perfectly. Please help me to solve this problem. Thanks.
public class XmppStreamHandler {
public static final String URN_SM_3 = "urn:xmpp:sm:3";
private static final int MAX_OUTGOING_QUEUE_SIZE = 20;
private static final int OUTGOING_FILL_RATIO = 4;
private XMPPConnection mConnection;
private boolean isSmAvailable = false;
private boolean isSmEnabled = false;
private boolean isOutgoingSmEnabled = false;
private long previousIncomingStanzaCount = -1;
private String sessionId;
private long incomingStanzaCount = 0;
private long outgoingStanzaCount = 0;
private Queue<Packet> outgoingQueue;
private int maxOutgoingQueueSize = MAX_OUTGOING_QUEUE_SIZE;
private ConnectionListener mConnectionListener;
public XmppStreamHandler(XMPPConnection connection, ConnectionListener connectionListener) {
mConnection = connection;
mConnectionListener = connectionListener;
startListening();
}
/** Perform a quick shutdown of the XMPPConnection if a resume is possible */
public void quickShutdown() {
if (isResumePossible()) {
mConnection.quickShutdown();
// We will not necessarily get any notification from a quickShutdown, so adjust our state here.
closeOnError();
} else {
mConnection.shutdown();
}
}
public void setMaxOutgoingQueueSize(int maxOutgoingQueueSize) {
this.maxOutgoingQueueSize = maxOutgoingQueueSize;
}
public boolean isResumePossible() {
return sessionId != null;
}
public boolean isResumePending() {
return isResumePossible() && !isSmEnabled;
}
public static void addExtensionProviders() {
addSimplePacketExtension("sm", URN_SM_3);
addSimplePacketExtension("r", URN_SM_3);
addSimplePacketExtension("a", URN_SM_3);
addSimplePacketExtension("enabled", URN_SM_3);
addSimplePacketExtension("resumed", URN_SM_3);
addSimplePacketExtension("failed", URN_SM_3);
}
public void notifyInitialLogin() {
if (sessionId == null && isSmAvailable)
sendEnablePacket();
}
private void sendEnablePacket() {
debug("sm send enable " + sessionId);
if (sessionId != null) {
isOutgoingSmEnabled = true;
// TODO binding
StreamHandlingPacket resumePacket = new StreamHandlingPacket("resume", URN_SM_3);
resumePacket.addAttribute("h", String.valueOf(previousIncomingStanzaCount));
resumePacket.addAttribute("previd", sessionId);
mConnection.sendPacket(resumePacket);
} else {
outgoingStanzaCount = 0;
outgoingQueue = new ConcurrentLinkedQueue<Packet>();
isOutgoingSmEnabled = true;
StreamHandlingPacket enablePacket = new StreamHandlingPacket("enable", URN_SM_3);
enablePacket.addAttribute("resume", "true");
mConnection.sendPacket(enablePacket);
}
}
private void closeOnError() {
if (isSmEnabled && sessionId != null) {
previousIncomingStanzaCount = incomingStanzaCount;
}
isSmEnabled = false;
isOutgoingSmEnabled = false;
isSmAvailable = false;
}
private void startListening() {
mConnection.forceAddConnectionListener(new ConnectionListener() {
public void reconnectionSuccessful() {
}
public void reconnectionFailed(Exception e) {
}
public void reconnectingIn(int seconds) {
}
public void connectionClosedOnError(Exception e) {
if (e instanceof XMPPException &&
((XMPPException)e).getStreamError() != null) {
// Non-resumable stream error
close();
} else {
// Resumable
closeOnError();
}
}
public void connectionClosed() {
previousIncomingStanzaCount = -1;
}
});
mConnection.addPacketSendingListener(new PacketListener() {
public void processPacket(Packet packet) {
// Ignore our own request for acks - they are not counted
if (!isStanza(packet)) {
trace("send " + packet.toXML());
return;
}
if (isOutgoingSmEnabled && !outgoingQueue.contains(packet)) {
outgoingStanzaCount++;
outgoingQueue.add(packet);
trace("send " + outgoingStanzaCount + " : " + packet.toXML());
// Don't let the queue grow beyond max size. Request acks and drop old packets
// if acks are not coming.
if (outgoingQueue.size() >= maxOutgoingQueueSize / OUTGOING_FILL_RATIO) {
mConnection.sendPacket(new StreamHandlingPacket("r", URN_SM_3));
}
if (outgoingQueue.size() > maxOutgoingQueueSize) {
// Log.e(XmppConnection.TAG, "not receiving acks? outgoing queue full");
outgoingQueue.remove();
}
} else if (isOutgoingSmEnabled && outgoingQueue.contains(packet)) {
outgoingStanzaCount++;
trace("send DUPLICATE " + outgoingStanzaCount + " : " + packet.toXML());
} else {
trace("send " + packet.toXML());
}
}
}, new PacketFilter() {
public boolean accept(Packet packet) {
return true;
}
});
mConnection.addPacketListener(new PacketListener() {
public void processPacket(Packet packet) {
if (isSmEnabled && isStanza(packet)) {
incomingStanzaCount++;
trace("recv " + incomingStanzaCount + " : " + packet.toXML());
} else {
trace("recv " + packet.toXML());
}
if (packet instanceof StreamHandlingPacket) {
StreamHandlingPacket shPacket = (StreamHandlingPacket) packet;
String name = shPacket.getElementName();
if ("sm".equals(name)) {
debug("sm avail");
isSmAvailable = true;
if (sessionId != null)
sendEnablePacket();
} else if ("r".equals(name)) {
StreamHandlingPacket ackPacket = new StreamHandlingPacket("a", URN_SM_3);
ackPacket.addAttribute("h", String.valueOf(incomingStanzaCount));
mConnection.sendPacket(ackPacket);
} else if ("a".equals(name)) {
long ackCount = Long.valueOf(shPacket.getAttribute("h"));
removeOutgoingAcked(ackCount);
trace(outgoingQueue.size() + " in outgoing queue after ack");
} else if ("enabled".equals(name)) {
incomingStanzaCount = 0;
isSmEnabled = true;
mConnection.getRoster().setOfflineOnError(false);
String resume = shPacket.getAttribute("resume");
if ("true".equals(resume) || "1".equals(resume)) {
sessionId = shPacket.getAttribute("id");
}
debug("sm enabled " + sessionId);
} else if ("resumed".equals(name)) {
debug("sm resumed");
incomingStanzaCount = previousIncomingStanzaCount;
long resumeStanzaCount = Long.valueOf(shPacket.getAttribute("h"));
// Removed acked packets
removeOutgoingAcked(resumeStanzaCount);
trace(outgoingQueue.size() + " in outgoing queue after resume");
// Resend any unacked packets
for (Packet resendPacket : outgoingQueue) {
mConnection.sendPacket(resendPacket);
}
// Enable only after resend, so that the interceptor does not
// queue these again or increment outgoingStanzaCount.
isSmEnabled = true;
// Re-notify the listener - we are really ready for packets now
// Before this point, isSuspendPending() was true, and the listener should have
// ignored reconnectionSuccessful() from XMPPConnection.
mConnectionListener.reconnectionSuccessful();
} else if ("failed".equals(name)) {
// Failed, shutdown and the parent will retry
debug("sm failed");
mConnection.getRoster().setOfflineOnError(true);
mConnection.getRoster().setOfflinePresences();
sessionId = null;
mConnection.shutdown();
// isSmEnabled / isOutgoingSmEnabled are already false
}
}
}
}, new PacketFilter() {
public boolean accept(Packet packet) {
return true;
}
});
}
private void removeOutgoingAcked(long ackCount) {
if (ackCount > outgoingStanzaCount) {
// Log.e(XmppConnection.TAG,
// "got ack of " + ackCount + " but only sent " + outgoingStanzaCount);
// Reset the outgoing count here in a feeble attempt to re-sync. All bets
// are off.
outgoingStanzaCount = ackCount;
}
int size = outgoingQueue.size();
while (size > outgoingStanzaCount - ackCount) {
outgoingQueue.remove();
size--;
}
}
private static void addSimplePacketExtension(final String name, final String namespace) {
ProviderManager.getInstance().addExtensionProvider(name, namespace,
new PacketExtensionProvider() {
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
StreamHandlingPacket packet = new StreamHandlingPacket(name, namespace);
int attributeCount = parser.getAttributeCount();
for (int i = 0; i < attributeCount; i++) {
packet.addAttribute(parser.getAttributeName(i),
parser.getAttributeValue(i));
}
return packet;
}
});
}
private void debug(String message) {
System.out.println(message);
}
private void trace(String message) {
System.out.println(message);
}
public static class StreamHandlingPacket extends UnknownPacket {
private String name;
private String namespace;
Map<String, String> attributes;
StreamHandlingPacket(String name, String namespace) {
this.name = name;
this.namespace = namespace;
attributes = Collections.emptyMap();
}
public void addAttribute(String name, String value) {
if (attributes == Collections.EMPTY_MAP)
attributes = new HashMap<String, String>();
attributes.put(name, value);
}
public String getAttribute(String name) {
return attributes.get(name);
}
public String getNamespace() {
return namespace;
}
public String getElementName() {
return name;
}
public String toXML() {
StringBuilder buf = new StringBuilder();
buf.append("<").append(getElementName());
// TODO Xmlns??
if (getNamespace() != null) {
buf.append(" xmlns=\"").append(getNamespace()).append("\"");
}
for (String key : attributes.keySet()) {
buf.append(" ").append(key).append("=\"")
.append(StringUtils.escapeForXML(attributes.get(key))).append("\"");
}
buf.append("/>");
return buf.toString();
}
}
/** Returns true if the packet is a Stanza as defined in RFC-6121 - a Message, IQ or Presence packet. */
public static boolean isStanza(Packet packet) {
if (packet instanceof Message)
return true;
if (packet instanceof IQ)
return true;
if (packet instanceof Presence)
return true;
return false;
}
public void queue(Packet packet) {
if (outgoingQueue.size() >= maxOutgoingQueueSize) {
System.out.println("outgoing queue full");
return;
}
outgoingStanzaCount++;
outgoingQueue.add(packet);
}
private void close() {
isSmEnabled = false;
isOutgoingSmEnabled = false;
sessionId = null;
}
}
when I send the Enable packet to server it says Service
Unavailable(503)
Service Unavailable means that the service is unavailable on the server. Does ejabberd support XEP-198? Did you enable it?
You should also consider switching to Smack 4.1.0-alpha, which also runs on Android and comes with Stream Management support. yaxim will soon switch to from it's custom XmppStreamHandler implementation to Smack 4.1.