The editor class has a method called GetString which prompts the user for a string value via AutoCAD's command prompt. I call it in this wrapper method:
public static string PromptUserForString(string message = "Enter a string: ", string defaultAnswer = "")
{
return _editor.GetString("\n" + message).StringResult;
}
The argument message becomes the message the user sees when prompted for a string. How do I set it up so that the value of default answer is automatically set to be the answer so that if the user hits enter right away that becomes the value like in the screen shot below
So 1 is automatically typed as an answer meaning the user can either hit enter for the value of 1 or change 1 to whatever non-default answer they want
I paste you some code as example for the different prompts :
using System;
using System.Collections.Generic;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.ApplicationServices;
namespace EditorUtilities
{
/// <summary>
/// Prompts with the active document ( MdiActiveDocument )
/// </summary>
public class EditorHelper : IEditorHelper
{
private readonly Editor _editor;
public EditorHelper(Document document)
{
_editor = document.Editor;
}
public PromptEntityResult PromptForObject(string promptMessage, Type allowedType, bool exactMatchOfAllowedType)
{
var polyOptions = new PromptEntityOptions(promptMessage);
polyOptions.SetRejectMessage("Entity is not of type " + allowedType);
polyOptions.AddAllowedClass(allowedType, exactMatchOfAllowedType);
var polyResult = _editor.GetEntity(polyOptions);
return polyResult;
}
public PromptPointResult PromptForPoint(string promptMessage, bool useDashedLine = false, bool useBasePoint = false, Point3d basePoint = new Point3d(),bool allowNone = true)
{
var pointOptions = new PromptPointOptions(promptMessage);
if (useBasePoint)
{
pointOptions.UseBasePoint = true;
pointOptions.BasePoint = basePoint;
pointOptions.AllowNone = allowNone;
}
if (useDashedLine)
{
pointOptions.UseDashedLine = true;
}
var pointResult = _editor.GetPoint(pointOptions);
return pointResult;
}
public PromptPointResult PromptForPoint(PromptPointOptions promptPointOptions)
{
return _editor.GetPoint(promptPointOptions);
}
public PromptDoubleResult PromptForDouble(string promptMessage, double defaultValue = 0.0)
{
var doubleOptions = new PromptDoubleOptions(promptMessage);
if (Math.Abs(defaultValue - 0.0) > Double.Epsilon)
{
doubleOptions.UseDefaultValue = true;
doubleOptions.DefaultValue = defaultValue;
}
var promptDoubleResult = _editor.GetDouble(doubleOptions);
return promptDoubleResult;
}
public PromptIntegerResult PromptForInteger(string promptMessage)
{
var promptIntResult = _editor.GetInteger(promptMessage);
return promptIntResult;
}
public PromptResult PromptForKeywordSelection(
string promptMessage, IEnumerable<string> keywords, bool allowNone, string defaultKeyword = "")
{
var promptKeywordOptions = new PromptKeywordOptions(promptMessage) { AllowNone = allowNone };
foreach (var keyword in keywords)
{
promptKeywordOptions.Keywords.Add(keyword);
}
if (defaultKeyword != "")
{
promptKeywordOptions.Keywords.Default = defaultKeyword;
}
var keywordResult = _editor.GetKeywords(promptKeywordOptions);
return keywordResult;
}
public Point3dCollection PromptForRectangle(out PromptStatus status, string promptMessage)
{
var resultRectanglePointCollection = new Point3dCollection();
var viewCornerPointResult = PromptForPoint(promptMessage);
var pointPromptStatus = viewCornerPointResult.Status;
if (viewCornerPointResult.Status == PromptStatus.OK)
{
var rectangleJig = new RectangleJig(viewCornerPointResult.Value);
var jigResult = _editor.Drag(rectangleJig);
if (jigResult.Status == PromptStatus.OK)
{
// remove duplicate point at the end of the rectangle
var polyline = rectangleJig.Polyline;
var viewPolylinePoints = GeometryUtility.GetPointsFromPolyline(polyline);
if (viewPolylinePoints.Count == 5)
{
viewPolylinePoints.RemoveAt(4); // dont know why but true, probably mirror point with the last point
}
}
pointPromptStatus = jigResult.Status;
}
status = pointPromptStatus;
return resultRectanglePointCollection;
}
public PromptSelectionResult PromptForSelection(string promptMessage = null, SelectionFilter filter = null)
{
var selectionOptions = new PromptSelectionOptions { MessageForAdding = promptMessage };
var selectionResult = String.IsNullOrEmpty(promptMessage) ? _editor.SelectAll(filter) : _editor.GetSelection(selectionOptions, filter);
return selectionResult;
}
public PromptSelectionResult PromptForSelection(PromptSelectionOptions promptSelectionOptions,SelectionFilter filter = null)
{
return _editor.GetSelection(promptSelectionOptions, filter);
}
public void WriteMessage(string message)
{
_editor.WriteMessage(message);
}
public void DrawVector(Point3d from, Point3d to, int color, bool drawHighlighted)
{
_editor.DrawVector(from, to, color, drawHighlighted);
}
}
}
Related
I'm not able to print the display name stored in playfab in the settings, reception of the username is enabled, and the user exists but I can't retrieve and print it so that the script fills in the user in the text field I always get this error:
error CS1061: 'string' does not contain a definition for 'text' and no accessible extension method 'text' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
When I comment out the line containing the username.text the debug prints the username correctly to the console.
public void PlayerData()
{
PlayFabClientAPI.GetAccountInfo(new GetAccountInfoRequest(
(result) =>
{
Debug.Log(username);
username.text = result.AccountInfo.Username;
},
(error) =>
{
Debug.LogError(error.GenerateErrorReport());
});
}
Full code PlayFabControler
using PlayFab;
using PlayFab.ClientModels;
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections.Generic;
using PlayFab.Json;
using PlayFab.ProfilesModels;
using JsonObject = PlayFab.Json.JsonObject;
using System;
public class PlayFabController : MonoBehaviour
{
public static PlayFabController PFC;
private string userEmail;
private string userPassword;
private string username;
public GameObject loginPanel;
private void OnEnable()
{
if(PlayFabController.PFC == null)
{
PlayFabController.PFC = this;
}
else
{
if(PlayFabController.PFC != this)
{
Destroy(this.gameObject);
}
}
DontDestroyOnLoad(this.gameObject);
}
public void Start()
{
//Note: Setting title Id here can be skipped if you have set the value in Editor Extensions already.
if (string.IsNullOrEmpty(PlayFabSettings.TitleId))
{
PlayFabSettings.TitleId = "8741"; // Please change this value to your own titleId from PlayFab Game Manager
}
PlayerPrefs.DeleteAll();
//var request = new LoginWithCustomIDRequest { CustomId = "GettingStartedGuide", CreateAccount = true };
// PlayFabClientAPI.LoginWithCustomID(request, OnLoginSuccess, OnLoginFailure);
if(PlayerPrefs.HasKey("EMAIL"))
{
userEmail = PlayerPrefs.GetString("EMAIL");
userPassword = PlayerPrefs.GetString("PASSWORD");
var request = new LoginWithEmailAddressRequest { Email = userEmail, Password = userPassword };
PlayFabClientAPI.LoginWithEmailAddress(request, OnLoginSuccess, OnLoginFailure);
}
}
#region Login
private void OnLoginSuccess(LoginResult result)
{
Debug.Log("Congratulations, you made your first successful API call!");
PlayerPrefs.SetString("EMAIL", userEmail);
PlayerPrefs.SetString("PASSWORD", userPassword);
loginPanel.SetActive(false);
GetStats();
// SceneManager.LoadScene("Menu");
}
private void OnRegisterSuccess(RegisterPlayFabUserResult result)
{
Debug.Log("Congratulations, you made your first successful API call!");
PlayerPrefs.SetString("EMAIL", userEmail);
PlayerPrefs.SetString("PASSWORD", userPassword);
GetStats();
loginPanel.SetActive(false);
}
private void OnLoginFailure(PlayFabError error)
{
var registerRequest = new RegisterPlayFabUserRequest { Email = userEmail, Password = userPassword, Username = username };
PlayFabClientAPI.RegisterPlayFabUser(registerRequest, OnRegisterSuccess, OnRegisterFailure);
}
private void OnRegisterFailure(PlayFabError error)
{
Debug.LogError(error.GenerateErrorReport());
}
public void GetUserEmail(string emailIn)
{
userEmail = emailIn;
}
public void GetUsername(string usernameIn)
{
username = usernameIn;
}
public void GetUserPassword(string passwordIn)
{
userPassword = passwordIn;
}
public void OnClickLogin()
{
var request = new LoginWithEmailAddressRequest { Email = userEmail, Password = userPassword, };
PlayFabClientAPI.LoginWithEmailAddress(request, OnLoginSuccess, OnLoginFailure);
}
#endregion Login
public int playerLevel;
public int gameLevel;
public int playerHealth;
public int playerDamage;
public int playerHighScore;
public int playerGolds;
public void PlayerData()
{
PlayFabClientAPI.GetAccountInfo(new GetAccountInfoRequest(),
(result) =>
{
Debug.Log(username);
//username.text = result.username;
username = result.AccountInfo.Username;
},
(error) =>
{
Debug.LogError(error.GenerateErrorReport());
});
}
#region PlayerStats
public void SetStats()
{
PlayFabClientAPI.UpdatePlayerStatistics(new UpdatePlayerStatisticsRequest{
// request.Statistics is a list, so multiple StatisticUpdate objects can be defined if required.
Statistics = new List<StatisticUpdate> {
new StatisticUpdate { StatisticName = "PlayerLevel", Value = playerLevel },
new StatisticUpdate { StatisticName = "GameLevel", Value = gameLevel },
new StatisticUpdate { StatisticName = "PlayerHealth", Value = playerHealth },
new StatisticUpdate { StatisticName = "PlayerDamage", Value = playerDamage },
new StatisticUpdate { StatisticName = "PlayerHighScore", Value = playerHighScore },
new StatisticUpdate { StatisticName = "PlayerGolds", Value = playerGolds },
}
},
result => { Debug.Log("User statistics updated"); },
error => { Debug.LogError(error.GenerateErrorReport()); });
}
void GetStats()
{
PlayFabClientAPI.GetPlayerStatistics(
new GetPlayerStatisticsRequest(),
OnGetStats,
error => Debug.LogError(error.GenerateErrorReport())
);
}
void OnGetStats(GetPlayerStatisticsResult result)
{
Debug.Log("Received the following Statistics:");
foreach (var eachStat in result.Statistics)
{
Debug.Log("Statistic (" + eachStat.StatisticName + "): " + eachStat.Value);
switch(eachStat.StatisticName)
{
case "PlayerLevel":
playerLevel = eachStat.Value;
break;
case "GameLevel":
gameLevel = eachStat.Value;
break;
case "PlayerHealth":
playerHealth = eachStat.Value;
break;
case "PlayerDamage":
playerDamage = eachStat.Value;
break;
case "PlayerHighScore":
playerHighScore = eachStat.Value;
break;
case "PlayerGolds":
playerGolds = eachStat.Value;
break;
}
}
}
// Build the request object and access the API
public void StartCloudUpdatePlayerStats()
{
PlayFabClientAPI.ExecuteCloudScript(new ExecuteCloudScriptRequest()
{
FunctionName = "UpdatePlayerStats", // Arbitrary function name (must exist in your uploaded cloud.js file)
FunctionParameter = new { Level = playerLevel, GameLevel = gameLevel, Health = playerHealth, Damage = playerDamage, highScore = playerHighScore, Golds = playerGolds },
GeneratePlayStreamEvent = true, // Optional - Shows this event in PlayStream
}, OnCloudUpdateStats, OnErrorShared);
}
// OnCloudHelloWorld defined in the next code block
private static void OnCloudUpdateStats(ExecuteCloudScriptResult result)
{
// CloudScript (Legacy) returns arbitrary results, so you have to evaluate them one step and one parameter at a time
JsonObject jsonResult = (JsonObject)result.FunctionResult;
object messageValue;
jsonResult.TryGetValue("messageValue", out messageValue); // note how "messageValue" directly corresponds to the JSON values set in CloudScript (Legacy)
Debug.Log((string)messageValue);
}
private static void OnErrorShared(PlayFabError error)
{
Debug.Log(error.GenerateErrorReport());
}
#endregion PlayerStats
}
I have 4 buttons in my project and all these buttons call different score results from server. At awake function I generate a table with random values and it works pretty fine. However when I call a function and get results I couldnt set the table with received values because entryContainer and entryTemplate would be null which I think should not be because they are instantiated!. Here is my code which works pretty fine at page opening but could not set items after receiving request
using Assets.Scripts.HttpPost;
using Assets.Scripts.Models;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
public class HighScoreTable : MonoBehaviour
{
public static Transform entryContainer;
public static Transform entryTemplate;
public List<HighscoreEntry> highscoreEntryList;
public static List<Transform> highscoreEntryTransformList;
public static bool isChanged = false;
public void Awake()
{
// Works pretty fine at opening
entryContainer = transform.Find("highscoreEmptyContainer");
entryTemplate = entryContainer.Find("highscoreEntryTemplate");
entryTemplate.gameObject.SetActive(false);
highscoreEntryList = new List<HighscoreEntry>() {
new HighscoreEntry{score = 33.6, username = "Osman"},
new HighscoreEntry{score = 46033.5, username = "Ahmet"},
new HighscoreEntry{score = 1833.5, username = "Veli"},
new HighscoreEntry{score = 563.5, username = "Can"},
new HighscoreEntry{score = 433.5, username = "Esma"},
new HighscoreEntry{score = 6533.5, username = "Gözde"},
new HighscoreEntry{score = 733.5, username = "Ayşe"},
new HighscoreEntry{score = 733.5, username = "Fatma"},
new HighscoreEntry{score = 383.5, username = "Hayriye"}
};
//generates table its ok
highscoreEntryTransformList = new List<Transform>();
foreach (var highscoreEntry in highscoreEntryList)
{
CreateHighscoreEntryTransform(highscoreEntry, entryContainer, highscoreEntryTransformList);
}
}
public void Update()
{
}
private void CreateHighscoreEntryTransform(HighscoreEntry highscoreEntry, Transform container, List<Transform> transformList)
{
float templateHeight = 30f;
while (container == null)
container = transform.Find("highscoreEmptyContainer");
Transform entryTransform = Instantiate(entryTemplate, entryContainer);
RectTransform entryRectTransform = entryTransform.GetComponent<RectTransform>();
entryRectTransform.anchoredPosition = new Vector2(0, -30 - templateHeight * transformList.Count);
entryTransform.gameObject.SetActive(true);
int rank = transformList.Count + 1;
string rankString;
switch (rank)
{
default:
rankString = rank + "TH"; break;
case 1: rankString = "1ST"; break;
case 2: rankString = "2ND"; break;
case 3: rankString = "3RD"; break;
}
entryTransform.Find("posText").GetComponent<Text>().text = rankString;
double score = highscoreEntry.score;
entryTransform.Find("scoreText").GetComponent<Text>().text = score.ToString();
entryTransform.Find("nameText").GetComponent<Text>().text = highscoreEntry.username;
transformList.Add(entryTransform);
}
#region AllVerbal
public WWW AllVerbalGet(string postAddress, string jsonData)
{
entryContainer = transform.Find("highscoreEmptyContainer");
entryTemplate = entryContainer.Find("highscoreEntryTemplate");
entryTemplate.gameObject.SetActive(false);
WWW www;
byte[] formData = null;
try
{
Hashtable postHeader = new Hashtable();
postHeader.Add("Content-Type", "application/json");
// convert json string to byte
if (!string.IsNullOrEmpty(jsonData))
{
formData = System.Text.Encoding.UTF8.GetBytes(jsonData);
}
www = new WWW(postAddress, formData, postHeader);
StartCoroutine(WaitForAllVerbalGetRequest(www));
}
catch (System.Exception ex)
{
www = null;
}
return www;
}
//Wait for the www Request
IEnumerator WaitForAllVerbalGetRequest(WWW www)
{
yield return www;
if (www.error == null)
{
//Print server response
try
{
LeaderboardListModel result = JsonUtility.FromJson<LeaderboardListModel>("{\"leaders\":" + www.text + "}");
highscoreEntryList = new List<HighscoreEntry>();
// Items received and converted to model successfully!
HighscoreEntry temp;
foreach (var item in result.leaders)
{
temp = new HighscoreEntry();
temp.username = item.username;
temp.score = item.totalScore;
temp.weekscore = item.weeklyTotalScore;
highscoreEntryList.Add(temp);
}
highscoreEntryTransformList = new List<Transform>();
foreach (var highscoreEntry in highscoreEntryList)
{
// HERE IS THE PROBLEM ENTRY CONTAINER IS NULL BUT ITS INSTANTIATED AT AWAKE FUNCTION!!!!!!!!!!!
// I ALSO TRY TO SET THESE ITEMS AGAIN BUT STILL RETURNS NULL
//entryContainer = transform.Find("highscoreEmptyContainer");
//entryTemplate = entryContainer.Find("highscoreEntryTemplate");
//entryTemplate.gameObject.SetActive(false);
//WHAT SHOULD I DO entry container is null!! :(
CreateHighscoreEntryTransform(highscoreEntry, entryContainer, highscoreEntryTransformList);
}
isChanged = true;
}
catch (System.Exception ex)
{
}
}
else
{
try
{
highscoreEntryList = new List<HighscoreEntry>();
highscoreEntryTransformList = new List<Transform>();
foreach (var highscoreEntry in highscoreEntryList)
{
CreateHighscoreEntryTransform(highscoreEntry, entryContainer, highscoreEntryTransformList);
}
}
catch (System.Exception)
{
}
}
}
#endregion
public void allVerbalButton_Click()
{
isChanged = true;
AllVerbalGet(HttpOperations.allVerbalURL, null);
}
public void allCompButton_Click()
{
isChanged = true;
AllVerbalGet(HttpOperations.allCompURL, null);
}
public void Top10VerbalButton_Click()
{
isChanged = true;
AllVerbalGet(HttpOperations.Top10VerbalURL, null);
}
public void Top10ComputButton_Click()
{
isChanged = true;
AllVerbalGet(HttpOperations.Top10CompURL, null);
}
[System.Serializable]
public class HighscoreEntry
{
public double score;
public string username;
public double weekscore;
}
}
I am pretty new at unity but couldnt solve it :( Any help would be great
I have a Xamarin application and have managed to download my data from my server to my device. I have also got it set up so that it can take a SqlCipher Encryption key to encrypt the data.
My question is where is the correct location to store my key that I use to encrypt this data? Is it to you KeyStore / KeyChain? Which mono classes should I be looking to use?
Due to the popularity of this question I am going to post my implementation of this:
PCL interface
public interface IAuth
{
void CreateStore();
IEnumerable<string> FindAccountsForService(string serviceId);
void Save(string pin,string serviceId);
void Delete(string serviceId);
}
Android
public class IAuthImplementation : IAuth
{
Context context;
KeyStore ks;
KeyStore.PasswordProtection prot;
static readonly object fileLock = new object();
const string FileName = "MyProg.Accounts";
static readonly char[] Password = null;
public void CreateStore()
{
this.context = Android.App.Application.Context;
ks = KeyStore.GetInstance(KeyStore.DefaultType);
prot = new KeyStore.PasswordProtection(Password);
try
{
lock (fileLock)
{
using (var s = context.OpenFileInput(FileName))
{
ks.Load(s, Password);
}
}
}
catch (Java.IO.FileNotFoundException)
{
//ks.Load (null, Password);
LoadEmptyKeyStore(Password);
}
}
public IEnumerable<string> FindAccountsForService(string serviceId)
{
var r = new List<string>();
var postfix = "-" + serviceId;
var aliases = ks.Aliases();
while (aliases.HasMoreElements)
{
var alias = aliases.NextElement().ToString();
if (alias.EndsWith(postfix))
{
var e = ks.GetEntry(alias, prot) as KeyStore.SecretKeyEntry;
if (e != null)
{
var bytes = e.SecretKey.GetEncoded();
var password = System.Text.Encoding.UTF8.GetString(bytes);
r.Add(password);
}
}
}
return r;
}
public void Delete(string serviceId)
{
var alias = MakeAlias(serviceId);
ks.DeleteEntry(alias);
Save();
}
public void Save(string pin, string serviceId)
{
var alias = MakeAlias(serviceId);
var secretKey = new SecretAccount(pin);
var entry = new KeyStore.SecretKeyEntry(secretKey);
ks.SetEntry(alias, entry, prot);
Save();
}
void Save()
{
lock (fileLock)
{
using (var s = context.OpenFileOutput(FileName, FileCreationMode.Private))
{
ks.Store(s, Password);
}
}
}
static string MakeAlias(string serviceId)
{
return "-" + serviceId;
}
class SecretAccount : Java.Lang.Object, ISecretKey
{
byte[] bytes;
public SecretAccount(string password)
{
bytes = System.Text.Encoding.UTF8.GetBytes(password);
}
public byte[] GetEncoded()
{
return bytes;
}
public string Algorithm
{
get
{
return "RAW";
}
}
public string Format
{
get
{
return "RAW";
}
}
}
static IntPtr id_load_Ljava_io_InputStream_arrayC;
void LoadEmptyKeyStore(char[] password)
{
if (id_load_Ljava_io_InputStream_arrayC == IntPtr.Zero)
{
id_load_Ljava_io_InputStream_arrayC = JNIEnv.GetMethodID(ks.Class.Handle, "load", "(Ljava/io/InputStream;[C)V");
}
IntPtr intPtr = IntPtr.Zero;
IntPtr intPtr2 = JNIEnv.NewArray(password);
JNIEnv.CallVoidMethod(ks.Handle, id_load_Ljava_io_InputStream_arrayC, new JValue[]
{
new JValue (intPtr),
new JValue (intPtr2)
});
JNIEnv.DeleteLocalRef(intPtr);
if (password != null)
{
JNIEnv.CopyArray(intPtr2, password);
JNIEnv.DeleteLocalRef(intPtr2);
}
}
Call Create Store in the main activity of Android app first. - This could possibly be improved and remove CreateStrore() from the interface by checking if ks == null in Save and Delete and calling the method if true
iOS
public class IAuthImplementation : IAuth
{
public IEnumerable<string> FindAccountsForService(string serviceId)
{
var query = new SecRecord(SecKind.GenericPassword);
query.Service = serviceId;
SecStatusCode result;
var records = SecKeyChain.QueryAsRecord(query, 1000, out result);
return records != null ?
records.Select(GetAccountFromRecord).ToList() :
new List<string>();
}
public void Save(string pin, string serviceId)
{
var statusCode = SecStatusCode.Success;
var serializedAccount = pin;
var data = NSData.FromString(serializedAccount, NSStringEncoding.UTF8);
//
// Remove any existing record
//
var existing = FindAccount(serviceId);
if (existing != null)
{
var query = new SecRecord(SecKind.GenericPassword);
query.Service = serviceId;
statusCode = SecKeyChain.Remove(query);
if (statusCode != SecStatusCode.Success)
{
throw new Exception("Could not save account to KeyChain: " + statusCode);
}
}
//
// Add this record
//
var record = new SecRecord(SecKind.GenericPassword);
record.Service = serviceId;
record.Generic = data;
record.Accessible = SecAccessible.WhenUnlocked;
statusCode = SecKeyChain.Add(record);
if (statusCode != SecStatusCode.Success)
{
throw new Exception("Could not save account to KeyChain: " + statusCode);
}
}
public void Delete(string serviceId)
{
var query = new SecRecord(SecKind.GenericPassword);
query.Service = serviceId;
var statusCode = SecKeyChain.Remove(query);
if (statusCode != SecStatusCode.Success)
{
throw new Exception("Could not delete account from KeyChain: " + statusCode);
}
}
string GetAccountFromRecord(SecRecord r)
{
return NSString.FromData(r.Generic, NSStringEncoding.UTF8);
}
string FindAccount(string serviceId)
{
var query = new SecRecord(SecKind.GenericPassword);
query.Service = serviceId;
SecStatusCode result;
var record = SecKeyChain.QueryAsRecord(query, out result);
return record != null ? GetAccountFromRecord(record) : null;
}
public void CreateStore()
{
throw new NotImplementedException();
}
}
WP
public class IAuthImplementation : IAuth
{
public IEnumerable<string> FindAccountsForService(string serviceId)
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
string[] auths = store.GetFileNames("MyProg");
foreach (string path in auths)
{
using (var stream = new BinaryReader(new IsolatedStorageFileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, store)))
{
int length = stream.ReadInt32();
byte[] data = stream.ReadBytes(length);
byte[] unprot = ProtectedData.Unprotect(data, null);
yield return Encoding.UTF8.GetString(unprot, 0, unprot.Length);
}
}
}
}
public void Save(string pin, string serviceId)
{
byte[] data = Encoding.UTF8.GetBytes(pin);
byte[] prot = ProtectedData.Protect(data, null);
var path = GetAccountPath(serviceId);
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
using (var stream = new IsolatedStorageFileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, store))
{
stream.WriteAsync(BitConverter.GetBytes(prot.Length), 0, sizeof(int)).Wait();
stream.WriteAsync(prot, 0, prot.Length).Wait();
}
}
public void Delete(string serviceId)
{
var path = GetAccountPath(serviceId);
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
store.DeleteFile(path);
}
}
private string GetAccountPath(string serviceId)
{
return String.Format("{0}", serviceId);
}
public void CreateStore()
{
throw new NotImplementedException();
}
}
This is an adaptation of the Xamarin.Auth library (Found Here) but removes the dependency from the Xamarin.Auth library to provide cross platform use through the interface in the PCL. For this reason I have simplified it to only save one string. This is probably not the best implementation but it works in my case. Feel free to expand upon this
There is a nuget package called KeyChain.NET that encapsulated this logic for iOs, Android and Windows Phone.
It's open source and you have find sample at its github repository
More info at this blog post
I'm using Craft.Net.Client library but I've got an error when trying to use the ServerList.SaveTo(string file) method : Unable to read beyond the end of the stream (EndOfStreamException)
ServerList.cs :
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using fNbt;
namespace Craft.Net.Client
{
/// <summary>
/// Provides functionality for interacting with
/// the saved vanilla server list.
/// </summary>
public class ServerList
{
public static string ServersDat
{
get
{
return Path.Combine(DotMinecraft.GetDotMinecraftPath(), "servers.dat");
}
}
public ServerList()
{
Servers = new List<Server>();
}
public List<Server> Servers { get; set; }
public void Save()
{
SaveTo(ServersDat);
}
public void SaveTo(string file)
{
var nbt = new NbtFile(file); // ERROR : Unable to read beyond the end of the stream (EndOfStreamException)
nbt.RootTag = new NbtCompound("");
var list = new NbtList("servers", NbtTagType.Compound);
foreach (var server in Servers)
{
var compound = new NbtCompound();
compound.Add(new NbtString("name", server.Name));
compound.Add(new NbtString("ip", server.Ip));
compound.Add(new NbtByte("hideAddress", (byte)(server.HideAddress ? 1 : 0)));
compound.Add(new NbtByte("acceptTextures", (byte)(server.AcceptTextures ? 1 : 0)));
list.Add(compound);
}
nbt.RootTag.Add(list);
nbt.SaveToFile(file, NbtCompression.None);
}
public static ServerList Load()
{
return LoadFrom(ServersDat);
}
public static ServerList LoadFrom(string file)
{
var list = new ServerList();
var nbt = new NbtFile(file);
foreach (NbtCompound server in nbt.RootTag["servers"] as NbtList)
{
var entry = new Server();
if (server.Contains("name"))
entry.Name = server["name"].StringValue;
if (server.Contains("ip"))
entry.Ip = server["ip"].StringValue;
if (server.Contains("hideAddress"))
entry.HideAddress = server["hideAddress"].ByteValue == 1;
if (server.Contains("acceptTextures"))
entry.AcceptTextures = server["acceptTextures"].ByteValue == 1;
list.Servers.Add(entry);
}
return list;
}
public class Server
{
public string Name { get; set; }
public string Ip { get; set; }
public bool HideAddress { get; set; }
public bool AcceptTextures { get; set; }
public override string ToString()
{
return Name;
}
}
}
}
And where I call the method :
ServerList.Server server = new ServerList.Server();
server.Name = "x";
server.Ip = "x";
server.HideAddress = true;
server.AcceptTextures = true;
ServerList list = new ServerList();
list.Servers.Add(server);
if (!File.Exists(RuntimeInfo.getMinecraftDir() + #"\servers.dat"))
{
File.Create(RuntimeInfo.getMinecraftDir() + #"\servers.dat");
}
list.SaveTo(RuntimeInfo.getMinecraftDir() + #"\servers.dat");
One thing I've noticed is that I only got the error when servers.dat is empty but not if it has already servers saved.
Can anyone help me?
Thanks in advance,
EDIT : Thanks to steveg89, the solution below solves the problem (updating SaveTo method) :
public void SaveTo(string file)
{
NbtFile nbt;
if (File.Exists(RuntimeInfo.getMinecraftDir() + #"\servers.dat"))
{
nbt = new NbtFile();
nbt.SaveToFile(RuntimeInfo.getMinecraftDir() + #"\servers.dat", NbtCompression.None);
}
else
nbt = new NbtFile();
nbt.RootTag = new NbtCompound("");
var list = new NbtList("servers", NbtTagType.Compound);
foreach (var server in Servers)
{
var compound = new NbtCompound();
compound.Add(new NbtString("name", server.Name));
compound.Add(new NbtString("ip", server.Ip));
compound.Add(new NbtByte("hideAddress", (byte)(server.HideAddress ? 1 : 0)));
compound.Add(new NbtByte("acceptTextures", (byte)(server.AcceptTextures ? 1 : 0)));
list.Add(compound);
}
nbt.RootTag.Add(list);
nbt.SaveToFile(file, NbtCompression.None);
}
I notice their constructor for an NbtFile tries to read from the file. It's assuming the file is in the correct format already. That means you'd have to create it and save it. Their API SHOULD handle this for you but doesn't, so try this
if (!File.Exists(RuntimeInfo.getMinecraftDir() + #"\servers.dat"))
{
NbtFile nbt = new NbtFile();
nbt.SaveToFile(RuntimeInfo.getMinecraftDir() + #"\servers.dat", Nbt.Compression.None);
}
I think a smarter way to do it would be to correct the SaveTo method of the ServerList. This method would mean you wouldn't have to check it in your code, but it does mean you'd be using your own flavor of Craft.Net. I'd do it like so:
public void SaveTo(string file)
{
NbtFile nbt;
if( File.Exists( file ) )
{
nbt = new NbtFile(file);
}
else
{
nbt = new NbtFile();
}
nbt.RootTag = new NbtCompound("");
var list = new NbtList("servers", NbtTagType.Compound);
foreach (var server in Servers)
{
var compound = new NbtCompound();
compound.Add(new NbtString("name", server.Name));
compound.Add(new NbtString("ip", server.Ip));
compound.Add(new NbtByte("hideAddress", (byte)(server.HideAddress ? 1 : 0)));
compound.Add(new NbtByte("acceptTextures", (byte)(server.AcceptTextures ? 1 : 0)));
list.Add(compound);
}
nbt.RootTag.Add(list);
nbt.SaveToFile(file, NbtCompression.None);
}
would anyone have a working example of an amazon ITEMLOOKUP ?>
i have the following code but it does not seem to work:
string ISBN = "0393326381";
string ASIN = "";
if (!(string.IsNullOrEmpty(ISBN) && string.IsNullOrEmpty(ASIN)))
{
AWSECommerceServicePortTypeChannel service = new AWSECommerceServicePortTypeChannel();
ItemLookup lookup = new ItemLookup();
ItemLookupRequest request = new ItemLookupRequest();
lookup.AssociateTag = secretKey;
lookup.AWSAccessKeyId = accessKeyId;
if (string.IsNullOrEmpty(ASIN))
{
request.IdType = ItemLookupRequestIdType.ISBN;
request.ItemId = new string[] { ISBN.Replace("-", "") };
}
else
{
request.IdType = ItemLookupRequestIdType.ASIN;
request.ItemId = new string[] { ASIN };
}
request.ResponseGroup = new string[] { "OfferSummary" };
lookup.Request = new ItemLookupRequest[] { request };
response = service.ItemLookup(lookup);
if (response.Items.Length > 0 && response.Items[0].Item.Length > 0)
{
Item item = response.Items[0].Item[0];
if (item.MediumImage == null)
{
//bookImageHyperlink.Visible = false;
}
else
{
//bookImageHyperlink.ImageUrl = item.MediumImage.URL;
}
//bookImageHyperlink.NavigateUrl = item.DetailPageURL;
//bookTitleHyperlink.Text = item.ItemAttributes.Title;
//bookTitleHyperlink.NavigateUrl = item.DetailPageURL;
if (item.OfferSummary.LowestNewPrice == null)
{
if (item.OfferSummary.LowestUsedPrice == null)
{
//priceHyperlink.Visible = false;
}
else
{
//priceHyperlink.Text = string.Format("Buy used {0}", item.OfferSummary.LowestUsedPrice.FormattedPrice);
//priceHyperlink.NavigateUrl = item.DetailPageURL;
}
}
else
{
//priceHyperlink.Text = string.Format("Buy new {0}", item.OfferSummary.LowestNewPrice.FormattedPrice);
//priceHyperlink.NavigateUrl = item.DetailPageURL;
}
if (item.ItemAttributes.Author != null)
{
//authorLabel.Text = string.Format("By {0}", string.Join(", ", item.ItemAttributes.Author));
}
else
{
//authorLabel.Text = string.Format("By {0}", string.Join(", ", item.ItemAttributes.Creator.Select(c => c.Value).ToArray()));
}
/*
ItemLink link = item.ItemLinks.Where(i => i.Description.Contains("Wishlist")).FirstOrDefault();
if (link == null)
{
//wishListHyperlink.Visible = false;
}
else
{
//wishListHyperlink.NavigateUrl = link.URL;
}
* */
}
}
}
the problem is with this:
thisshould be defined differently but i do not know how AWSECommerceServicePortTypeChannel service = new AWSECommerceServicePortTypeChannel();
Say, that code looks awful familiar. You're missing the Endpoint signing piece from when they switched over to requiring that you add message signing. You need to add a behavior on your client. Here's the change to your code above:
if (!(string.IsNullOrEmpty(ISBN) && string.IsNullOrEmpty(ASIN)))
{
AWSECommerceServicePortTypeClient client = new AWSECommerceServicePortTypeClient();
client.ChannelFactory.Endpoint.Behaviors.Add(
new Amazon.AmazonSigningEndpointBehavior(
accessKeyId,
secretKey);
ItemLookup lookup = new ItemLookup();
ItemLookupRequest request = new ItemLookupRequest();
lookup.AssociateTag = accessKeyId;
lookup.AWSAccessKeyId = secretKey;
//... etc.
And here's the Endpoint (I can't take credit for this, I wish I could remember who should):
namespace Amazon
{
public class AmazonSigningEndpointBehavior : IEndpointBehavior {
private string accessKeyId = "";
private string secretKey = "";
public AmazonSigningEndpointBehavior(string accessKeyId, string secretKey) {
this.accessKeyId = accessKeyId;
this.secretKey = secretKey;
}
public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime clientRuntime) {
clientRuntime.MessageInspectors.Add(new AmazonSigningMessageInspector(accessKeyId, secretKey));
}
public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher) { return; }
public void Validate(ServiceEndpoint serviceEndpoint) { return; }
public void AddBindingParameters(ServiceEndpoint serviceEndpoint, BindingParameterCollection bindingParameters) { return; }
}
}
Oh. And you'll need the MessageInspector for that to work.
namespace Amazon
{
public class AmazonSigningMessageInspector : IClientMessageInspector {
private string accessKeyId = "";
private string secretKey = "";
public AmazonSigningMessageInspector(string accessKeyId, string secretKey) {
this.accessKeyId = accessKeyId;
this.secretKey = secretKey;
}
public object BeforeSendRequest(ref Message request, IClientChannel channel) {
// prepare the data to sign
string operation = Regex.Match(request.Headers.Action, "[^/]+$").ToString();
DateTime now = DateTime.UtcNow;
string timestamp = now.ToString("yyyy-MM-ddTHH:mm:ssZ");
string signMe = operation + timestamp;
byte[] bytesToSign = Encoding.UTF8.GetBytes(signMe);
// sign the data
byte[] secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
HMAC hmacSha256 = new HMACSHA256(secretKeyBytes);
byte[] hashBytes = hmacSha256.ComputeHash(bytesToSign);
string signature = Convert.ToBase64String(hashBytes);
// add the signature information to the request headers
request.Headers.Add(new AmazonHeader("AWSAccessKeyId", accessKeyId));
request.Headers.Add(new AmazonHeader("Timestamp", timestamp));
request.Headers.Add(new AmazonHeader("Signature", signature));
return null;
}
public void AfterReceiveReply(ref Message reply, object correlationState) { }
}
}
And finally, the Header:
namespace Amazon
{
public class AmazonHeader : MessageHeader
{
private string name;
private string value;
public AmazonHeader(string name, string value)
{
this.name = name;
this.value = value;
}
public override string Name { get { return name; } }
public override string Namespace { get { return "http://security.amazonaws.com/doc/2007-01-01/"; } }
protected override void OnWriteHeaderContents(XmlDictionaryWriter xmlDictionaryWriter, MessageVersion messageVersion)
{
xmlDictionaryWriter.WriteString(value);
}
}
}
Yes, they made it complicated when they started requiring message signing...
A simple and easy library is available on nuget.
PM> Install-Package Nager.AmazonProductAdvertising
Example
var authentication = new AmazonAuthentication("accesskey", "secretkey");
var client = new AmazonProductAdvertisingClient(authentication, AmazonEndpoint.US);
var result = await client.GetItemsAsync("B00BYPW00I");
To perform a lookup for anything other then an ASIN, you need to specify the "SearchIndex" property. You can simply set it to "All".
var request = new ItemLookupRequest();
request.ItemId = new[] {upcCode};
request.IdType = ItemLookupRequestIdType.UPC;
request.IdTypeSpecified = true;
request.SearchIndex = "All";
Here is a link to the documentation: http://docs.amazonwebservices.com/AWSECommerceService/2011-08-01/DG/index.html?ItemLookup.html. Note the description of the SearchIndex parameter:
Constraint:If ItemIdis an ASIN, a search index cannot be specified in
the request. Required for non-ASIN ItemIds.
I actually built a little wrapper around it so it hands you back a handy object graph. I have the source up on BitBucket and a little more about it on the C# Amazon ItemLookup page.
C# Amazon ItemLookup
You can make calls like:
var item = client.LookupByAsin("B0037X9N5U");
double? price = item.GetLowestPrice();