I'm pretty new to C# and coding in general so it is hard for me to explain
and this might be something simple.
The program I am trying to make changes values in the game (Assault Cube).
Without the inner while loops, it just changes the values, I would like them to loop.
I have one outer while loop with multiple loops inside.
The inner while loops are there to loop the function but it stops the outer while loop.
I would like multiple inner loops to run along with the outer one.
I have tried what feels like everything. I tried Booleans, Breaks, Returns.
But nothing I have tried has fixed my problem. It may not be because they don't work, it may just be me using them wrong.
while (true) //outer loop
{
Console.Title = "SlavScripts";
int address1 = LocalPlayer + Health;
int address2 = LocalPlayer + Armor;
string Player = "";
Console.WriteLine("---SlavScripts v2.0---");
Console.WriteLine("");
Console.WriteLine("[1] Player Options");
Console.WriteLine("");
string answer = "";
answer = Console.ReadLine();
if (answer == "1")
{
Console.WriteLine("--Player Options--");
Console.WriteLine("");
Console.WriteLine("[1] Godmode");
Console.WriteLine("[2] Armor");
Console.WriteLine("");
Player = Console.ReadLine();
if (Player == "1")
{
Console.WriteLine("Godmode Enabled");
Console.WriteLine("");
while (true)
{
vam.WriteInt32((IntPtr)address1, 6969); //value to loop.
}
}
else if (Player == "2")
{
Console.WriteLine("Infinite Armor Enabled");
Console.WriteLine("");
while (true)
{
vam.WriteInt32((IntPtr)address2, 6969); //value to loop.
}
}
}
}
(full code: https://pastebin.com/bBcBPYs6)
Expected:
I enter the corresponding digit to activate the function
The function that was activated loops, and original text appears which allows me to navigate to another function.
Actual:
I enter the corresponding digit to activate the function.
The function activates and loops, but does not show opening text and disallows my to type further.
Think about what is happening in your code. Each instruction in your code is executing one after the other (superficially thinking - this might not be exactly true at assembly execution level but you get the idea). For example the line if (answer == "1") will only executed when the line before it (that is answer = Console.ReadLine();) completes its execution.
When you create the "outer while loop" (as you called), everything inside the loop will execute 1 instruction at a time following the order they are written and when the last instruction inside the loop is executed, the execution will jump back to the first line of code inside the loop.
If you put another while loop inside the "outer one", and say it will not have an exit condition (by declaring while(true) you are creating a loop that will never stop executing its embedded statements). Therefore when the execution reaches one of these while(true) loops it will be trapped inside them looping its embedded statements forever.
What I described is how instructions are executed. The other part you need to know is what is a thread. You can think of a thread as an isolated machine where every code is executed. When you don't deal with threads directly in your code, the compiler will automatically asks the operating system to create a thread to run your code at - this is usually referred to as the main thread.
So, what you actually need in your code is to inform the operating system to put the execution of each one of those "inner while(true)" loops inside a thread other than the main one and leaving the main thread to execute only code that can sequentially be executed.
You can learn how to use threads in your application here.
And as a side-note: that is probably not what you want to create a loop spinning code every cycle. You should consider pausing the execution inside those "inner loops" by putting the thread that is executing it to sleep for some time each time it iterates through (you can do this by just calling System.Threading.Thread.Sleep(100) - it will pause the thread execution by 100 milliseconds and will save some cpu execution time).
As per your expectation,
Please find the sample code snippet for getting user input continuously - achieved using Task in C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SampleTaskStop
{
class Program
{
static public void SetValue(string address, int value)
{
while (!_cancelled)
{
//vam.WriteInt32((IntPtr)address1, 6969); //value to loop.
}
Console.WriteLine(address + " - completed");
}
static bool _cancelled = false;
static void Main(string[] args)
{
while (true) //outer loop
{
Console.Title = "SlavScripts";
Console.WriteLine("---SlavScripts v2.0---");
Console.WriteLine("");
Console.WriteLine("[1] Player Options");
Console.WriteLine("");
string answer = "";
answer = Console.ReadLine();
_cancelled = false;
if (answer == "1")
{
var acceptUserInput = Task.Factory.StartNew(AcceptUserInput);
acceptUserInput.Wait();
}
}
}
private static void AcceptUserInput()
{
// Task to perform the value setting in the
Task computationTask = null;
Console.WriteLine("Enter Player Input");
Console.WriteLine("1 God Mode");
Console.WriteLine("2 Armour Mode");
Console.WriteLine("Press Esc to cancel");
var key = Console.ReadKey(true);
while (key.Key != ConsoleKey.Escape)
{
if (key.Key == ConsoleKey.D1 || key.Key == ConsoleKey.NumPad1 )
{
Console.WriteLine("Godmode Enabled");
Console.WriteLine("");
_cancelled = true;
if (computationTask != null)
{
computationTask.Wait(new System.Threading.CancellationToken());
}
_cancelled = false;
computationTask = Task.Factory.StartNew(() => SetValue("data1", 6979));
}
else if (key.Key == ConsoleKey.D2 || key.Key == ConsoleKey.NumPad2)
{
Console.WriteLine("Infinite Armor Enabled");
Console.WriteLine("");
_cancelled = true;
if (computationTask != null)
{
computationTask.Wait(new System.Threading.CancellationToken());
}
_cancelled = false;
computationTask = Task.Factory.StartNew(() => SetValue("data2", 6979));
}
key = Console.ReadKey(true);
}
_cancelled = true;
Console.Write("Computation was cancelled");
}
}
}
Thanks to everyone who replied!
Multi-Threading sounds alot harder than it actually is and it also simplified my code alot!
Still have to add Thread.Sleep's :D
Cheers!
New & Improved - pastebin.com/qN7ci0By
Related
I am working on a text based adventure game. In the game, there is a ListBox which displays text, and TextBox where user types the commands:
I put the game loop in the Game_Load method which makes a problem because it keeps checking the TextBox content at all times and since it's empty before the user gets a chance to type anything it returns null and crashes.
I've made a similar game as a Console app before with this code:
while(run)
{
Console.WriteLine("What is your next step?");
var input = Console.ReadLine().ToLower();
if (input == Text.Language.Quit)
run = false;
else
Actions.Instance.Execute(input.Split(" "));
}
And it worked fine. Now I'm struggling to convert this to Windows Forms, I don't know where to put which method to avoid this problem.
How do I make the game not ask for input before it is needed?
I tried making a GetText method and calling that instead of var input = ... but that didn't work either. I tried moving the while(run) out of the Form_Load, but then it doesn't run at all times like it should.
As I understand it, you want something that looks and behaves like a Console, but in a WinForms app, and this is a stylistic choice. Otherwise there are better ways to prompt user for input in WinForms!
Running a "game loop" similar to your console app is possible but requires special care. We say that WinForms is "event-driven" because the application has a message loop that listens for events like mouse clicks and key presses. But this means, for example, that this loop is going to get stuck waiting for keypresses because it's "blocking the UI thread" that detects those keypresses.
void BadGameLoop()
{
while(run) // Don't do this!
{
// The `Form` becomes unresponsive i.e.
// The app "freezes" and will have to be killed.
string input = ReadLine();
switch(input)
{
// Do something
}
}
}
On the other hand, the await keyword used inside an async method will cause the method to return immediately, but then resume at this spot when "something happens":
async Task GoodGameLoop()
{
while(run)
{
string input = await ReadLineAsync();
switch(input)
{
// Do something
}
}
}
Read a command asynchronously
Block when the ReadLineAsync() is called.
SemaphoreSlim awaiter = new SemaphoreSlim(1, 1);
private async Task<string> ReadLineAsync()
{
int charIndex = Console.GetFirstCharIndexOfCurrentLine();
int line = Console.GetLineFromCharIndex(charIndex);
string textB4 = Console.Lines[line];
// Instruct the semaphore to block until further notice.
awaiter.Wait(0);
// Return from this method immediately.
await awaiter.WaitAsync();
// Resume here when [Enter] key unblocks the semaphore.
string input =
string.IsNullOrWhiteSpace(textB4) ?
Console.Lines[line] :
Console.Lines[line].Replace(textB4, string.Empty);
return input;
}
Unblock the semaphore when the [Enter] key is pressed.
private void onConsoleKeyDown(object? sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Enter)
{
// Call Wait(0) so there's something to release
// in case the awaiter isn't currently awaiting!
try { awaiter.Wait(0); }
finally{ awaiter.Release(); }
}
}
Asynchronous Game Loop Example
Here's the code I used to test this answer:
enum GameState
{
DisplayLogo,
PromptName,
Intro,
Play,
}
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
Console.KeyDown += onConsoleKeyDown;
_ = execGameLoop();
}
private GameState GameState = GameState.DisplayLogo;
private async Task execGameLoop()
{
while (true)
{
switch (GameState)
{
case GameState.DisplayLogo:
Console.Font = new Font("Consolas", 10);
Console.Text =
#"
---------------
--------------- 0
____________ ---------------
0 --------------- |
------------ |
| WELCOME TO GORK |
| ---------------
| --------------- 0
|____________ ---------------
0 ---------------
------------
";
Console.Select(Console.Text.Length, 0);
await Task.Delay(TimeSpan.FromSeconds(1));
GameState= GameState.PromptName;
continue;
case GameState.PromptName:
Console.AppendText(#"
What is your name hero? ");
break;
case GameState.Intro:
Console.Clear();
Console.AppendText(#"
OK let's PLAY!
Here are the rules:
#1 Don't cheat, unless winning requires it.
#2 Never forget rule #1.
For a list of commands, type 'help'.
Press Enter to continue.
"
);
break;
case GameState.Play:
Console.Clear();
Console.AppendText(#"
Enter command: "
);
break;
}
string input = await ReadLineAsync();
if(string.IsNullOrWhiteSpace(input))
{
if(GameState.Equals(GameState.Intro))
{
GameState = GameState.Play;
}
}
else
{
switch (GameState)
{
case GameState.PromptName:
Console.AppendText($"Welcome {input} to this adventure!" + Environment.NewLine);
for (int i = 0; i < 50; i++)
{
Console.AppendText(">");
Task.Delay(TimeSpan.FromMilliseconds(10)).Wait();
}
GameState= GameState.Intro;
break;
case GameState.Intro:
GameState= GameState.Play;
break;
case GameState.Play:
Console.AppendText($"You entered: {input}" + Environment.NewLine);
await Task.Delay(TimeSpan.FromSeconds(1.5));
break;
}
}
}
.
.
.
// ReadLineAsync method ...
// onConsoleKeyDown method ...
}
}
Okay so i'm posting this again because someone thought I asked a question that already has the answer...
This link (Listen for key press in .NET console app) did not help me because I can't use the " while (! Console.KeyAvailable)" I need to know the value of the key.
I have a problem with a 'game' I'm trying to build on a console. So basically I am waiting for a key (A, S, D or W) to be pressed so I can move my character, but with that I can't use Console.ReadKey(true) because it freezes everything and I can't lets say update the enemies' location. Can you help me out a little? Here is part of my code:
ConsoleKey key;
do
{
key = Console.ReadKey(true).Key;
//Let enemies move on their own here!
map[player.Position.y, player.Position.x] = ' ';
CalculateMap(true);
if (player.IsMoveable(key, mapSize))
{
player.Move(key);
}
map[player.Position.y, player.Position.x] = player.icon;
CalculateMap(false);
Thread.Sleep(8);
} while (key != ConsoleKey.Escape);
You'll need to start your game loop in a new thread, and check a static variable containing the pressed key somewhere inside the game loop.
Static variables in C# are not thread static by default: their value is the same regardless of the thread that accesses them.
So you should be able to do something like the following:
class MyGame {
static ConsoleKeyInfo THE_PRESSED_KEY;
public static void main() {
Task.Run( () => UpdateAndDrawTheGame() );
do {
THE_PRESSED_KEY = Console.ReadKey();
} while (THE_PRESSED_KEY.Key != ConsoleKey.Escape);
}
public static void UpdateAndDrawTheGame() {
while (THE_PRSSED_KEY == null) ; // Wait for the first key read
while (THE_PRESSED_KEY.Key != ConsoleKey.Escape) {
// Update the enemies, scenery, HUD etc.
// Move the character if THE_PRESSED_KEY is 'A'
// Move the character differently if THE_PRESSED_KEY is 'S'
// etc.
// Redraw the game with the updates
}
}
}
Note that accessing static variables across threads can lead to some wonky behavior, because sometimes a thread reading the variable tries to do so while another thread is writing it. You should research sharing variables between threads for ways to prevent that.
Console.KeyAvailable can still be used here, just check that a key is available before trying to read it. If it is not, Console.ReadKey will not be called and will not block the thread:
if (Console.KeyAvailable)
{
key = Console.ReadKey(true).Key;
}
I've created a small application that does a small conversion. At the end of the program I've created a method that allows the user to make another calculation if they press 'r'. All I want it to do is if they press r, take them back to the beginning of Main, else terminate program. I do not want to use goto. This is what I've got so far, and the error I'm getting.
http://puu.sh/juBWP/c7c3f7be61.png
I recommend you use another function instead of Main(). Please refer to the code below:
static void Main(string[] args)
{
doSomething();
}
public static void WouldYouLikeToRestart()
{
Console.WriteLine("Press r to restart");
ConsoleKeyInfo input = Console.ReadKey();
Console.WriteLine();
if (input.KeyChar == 'r')
{
doSomething();
}
}
public static void doSomething()
{
Console.WriteLine("Do Something");
WouldYouLikeToRestart();
}
A while loop would be a good fit, but since you say the program should run and then give the user the option to run again, an even better loop would be a Do While. The difference between while and Do While is that Do While will always run at least once.
string inputStr;
do
{
RunProgram();
Console.WriteLine("Run again?");
inputStr = Console.ReadLine();
} while (inputStr == "y");
TerminateProgram();
In your case, you want to repeat something so of course you should use a while loop. Use a while loop to wrap all your code up like this:
while (true) {
//all your code in the main method.
}
And then you prompt the user to enter 'r' at the end of the loop:
if (Console.ReadLine () != "r") {//this is just an example, you can use whatever method to get the input
break;
}
If the user enters r then the loop continues to do the work. break means to stop executing the stuff in the loop.
I'm trying to use an if statement with a bool that will make it that if a code runs once it will not run again. Here is the code I am using.
int random = Program._random.Next(0, 133);
if (random < 33) {
bool done = false;
if(done)
{
continue; // Error is shown for this statement
}
Console.WriteLine("Not done!");
done = true;
}
The error that Visual Studio is displaying is: "No enclosing loop out of which to break or continue".
Depending on the class/method requirements, you could possibly reverse your logic:
if (!done)
{
Console.WriteLine("Not done!");
done = true;
}
You can't use a continue only inside a loop. So you must live without this:
int random = Program._random.Next(0, 133);
if(random < 33)
{
bool done = false;
if(!done)
{
Console.WriteLine("Not done!");
done = true;
}
}
In this case, you should reverse the if with if (!done) { ... }
You can't use continue like that, it can only be used in a loop. The continue statement will go to the end of the loop and continue with the next iteration, without a loop there is no end of the loop to go to.
You can use else instead:
if (done) {
// anything to do?
} else {
Console.WriteLine("Not done!");
done = true;
}
If there is nothing to do if the variable is true, you can just reverse the expression instead:
if (!done) {
Console.WriteLine("Not done!");
done = true;
}
Note: You need to store the variable done outside the scope. Now you have a local variable that is always set to false, so the code will never be skipped.
The exception is telling you that continue is ineffective here. It simply has nothing to do, and doesn't know where to continue. It is meant to be used within the iteration of a loop.
I would be very thankful if anyone could give me a pointer in the right direction towards solving this problem I've been having in the last two days: I am working on a typing software for the Visual Studio 2010 console and need to represent time and be able to get input from the keyboard at the same time.
The problem is that the ReadLine() method allows me to write only when the timer thread is sleeping, maybe I could solve it via delegates or advanced task continuation methods, I'm a bit confused really since these are new concepts. Here is the code (sorry for the ugly formatting):
using System;
using System.Threading.Tasks;
using System.Threading;
namespace Typing_Game_tests
{
class Input_and_timer_multithread
{
public static void TimerThread()
{
for (int i = 1, mins = -1; i <= 1860; i++)
{
Console.SetCursorPosition(0, 0);
if (i % 60 == 1)
{
mins++;
}
Console.WriteLine("Timer: " + mins + " minute(s) and " + i % 60 + " seconds elapsed");
Console.SetCursorPosition(0, 6);
Thread.Sleep(1000);
}
}
static string keysRead;
public static void GetInput()
{
while (Console.ReadKey(true).Key != ConsoleKey.Enter)
{
Console.SetCursorPosition(0, 6);
keysRead = Console.ReadLine();
Console.SetCursorPosition(0, 6);
}
Console.SetCursorPosition(0, 6);
Console.WriteLine(keysRead);
}
static void Main(string[] args)
{
Thread timerMain = new Thread(new ThreadStart(TimerThread));
timerMain.Start();
Task getinputMain = new Task(GetInput);
getinputMain.Start();
}
}
}
Most of the examples I have found and studied have lambda expressions and delegates, which are new subjects I still have some difficulties understanding and thus implementing.
It would've been easier if I could've stopped the main timer thread until the ReadLine() did his job, but it wouldn't have made sense; plus I've tried using the Timers namespace, but I've had the same problem.
I wanted to give the impression that the cursor was permanently getting input from the user, but I still haven't found a way to efficiently switch back and forth the threads without deadlocking them.
Thanks.
This kind of code doesn't work any more since .NET 4.5. Console.ReadKey() and ReadLine() take a lock that prevents other threads from writing to the console. You'll need to replace it so the lock can't be taken anymore. That requires polling the keyboard with Console.KeyAvailable. Here is a simplistic replacement method for Console.ReadLine():
static object ConsoleLock = new object();
static string ReadString() {
var buf = new StringBuilder();
for (; ; ) {
while (!Console.KeyAvailable) System.Threading.Thread.Sleep(31);
lock(ConsoleLock) {
var key = Console.ReadKey(true);
if (key.Key == ConsoleKey.Enter) {
Console.WriteLine();
return buf.ToString();
}
else if (key.Key == ConsoleKey.Backspace) {
if (buf.Length > 0) {
buf.Remove(buf.Length - 1, 1);
Console.Write("\b \b");
}
}
else {
buf.Append(key.KeyChar);
Console.Write(key.KeyChar);
}
}
}
}
Also lock on the ConsoleLock in your other thread that moves the cursor so output is strictly separated.