I am working on a MUD (Text based multiplayer RPG, in case you don't know) as a hobby. I had all my monster spawners each on their own thread, but quickly ran into issues keeping the code synchronized and realized it was way overkill anyway. I moved all the spawners (which handle their own spawns logic--so each spawner runs 3 or 4 monsters and replaces them if they die) to a single child thread of the main thread. There I have a "World" class loop over every spawner in the game to run its monster's logic. In theory nothing should really change. I barely modified the code. I just have it called from a single loop in 1 thread instead of each spawner looping for itself in its own thread.
Here's where things get weird and why I'm here asking for help (And I've already spent a few hours googling and troubleshooting). When I start the server nothing happens. I have debugging console writes everywhere and nothing shows up. So naturally I placed some breakpoints and while hitting F10 and walking through the logic (finding no issues) I start seeing the console writes showing up as I pass over them. If I sit there and press F10 forever I can essentially "manually" run the server... But as soon as I hit "Continue" to pause debugging everything stops. The server is still up, and I can set a breakpoint again and walk repeat the whole process.
What on earth!?
I've only been coding for about 10 years but I have never seen anything like this. Has anyone ever experience this before? I'll paste the code that runs the logic below, but again everything works perfectly if I just spam F10 via a breakpoint, lol.
EDIT: I forgot to mention that before the code refactor bringing everything back to 1 thread EVERYTHING WORKED PERFECTLY. So, having it suddenly not running, and only "working" if I spam F10 from a breakpoint is just weird.
World instance code that manages the spawners
internal static void StartSpawnerThread() {
SpawnThread = new Thread(DoSpawnerLogic) {
Name = "SpawnerThread"
};
SpawnThread.Start();
}
internal static void DoSpawnerLogic() {
while (true) {
for (int i = 0; i < SpawnerList.Count; i++) {
SpawnerList[i].SpawnLogic();
}
if (NewSpawners.Count > 0) {
SpawnerList.AddRange(NewSpawners);
NewSpawners.Clear();
}
if (RemoveSpawners.Count > 0) {
for (int i = 0; i < RemoveSpawners.Count; i++) {
SpawnerList.Remove(RemoveSpawners[i]);
}
RemoveSpawners.Clear();
}
// Regulates AI logic to roughly 30 ticks a second.
//Thread.Sleep(33);
}
}
Spawner code that handles spawning and executing the AI of the spawns
[OnDeserialized]
private void StartSpawn(StreamingContext context) {
_spawning = true;
_spawns = new List<Mobile>();
_deadSpawn = new List<Mobile>();
World.NewSpawners.Add(this);
ActiveSpawnerCount++;
Console.WriteLine(ActiveSpawnerCount + " spawners active...");
//Thread thread = new Thread(SpawnLogic);
//thread.Name = "Spawner-" + ActiveSpawnerCount++;
//World.SpawnThreads.Add(thread);
//thread.Start();
}
internal void SpawnLogic() {
Random rand = new Random();
DateTime spawnTime = DateTime.Now.Add(new TimeSpan(0, 0, 1));
if (_spawning) {
if (_spawns.Count < MaxNumberOfSpawn) {
if (spawnTime < DateTime.Now) {
Mobile newMob = new Mobile(_spawnData[rand.Next(0, _spawnData.Length)], this);
_spawns.Add(newMob);
newMob.Stats.OnZeroHealth += QueueDestroyMob;
newMob.Move(_location);
World.Mobiles.Add(newMob.ID, newMob);
spawnTime = DateTime.Now.Add(new TimeSpan(0, 0, 1));
Console.WriteLine(string.Format("{0} spawned in {1} at {2}",
newMob.FullName, _location, spawnTime));
}
}
foreach (Mobile mob in _spawns) {
if (!mob.IsDead)
mob.ExecuteLogic();
}
if (_deadSpawn.Count > 0) {
lock (_deadSpawn) {
for (int i = 0; i < _deadSpawn.Count; i++) {
DestroyMob(_deadSpawn[i]);
}
_deadSpawn.Clear();
}
}
}
}
Related
For the life of me I can't understand why it stops looping. I have an engine acceleration script that works great, except for the fact that it stops looping after a certain amount of time. I'm only calling the audiosource to be stopped if the user presses a button, it should work just fine. It has worked before implementing this too, I've no idea what breaks it.
private void PlayAccelerateSound()
{
m_audioSource2.loop = true;
m_audioSource2.clip = m_engineSound;
if (!alreadyPlayed)
{
m_audioSource2.PlayOneShot(m_audioSource2.clip);
alreadyPlayed = true;
}
if (rb.velocity.x < minPitch)
{
m_audioSource2.pitch = minPitch;
}
else if (rb.velocity.x > maxPitch)
{
m_audioSource2.pitch = maxPitch;
}
else
{
m_audioSource2.pitch = rb.velocity.x;
}
}
Fixed it by using m_audioSource2.Play() instead of m_audioSource2.PlayOneShot(m_audioSource2.clip).
I have implemented the dialog system from this Brackeys video in my project. Everything works perfectly, but when I have done the build for android I have seen that printing long texts slows the game down.
The code divides the sentence to be displayed in the UI into an array of characters and then prints each character one by one, with a small delay. I have been doing several tests and first I thought that the problem was the coroutine from where the characters were printed. But then I have removed the code from the coroutine and I have seen that the more characters it prints, the more the game slows down.
void FixedUpdate()
{
if(typeSentence)
{
if(t <= 0)
{
TypeChar();
t = charDelay;
}
t -= Time.fixedDeltaTime;
}
}
private void TypeChar()
{
GUIs[dialogue.UIIndex].dialogueText.text += charSentence[sentenceIndex];
sentenceIndex++;
if (sentenceIndex >= charSentence.Length)
{
typeSentence = false;
sentenceIndex = 0;
GUIs[dialogue.UIIndex].continueButton.SetActive(true);
}
}
I don't know if there is a more efficient way to do it, or if someone can explain to me what is happening and why it slows down so much.
Instead of triggering TypeChar() method in fixed update, you can convert TypeChar() to a Coroutine that can handle time more performing way.
private IEnumerator TypeChar()
{
while(sentenceIndex >= charSentence.Length)
{
if(typeSentence)
{
GUIs[dialogue.UIIndex].dialogueText.text += charSentence[sentenceIndex];
sentenceIndex++;
yield return new WaitForSeconds(charDelay);
}
}
typeSentence = false;
sentenceIndex = 0;
GUIs[dialogue.UIIndex].continueButton.SetActive(true);
}
And you can delete typeSentence variable completely if you do not change it out of scope.
And you can call it at Start instead of FixedUpdate.
private void Start()
{
StartCoroutine(nameof(TypeChar));
}
I'm making a practice game to get used to coding where you have to shoot a bird. When you run out of bullets, you need to press the 'r' key to reload your bullets. I want there to be a delay between when the button is pressed and when the bullets reload, but so far what I found is code that freezes everything (shown below). Is there a way to keep the code from freezing everything?
Summary: The code below freezes everything (the whole game) when pressing the 'r' button. Is there code I can use that won't freeze everything and will only wait 2 seconds before running the next action?
IEnumerator TimerRoutine()
{
if (Input.GetKeyDown(KeyCode.R))
{
yield return new WaitForSeconds(2); //Fix this, freezes everything
activeBullets = 0;
}
}
Use Coroutines To set This delay
if (Input.GetKeyDown(KeyCode.R) && isDelayDone) // defined isDelayDone as private bool = true;
{
// When you press the Key
isDelayDone = false;
StartCoroutine(Delay());
IEnumerator Delay()
{
yield return new WaitForSeconds(2);
isDelayDone = true;
activeBullets = 0;
}
}
Your problem is that you are waiting 2 seconds after the key press but not waiting for the actual key event it.
Here is a modified version of your method doing what you want.
IEnumerator TimerRoutine()
{
while(activeBullets == 0) // Use the bullets value to check if its been reloaded
{
if (Input.GetKeyDown(KeyCode.R)) // Key event check each frame
{
// Key event fired so wait 2 seconds before reloading the bullets and exiting the Coroutine
yield return new WaitForSeconds(2);
activeBullets = reloadBulletsAmount;
break;
}
yield return null; // Use null for the time value so it waits each frame independant of how long it is
}
}
(I know this has an accepted answer I just feel this method would be better)
I am working on voltmeter application, that draws voltage waveform. Hardware sends 1000 numbers (range 0 - 1023, always whole numbers) in string format per second through serial port.
public SerialPort serialPort = new SerialPort("COM3", 57600);
serialPort.Open();
String is converted into int and then drawn with DrawLine into PictureBox.
// variable declarations, all is int, runs in its own thread
while (blToMeasure) // true after clicking on button
{
iPrevY = iY;
iY = Int16.Parse(serialPort.ReadLine());
graphicsGraph.DrawLine(penBlack, iX, iPrevY, iX + 1, iY);
// only this thread is accessing PictureBox
iX++;
if (iX > picBoxGraph.Width)
{
graphicsGraph.Clear(SystemColors.Control);
iX = 0;
}
if (iY > picBoxGraph.Height)
{
}
}
Issue is that drawing lines itself is fast as it should be only for a couple of seconds, but gets gradually slower.
I tried Int.Parse, Int32.Parse and splitting thread function multiple ways using lock (graphicsGraph) (moving conditions with Clear into another thread) or using BlockingCollection<int> (moving DrawLine into another thread, away from Parse). Nothing seems to work and app still gets slower a couple of times after like a minute of running.
There isn't issue with hardware itself, checked with another software. Is this too fast for C#?
Solution:
I got the best results using Port.ReadTimeout = 1 and Port.DiscardInBuffer(). Also using Form.DoubleBuffered = true, but it doesn't make a huge difference in this particular case.
// constructor
Port.ReadTimeout = 1;
Form.DoubleBuffered = true;
Here is the loop itself:
btn.Click() // click to start measuring
{
Port.DiscardInBuffer();
blToMeasure = true;
}
while (blToMeasure) // true after clicking on button
{
iPrevY = iY;
try {
iY = Int16.Parse(serialPort.ReadLine());
}
catch
{
// exception logic
}
graphicsGraph.DrawLine(penBlack, iX, iPrevY, iX + 1, iY);
// only this thread is accessing PictureBox
iX++;
if (iX > picBoxGraph.Width)
{
graphicsGraph.Clear(SystemColors.Control);
iX = 0;
}
if (iY > picBoxGraph.Height)
{
}
}
When the app starts to read from the port, there is always accumulated data, because my hardware is sending numbers all the time, so I get rid of the buffer. Than the drawing of lines is not executed in differing spikes and the speed is constant. Analyzing the issue with Watch, I found out, that it occasionaly takes much longer to read this data and because of 1000 reads per second, it slows down. So to prevent slowing down, I used Port.ReadTimeout, that skips the read, if it takes too long.
The difference is visible, drawing no longer slows down and it keeps the same pace for minutes from what I've tried. I consider this sufficient solution for my issue, thank you!
I'm creating a console game as simple as "I generate a random number, find it", but with many options.
My current code (without what I want here) is availlable on GitHub: https://github.com/crakmaniaque/trouvezmoi
What I want is to create a version of my game which will be timed, so the computer generates numbers, the user finds it, it generates a new one and the player have 90 seconds to find a max lot of random numbers. I can code this easily.
What I will need help is to stop the game (a thread) after 90 seconds and retrieve the number of answers founded from the thread. The Console.Title should also show time remaining. The attempt I've tried works, but the thread is not interrupted if console is asking for number input (Console.ReadLine()). But the timer is for the entire process, not only user input.
private static void timerb()
{
int t = 90;
for (int i = 0; i < 90; i++)
{
Console.Title = t + " seconds remaining";
Thread.Sleep(1000);
t--;
}
}
private static void cGame()
{
Thread t = new Thread(timerb);
t.Start();
while (t.IsAlive)
{
bool good = false;
int rnd = new Random().Next(0,10); // 0 and 10 are sample
while (!good)
{
try
{
Console.Write("Enter a number between x and y >");
int i = int.Parse(Console.ReadLine());
if (i == rnd)
{
good = true;
}
}
catch (FormatException)
{
Console.WriteLine("Invalid answer.");
}
}
}
}
I don't know much about threading and at that point I'm stuck.
Can someone help me with my problem? I'm using .NET 2.0.
Perhaps you are looking for a timer? You could register an event, that would fire after 90 seconds, that would run while the loop is happening. The documentation can be found here: Timer class MSDN documentation.
I believe the usage would be:
Timer timer = new Timer { Interval = new Timespan (0,1,30);
timer.elapsed += //function to fire to kill the app or the game
You'd need to make each console read with a timeout equal to the amount of time left in the game. That solves that issue.
Then, you need a way to signal the timerb thread to shut down when the main game loop has ended. I think the simplest way would be to end the game loop when the remaining time is <= zero. Alternatively, you could make timerb singnal the main thread to shut down when t == 0. Inter-thread communication is always complicated and error-prone, though.
You can signal the timerb thread to shut down by setting a volatile bool shutdown to true and by making timerb poll that variable and shut itself down.