WPF handle multiple key down events in several threads - c#

I'm trying to make a game with two players in WPF, that are controlled with WASD/ arrow keys and can move on the game field. When a control key of first player is pressed while second player is already moving (key is down), the second player stops moving. But I'd like them to move simultaneously.
MainWindow.xaml.cs:
private void Window_KeyDown(object sender, KeyEventArgs e)
{
//Player1
if(Keyboard.IsKeyDown(Key.W)) GameVM.MovePlayer("1W");
if (Keyboard.IsKeyDown(Key.S)) GameVM.MovePlayer("1S");
if (Keyboard.IsKeyDown(Key.A)) GameVM.MovePlayer("1A");
if (Keyboard.IsKeyDown(Key.D)) GameVM.MovePlayer("1D");
//Player2
if (Keyboard.IsKeyDown(Key.Up)) GameVM.MovePlayer("2W");
if (Keyboard.IsKeyDown(Key.Down)) GameVM.MovePlayer("2S");
if (Keyboard.IsKeyDown(Key.Left)) GameVM.MovePlayer("2A");
if (Keyboard.IsKeyDown(Key.Right)) GameVM.MovePlayer("2D");
}

I would do it with an external class from c++ (I guess it is c++) and a main class where all the threads get started from.
For 1 Player you would need 1 class or even better 1 struct which contains the states for the keys aswell as the specific buttons you want for it (changeable because of variables, maybe consider settings.txt/json to safe the new keys etc....)
Key Settings and State Class:
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Media; //Maybe you dont need some of them but you will see it in your IDE if you use them or not
namespace GAME.Settings
{
public class Settings //standard Values and states for the keys
{
internal struct SettingsPlayer1
{
public static Keys UPkey = Keys.A; //List https://learn.microsoft.com/en-en/dotnet/api/system.windows.forms.keys?view=netcore-3.1
public static Keys LEFTkey = Keys.A;
public static Keys DOWNkey = Keys.S;
public static Keys RIGHTkey = Keys.D;
public static bool UP = false; //key states
public static bool LEFT = false;
public static bool DOWN = false;
public static bool RIGHT = false;
}
internal struct SettingsPlayer1
{
public static Keys UPkey = Keys.UP;
public static Keys LEFTkey = Keys.LEFT;
public static Keys DOWNkey = Keys.DOWN;
public static Keys RIGHTkey = Keys.RIGHT;
public static bool UP = false; //key states
public static bool LEFT = false;
public static bool DOWN = false;
public static bool RIGHT = false;
}
}
}
Get Key State class
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using GAME.Settings;
namespace GAME.Keys
{
internal class Imports //imports from .dll
{
[DllImport("user32.dll")]
public static extern short GetAsyncKeyState(Keys vKey); //needed to get the async Key state
}
public class GetKeysPressedP1
{
static bool GetAsyncKeyState(Keys key) => Imports.GetAsyncKeyState(key) != 0;
public static void Run() //this is the method which the thread will run
{
while (true)
{
Thread.Sleep(5); //you can choose your sleep time for input delay
if (GetAsyncKeyState(Settings.SettingsPlayer1.UPkey)) Settings.SettingsPlayer1.UP = true;
else Settings.SettingsPlayer1.UP = false;
if (GetAsyncKeyState(Settings.SettingsPlayer1.LEFTkey)) Settings.SettingsPlayer1.LEFT = true;
else Settings.SettingsPlayer1.LEFT = false;
if (GetAsyncKeyState(Settings.SettingsPlayer1.DOWNkey)) Settings.SettingsPlayer1.DOWN = true; //you could also do something in here like move to x direction etc.
else Settings.SettingsPlayer1.DOWN = false;
if (GetAsyncKeyState(Settings.SettingsPlayer1.RIGHTkey)) Settings.SettingsPlayer1.RIGHT = true;
else Settings.SettingsPlayer1.RIGHT = false;
}
}
}
}
Main Class
using System;
using System.Threading;
using GAME.Keys;
using GAME.Settings;
namespace GAME.Main
{
public class main //can be any class in which the thread will be created and started
{
public main()
{
public static Thread GetKeysPressedThreadP1 = new Thread(GetKeysPressedP1.Run); //new thread
GetKeysPressedThreadP1.Start(); //starting the new thread in the background
while(true)
{
//Do Something with it if(GAME.Settings.SettingsPlayer1.UP){ P1.position.x++;}
}
}
}
}
As I've done this on notepad right now, I can not check if everything is right, I will check it in 20mins but maybe this will already help you
If you have questions regarding this code, just ask.
If this is all wrong or makes no sense in this specific case, feel free to edit or mark it so I can either delete or edit it because I'm not an expert in c# myself:)

Related

(UNITY) My JSON file is not loading on start

I am making a basic shop system includes car name, price and buyable. I am saving data to a text file. When I replay, I can't get the last variables I've changed. But, if I refresh at Editor by CTRL+R and start the game, variables are loading correct. What am I doing wrong? I am new at storage and JSON issues. Thanks for answers...
Here is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;
public class GameHandler : MonoBehaviour
{
[Serializable]
public class Car
{
public string name;
public int price;
public bool unlocked;
}
[Serializable]
public class CarList{
public Car[] cars;
}
private Car cars = new Car();
public CarList myCars = new CarList();
//-------
public int chosenCar;
//-------
public TextAsset CARS;
//-------
private void Start() {
GetFromJSON();
}
private void Update() {
if(Input.GetKeyDown(KeyCode.L))
GetFromJSON();
if(Input.GetKeyDown(KeyCode.S))
SaveToJSON();
}
public void ChangeCarIndex(int a){
chosenCar = a;
}
//This is a button func.
public void ChangePrice(int a){
myCars.cars[chosenCar].price = a;
SaveToJSON();
}
public void GetCarName(){
Debug.Log(myCars.cars[chosenCar].name);
}
public void GetCarPrice(){
Debug.Log(myCars.cars[chosenCar].price);
}
public void SaveToJSON(){
string jsOutput = JsonUtility.ToJson(myCars);
File.WriteAllText(Application.dataPath + "/CARS.txt", jsOutput);
Debug.Log("SaveToJSON() called");
}
public void GetFromJSON(){
myCars = JsonUtility.FromJson<CarList>(CARS.text);
Debug.Log("GetFromJSON() called");
}
}
When running this in the editor try to add
public void SaveToJSON()
{
string jsOutput = JsonUtility.ToJson(myCars);
File.WriteAllText(Application.dataPath + "/CARS.txt", jsOutput);
Debug.Log("SaveToJSON() called");
#if UNITY_EDITOR
UnityEditor.AssetDatabase.Refresh();
#endif
}
Though actually this isn't really necessary! Afaik you could also simply set the text of the textasset:
public void SaveToJSON()
{
string jsOutput = JsonUtility.ToJson(myCars);
CARS.text = jsOutput;
Debug.Log("SaveToJSON() called");
}
BUT NOTE:
in general note that this makes only sense in the editor itself.
If you target to do this in an actually later built application you would rather go via a file in Application.persistentDataPath. Problem with that though: The data can easily be seen and edited by the user. So if this is anything sensitive you will need to go for a central database server with user login.

C# Open Form and run fuction at the same time

I am trying to code a memory cheat for a game but I want to create a menu so I decided to make it. And now I want that my program is opening the form and doing the cheat at the same time. Because now the cheat is doing it or otherwise the cheat is not working and the form is opening.
I am pretty new in C# so sorry if I am noob ... :P
Thanks,
IzzyMichiel
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ac_client_cheat
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Main();
}
public static int Pbase = 0x00509B74;
public static int Health = 0xf8;
public static int mgammo = 0x150;
public static int fly = 0x44;
public void Main()
{
VAMemory vam = new VAMemory("ac_client");
int localplayer = vam.ReadInt32((IntPtr)Pbase);
{
while (true)
{
int address = localplayer + fly;
float aimlock = vam.ReadFloat((IntPtr)address);
vam.WriteFloat((IntPtr)address, -5);
address = localplayer + Health;
vam.WriteInt32((IntPtr)address, 1337);
address = localplayer + mgammo;
vam.WriteInt32((IntPtr)address, +1337);
}
}
}
}
}
You can show the form and run your cheat loop simultaneously as following:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public static int Pbase = 0x00509B74;
public static int Health = 0xf8;
public static int mgammo = 0x150;
public static int fly = 0x44;
/// <summary>The main entry point for the application.</summary>
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var f = new Form1();
f.Show();
VAMemory vam = new VAMemory("ac_client");
int localplayer = vam.ReadInt32((IntPtr)Pbase);
while (f.Visible) //loop while form is not closed
{
int address = localplayer + fly;
float aimlock = vam.ReadFloat((IntPtr)address);
vam.WriteFloat((IntPtr)address, -5);
address = localplayer + Health;
vam.WriteInt32((IntPtr)address, 1337);
address = localplayer + mgammo;
vam.WriteInt32((IntPtr)address, +1337);
Application.DoEvents(); //Yield control to the message loop
}
}
private void Form1_Load(object sender, EventArgs e)
{
//...
}
}
Windows forms applications start out as single-threaded, so you can't do two things at once.
The typical way to handle this would be to start up a worker thread, e.g. using Task.Run.
However, since your application is so simple, we can use
Application.DoEvents instead.
When you call DoEvents, your thread will check the message loop for the form and handle any pending events, such as menu clicks or whathaveyou. If there are no events pending, it'll let your main loop resume. This scheme lets you get away with running the form and the loop on the same thread.
while (true)
{
int address = localplayer + fly;
float aimlock = vam.ReadFloat((IntPtr)address);
vam.WriteFloat((IntPtr)address, -5);
address = localplayer + Health;
vam.WriteInt32((IntPtr)address, 1337);
address = localplayer + mgammo;
vam.WriteInt32((IntPtr)address, +1337);
Application.DoEvents(); //Yield control to the message loop
}
Also, make sure you are starting your message loop with Application.Run. See this answer for more information. Here's one way to do it:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
The problem with your code is that the constructor never exits, so the form can never be displayed.
Remove the call to Main from your constructor and put it in a Timer event handler instead. Since the timer will handle repetition for you, you can remove the while loop too.

use multithread to write into text files using C#

Hope you can help me with this one.i am beginner in multithreading programming with C#.
I am trying to build a program to write all numbers from range 1 to 2000 in two text files using two threads.
Every thread should write number from 1 to 2000 that not found at any of the two files "there is no duplicated numbers in the files" and every thread should't write the number that have been wrote by the another thread.
At the end if we merged the numbers of the two files we should have the numbers from 1 to 2000
Here is the source code i am trying but there is a problem in the writing for loop in the below image
i can't handle the process of writing by the two synchronized threads and i had exception:
Object synchronization method was called from an unsynchronized block of code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
namespace Multithreading
{
class Program
{
static TextWriter file2 = new StreamWriter("file2 location");
static TextWriter file1 = new StreamWriter("file1 location");
static void Main(string[] args)
{
try
{
int[] array = new int[2000];
Thread thread1 = new Thread(Program.writeinfile1);
Thread thread2 = new Thread(Program.writeinfile2);
for (int counter = 1; counter <= 2000; counter++)
{
thread1.Start(counter);
thread2.Start(++counter);
Monitor.Enter(thread1);
Monitor.Wait(thread1);
Monitor.PulseAll(thread2);
}
}
catch (FileNotFoundException)
{
Console.WriteLine("the file you are trying to open is not found");
}
}
public static void writeinfile1(object x)
{
int converttointx = (int)x;
file1.WriteLine(converttointx);
file1.Close();
}
public static void writeinfile2(object y)
{
int converttointy = (int)y;
file2.WriteLine(converttointy);
file2.Close();
}
}
}
Here's an example of multi-threaded calls talking to one another to ensure they don't duplicate work.
I've not done exactly what you've asked for, since this looks quite homeworky; but hopefully this will help you to figure out the solution to your issue...
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace StackOverflow
{
class Program
{
static void Main(string[] args)
{
new Program();
Console.WriteLine("done");
Console.ReadKey();
}
Program()
{
int noThreads = 5;
int target = 2000;
StartThread(noThreads, target);
}
//kicks off our threads / waits for all threads to complete before returning
void StartThread(int noThreads, int target)
{
int id = noThreads--;
if (id > 0)
{
Doer doer = new Doer(id, target);
Thread t = new Thread(doer.Do);
t.Start();
StartThread(noThreads,target);
t.Join();
}
}
}
class Doer
{
static int marker = 0;
static readonly object syncLocker = new object();
readonly int id;
readonly int target;
public Doer(int id, int target)
{
this.id = id;
this.target = target;
}
public void Do()
{
while (marker < this.target)
{
int i;
lock (syncLocker)
{
i = ++marker;
}
System.Console.WriteLine("{0:00}: {1:###0}", id, i);
//Thread.Sleep(RandomNo()); //uncomment this & code below if your threads are taking turns / behaving too predictably
}
}
/*
static readonly Random rnd = new Random();
static readonly object rndSyncLocker = new object();
public static int RandomNo()
{
lock (rndSyncLocker)
{
return rnd.Next(0, 1000);
}
}
*/
}
}
You are not using the Monitor class correctly. The call to Monitor.PulseAll(thread2); should be called within thread the thread which owns the lock, which in this case would be within the writeinfile1 and writeinfile2 method.
This is why you are getting the exception:
Object synchronization method was called from an unsynchronized block of code.
See the following StackOverflow question for the correct way to use Monitor.PulseAll(object):
Help needed in Monitor.PulseAll()

Is it possible to hook objects being collected by GC?

Suppose I have a WeakReference of a target strong reference. I'd like to be informed when the target object itself is being collected by the GC. Is it possible?
EDIT: Adding code to the finalizer/destructor is not an option here. I need something that is not dependent on class code.
It's possible under .NET 4.0 and following using ConditionalWeakTable<TKey, TValue>. Thanks this, and other sites. It follows proof of concept code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
namespace Test
{
public static class GCInterceptor
{
private static ConditionalWeakTable<object, CallbackRef> _table;
static GCInterceptor()
{
_table = new ConditionalWeakTable<object, CallbackRef>();
}
public static void RegisterGCEvent(this object obj, Action<int> action)
{
CallbackRef callbackRef;
bool found = _table.TryGetValue(obj, out callbackRef);
if (found)
{
callbackRef.Collected += action;
return;
}
int hashCode = RuntimeHelpers.GetHashCode(obj);
callbackRef = new CallbackRef(hashCode);
callbackRef.Collected += action;
_table.Add(obj, callbackRef);
}
public static void DeregisterGCEvent(this object obj, Action<int> action)
{
CallbackRef callbackRef;
bool found = _table.TryGetValue(obj, out callbackRef);
if (!found)
throw new Exception("No events registered");
callbackRef.Collected -= action;
}
private class CallbackRef
{
private int _hashCode;
public event Action<int> Collected;
public CallbackRef(int hashCode)
{
_hashCode = hashCode;
}
~CallbackRef()
{
Action<int> handle = Collected;
if (handle != null)
handle(_hashCode);
}
}
}
}
Tested with the following code:
public partial class Form1 : Form
{
private object _obj;
public Form1()
{
InitializeComponent();
_obj = new object();
_obj.RegisterGCEvent(delegate(int hashCode)
{
MessageBox.Show("Object with hash code " + hashCode + " recently collected");
});
}
private void button1_Click(object sender, EventArgs e)
{
_obj = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
What about Object.Finalize() method? Won't that be invoked upon finalization?
You may use interception to catch Finalize for each classes which is inherited from a custom interface/class. I think, this is what you want to try to achieve, right? You can use Unity for that. Here is a very short example how to do interception with Unity.

Duplicate Logon Script

ok, so I'm writing a duplicate logon "worker" class in C# and I've ran into a bit of a snag. My logic behind it, I thought, was flawless! :(
But, I can't for the life of me figure out why it's triggering on the first occurence rather than on just duplicates :(
namespace Lab.Core.BackgroundWorker {
using Lab.Core;
using Lab.Core.Net;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Windows.Forms;
public class MultiLogon : IWorker {
private static Hashtable LoggedOnUsers = new Hashtable();
private Thread _worker = null;
//private Thread m_UsersUpdate = null;
public delegate Boolean AddUserToCollectionDelegate(String user, String computer);
public delegate void ClearCollectionDelegate(String user);
public delegate Boolean IsUserLoggedInDelegate(String user);
public Boolean AddUserToCollection(String user, String computer) {
int retVal = MultiLogon.LoggedOnUsers.Count + 1;
if (String.IsNullOrEmpty(user) || String.IsNullOrEmpty(computer))
return false;
if (!MultiLogon.LoggedOnUsers.ContainsKey(user))
MultiLogon.LoggedOnUsers.Add(user, computer);
return (MultiLogon.LoggedOnUsers.Count == retVal);
}
public void ClearCollection() {
if (MultiLogon.LoggedOnUsers.Count > 0)
MultiLogon.LoggedOnUsers.Clear();
}
public Boolean IsUserLoggedIn(String user) {
if (String.IsNullOrEmpty(user))
return false;
return (LoggedOnUsers.Contains(user));
}
#region IWorker Members
public void Run(object obj) {
AddUserToCollectionDelegate add = new AddUserToCollectionDelegate(AddUserToCollection);
//ClearCollectionDelegate clear = new ClearCollectionDelegate(ClearCollection);
//IsUserLoggedInDelegate isLogged = new IsUserLoggedInDelegate(IsUserLoggedIn);
while (true) {
foreach (Computer c in ComputerList.Instance)
if (!add.Invoke(c.UserName, c.MachineName)) {
// duplicate! or not? :/
// Credit (through adoption of code) goes to:
// http://bytes.com/groups/net-c/263778-quickly-finding-duplicates-arraylist#post1059834
foreach (DictionaryEntry item in MultiLogon.LoggedOnUsers) {
MessageBox.Show((String)item.Key, (String)item.Value);
//NetworkMessage.Send((String)item.Value, String.Format("It is against lab policy to share your account with anyone other than yourself or use someone else's account! Logout immediately or further action will be taken. Your action has been logged."));
//OffenseManager.Instance.AddOffense((String)item.Key, null, String.Format("Account sharing - Computer: {0}", item.Value), false);
}
}
Thread.Sleep(750);
}
}
public void Start() {
_worker = new Thread(new ParameterizedThreadStart(Run));
_worker.IsBackground = true;
_worker.Start();
}
public void Stop() {
if (_worker.IsAlive)
_worker.Abort();
}
#endregion
}
}
Apologies for the long code file. I don't know exactly what to paste to help you guys help me. :/
Thanks in advance! :)
Could be a thread race condition.
Have you tried to lock the collection when you're searching/inserting into it?
I don't believe Hashtable are threadsafe.
You might want to put a
lock(this) {
}
block around anything accessing the hashtable.

Categories