How to wait for all responses of WCF Method ? - c#

I have WCF method that take the scores of all players(clients) and add them to a list of them for sorting .
but Unfortunately when the first score sent to the server , the function add it to the list and sent the sorted list to the client , dose not wait for all other scores from other Players .
i tried to use async & await in order to delay continuation of the method for about 30 seconds as the following :
public List<Player> GetListOfWinners(int SentScore , string _SentPlayerID )
{
List<Player> PlayersList = new List<Player>();
//Add to the List
PlayersList.Add(new Player { PlayerID = _SentPlayerID, Score = SentScore });
DelayMethod();
//Order It
List<Player> SortedList = PlayersList.OrderByDescending(p => p.Score).ToList();
// sent fULL SORTHEDlist to the Clients
return SortedList;
}
private async void DelayMethod()
{
await Task.Delay(30000);
}
but it dosn`t work , so what should i do ?

I've created a very basic service as a sample to show how you can accomplish some of your goal. It is provided as a means to introduce you to constructs such as lock and ManualResetEvent.
In my ISudokuConcurrentService.cs:
namespace SudokuTest
{
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
[ServiceContract]
public interface ISudokuConcurrentService
{
[OperationContract]
SudokuGame ClientConnect();
[OperationContract]
List<Player> GetListOfWinners(int SentScore, string _SentPlayerID);
}
[DataContract]
public class SudokuGame
{
[DataMember]
public string PlayerID { get; set; }
[DataMember]
public int TimeLimitInSeconds { get; set; }
}
[DataContract]
public class Player
{
[DataMember]
public string PlayerID { get; set; }
[DataMember]
public int Score { get; set; }
}
}
In my SudokuConcurrentService.svc.cs:
namespace SudokuTest
{
using System.Collections.Generic;
using System.Linq;
using System.Threading;
public class SudokuConcurrentService : ISudokuConcurrentService
{
static int playerCount = 0;
static List<Player> PlayersList = null;
static object ListLock = new object();
const int TimeLimit = 120;
static ManualResetEvent AllPlayerResponseWait = new ManualResetEvent(false);
public SudokuGame ClientConnect()
{
lock (ListLock)
{
if (PlayersList != null)
{
// Already received a completed game response -- no new players.
return null;
}
}
return new SudokuGame()
{
PlayerID = "Player " + Interlocked.Increment(ref playerCount).ToString(),
TimeLimitInSeconds = TimeLimit
};
}
public List<Player> GetListOfWinners(int SentScore, string _SentPlayerID)
{
lock (ListLock)
{
// Initialize the list.
if (PlayersList == null) PlayersList = new List<Player>();
//Add to the List
PlayersList.Add(new Player { PlayerID = _SentPlayerID, Score = SentScore });
}
// Now decrement through the list of players to await all responses.
if (Interlocked.Decrement(ref playerCount) == 0)
{
// All responses received, unlock the wait.
AllPlayerResponseWait.Set();
}
// Wait on all responses, as necessary, up to 150% the timeout (no new players allowed one responses received,
// so the 150% should allow time for the last player to receive game and send response, but if any players have
// disconnected, we don't want to wait indefinitely).
AllPlayerResponseWait.WaitOne(TimeLimit * 1500);
//Order It
List<Player> SortedList = PlayersList.OrderByDescending(p => p.Score).ToList();
// sent fULL SORTHEDlist to the Clients
return SortedList;
}
}
}
Note that the main limitation of this sample is that it only allows a single game to be played during the life of the service. I'll leave running multiple games as an exercise for you, but will point out that you will need to track everything being done now per each game. You will likely want to bundle up the list and wait locks into objects, which you can then store another list of, in some manner such as MemoryCache

Related

Confusion over using abstract class in c#

I have a question since I'm new in programming Im really confused about how to implement a best practice solution for the following problem, Its a game logic,here are the possible ways of making points
EnemyA 400,
EnemyB 500,
EnemyC 700,
Coin 200,
FireBall 300
means hitting Coin gives you 200 points and shooting FireBall gives you 300 &...
if you reach 1000 points you will get an extra life, the logic simple but implementing the best practice is not(at least to me)should I use abstract class or Dictionary?
So far, I used a Dictionary, I read a given file (txt) file which is written (EnemyA,EnemyB,Coin,Coin,Coin,Coin) then I calculate the points:
public int pointCal(IEnumerable<string> enemyType)
{
var possiblePoints = new Dictionary< EnemisEntity,int>()
{
{new EnemisEntity{enemyType="EnemyA"},400 },
{new EnemisEntity{enemyType="EnemyB" },500 },
{new EnemisEntity{enemyType="EnemyC"},700 },
{new EnemisEntity{enemyType="Fireball"},300 },
{new EnemisEntity{enemyType="Coin"},200 },
};
int z=0;
List<int> myPoints=new List<int> ();
foreach (var item in enemyType)
{
z = possiblePoints.FirstOrDefault(f => f.Key.enemyType.Equals(item)).Value;
myPoints.Add(z);
}
int finalPonts= g.Sum(s=>Convert.ToInt32(s));
return finalPonts;
}
Enemy entity class:
public class EnemisEntity
{
public string enemyType { get; set; }
}
It depends. Are the enemies different types of objects (with different properties and such)?
Then it might make sense to create an abstract class and child classes.
public abstract class Shootable {
public int points;
}
public class EnemyA: Shootable {
}
public class EnemyB: Shootable {
}
public class Coin: Shootable {
}
// etc
If all your items are just shootable with one description, then
public class Shootable {
public int points { get; set; }
public string enemyType { get; set; }
public Shootable(int points, string enemyType ){
this.points = points;
this.enemyType = enemyType;
}
}
// then create like
var coin = new Shootable(500, "coin");
If all enemies can be modeled in the same class, then you only need shootable class
Then get the points:
IEnumerble<Shootable> shootableItems = GetShootableFromFile();
var score = shootableItems.Sum(s => s.Points);
You GetShootableFromFile should create one object per file row. So it is a viable situation to create the same objects twice:
// This is a mock to indicate multiple instances of the same class.
public IEnumerble<Shootable> GetShootableFromFile() {
List<Shootable> shootable = new List<Shootable>();
shootable.Add(new Shootable(500,"coin"));
shootable.Add(new Shootable(500,"coin"));
shootable.Add(new Shootable(500,"coin"));
shootable.Add(new Shootable(300,"enemyA"));
shootable.Add(new Shootable(300,"enemyB"));
// etc
}
To me, this is a question of design. When the only difference between enemies is just the value of points as well as their name it is not a good idea to define a class hierarchy. The only thing different between a EnemyA and a EnemyB class would be just the values contained within each class. So you can use a single common class to hold information for each enemy and process the points.
Below is the simplest working prototype that I could code that implements this design. It relies on two classes. The EnemyEntity class to hold the type of enemy and its points, and a Game class that contains the logic behind scoring and keeping a record of all possible enemies.
public class EnemyEntity
{
public EnemyEntity(string type, int points)
{
Type=type;
Points=points;
}
public string Type { get; }
public int Points { get; }
}
public class Game
{
public Game(params (string type, int points)[] values)
{
this.Enemies = new List<EnemyEntity>();
foreach (var (type, points) in values)
{
Enemies.Add(new EnemyEntity(type, points));
}
}
public List<EnemyEntity> Enemies { get; }
public int CalculatePoints(IEnumerable<string> targets)
{
int points = 0;
foreach (var item in targets)
{
var target = Enemies.FirstOrDefault((enemy) => enemy.Type.Equals(item));
if (target!=null)
{
points+= target.Points;
}
}
return points;
}
}
class Program
{
static void Main(string[] args)
{
var game = new Game(
("EnemyA", 400),
("EnemyB", 500),
("EnemyC", 700),
("Coin", 200),
("FireBall", 300));
var input = "EnemyA,EnemyB,Coin,Coin,Coin,Coin";
var targets = input.Split(',');
var points = game.CalculatePoints(targets);
Console.WriteLine(points);
// 1700
}
}
NOTES:
The simplest approach is to use a List<EnemyEntity> and do the lookup with .FirstOrDefault(). I could use a Dictionary<string,EnemyEntity> which would simplify the lookup process. Here is how the Game class would change using a dictionary.
public class Game
{
public Game(params (string type, int points)[] values)
{
this.Enemies = new Dictionary<string, EnemyEntity>();
foreach (var (type, points) in values)
{
Enemies[type] = new EnemyEntity(type, points);
}
}
public Dictionary<string, EnemyEntity> Enemies { get; }
public int CalculatePoints(IEnumerable<string> targets)
{
int points = 0;
foreach (var item in targets)
{
var target = Enemies[item];
if (target!=null)
{
points+= target.Points;
}
}
return points;
}
}

Threads monitoring a Queue<Actions>

I doing a small project to map a network (routers only) using SNMP. In order to speed things up, I´m trying to have a pool of threads responsible for doing the jobs I need, apart from the first job which is done by the main thread.
At this time I have two jobs, one takes a parameter the other doesn´t:
UpdateDeviceInfo(NetworkDevice nd)
UpdateLinks() *not defined yet
What I´m trying to achieve is to have those working threads waiting for a job to
appear on a Queue<Action> and wait while it is empty. The main thread will add the first job and then wait for all workers, which might add more jobs, to finish before starting adding the second job and wake up the sleeping threads.
My problem/questions are:
How to define the Queue<Actions> so that I can insert the methods and the parameters if any. If not possible I could make all functions accept the same parameter.
How to launch the working threads indefinitely. I not sure where should I create the for(;;).
This is my code so far:
public enum DatabaseState
{
Empty = 0,
Learning = 1,
Updating = 2,
Stable = 3,
Exiting = 4
};
public class NetworkDB
{
public Dictionary<string, NetworkDevice> database;
private Queue<Action<NetworkDevice>> jobs;
private string _community;
private string _ipaddress;
private Object _statelock = new Object();
private DatabaseState _state = DatabaseState.Empty;
private readonly int workers = 4;
private Object _threadswaitinglock = new Object();
private int _threadswaiting = 0;
public Dictionary<string, NetworkDevice> Database { get => database; set => database = value; }
public NetworkDB(string community, string ipaddress)
{
_community = community;
_ipaddress = ipaddress;
database = new Dictionary<string, NetworkDevice>();
jobs = new Queue<Action<NetworkDevice>>();
}
public void Start()
{
NetworkDevice nd = SNMP.GetDeviceInfo(new IpAddress(_ipaddress), _community);
if (nd.Status > NetworkDeviceStatus.Unknown)
{
database.Add(nd.Id, nd);
_state = DatabaseState.Learning;
nd.Update(this); // The first job is done by the main thread
for (int i = 0; i < workers; i++)
{
Thread t = new Thread(JobRemove);
t.Start();
}
lock (_statelock)
{
if (_state == DatabaseState.Learning)
{
Monitor.Wait(_statelock);
}
}
lock (_statelock)
{
if (_state == DatabaseState.Updating)
{
Monitor.Wait(_statelock);
}
}
foreach (KeyValuePair<string, NetworkDevice> n in database)
{
using (System.IO.StreamWriter file = new System.IO.StreamWriter(n.Value.Name + ".txt")
{
file.WriteLine(n);
}
}
}
}
public void JobInsert(Action<NetworkDevice> func, NetworkDevice nd)
{
lock (jobs)
{
jobs.Enqueue(item);
if (jobs.Count == 1)
{
// wake up any blocked dequeue
Monitor.Pulse(jobs);
}
}
}
public void JobRemove()
{
Action<NetworkDevice> item;
lock (jobs)
{
while (jobs.Count == 0)
{
lock (_threadswaitinglock)
{
_threadswaiting += 1;
if (_threadswaiting == workers)
Monitor.Pulse(_statelock);
}
Monitor.Wait(jobs);
}
lock (_threadswaitinglock)
{
_threadswaiting -= 1;
}
item = jobs.Dequeue();
item.Invoke();
}
}
public bool NetworkDeviceExists(NetworkDevice nd)
{
try
{
Monitor.Enter(database);
if (database.ContainsKey(nd.Id))
{
return true;
}
else
{
database.Add(nd.Id, nd);
Action<NetworkDevice> action = new Action<NetworkDevice>(UpdateDeviceInfo);
jobs.Enqueue(action);
return false;
}
}
finally
{
Monitor.Exit(database);
}
}
//Job1 - Learning -> Update device info
public void UpdateDeviceInfo(NetworkDevice nd)
{
nd.Update(this);
try
{
Monitor.Enter(database);
nd.Status = NetworkDeviceStatus.Self;
}
finally
{
Monitor.Exit(database);
}
}
//Job2 - Updating -> After Learning, create links between neighbours
private void UpdateLinks()
{
}
}
Your best bet seems like using a BlockingCollection instead of the Queue class. They behave effectively the same in terms of FIFO, but a BlockingCollection will let each of your threads block until an item can be taken by calling GetConsumingEnumerable or Take. Here is a complete example.
http://mikehadlow.blogspot.com/2012/11/using-blockingcollection-to-communicate.html?m=1
As for including the parameters, it seems like you could use closure to enclose the NetworkDevice itself and then just enqueue Action instead of Action<>

Parallel Tasks Calling Static function

I Have issue with parllel Tasks
My Code
namespace ITDevices
{
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using System;
/*Device Modal*/
public class Device
{
public string IP { get; set; }
public string Name { get; set; }
public string MAC { get; set; }
}
/*Entry Class*/
class Program
{
static async void Main(string[] args)
{
List<Task<Device>> Tasks = new List<Task<Device>>();
for(int i=2;i==0;i--)
{
Tasks.Add(Task.Factory.StartNew<Device>(
()=> {
Device free = Helper.GetFreeDevice();
return free;
}
));
}
await Task.WhenAll(Tasks.ToArray());
foreach(Task<Device> item in Tasks)
{
Console.WriteLine(item.Result.IP);
}
Console.ReadLine();
}
}
/*Devices Helper*/
static class Helper
{
public static List<Device> UsedDevices = new List<Device>();
public static Device GetFreeDevice()
{
List<Device> OnlineDevices = new List<Device>()
{
new Device { IP="192.168.1.15",Name="PerryLabtop",MAC="AC:DS:F2:CC:2D:7A"},
new Device { IP="192.168.1.20",Name="MAYA-PC",MAC="7D:E9:2C:FF:E7:2D"},
new Device { IP="192.168.1.2",Name="server",MAC="D8:C2:A4:DC:E5:3A"}
};
Device FreeDevice = OnlineDevices.Where(x => !UsedDevices.Contains(x)).SingleOrDefault();
if (FreeDevice != null)
UsedDevices.Add(FreeDevice);
return FreeDevice;
}
}
}
//Output
//192.168.1.15
//192.168.1.15
But expected output must be
//192.168.1.15
//192.168.1.20
When debugging the project
all tasks execute GetFreeDevice() function at same time line by line
I need to make tasks wait while current GetFreeDevice() function execution done .. or any thing helpful
THANKS ALL
Several problems must be solved to make it work properly:
You probably reversed condition in for loop, because int i = 2; i == 0; i-- will do nothing. Replace i == 0 with i != 0
async Main method doesn't make sense (see for example this blog) and actually doesn't even compile in Visual Studio. To fix this, you can for example wait for tasks to complete synchronously (use .Wait() instead of await)
To prevent multiple threads to run GetFreeDevice() method simultaneously, simply place lock around code that uses shared objects - which is whole method body in your case.
Because you create new list of OnlineDevices each time GetFreeDevice() method is called, UsedDevices.Contains(x) will not work as expected. By default, objects are compared by their reference. So .Contains(x) will compare Device objects in UsedDevices list (which were put there in one of previous calls) with newly created Device objects, which will never be equal (references of these object will be different despite IP, Name and MAC being the same). To fix this, you could either override Equals() and GetHashCode() methods on Device class, or (as i did) create just one static list of Device objects.
You must replace SingleOrDefault() with FirstOrDefault(). With SingleOrDefault(), program would throw exception if there is more than one unused device, while FirstOrDefault() will take first unused device even if there is more than one.
Full source code with all proposed fixes:
namespace ITDevices
{
/*Device Modal*/
public class Device
{
public string IP { get; set; }
public string Name { get; set; }
public string MAC { get; set; }
}
/*Entry Class*/
class Program
{
static void Main(string[] args)
{
List<Task<Device>> Tasks = new List<Task<Device>>();
for (int i = 2; i != 0; i--)
{
Tasks.Add(Task.Factory.StartNew<Device>(
() => {
Device free = Helper.GetFreeDevice();
return free;
}
));
}
Task.WhenAll(Tasks.ToArray()).Wait();
foreach (Task<Device> item in Tasks)
{
Console.WriteLine(item.Result.IP);
}
Console.ReadLine();
}
}
/*Devices Helper*/
static class Helper
{
public static List<Device> UsedDevices = new List<Device>();
static List<Device> OnlineDevices = new List<Device>()
{
new Device { IP="192.168.1.15",Name="PerryLabtop",MAC="AC:DS:F2:CC:2D:7A"},
new Device { IP="192.168.1.20",Name="MAYA-PC",MAC="7D:E9:2C:FF:E7:2D"},
new Device { IP="192.168.1.2",Name="server",MAC="D8:C2:A4:DC:E5:3A"}
};
static Object LockObject = new Object();
public static Device GetFreeDevice()
{
lock (LockObject)
{
Device FreeDevice = OnlineDevices.Where(x => !UsedDevices.Contains(x)).FirstOrDefault();
if (FreeDevice != null)
UsedDevices.Add(FreeDevice);
return FreeDevice;
}
}
}
}
try:
public static Device GetFreeDevice()
{
List<Device> OnlineDevices = new List<Device>()
{
new Device { IP="192.168.1.15",Name="PerryLabtop",MAC="AC:DS:F2:CC:2D:7A"},
new Device { IP="192.168.1.20",Name="MAYA-PC",MAC="7D:E9:2C:FF:E7:2D"},
new Device { IP="192.168.1.2",Name="server",MAC="D8:C2:A4:DC:E5:3A"}
};
Device FreeDevice = OnlineDevices.Where(x => !UsedDevices.Contains(x)).SingleOrDefault();
if (FreeDevice != null)
lock (UsedDevices)
UsedDevices.Add(FreeDevice);
return FreeDevice;
}
------------------------ UPDATE
try:
public static Device GetFreeDevice()
{
List<Device> OnlineDevices = new List<Device>()
{
new Device { IP="192.168.1.15",Name="PerryLabtop",MAC="AC:DS:F2:CC:2D:7A"},
new Device { IP="192.168.1.20",Name="MAYA-PC",MAC="7D:E9:2C:FF:E7:2D"},
new Device { IP="192.168.1.2",Name="server",MAC="D8:C2:A4:DC:E5:3A"}
};
lock (UsedDevices)
{
Device FreeDevice = OnlineDevices.Where(x => !UsedDevices.Contains(x)).SingleOrDefault();
if (FreeDevice != null)
UsedDevices.Add(FreeDevice);
}
return FreeDevice;
}

Unity, C#, Dictionary, calculating scores of teams in a match

ok its hard for me to wrap my head around this as it is so ill try to explane as clearly as I can what my problem is.
First of all im creating a score system for a fps game that im making.
now right now the score system uses a dubble Dictionary structure that is it has a Dictionary inside a Dictionary.
playerScores = new Dictionary<string, Dictionary<string, int> > ();
the first dictionary is creating a list of user names the 2nd dictionery is used to hold 4 keys (kills, deaths, assists, teamID)
so basicley its
playerScores[username][scoretype][scorevalue]
now it currently storing the kills and deaths of players and then is displaying them in a leader board in descending order of kills and are grouped in what team they are in and that's working all fine.
the issue I am having is trying to work out a way to calculate the teams current score for each team. and also work out what team has the highest score. I just cant get my head around it.
this here is the class that handles this data (not the leaderboard code but the scoring and stuff).
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class MyMatchData : MonoBehaviour
{
Dictionary<string, Dictionary<string, int> > playerScores;
int changeCounter = 0;
public GameObject scoreBoard;
Canvas scoreBoardCan;
public float myTeamID = 0f;
string playerNameKey = "blank";
float myDeaths = 0f;
float myKills = 0f;
// Use this for initialization
void Start ()
{
scoreBoardCan = scoreBoard.transform.parent.GetComponent<Canvas> ();
myDeaths = 0f;
myKills = 0f;
}
void Init ()
//init is just to make shure that we know playerscores is set b4 some one trys to use it
//(when ever some one calls the playerscores first they will call Init())
{
if (playerScores != null) {
return;
}//If the player scores has been set then return out so we dont erase the scores
playerScores = new Dictionary<string, Dictionary<string, int> > (); //must tell it that theres a new dictionery else u will get null error
//playerScores [playerNameKey] = new Dictionary<string, int> (); // agane must tell it theres a new dictionery because this is the sub dictionery
//a new sub dictionerey must be created for evey player. setScore() will create a new sjub dictionerey for a player if one dose not yet exsist.
}
void Update ()
{
if (Input.GetKeyDown (KeyCode.Tab)) {
changeCounter ++;
//scoreBoard.SetActive ( !scoreBoard.activeSelf);
bool scoreBoardCanIsActive = scoreBoardCan.enabled;
scoreBoardCan.enabled = !scoreBoardCanIsActive;
}
if (Input.GetKeyDown (KeyCode.P)) {
ChangeScore ("sky", "kills", 1);
}
}
[RPC]
public void playerDied (bool wasBot, bool wasKilledBySomeone, string killedBy, string playerName, int playerTeam)
{
if (PhotonNetwork.isMasterClient) {
if (wasBot) {
Health hlscBot = GameObject.Find (playerName).GetComponent<Health> ();
hlscBot.GetComponent<PhotonView> ().RPC ("DeSpawn", PhotonTargets.AllBuffered, "bot", playerName, playerName, playerTeam);
} else {
//player respawn stuff here
Debug.Log ("Host MyMatchData " + playerName);
Health hlsc = GameObject.Find (playerName).GetComponent<Health> ();
hlsc.GetComponent<PhotonView> ().RPC ("DeSpawn", PhotonTargets.AllBuffered, "player", playerName, "null", -1);
}
//do Score dumping stuff here
if (wasKilledBySomeone == true) {
ChangeScore (playerName, "deaths", 1);//increase the players deaths
ChangeScore (killedBy, "kills", 1);//increase the killers score
} else {
ChangeScore (playerName, "deaths", 1);//increase the players deaths
}
}
}
[RPC]
public void firstSpawn (string userName, int teamID)
{
if (!PhotonNetwork.isMasterClient) {
return;
}
SetScore (userName, "kills", 0);
SetScore (userName, "deaths", 0);
SetScore (userName, "assists", 0);
SetScore (userName, "teamID", teamID);
}
public int GetScore (string username, string scoreType)
{
Init ();
if (playerScores.ContainsKey (username) == false) { //this just meens if the player name receaved has not reconised then it must not of been set yet
return 0;// return the player score as 0
}
if (playerScores [username].ContainsKey (scoreType) == false) {// at this stage we know the player name is recorded in the dictionery but we need to check if there score has been recorded yet. if not then return 0
return 0;// return the player score as 0
}
return playerScores [username] [scoreType];
}
public void SetScore (string username, string scoreType, int value)
{
Init ();
changeCounter ++;
if (playerScores.ContainsKey (username) == false) {//if the player has not been recorded in the dictionery yet.
playerScores [username] = new Dictionary<string, int> ();// Creates a new dictinerey(sub dictionery) for that player.
}
playerScores [username] [scoreType] = value; // the players score is now the value of value
}
public void ChangeScore (string username, string scoreType, int amount)
{
Init ();
int currScore = GetScore (username, scoreType);
SetScore (username, scoreType, currScore + amount);
}
public string[] GetPlayerNames ()
{
Init ();
return playerScores.Keys.ToArray ();
}
public string[] GetPlayerNames (string sortingScoreType)
{
Init ();
string[] names = playerScores.Keys.ToArray (); //have to convert to array because keys dont retun a array but a list of string of keys or somthing
return names.OrderByDescending (n => GetScore (n, sortingScoreType)).ToArray (); //this needs to beconverted toArray because of the same issue above
// ok so the names in this array (names) gets sorted by running the getscore function to find out who has what score. its complacated but works
//for that to work you need using System.Linq
}
public int GetChangeCounter ()
{
return changeCounter;
}
}
can any one help me work out some code to calculate the teams scores?
also there may be 2 teams or 3 or even 12 teams it just depends on how the match is set up.
also most of the comments in the code are for my understanding of the code when I wrote it.
First:
float myDeaths = 0f;
float myKills = 0f;
Unless you can achieve half death / quarter death or even 0.1569865 death, use int.
What I would have done something like this:
internal class Game
{
public List<Team> Teams { get; set; }
public void firstSpawn(string userName, int teamID)
{
if (!PhotonNetwork.isMasterClient)
{
return;
}
Team team;
if (Teams.Any(t => t.TeamID == teamID))
{
team = Teams.FirstOrDefault(t => t.TeamID == teamID);
}
else
{
team = new Team(teamID);
Teams.Add(team);
}
team.Users.Add(new User(userName));
}
public void playerDied(bool wasBot, bool wasKilledBySomeone, User killer, User dead, Team playerTeam)
{
if (PhotonNetwork.isMasterClient)
{
if (wasBot)
{
Health hlscBot = GameObject.Find(dead.Name).GetComponent<Health>();
hlscBot.GetComponent<PhotonView>().RPC("DeSpawn", PhotonTargets.AllBuffered, "bot", dead.Name, dead.Name, playerTeam);
}
else
{
//player respawn stuff here
Debug.Log("Host MyMatchData " + dead.Name);
Health hlsc = GameObject.Find(playerName).GetComponent<Health>();
hlsc.GetComponent<PhotonView>().RPC("DeSpawn", PhotonTargets.AllBuffered, "player", dead.Name, "null", -1);
}
//do Score dumping stuff here
dead.Death(); // The dead is dead, no matter if suicide or not
if (wasKilledBySomeone)
{
killer.Kill();//increase the killers score
}
}
}
public bool getTheHigherTeamBy(Types type)
{
return Teams.Any(team => team.getTeamScore(type) == Teams.Max(t => t.getTeamScore(type)));
}
}
internal class Team
{
public int TeamID { get; set; }
public List<User> Users { get; set; }
public Team(int id = 0)
{
TeamID = id;
}
public int getTeamScore(Types type)
{
return Users.Sum(u => u.Score.getScoreFromType(type));
}
public string[] getPlayerNames()
{
return Users.Select(u => u.Name).ToArray();
}
public string[] GetPlayerNames(Types sortingScoreType)
{
return Users.OrderByDescending(u => u.Score.getScoreFromType(sortingScoreType)).Select(u => u.Name).ToArray();
}
}
public enum Types
{
Kill,
Death,
Assist // Not sure about that one
}
internal class Score
{
private readonly Dictionary<Types, int> points = new Dictionary<Types, int>();
public Score()
{
// Fill the dictionnary with all the available types
foreach (Types type in Enum.GetValues(typeof(Types)))
{
points.Add(type, 0);
}
}
public int getScoreFromType(Types type)
{
return points[type];
}
public void incrementScoreForType(Types type, int amount = 1)
{
points[type] += amount;
}
}
internal class User
{
public string Name { get; set; }
public Score Score { get; set; }
public User(string name = "Default")
{
Name = name;
Score = new Score();
}
public int getScoreFromType(Types type)
{
return Score.getScoreFromType(type);
}
public void changeScore(Types type, int amount)
{
Score.incrementScoreForType(type, amount);
}
public void Death()
{
Score.incrementScoreForType(Types.Death);
}
public void Kill()
{
Score.incrementScoreForType(Types.Kill);
}
public void Assist()
{
Score.incrementScoreForType(Types.Assist);
}
}
In general, it's better to work with classes than with strings that represents fake objects: How can you take care of two players named ARandomName which are in the same team?

WCF task scheduler, or timer like in OGame, Travian etc

I'm looking for resources, or anyone who have writting scheduler in WCF.
What I want to achive is basically what can you see in OGame, or Travian or doznes of other text browser based game.
Player click and send task to server to make building for him, or unit or something else.
From what I figured out I need to run some kind if scheduler on server that will gather all tasks, and will track them, until some perdiod of time will pass (2 mins, 10 mins, 3 days etc.), and after that period end service should call an action, like send data to database.
Well. I've been trying to make something very simply, that I can start from and ended with this:
public class BuildingScheduler
{
public int TaskID { get; set; }
public string UserName { get; set; }
public DateTime TaskEnd { get; set; }
public string BuildingName { get; set; }
public bool TaskDone { get; set; }
public DateTime RemainingTime { get; set; }
TestDBModelContainer _ctx;
TestData _testData;
public IDuplexClient Client { get; set; }
public BuildingScheduler()
{
TaskDone = false;
}
public void MakeBuilding()
{
while (DateTime.Now <= TaskEnd)
{
//Client.DisplayMessage(DateTime.Now.ToString());
RemainingTime = DateTime.Now;
}
_testData = new TestData { DataName = BuildingName, Created = DateTime.Now };
_ctx = new TestDBModelContainer();
_ctx.TestDataSet.AddObject(_testData);
_ctx.SaveChanges();
TaskDone = true;
//Client.DisplayMessage("Building completed!");
}
}
static List<UserChannel> _userChannels = new List<UserChannel>();
static List<BuildingScheduler> _buildingSchedules = new List<BuildingScheduler>();
List<BuildingScheduler> _buildingSchedulesToRemove = new List<BuildingScheduler>();
[OperationContract]
public void DoWork()
{
IDuplexClient client = OperationContext.Current.GetCallbackChannel<IDuplexClient>();
UserChannel userChannel = new UserChannel { Client = client, ClientName = "TestClient" };
string clientName = (from p in _userChannels
where p.ClientName == "TestClient"
select p.ClientName).FirstOrDefault();
lock (((ICollection)_userChannels).SyncRoot)
{
if (clientName == null)
{
_userChannels.Add(userChannel);
}
}
CheckBuilding();
}
private void CheckBuilding()
{
BuildingScheduler bs = (from p in _buildingSchedules
where p.UserName == "TestClient"
select p).FirstOrDefault();
IDuplexClient client = (from p in _userChannels
where p.ClientName == "TestClient"
select p.Client).FirstOrDefault();
if (bs != null)
{
client.DisplayMessage(bs.RemainingTime);
}
}
private void StartBuilding()
{
foreach (BuildingScheduler bs in _buildingSchedules)
{
if (bs.TaskDone == false)
{
bs.MakeBuilding();
}
else if (bs.TaskDone == true)
{
_buildingSchedulesToRemove.Add(bs);
}
}
for(int i = 0; i <= _buildingSchedulesToRemove.Count; i++)
{
BuildingScheduler bs = _buildingSchedulesToRemove.Where(p => p.TaskDone == true).Select(x => x).FirstOrDefault();
_buildingSchedules.Remove(bs);
_buildingSchedulesToRemove.Remove(bs);
}
CheckBuilding();
}
[OperationContract]
public void MakeBuilding(string name)
{
BuildingScheduler _buildng = new BuildingScheduler();
//_buildng.Client = client;
_buildng.TaskID = 1;
_buildng.UserName = "TestClient";
_buildng.TaskEnd = DateTime.Now.AddSeconds(50);
_buildng.BuildingName = name;
_buildingSchedules.Add(_buildng);
StartBuilding();
}
I have hardcoded most values, for testing.
InstanceContextMode is set for PerCall.
Anyway. This code is working. At least to some point. If we ignore zylion exceptions from Entity Framework, I can add tasks from multiple clients and they are added to db in order from newset to oldest (or shortest to longest ?).
The point is, user CANT track his tasks. When I change page I don't see how much time remaining till task done. This code essentialy do not provide time tracking because i got rid of it as it wasn't working anyway.
I guess I should store my task in some presitant data storage and everytime user check page newset state should be draw from that storage and send to him.
I'm not and expert but i think best option here is to store all data in Memory until task is done.
Any relational database will be probably to slow, if I will have to constantly update records with newset state of task.
I know I should just synchronize client side timer with server and do not stream constatly time from server. But Big question is here how to get newset state of task pogress when user come back to page after 3 second or 3 hours ?
Any advices how to make it work as expected.
And btw. I'm using pollingduplex and Silverlight.
It sounds like you are using WCF to do something for which it wasn't designed (but I understand the need). I would suggest you look at Workflow Foundation (WF) as a possible solution to your problem. Here is a good explanation:
http://msdn.microsoft.com/library/dd851337.aspx
Here is also a good intro video:
http://channel9.msdn.com/Blogs/mwink/Introduction-to-Workflow-Services-building-WCF-Services-with-WF
Workflow can consume WCF services and it is designed to work over time. It holds data in state until something changes (regardless of time) without consuming excess resources. Also, it allows for persistance and parallel processes.

Categories