My question is about retrieving the message type after saving the message index in PlayerPrefs in Unity.
I'm using a message system in my client / server system, based on this tutorial set
For example message "CreateAccount":
The NetOP script hold the index
public static class NetOP
{
public const int None = 0;
public const int CreateAccount = 1;
public const int LoginRequest = 2;
}
The NetMsg class manages the messages
public abstract class NetMsg
{
public byte OP { set; get; } //OP = operation code, a single number to know what is this message
public NetMsg()
{
OP = NetOP.None;
}
}
There's a class for this message:
[System.Serializable]
public class Net_CreateAccount : NetMsg
{
public Net_CreateAccount()
{
OP = NetOP.CreateAccount;
}
public string UserName { set; get; }
public string Password { set; get; }
public string Email { set; get; }
}
When the client receive the message, on Client.cs script:
public void MessageReceived(NetMsg msg)
{
PlayerPrefs.SetInt("msg",msg.OP); //save the msg index in PlayerPrefs
print(msg.OP); //check the message index is correct
}
So far so good, now the Client need to retrieve the received message since it got disconnected (by mistake), so on Client.cs
...case NetworkEventType.ConnectEvent:
isConnected = true;
Debug.Log("We have connected to the server ");
if (PlayerPrefs.GetInt("msg") > 0) //we have a msg in PlayerPref
{
//ISSUE HERE:
communicationManager.CheckMessageStatus(connectionId,PlayerPrefs.GetInt("msg"));
}
break;
....
the communicationManager.CheckMessageStatus(int connectionID, NetMsg msg) - takes an int, and a NetMsg (not the NetMsg.OP which is the index).
I know how to find the index of a NetMsg, but I don't know how to trace the NetMsg back based on its index.
Is it possible (from the script snippets I've posted) to trace the message based on the index?
I'm still learning C# so please help.
Related
I'm having trouble enabling logging for a ServiceStack.JsonServiceClient. I'm working from the documentation Capture HTTP Headers in .NET Service Clients but I must be missing something becasue I'm only ever getting an empty string.
Here's my code:
public class Client
{
private bool Logging { get; }
public Client(bool logging = false)
{
Api = new("https://api.service.com/");
Logging = logging;
if (Logging) Api.CaptureHttp(true, true);
}
public string GetInfo(string name)
{
if (Logging) Api.HttpLog.Clear();
var response = Api.Get(new Requests.GetInfo(name));
Debug.WriteLine(Api.HttpLog.ToString());
return response.Result;
}
}
[Route("/v1/getinfo/{name}")]
[DataContract]
public class GetInfo : Base, IReturn<Responses.GetInfo>
{
public GetInfo(string name) => Name = name;
[DataMember(Name = "name")]
public string Name { get; init; }
}
[TestMethod]
public void Client_GetInfo()
{
var client = new Client(true);
client.GetInfo()
}
Because I have Api.CaptureHttp(true, true) it is logging the info to the test log but I want to be able to capture the httpLog in code and like I said, it's only ever an empty string there.
CaptureHttp has 3 flags:
public void CaptureHttp(bool print = false, bool log = false, bool clear = true)
You're only setting Api.CaptureHttp(print:true,log:true) which should print to the console and log to your logger if IsDebugEnabled, your call only sets the first 2 flags but you're code is relying on capturing the log to print it out in which case you wanted to specify that it shouldn't clear the HttpLog after each request with:
Api.CaptureHttp(clear:false)
first I'm sorry if I mix up some terminology or overlooked some very obvious method, but I'm quite new in C# and OOP in general and this is my project I "self learn" c# with at the moment. That's why I will share larger parts of my code to hopefully iron out some unclarities.
I wan't to get information from an .xml file and use those information in various different methods. For this I found a solution which I implemented and works fine, but is kind of clunky.
I use this Method to read out everything I need from the XML:
class ReadAndLoad
{
public List<CharacterAttributes> FetchAttributes(string userPath)
{
// Declare a new XML Document
XmlDocument XmlDoc = new XmlDocument();
// Try to open the XML file
try
{
Console.WriteLine("\nNow Loading: {0}\n", userPath);
XmlDoc.Load(userPath);
}
// Catch "File Not Found" errors
catch (System.IO.FileNotFoundException)
{
Console.WriteLine("No file found!");
Environment.Exit(1);
}
// Catch Argument Exceptions
catch (System.ArgumentException)
{
Console.WriteLine("Invalid path detected!");
Environment.Exit(1);
}
// Catach all other errors, and print them to console.
catch (Exception err)
{
Console.WriteLine("An Exception has been caught:");
Console.WriteLine(err);
Environment.Exit(1);
}
// Declare the xpath for finding objects inside the XML file
XmlNodeList XmlDocNodes = XmlDoc.SelectNodes("/character/attributes/attribute");
// Define a new List, to store the objects we pull out of the XML
List<CharacterAttributes> attributeList = new List<CharacterAttributes>();
// Loop through the nodes, extracting Person information.
// We can then define a person object and add it to the list.
foreach (XmlNode node in XmlDocNodes)
{
int tempValue = int.Parse(node["totalvalue"].InnerText);
CharacterAttributes obj = new CharacterAttributes(node["name"].InnerText, tempValue);
attributeList.Add(obj);
}
for (int i = 0; i < attributeList.Count; i++)
{
Console.WriteLine(attributeList[i].AttributeName);
Console.WriteLine(attributeList[i].TotalValue);
}
return attributeList;
}
Created a "Character class" that has all attributes in an constructor
class Character
{
//Attribute Fields
public int Body { get; set; }
public int Agility { get; set; }
public int Reaction { get; set; }
public int Strength { get; set; }
public int Willpower { get; set; }
public int Logic { get; set; }
public int Intuition { get; set; }
public int Charisma { get; set; }
public int Edge { get; set; }
public int Essence { get; set; }
public int Resonance { get; set; }
public int Magic { get; set; }
//Attribute Constructor
public Character(int xmlBody, int xmlAgility, int xmlReaction, int xmlStrength, int xmlIntuition, int xmlCharisma, int xmlLogic, int xmlWillpower, int xmlEdge, int xmlMagic, int xmlResonance, int xmlEssence)
{
this.Body = xmlBody;
this.Agility = xmlAgility;
this.Reaction = xmlReaction;
this.Strength = xmlStrength;
this.Intuition = xmlIntuition;
this.Charisma = xmlCharisma;
this.Logic = xmlLogic;
this.Willpower = xmlWillpower;
this.Edge = xmlEdge;
this.Essence = xmlEssence;
this.Resonance = xmlResonance;
this.Magic = xmlMagic;
}
And to create a character I created this Method which takes the list provided by ReadAndLoad.FetchAttributes and feeds them in the constructor
class CreateCharacters
{
public Character CreateCharacterFromXML(string userPath)
{
ReadAndLoad readAndLoad = new ReadAndLoad();
List<CharacterAttributes> attributeList = new List<CharacterAttributes>();
attributeList = readAndLoad.FetchAttributes(userPath);
int bod = attributeList[0].TotalValue;
int agi = attributeList[1].TotalValue;
int rea = attributeList[2].TotalValue;
int str = attributeList[3].TotalValue;
int cha = attributeList[4].TotalValue;
int intuition = attributeList[5].TotalValue;
int log = attributeList[6].TotalValue;
int wil = attributeList[7].TotalValue;
int edg = attributeList[8].TotalValue;
int mag = attributeList[9].TotalValue;
int res = attributeList[11].TotalValue;
int ess = attributeList[12].TotalValue;
Character myCharacter = new Character(bod, agi, rea, str, cha, intuition, log, wil, edg, mag, res, ess);
return myCharacter;
}
}
I feel like there is a more elegant and efficient way to do this, which is easier to expand upon with more data from the same XML. Because at the moment if I would like to introduce some other data I would have to create a new ReadAndLoad Method, the new class for that information, expand the character class and constructor and add all that in the CreateCharacter Method.
Someone pointed me towards xml deserialization, but I wasn't able to make the examples given here work (I tried deserialization from TextReader).
The xml I tried to deserialize / get information from doesn't provide a schema as url, because of that I don't really know how to deserialize it. Given below is the start of the xml.
After some searching I found the schema .xsd that probably gives me the correct schema.
Deserialisazion is sadly not possible at the moment because of an error in the .xsd I can't find.
The .xsd I try to use for deserialization references another .xsd which seems to contain errors, which leads to some elements not being declared.
InteliSense sadly doesn't provide any information regarding that error.
The super-simple way to do this is to copy an XML document to your clipboard and in a C# code file Paste XML as Classes:
Basically, I have OBS running on a computer and I need to interact with it through a second computer.
I am using an OBS-WebSocket plugin which is creating a websocket server on OBS to send remote commands.
My goal is to set visible / invisible a source on OBS whenever I press a key on the second (remote) computer.
I have no issue with that key press part. My issue is that I have no idea how to correctly send a request/command to the websocket plugin (it requires JSON format as this is how data is formatted).
Main code (C#)
static void Main(string[] args)
{
using (var ws = new WebSocket("ws://192.168.1.1:1122"))
{
ws.Connect();
ws.OnMessage += (sender, e) =>
Console.WriteLine(e.Data);
ws.Send("REPLACE WITH JSON REQUEST DATA");
Console.ReadKey(true);
}
}
Down below is an example of data received from the websocket whenever I turn on or off a source in OBS.
You can clearly see that the 'visibility' is either false or true.
*This 'item-visible' is the setting I need to mess with to tell OBS to hide or show the source.*
↓ Source is visible in OBS ↓
{
"item-id": 10,
"item-name": "map",
"item-visible": true,
"scene-name": "ONHOLD",
"update-type": "SceneItemVisibilityChanged"
}
↓ Source is hidden in OBS ↓
{
"item-id": 10,
"item-name": "map",
"item-visible": false,
"scene-name": "ONHOLD",
"update-type": "SceneItemVisibilityChanged"
}
You can see example test usages in the official Github repo.
If you look there, you'd see that that e.Data should be parsed. Then, you can assign it as needed, for example:
public string SetIsVisible(MessageEventArgs e, bool isVisible)
{
JObject body = JObject.Parse(e.Data);
body["item-visible"] = isVisible;
return body.ToString();
}
... and then just send the returned string into your websocket:
ws.Send(SetIsVisible(e, isVisible)); // isVisible is boolean you should assign
P.S. If you'd like to work with a static-type DTO, simply generate one (e.g. here):
public partial class OBSSource
{
[JsonProperty("item-id")]
public long ItemId { get; set; }
[JsonProperty("item-name")]
public string ItemName { get; set; }
[JsonProperty("item-visible")]
public bool ItemVisible { get; set; }
[JsonProperty("scene-name")]
public string SceneName { get; set; }
[JsonProperty("update-type")]
public string UpdateType { get; set; }
}
... and then:
public string SetIsVisible(MessageEventArgs e, bool isVisible)
{
OBSSource obsSource = JsonConvert.DeserializeObject<OBSSource>(e.Data);
obsSource.ItemVisible = isVisible;
return JsonConvert.SerializeObject(obsSource);
}
... and then send it serialized into the websocket as above.
this is part of my source code:
var result = DiceWebAPI.PlaceAutomatedBets(
Session, baseBet, guessLow, guessHigh,
betCount > Session.MaxBetBatchSize ? Session.MaxBetBatchSize : betCount,
resetOnWin, resetOnLoss,
increaseOnWin, increaseOnLoss,
maxBet, resetOnMaxLoss, stopOnMaxLoss, stopMaxBalance);
the viusal c# studio 2010 says:
Error 1 No overload for method 'PlaceAutomatedBets' takes 13 arguments D:\Downloads\SampleBot_NET_3_5\SampleBot_NET_Source\Dice.Sample.Bot.3_5\Main.cs 359 30 DiceSampleBot35
I discovered that all arguments of method have definition except the Session one. Can anybody tell me how to write and where to place the definition?
maybe that would help:
in the other file
readonly SessionInfo Session;
and in another one
namespace Dice.Client.Web
{
public sealed class SessionInfo : INotifyPropertyChanged
{
public string AccountCookie { get; }
public long AccountId { get; }
public decimal Balance { get; }
public long BetCount { get; }
public decimal BetPayIn { get; }
public decimal BetPayOut { get; }
public long BetWinCount { get; }
public long ClientSeed { get; }
public string DepositAddress { get; }
public string Email { get; }
public string EmergencyAddress { get; }
public int MaxBetBatchSize { get; }
public string SessionCookie { get; }
public string Username { get; }
public event PropertyChangedEventHandler PropertyChanged;
}
}
UPDATE:
PlaceAutomatedBets.. definition
public static PlaceAutomatedBetsResponse PlaceAutomatedBets(SessionInfo session, AutomatedBetsSettings settings);
You need to go to PlaceAutomatedBets method, and, you will find that your PlaceAutomatedBets only takes 12 arguments, you will need to add the one who is missing.
you are passing extra arguments. Exception is self explanatory. and there is no need to re-define HttpContext.Session Property it's already there in System.Web if you haven't included this just put this line on top of your class Using System.Web;
The problem here is that you are calling a method that has a signature of two parameters:
public static PlaceAutomatedBetsResponse PlaceAutomatedBets(
SessionInfo session,
AutomatedBetsSettings settings);
However, you are calling it with 12:
var result = DiceWebAPI.PlaceAutomatedBets(
Session, baseBet, guessLow, guessHigh,
betCount > Session.MaxBetBatchSize ? Session.MaxBetBatchSize : betCount,
resetOnWin, resetOnLoss,
increaseOnWin, increaseOnLoss,
maxBet, resetOnMaxLoss, stopOnMaxLoss, stopMaxBalance);
C# cannot take your 12 parameters and automatically form them into an object. Instead, you have to build an AutomatedBetsSettings object and pass that in with Session. That's pretty standard in software design, where you want to reduce the number of parameters. The object with all the detail in is often called a context.
For example, you need to do something like:
// I am guessing the content of AutomatedBetsSettings here, you will need
// to check the actual property values.
var betSettingsContext = new AutomatedBetsSettings() {
BaseBet = baseBet,
GuessLow = guessLow,
GuessHigh = guessHigh,
BestCount = betCount > Session.MaxBetBatchSize ? Session.MaxBetBatchSize : betCount,
ResetOnWin = resetOnWin,
ResetOnLoss = resetOnLoss,
IncreaseOnWin = increaseOnWin,
IncreaseOnLoss = increaseOnLoss,
MaxBet = maxBet,
ResetOnMaxLoss = resetOnMaxLoss,
StopOnMaxLoss = stopOnMaxLoss,
StopMaxBalance = stopMaxBalance
};
var result = DiceWebAPI.PlaceAutomatedBets(Session, betSettingsContext);
Try to use like this.
var result = DiceWebAPI.PlaceAutomatedBets(
HttpContext.Current.Session, baseBet, guessLow, guessHigh,
betCount > Session["MaxBetBatchSize"] ? Session["MaxBetBatchSize"] : betCount,
resetOnWin, resetOnLoss,
increaseOnWin, increaseOnLoss,
maxBet, resetOnMaxLoss, stopOnMaxLoss, stopMaxBalance);
With my program I'm trying to automatize another program of which there can be multiple instances. I've already written functionality that will watch the processlist and detect all processes of the program that I want to automatize.
It will store some basic informations about found instances into this ConcurrentDictionary which has its ProcessId as key and the class ProgramToWatch as value:
public static ConcurrentDictionary<int, ProgramToWatch> ProgramToWatchDictionary = new ConcurrentDictionary<int, ProgramToWatch>();
public class ProgramToWatch
{
public string ListItemName { get; set; }
public Process BEProcess { get; set; }
public string BEMainWindowTitle { get; set; }
public Application BEApplication { get; set; }
public Window BEWindow { get; set; }
public bool isLoggedIn { get; set; }
public List<ProgramToWatchDataGrid> BEDataGrid = new List<ProgramToWatchDataGrid>();
}
Now the part I am having problems with. The program I want to watch has a DataGridView which I want to copy into my dictionary. For this I have the BEDataGrid List. The list is using this class as its type:
public class ProgramToWatchDataGrid
{
public int ListID { get; set; }
public string Name { get; set; }
public string Mail { get; set; }
}
Now to store it, I create another instance of the ProgramToWatchDataGrid (called updateGrid), write all the data I read into it, and place this into my Dictionary (I simplified). To do this, I iterate through the DataGrid (first while loop), row for row and copy the updateGrid to my Dictionary - in the second while loop I display the values to verify:
public void ReadDataGridView(int ProcessId)
{
ProgramToWatchDataGrid updateGrid = new ProgramToWatchDataGrid();
//read and store every row
int i=0;
while(i<=totalRowsInGrid)
{
updateGrid.ListId = DataGrid.Rows[i].Cells[0].Value;
updateGrid.Name = DataGrid.Rows[i].Cells[1].Value;
updateGrid.Mail = DataGrid.Rows[i].Cells[2].Value;
ProgramToWatchDictionary[ProcessID].BEDataGrid.Insert(i, updateGrid);
Display(ProgramToWatchDictionary[ProcessID].BEDataGrid[i].Mail);
}
Display("Elements: " + ProgramToWatchDictionary[ProcessID].BeDataGrid.Count);
//display every rows mail
i=0;
while(i<=totalRowsInGrid)
{
Display("HI: " + ProgramToWatchDictionary[ProcessID].BEDataGrid[i].Mail);
i++;
}
}
The way I understand it, the Information rows I read should now be located in ProgramToWatchDictionary[ProcessID].BEDataGrid[accordingRow] because I inserted the Information at that place.
The strange thing is, that this output will be produced:
[01:19] christoferlindstrm#yahoo.com
[01:19] eliseisaksson#yahoo.com
[01:19] peter#pan.com
[01:19] Elements: 3
[01:19] HI: peter#pan.com
[01:19] HI: peter#pan.com
[01:19] HI: peter#pan.com
So right after I've inserted it, the List contains the right value. But after it will always be the last value that was read? Why does this happen and how can I fix this?
Some help would be greatly appreciated!
Got it.
You should create the instance of the object to be added in the while, otherwise you are using only one instance which happens to get different values in your cycle. And of course it will end up with the last value you have inserted.
public void ReadDataGridView(int ProcessId)
{
ProgramToWatchDataGrid updateGrid = null;
//read and store every row
int i=0;
while(i<=totalRowsInGrid)
{
//create a new instance
updateGrid = new ProgramToWatchDataGrid();
updateGrid.ListId = DataGrid.Rows[i].Cells[0].Value;
updateGrid.Name = DataGrid.Rows[i].Cells[1].Value;
updateGrid.Mail = DataGrid.Rows[i].Cells[2].Value;
ProgramToWatchDictionary[ProcessID].BEDataGrid.Insert(i, updateGrid);
Display(ProgramToWatchDictionary[ProcessID].BEDataGrid[i].Mail);
}
Display("Elements: " + ProgramToWatchDictionary[ProcessID].BeDataGrid.Count);
//display every rows mail
i=0;
while(i<=totalRowsInGrid)
{
Display("HI: " + ProgramToWatchDictionary[ProcessID].BEDataGrid[i].Mail);
i++;
}
}