How can i properly block method until object counter gets > 0? - c#

You can think that class is strange, but it's educational, so i have to do it this way.
class petriPool {
private int[] nodes = new int[3];
private int counter;
private readonly object _lock = new object();
public petriPool (int n, int m) {
this.nodes[0] = n;
this.nodes[1] = 0;
this.nodes[2] = 0;
this.counter = m;
}
public bool start() {
lock (_lock) {
if (this.nodes[0] != 0 && counter != 0) {
this.nodes[0]--;
this.nodes[1]++;
counter--;
return true;
} else
return false;
}}
public bool stop() {
lock (_lock) {
if (this.nodes[1] != 0) {
this.nodes[1]--;
this.nodes[2]++;
counter++;
return true;
} else
return false;
}}
}
I need to make start() method wait until counter get value > 0. I can do so:
public bool start() {
while (this.counter == 0) {
Thread.Sleep(10);
}
lock (_lock) {
if (this.nodes[0] != 0 && counter != 0) {
this.nodes[0]--;
this.nodes[1]++;
counter--;
return true;
} else
return false;
}}
But isn't here any better solution? I mean it looks like i can lose less time to sleep.
To see, for what it needed. I call start before starting thread and stop in the end of thread. So counter must reflect maximum number of threads running in the same time.

Signalling like this is done by using Event classes. In your case, ManualResetEventSlim should be enough.
You can Wait for it instead of the while-loop and you can Set it when the counter hits zero.

You can think of using `ManualResetEvent' for comminicating between two threads.
Following untested code might help.
class petriPool
{
private int[] nodes = new int[3];
private int counter;
private ManualResetEvent mevent;
private readonly object _lock = new object();
public petriPool (int n, int m)
{
mevent= new ManualResetEvent(false);
this.nodes[0] = n;
this.nodes[1] = 0;
this.nodes[2] = 0;
this.counter = m;
}
public bool start()
{
lock (_lock)
{
if (this.nodes[0] != 0 && counter != 0)
{
this.nodes[0]--;
this.nodes[1]++;
counter--;
if(counter>0) mevent.Set();
return true;
} else
return false;
}
}
public bool stop()
{
mevent.WaitOne();
lock (_lock) {
if (this.nodes[1] != 0) {
this.nodes[1]--;
this.nodes[2]++;
counter++;
return true;
} else
return false;
}
//reset 'mevent' if you want.
}
}

Related

How to make dialogue choices change properly?

I've been trying to implement a dialogue system which will let select a choice in a dialogue. Unity shows no errors, dialogue choices are displayed correctly, and yet a weird thing happens - when I press down arrow key (which is supposed to change the selected choice) the othe choices isn't highlighted and is shown just for a second.
Here is the code for the choice box:
public class ChoiceBox : MonoBehaviour
{
[SerializeField] ChoiceText choiceTextPrefab;
bool choiceSelected = false;
List<ChoiceText> choiceTexts;
int currentChoice;
public IEnumerator ShowChoices(List<string> choices, Action<int> onChoiceSelected)
{
choiceSelected = false;
currentChoice = 0;
gameObject.SetActive(true);
foreach (Transform child in transform)
{
Destroy(child.gameObject);
}
choiceTexts = new List<ChoiceText>();
foreach (var choice in choices)
{
var choiceTextObj = Instantiate(choiceTextPrefab, transform);
choiceTextObj.TextField.text = choice;
choiceTexts.Add(choiceTextObj);
}
yield return new WaitUntil(() => choiceSelected == true);
onChoiceSelected?.Invoke(currentChoice);
Debug.Log("choice is selected"); //this is showing in the console, but the choice box doesn't disappear
gameObject.SetActive(false);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.DownArrow))
++currentChoice;
else if (Input.GetKeyDown(KeyCode.UpArrow))
--currentChoice;
currentChoice = Mathf.Clamp(currentChoice, 0, choiceTexts.Count - 1);
for (int i = 0; i < choiceTexts.Count; i++)
{
choiceTexts[i].SetSelected(i == currentChoice);
}
if (Input.GetKeyDown(KeyCode.Return))
choiceSelected = true;
}
Then for the dialogue manager:
public class DialogueManager : MonoBehaviour
{
public Text dialogueName;
public Text dialogueText;
public static DialogueManager instance;
public int activeLineIndex;
public Animator anim;
public float typingSpeed = 0.2f;
public bool canPressE = true;
public bool makeYourChoice = false;
List<string> choices = new List<string>();
List<int> Action = new List<int>();
public bool dialogueFinished = false;
[SerializeField] ChoiceBox choiceBox;
public void EnqueueDialogue(DialogueBase db)
{
canPressE = false;
dialogueInfo.Clear();
foreach(DialogueBase.Info info in db.dialogueInfo)
{
dialogueInfo.Enqueue(info);
}
anim.SetBool("isopen", true);
DequeueDialogue();
}
public void DequeueDialogue()
{
if (dialogueInfo.Count == 0)
{
dialogueFinished = true;
StartCoroutine(DisplayChoices(choices));
makeYourChoice = true;
return;
}
if (makeYourChoice)
{
EndDialogue();
return;
}
StopAllCoroutines();
DialogueBase.Info info = dialogueInfo.Dequeue();
dialogueName.text = info.myName;
dialogueText.text = info.dialogueText;
StartCoroutine(TypeText(info));
}
public IEnumerator DisplayChoices(List<string> choices = null, Action<int> onChoiceSelected = null)
{
if ((choices != null) && choices.Count > 1)
{
yield return choiceBox.ShowChoices(choices, onChoiceSelected);
}
else
{
yield return null;
}
}
IEnumerator TypeText(DialogueBase.Info info)
{
dialogueText.text = "";
foreach (char letter in info.dialogueText.ToCharArray())
{
yield return new WaitForSeconds(typingSpeed);
dialogueText.text += letter;
yield return null;
}
}
void EndDialogue()
{
anim.SetBool("isopen", false);
}
public void GetNextLine()
{
if (!canPressE)
{
DequeueDialogue();
Debug.Log("dalshe");
}
}
public void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
GetNextLine();
}
}
public Queue<DialogueBase.Info> dialogueInfo;
public void Start()
{
dialogueInfo = new Queue<DialogueBase.Info>();
canPressE = true;
}
public void Awake()
{
if (instance != null)
{
Debug.Log("fix this" + gameObject.name);
}
else
{
instance = this;
}
}
}
For the choice text:
public class ChoiceText : MonoBehaviour
{
Text myText;
TextMeshProUGUI choiceText;
private void Awake()
{
choiceText = GetComponent<TextMeshProUGUI>();
}
public void SetSelected(bool selected)
{
choiceText.color = (selected) ? GlobalSettings.i.HighlightedColor: Color.blue;
}
public TextMeshProUGUI TextField => choiceText;
}
And for the object which the player collides and has a dialogue with:
if (Input.GetKeyDown(KeyCode.E) )
{
onTriggerEnter.Invoke();
dialogueStarted = true;
Debug.Log("запускаем диалог");
}
if (dialogueStarted && dM.dialogueFinished)
StartCoroutine(ShowDialogueOptions());
public IEnumerator ShowDialogueOptions()
{
int selectedChoice = 1;
Debug.Log("show choices");
yield return DialogueManager.instance.DisplayChoices(new List<string> { "Yes", "No" }, (choiceIndex) => selectedChoice = choiceIndex);
if(selectedChoice == 0) //"Yes"
{
Debug.Log("Cool"); //this is shown in the console
}
else if (selectedChoice == 1) //"No"
{
Debug.Log("You suck"); //this is never shown in the console
}
Only one choice ("No") is highlighted with blue, but the other one isn't showing at all. However, when I press down arrow key, it is shown in white colour just for a moment so that it's barely visible. What is shown in the console is marked in comments. Thank you all in advance.

How can I disable some objects in arrays?

I have a script for gun switching, it have an array to get the MeshRenderer component of all elements, then, i can enable the element that i want, but i cannot disable the others.
public class TrocarArma : MonoBehaviour
{
[Header("References")]
public int choosenGun = 0;
[Space(10)]
[SerializeField] MeshRenderer[] meshes = new MeshRenderer[4];
void Start()
{
SelectWeapon();
}
void Update()
{
ChangeGun();
}
void SelectWeapon()
{
int gunCount = 0;
meshes[0] = GameObject.Find("GravityGun").GetComponent<MeshRenderer>();
meshes[1] = GameObject.Find("IronBar").GetComponent<MeshRenderer>();
meshes[2] = GameObject.Find("Tablet").GetComponent<MeshRenderer>();
meshes[3] = null; //Will be filled later.
foreach(Transform gun in transform)
{
if (gunCount == choosenGun)
{
meshes[choosenGun].enabled = true;
}
else
{
///Code to make the others array meshes elements MeshRenderers disable.
}
gunCount++;
}
}
void ChangeGun()
{
int previousWeapon = choosenGun;
if (Input.GetAxis("Mouse ScrollWheel") < 0)
{
if(choosenGun >= transform.childCount -1)
{
choosenGun = 0;
}
else
{
choosenGun++;
}
}
if (Input.GetAxis("Mouse ScrollWheel") > 0)
{
if (choosenGun <= 0)
{
chooseGun = transform.childCount - 1;
}
else
{
chooseGun--;
}
}
if (previousWeapon != chooseGun)
{
SelectWeapon();
}
}
}
I simply not used SetActive(true)/(false) because I need my tablet active to upgrade my defenses. The video that i make this script: https://www.youtube.com/watch?v=Dn_BUIVdAPg , thanks for reading!
You have strange logic inside void SelectWeapon() method. But, I dare to suggest that the following code can help you:
foreach(Transform gun in transform)
{
if (gunCount == choosenGun)
{
meshes[choosenGun].enabled = true;
}
else
{
meshes[gunCount].enabled = false;
}
gunCount++;
}
or
foreach(Transform gun in transform)
{
switch(gunCount)
{
case 0:
meshes[choosenGun].enabled = true;
break;
default:
meshes[gunCount].enabled = false;
break;
}
gunCount++;
}
By the way:
meshes[3] = null; will throw an exception when you try to access the .enabled property. It is better not to allow such null elements, or always to check for null.

The member `System.Collections.Generic.List<BasePlayer>.Count' cannot be used as method or delegate

Here is some of my Code for a plugin on a game, if anyone could help it would be greatly appreciated.
namespace Oxide.Plugins
{
[Info("Fake Player Add Plugin", "NOT asmr", 0.0)]
class FakePlayerAddPlugin : CSharpPlugin
{
public int MaximumFakePlayer = 50; //if server has more than x legit players, dont add fakes.
public int PlayerFakeAdd = 43; //how many fake players to add
public float PlayerCheckDelay = 120f; //how often to check for player changes in seconds
void Loaded()
{
Puts("FakePlayerAddPlugin Loaded!");
}
float lasttime = 0f;
void OnFrame(float delta)
{
if (lasttime + PlayerCheckDelay > Time.realtimeSinceStartup) return;
lasttime = Time.realtimeSinceStartup;
var pcount = BasePlayer.activePlayerList?.Count(n => n.IsValid()) ?? 0;
var fcount = BasePlayer.activePlayerList?.Count(n => n == null) ?? 0;
if (pcount >= MaximumFakePlayer)
{
RemoveFakePlayers();
return;
}
if (PlayerFakeAdd > 0 && fcount != PlayerFakeAdd)
{
RemoveFakePlayers();
AddFakePlayers(PlayerFakeAdd);
}
}
public void RemoveFakePlayers()
{
BasePlayer.activePlayerList.RemoveAll(n => n == null);
}
public void AddFakePlayers(int amount)
{
for (int i = 0; i < amount; i++)
{
BasePlayer.activePlayerList.Add(null);
}
}
}
}
"The member `System.Collections.Generic.List.Count' cannot be used as method or delegate" is what I am getting back.

Cannot mimic performance of ConcurrentStack when using same code implementation

I am learning about writing concurrent data structures and looking at the ConcurrentStack implementation as a learning exercise. As a starting point I have created a copy of the ConcurrentStack implementation by using IlSpy to decompile it into C#. I have limited myself to investigating and using just the Push and TryPop methods for the time being.
But my implementation is significantly slower than using the original.
My testing uses 4 threads (on a single socket, 4 core CPU) with thread affinity for each thread against a different core. Each thread performs 1,000,000 loops and each loop does three pushes and three pops. Running the testing many times the average time to complete all the threads is...
ConcurrentStack => 445ms
Clone of Push/TryPop => 670ms
So even though the code, as far as I can tell, is identical between the two the clone is about 50% slower. I run the testing 500 times in a run and take the average over all the runs. So I do not believe the issue is the initial JIT'ing of the code.
Any ideas why a copy of the methods would be so much slower?
C# Implementation
(For the sake of completeness I have provided the C# console app code that can be used to replicate the result. For anyone interesting in seeing if they get the same result as me.)
class Program
{
static void Main(string[] args)
{
int processors = Environment.ProcessorCount;
Console.WriteLine("Processors: {0}", processors);
List<Type> runnersT = new List<Type>() { typeof(ThreadRunnerConcurrent),
typeof(ThreadRunnerCASStack)};
int cycles = 500;
foreach (Type runnerT in runnersT)
{
long total = 0;
for (int i = 0; i < cycles; i++)
{
// Create a thread runner per processor
List<ThreadRunner> runners = new List<ThreadRunner>();
for (int j = 0; j < processors; j++)
{
ThreadRunner runner = Activator.CreateInstance(runnerT) as ThreadRunner;
runner.Processor = j;
runners.Add(runner);
}
// Start each runner going
Stopwatch sw = new Stopwatch();
sw.Start();
runners.ForEach((r) => r.Start());
// Wait for all the runners to exit
runners.ForEach((r) => r.Join());
runners.ForEach((r) => r.Check());
sw.Stop();
total += sw.ElapsedMilliseconds;
}
Console.WriteLine("{0} Average: {1}ms", runnerT.Name, (total / cycles));
}
Console.WriteLine("Finished");
Console.ReadLine();
}
}
abstract class ThreadRunner
{
private int _processor;
private Thread _thread;
public ThreadRunner()
{
}
public int Processor
{
get { return _processor; }
set { _processor = value; }
}
public void Start()
{
_thread = new Thread(new ParameterizedThreadStart(Run));
_thread.Start();
}
public void Join()
{
_thread.Join();
}
public abstract void Check();
protected abstract void Run(int cycles);
private void Run(object param)
{
SetAffinity();
Run(1000000);
}
private void SetAffinity()
{
#pragma warning disable 618
int osThreadId = AppDomain.GetCurrentThreadId();
#pragma warning restore 618
// Set the thread's processor affinity
ProcessThread thread = Process.GetCurrentProcess().Threads.Cast<ProcessThread>().Where(t => t.Id == osThreadId).Single();
thread.ProcessorAffinity = new IntPtr(1L << Processor);
}
}
class ThreadRunnerConcurrent : ThreadRunner
{
private static ConcurrentStack<int> _stack = new ConcurrentStack<int>();
protected override void Run(int cycles)
{
int ret;
for (int i = 0; i < cycles; i++)
{
_stack.Push(i);
_stack.Push(i);
while (!_stack.TryPop(out ret)) ;
_stack.Push(i);
while (!_stack.TryPop(out ret)) ;
while (!_stack.TryPop(out ret)) ;
}
}
public override void Check()
{
if (_stack.Count > 0)
Console.WriteLine("ThreadRunnerConcurrent has entries!");
}
}
class ThreadRunnerCASStack : ThreadRunner
{
private static CASStack<int> _stack = new CASStack<int>();
protected override void Run(int cycles)
{
int ret;
for (int i = 0; i < cycles; i++)
{
_stack.Push(i);
_stack.Push(i);
while (!_stack.TryPop(out ret)) ;
_stack.Push(i);
while (!_stack.TryPop(out ret)) ;
while (!_stack.TryPop(out ret)) ;
}
}
public override void Check()
{
if (_stack.Count > 0)
Console.WriteLine("ThreadRunnerCASStack has entries!");
}
}
class CASStack<T>
{
private class Node
{
internal readonly T m_value;
internal CASStack<T>.Node m_next;
internal Node(T value)
{
this.m_value = value;
this.m_next = null;
}
}
private volatile CASStack<T>.Node m_head;
public void Push(T item)
{
CASStack<T>.Node node = new CASStack<T>.Node(item);
node.m_next = this.m_head;
if (Interlocked.CompareExchange<CASStack<T>.Node>(ref this.m_head, node, node.m_next) == node.m_next)
return;
PushCore(node, node);
}
private void PushCore(Node head, Node tail)
{
SpinWait spinWait = default(SpinWait);
do
{
spinWait.SpinOnce();
tail.m_next = this.m_head;
}
while (Interlocked.CompareExchange<CASStack<T>.Node>(ref this.m_head, head, tail.m_next) != tail.m_next);
}
public bool TryPop(out T result)
{
CASStack<T>.Node head = this.m_head;
if (head == null)
{
result = default(T);
return false;
}
if (Interlocked.CompareExchange<CASStack<T>.Node>(ref this.m_head, head.m_next, head) == head)
{
result = head.m_value;
return true;
}
return TryPopCore(out result);
}
private bool TryPopCore(out T result)
{
CASStack<T>.Node node;
if (TryPopCore(1, out node) == 1)
{
result = node.m_value;
return true;
}
result = default(T);
return false;
}
private int TryPopCore(int count, out CASStack<T>.Node poppedHead)
{
SpinWait spinWait = default(SpinWait);
int num = 1;
Random random = new Random(Environment.TickCount & 2147483647);
CASStack<T>.Node head;
int num2;
while (true)
{
head = this.m_head;
if (head == null)
break;
CASStack<T>.Node node = head;
num2 = 1;
while (num2 < count && node.m_next != null)
{
node = node.m_next;
num2++;
}
if (Interlocked.CompareExchange<CASStack<T>.Node>(ref this.m_head, node.m_next, head) == head)
goto Block_5;
for (int i = 0; i < num; i++)
spinWait.SpinOnce();
num = (spinWait.NextSpinWillYield ? random.Next(1, 8) : (num * 2));
}
poppedHead = null;
return 0;
Block_5:
poppedHead = head;
return num2;
}
}
#endregion
ConcurrentStack<T> has one advantage that your CASStack<T> doesn't have, even though the code for both is identical.
ConcurrentStack<T> has an ngen'd native image installed on your computer that was compiled when you installed your .Net framework install. Your CASStack<T> is being compiled via JIT, and because JIT has to be fast, it does not perform as many optimizations as the AOT compiler in ngen.
I tested your code on my computer. Without ngen'ing your image, I got these results:
Processors: 4
ThreadRunnerConcurrent Average: 764ms
ThreadRunnerCASStack Average: 948ms
Finished
After ngening:
Processors: 4
ThreadRunnerConcurrent Average: 778ms
ThreadRunnerCASStack Average: 742ms
Finished

(Unity) Trying to convert an object pool script from UnityScript to C#- stuck on an error

I found a great object pooling script but it's in UnityScript and my project is in C#. I've been trying to convert it but I'm stuck on one error that I don't quite understand. Here is my script:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class EasyObjectPool : MonoBehaviour {
private class PoolInfo{
public string poolName;
public GameObject prefab;
public int poolSize;
public bool canGrowPoolSize = true;
}
private class Pool{
private List<PoolObject> list = new List<PoolObject>();
public bool canGrowPoolSize;
public void Add (PoolObject poolObject){
list.Add(poolObject);
}
public int Count (){
return list.Count;
}
public GameObject ObjectAt ( int index ){
PoolObject result;
if(index < list.Count) {
result = list[index];
}
return result;
}
}
static public EasyObjectPool instance ;
PoolInfo[] poolInfo;
private Dictionary<string, Pool> poolDictionary = new Dictionary<string, Pool>();
void Start () {
instance = this;
CheckForDuplicatePoolNames();
CreatePools();
}
private void CheckForDuplicatePoolNames() {
for (int index = 0; index < poolInfo.Length; index++) {
string poolName= poolInfo[index].poolName;
if(poolName.Length == 0) {
Debug.LogError(string.Format("Pool {0} does not have a name!",index));
}
for (int internalIndex = index + 1; internalIndex < poolInfo.Length; internalIndex++) {
if(poolName == poolInfo[internalIndex].poolName) {
Debug.LogError(string.Format("Pool {0} & {1} have the same name. Assign different names.", index, internalIndex));
}
}
}
}
private void CreatePools() {
foreach(PoolInfo currentPoolInfo in poolInfo){
Pool pool = new Pool();
pool.canGrowPoolSize = currentPoolInfo.canGrowPoolSize;
for(int index = 0; index < currentPoolInfo.poolSize; index++) {
//instantiate
GameObject go = Instantiate(currentPoolInfo.prefab) as GameObject;
PoolObject poolObject = go.GetComponent<PoolObject>();
if(poolObject == null) {
Debug.LogError("Prefab must have PoolObject script attached!: " + currentPoolInfo.poolName);
} else {
//set state
poolObject.ReturnToPool();
//add to pool
pool.Add(poolObject);
}
}
Debug.Log("Adding pool for: " + currentPoolInfo.poolName);
poolDictionary[currentPoolInfo.poolName] = pool;
}
}
public GameObject GetObjectFromPool ( string poolName ){
PoolObject poolObject = null;
if(poolDictionary.ContainsKey(poolName)) {
Pool pool = poolDictionary[poolName];
//get the available object
for (int index = 0; index < pool.Count(); index++) {
PoolObject currentObject = pool.ObjectAt(index);
if(currentObject.AvailableForReuse()) {
//found an available object in pool
poolObject = currentObject;
break;
}
}
if(poolObject == null) {
if(pool.canGrowPoolSize) {
Debug.Log("Increasing pool size by 1: " + poolName);
foreach (PoolInfo currentPoolInfo in poolInfo) {
if(poolName == currentPoolInfo.poolName) {
GameObject go = Instantiate(currentPoolInfo.prefab) as GameObject;
poolObject = go.GetComponent<PoolObject>();
//set state
poolObject.ReturnToPool();
//add to pool
pool.Add(poolObject);
break;
}
}
} else {
Debug.LogWarning("No object available in pool. Consider setting canGrowPoolSize to true.: " + poolName);
}
}
} else {
Debug.LogError("Invalid pool name specified: " + poolName);
}
return poolObject;
}
}
The error is:
"Cannot implicitly convert type 'UnityEngine.GameObject' to `PoolObject'"
for lines:
PoolObject currentObject = pool.ObjectAt(index);
and
return poolObject;
This script references another script called PoolObject but it doesn't give me any errors so I didn't figure it was related to these errors. Here is that script:
using UnityEngine;
using System.Collections;
public class PoolObject : MonoBehaviour {
[HideInInspector]
public bool availableForReuse = true;
void Activate () {
availableForReuse = false;
gameObject.SetActive(true);
}
public void ReturnToPool () {
gameObject.SetActive(false);
availableForReuse = true;
}
public bool AvailableForReuse () {
//true when isAvailableForReuse & inactive in hierarchy
return availableForReuse && (gameObject.activeInHierarchy == false);
}
}
Anyone tell me what I'm doing wrong here?
Problem 1
In the function GetObjectFromPool() you are trying to return a GameObject but in reality you are giving it a PoolObject.
So change this line:
public GameObject GetObjectFromPool(string poolName)
{
...
}
To this:
public PoolObject GetObjectFromPool(string poolName)
{
...
}
Now you are returning a PoolObject type.
Problem 2
The same happens in ObjectAt
public GameObject ObjectAt(int index)
{
PoolObject result = null;
if(index < list.Count)
result = list[index];
return result;
}
Just swap GameObject for PoolObject again and you are done.
Foreseeable Future Problem
There is going to be another class that calls GetObjectFromPool(), so make sure that if that class needs a GameObject you take the gameObject component from the PoolObject.
pool.ObjectAt returns a GameObject, not the attached component of type PoolObject.
Change ObjectAt to this:
public PoolObject ObjectAt ( int index ){
PoolObject result;
if(index < list.Count) {
result = list[index];
}
return result;
}

Categories

Resources