I have made a batch game where users can log in / register. But there is no point in having passwords if a person standing nearby can peep at the password. Normal password fields mask the characters with asterisks (*).
How can mask characters on a batch file?
I've seen this done on cmd before but I have no clue how.
You can use XCOPY for a hidden input, it can handle nearly all characters and you can also implement a backspace logic.
#echo off
setlocal EnableDelayedExpansion
call :input
echo(
echo '!input!'
if "!input!"=="password" echo OK
exit /b
:input
for /F "tokens=1 delims=# " %%a in ('"prompt #$H# & echo on & for %%b in (1) do rem"') do (
set "\b=%%a"
)
set "input="
:keyLoop
call :GetKey
if not defined key exit /b
if "!key!"=="!\b!" (
if defined input (
set "input=!input:~0,-1!"
<nul set /p ".=!\b! !\b!"
)
) ELSE (
<nul set /p ".=*"
set "input=!input!!key!"
)
goto :keyLoop
:GetKey
setlocal DisableDelayedExpansion
set "key="
for /F "usebackq delims=" %%L in (`xcopy /L /w "%~f0" "%~f0" 2^>NUL`) do (
if not defined key set "key=%%L"
)
(
endlocal
set "key=^%key:~-1%" !
exit /b
)
This code should be able to handle all characters, like ^!&%<>.
It's also possible to use backspace to delete the last entered character.
The line set "key=^%key:~-1%" ! seems odd, but it's used to escape the ! character with set "key=^!" ! in the delayed expansion context.
And to avoid problems for all other characters the last ! removes the caret, like in set "key=^A" ! will be evaluated to ``set "key=A"`
Ok, this is a bit different to what you may have had in mind, but that's you're fault for choosing batch for game dev.
The way I see it is you have 3 options:
Use an external program you self made in C#, C++, Python, [etc.]
Howver this requires an application to already do this for you (Which there probably is) or for you to have a knowledge in one of these languages
Use the choice command, to continuously take one key input and wait for the user to hit space to signify the end of the password
However this limits the password characters choice, and makes the program look ugly
Use 2 Batch threads, one that masks and tallies input while the other stores it to a variable.
This may be a bit dodgey at times, at would be a bit complicated but may be the only choice you have.
Now, as I was typing this an idea stuck my head on how to achieve this. Since it might take some time to test I thought I'd post the idea (as it seems to be a soloution to this problem, which has been around for a while).
Logic
One Batch Thread will simply use set /p to store all the input into a variable and upon completion will communicate to the other batch thread through the use of waitfor or a simple directory file.
Another Batch Thread would loop the pause >nul statement and would tally the number of times the pause statement is looped, printing out the appropriate amount of *'s. The other important job of this thread is to sense when the user has finished typing the password, upon which it exits.
Im starting to make this batch program now, but for now I'll just keep you informed of my idea so far.
Code
Login.bat
#echo off
Title Password Please:
:: This is the main code
REM This code relies on Masker.bat
REM SET password to be first and thrid letter,
REM of the day of the week.
set pass=%Date:~0,1%%Date:~2,1%
REM START Masker in the same window using /b tag and create hold.pass:
Echo 0 >%temp%\hold.pass
start /b Masker.bat "%pass%" *
REM Simply take input an leave the rest to Masker.bat
set /p pass_input=:
Echo 1 >>%temp%\hold.pass
cls
if /i "%pass%" NEQ "%pass_input%" (
Title Worng Password
Echo Wrong Password... Sorry.
Sleep 5
Exit
)
Pause > nul
REM Rest of Main game code is below or simply
:: START Main.bat & Exit
Masker.bat
#echo off
Title Password Please:
setlocal enabledelayedexpansion
:: This is not the main code
REM This code is called upon by Login.bat (or the Main.bat game code)
REM CREATE the variables "passlen" and "mask":
set password=%~1
set passlen=0
:miniloop
set /a passlen+=1
if "!password:~%passlen%,1!" NEQ "" goto :miniloop
set password=
set mask=%~2
if "%mask%" EQU "" set mask=*
REM MAIN loop
:loop
cls
for /l %%a in (1,1,%passlen%) do (<nul set /p=%mask%)
sleep -m 150
for /f "usebackq" %%a in ("%temp%\hold.pass") do (if "%%~a" EQU "1" Del %temp%\hold.pass & Exit)
goto :loop
It still needs some more improvements, but I've spent aroung 30 min on it with little success to make it dynamically tell you how many characters you have typed in.
Anyone cane take this up, be my guest. Everything else works fine
Mona
This works without pressing enter after input of the password.
If you enter the correct password, ok.
if you enter a wrong password, it will stop when you enter the 9th character (can be adapted).
It does not care about capitalization.
Problem: the password is stored as pure text in the code
#echo off
setlocal enabledelayedexpansion
set "s= abcdefghijklmnopqrstuvwxyz"
set p=
:loop
choice /C %s% /N >nul
set p=%p%!s:~%errorlevel%,1!&set /p =*<nul
if /i "%p%"=="secured" goto :right
if not "%p:~8,1%"=="" goto :wrong
goto :loop
goto :wrong
:right
echo you entered correct password: %p%
goto :eof
:wrong
echo you entered wrong password: %p%
goto :eof
You may use ReadFormattedLine subroutine for all kind of formatted input. For example, the command below read a password of 8 characters, display asterisks in the screen, and continue automatically with no need to press Enter:
call :ReadFormattedLine password="********" /M "Enter password (8 chars): "
This subroutine is written in pure Batch so it does not require any additional program, and it allows several formatted input operations, like read just numbers, convert letters to uppercase, etc. You may download ReadFormattedLine subroutine from Read a line with specific format.
Related
please help! I was looking for the answer all over the internet.
Here's my code:
#echo off
title var test
:question
set a1=This
set a2=Is
set a3=a
set a4=Var
set a5=Test
choice /c 12345 /m "press a number"
if errorlevel=5 set num=5&goto answer
if errorlevel=4 set num=4&goto answer
if errorlevel=3 set num=3&goto answer
if errorlevel=2 set num=2&goto answer
if errorlevel=1 set num=1&goto answer
:answer
echo now change the answer.
set /p a%num%=
FOR /F "tokens=1-6" %%1 IN ("%a1% %a2% %a3% %a4% %a5% a%num%") DO echo %%1 %%2 %%4 %%5.&echo You typed=%%6
pause
goto question
As you can see I made the user select a number between 1 and 5 to change the specific word. But when I try same kind of code to show what he typed doesn't work :(
Environment variables should never begin with a digit and using a digits for loop variables should be also avoided. Run in a command prompt window call /? and output is the help for this command explaining how batch file arguments can be referenced with %0, %1, %2, ... which explains why environment variables with digit as first character and loop variables with a digit are not good in general even on being
a%num% in set of FOR does not reference the value of environment variable a1 or a2 or a3 or a4 or a5. It is just the name of the environment variable. The for loop is not necessary at all.
#echo off
title var test
:question
set "a1=This"
set "a2=Is"
set "a3=a"
set "a4=Var"
set "a5=Test"
%SystemRoot%\System32\choice.exe /C 12345E /N /M "Press a number in range 1-5 or E for exit: "
if errorlevel 6 goto :EOF
set "num=%ERRORLEVEL%"
set /P "a%num%=Now change the answer: "
echo %a1% %a2% %a3% %a4% %a5%.
call echo You typed: %%a%num%%%
pause
goto question
The command line call echo You typed: %%a%num%%% is parsed by Windows command processor before execution of the command line on number 3 entered to call echo You typed: %a3%. This command line is parsed a second time because of command call resulting in replacing %a3% by the value of environment variable a3 and so echo outputs the expected string.
It would be also possible to replace call echo You typed: %%a%num%%% by
setlocal EnableDelayedExpansion
echo You typed: !a%num%!
endlocal
The usage of delayed environment variable expansion results also in double parsing the command line before execution of command echo. For more details see How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Please read also this answer for details about the commands SETLOCAL and ENDLOCAL.
The two lines below in batch code above are also not really good taking into account that the user can really enter anything.
echo %a1% %a2% %a3% %a4% %a5%.
call echo You typed: %%a%num%%%
For example if the user enters number 1 and on next prompt enters:
Your user name is:& setlocal EnableDelayedExpansion & echo !UserName!& endlocal & rem
Then the batch file does something completely different than designed for and outputs the user's account name.
Secure would be the batch code:
#echo off
title var test
setlocal EnableExtensions DisableDelayedExpansion
:question
set "a1=This"
set "a2=Is"
set "a3=a"
set "a4=Var"
set "a5=Test"
%SystemRoot%\System32\choice.exe /C 12345E /N /M "Press a number in range 1-5 or E for exit: "
if errorlevel 6 goto :EOF
set "num=%ERRORLEVEL%"
set /P "a%num%=Now change the answer: "
setlocal EnableDelayedExpansion
echo !a1! !a2! !a3! !a4! !a5!.
echo You typed: !a%num%!
endlocal
pause
goto question
Now the user input string cannot modify anymore the command lines executed by Windows command processor.
A solution with the useless FOR loop would be:
setlocal EnableDelayedExpansion
for /F tokens^=1-6^ eol^= %%A in ("!a1! !a2! !a3! !a4! !a5! !a%num%!") do echo %%A %%B %%C %%D %%E.&echo You typed: %%F
endlocal
eol= is necessary to output everything correct also if use enters number 1 and next a string starting with a semicolon. The FOR options string cannot be enclosed in double quotes in this case like "tokens=1-6 eol=" because of this would define " as end of line character and nothing is output if user enters number 1 and enters next a string starting with ". The equal sign and the space must be escaped with ^ to be interpreted as literal characters by cmd.exe on double parsing the entire for command line before execution of command for.
Note: The FOR loop solution does not work correct on user enters for first variable value the special command line string as posted above. So it is also not really secure.
I am so confused and all help is appreciated...
so I'm making a little game and this is the code:
#echo off
:menu
echo Welcome To My Game!!!
echo Play & echo.Exit
:: add more menu items above
set /p menInp=
if /i %menInp%==play goto initFile
if /i %menInp$==exit EXIT
:initFile
cls
if NOT exist \MyAdventureGame\AdventureGameSave.txt\ GOTO newGame
if exist \MyAdventureGame\AdventureGameSave.txt\ GOTO Load
:newGame
echo. 2>AdventureGameSave.txt
set Gold = 50
set Xp = 0
set Level = 1
GOTO Save
:Save
echo %Gold% >>AdventureGameSave.txt
echo %Xp% >>AdventureGameSave.txt
echo %Level% >>AdventureGameSave.txt
GOTO Town
:Load
:: add Load functionallity here
GOTO Town
:Town
echo Welcome to town!!!
PAUSE
Now for my question...
when i run it, no matter what I type for my menInp, my program goes to the next line, :initFile, and its almost like my if statements
(if /i %menInp%==play goto initFile
if /i %menInp$==exit EXIT)
are being ignored.
if /i %menInp$==exit EXIT
should be
if /i %menInp%==exit EXIT
or preferably
if /i "%menInp%"=="exit" EXIT
since you are unable to predict the user's input and it may contains spaces. The "s make the string a single token.
This is somewhat of a comment, but may forestall further problems.
Tip for game-generation:
If you reserve a character as a prefix for variables-you-want-to-save (eg all variables I want to save/reload start with #) then all you need to save a game is
set #>"mygamefile.txt"
and all you need to reload a game is
for /f "usebackqdelims=" %%a in ("mygamefile.txt") do set "%%a"
To zap all # variables (useful before reloading a game) use
for /f "delims==" %%a in ('set # 2^>nul') do set "%%a="
Oh and you may consider removing the terminal \ from the filename in the if exist statements (also enclose the entire filename in quotes to allow the name to contain spaces (and follow good practice regardless of whether you actually need to cater for spaces in filenames))
Well I have a simple for loop to take user input and creat a file with this input, then ask for the next input and create "file2" etc., but I can't seem to figure out how my user can quit the loop with a certain input (maybe "exit" or "end").
setlocal enabledelayedexpansion
set x=1
:runName
set /p names="Enter header for file !x!:"
for %%a in (%names%) do (>>file!x!.txt (echo %%~a) set /a x+=1)
if !names!=end goto:eof else goto:runName
:eof
echo press any key to close
pause>nul
Ps. Sorry for the poor formatting, I cant figure how to enter my code in the grey field from my phone and dont have a computer at the moment.
I'm not quite shure why the input prompt says header.
Here is my best guess what you may have meant:
#Echo off
setlocal enabledelayedexpansion
set x=1
:runName
Set "names="
set /p names="Enter header for file%x%:"
if not defined names goto :end
if /I "%names%" Equ "end" goto :end
(
for %%a in (%names%) do (echo:%%~a)
) >file!x!.txt
set /a x+=1
goto :runName
:end
echo press any key to close
pause>nul
Running
Enter header for file1:anton bertha caesar
Enter header for file2:anchorage berlin
Enter header for file3:END
press any key to close
File content
> type file*.txt
file1.txt
anton
bertha
caesar
file2.txt
anchorage
berlin
I can't seem to figure out how my user can quit the loop
If you'll allow a suggestion, mine would be don't do that.
Almost any batch script is best written to accept command-line options and files as input. Once you trap the user into providing input interactively -- anything more than Y or N, and even that's a nuisance -- you're putting him in a bad spot. He can't re-run your script without going through the tedium again. He can't use tab completion, or pick from a list. He's trapped in a user-interface element that hasn't been improved in 30 years, and wasn't very good then.
I don't know what to suggest specifically. The example you provide is so trivial it would be easier to provide the file directly using Notepad than to create it with a script. Then you might want a verification script to make sure the provided input file is valid.
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
for /f "SKIP=0 EOL=; TOKENS=* DELIMS=" %%i in ('dir /b c:\windows\*.* ^|FIND /i *.exe') do (
CHOICE /D N /T 5 /C YN /M "Please pre Y to exit"
IF !ERRORLEVEL!==1 GOTO :END
IF !ERRORLEVEL!==2 ECHO CONTINUE
timeout 5
#echo %%i
)
:END
ENDLOCAL
I'm playing around with the new (limited) support for VT-100 escape sequences within the Windows 10 console. The supported sequences are documented at https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx.
In particular, the following sequence that reports the current cursor position interests me.
ESC[6n - responds with ESC[<n>;<m>R,
where <n> is the row number, and <m> the column number
The response is passed off as keyboard input, and appears on the screen, but I have no idea how to programmatically make use of the information. Ideally I would like to get the <n> and <m> values into environment variables from within a batch file.
But if anyone can demonstrate how to capture the variables using any language, then I may be able to use that knowledge to develop an effective batch file strategy.
I can get close with the following simple script called ANSI.BAT
#echo off
setlocal enableDelayedExpansion
for /f "delims=" %%C in (
'forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo(0x1B"'
) do set "esc=%%C"
set "csi=%esc%["
echo(Inquiry:%csi%6n
set /p "pos="
echo response=!pos:%esc%=ESC!
--OUTPUT--
C:\test>ansi
Inquiry:
^[[3;9R
response=ESC[3;9R
C:\test>
I can easily parse out the values using FOR /F, once I have the response in a variable. The problem I am having is I must manually press the <Enter> key after the response appears on the screen in order to terminate the input for my SET /P statement. I am stumped on where to go from here...
EDIT - One last requirement: I don't want the inquiry response to appear on the screen, as that disrupts the screen, and changes the cursor position. I suspect this may be the toughest nut to crack, perhaps impossible with pure batch.
Major change after three years
It works with reading the response by using XCOPY or REPLACE.
I'm using replace here, to avoid language dependent problems.
#echo off
for /F "delims=#" %%a in ('"prompt #$E# & for %%a in (1) do rem"') do set "ESC=%%a"
call :get_cursor_pos
exit /b
:get_cursor_pos
set "response="
set pos=2
:_get_loop
REM *** Request Cursor position
<nul set /p "=%ESC%[6n"
FOR /L %%# in (1 1 %pos%) DO pause < CON > NUL
for /F "tokens=1 skip=1 eol=" %%C in ('"REPLACE /W ? . < con"') DO (
set "char=%%C"
)
set "response=%response%%char%"
set /a pos+=1
if "%char%" NEQ "R" goto :_get_loop
set response
exit /b
The main problem is, XCOPY or REPLACE allows me to read one character from the input stream, but then clears the remaining buffer.
Conversely, PAUSE reads one character, preserving the remaining buffer, but does not reveal what character was read.
To solve this, I issue the query multiple times, reading a different character of the response each time. For each iteration I use a combination of 2 or more PAUSE statements followed by REPLACE to read a specific character of the response. Each iteration uses one more PAUSE than the prior iteration, until I am able to read the terminating R.
I developed this technique and initially posted it at DosTips - Query States using Console Virtual Terminal Sequences.
I have NOT Windows 10, so I can't complete any test. However, if the response of the Ansi ESC[6n sequence is fill the keyboard input buffer with ESC[<n>;<m>R characters, then it is just necessary to add an Enter key to such input in order to read it via SET /P command, and this can be done via SendKeys JScript method.
I also used a simpler method to get an ESC character in a variable.
EDIT: I modified the code accordingly to comments...
#if (#CodeSegment == #Batch) #then
#echo off
title Ansi Test
setlocal EnableDelayedExpansion
for /F %%a in ('echo prompt $E ^| cmd') do set "esc=%%a"
set "csi=%esc%["
echo Inquiry:%csi%6n
cscript //nologo //E:JScript "%~F0"
set /p "pos=" > NUL
echo response=!pos:%esc%=ESC!
#end
var sh = WScript.CreateObject("WScript.Shell");
sh.AppActivate("Ansi Test");
sh.SendKeys("{ENTER}");
Please, post the result...
So, I've been trying to create a simple spinning line thing that goes on for a set number of loops. I've encountered a problem: I can't find a way to add to a variable, or have a loop counter. This is my code so far (Other general criticisms are accepted too: I'm new to this and it all helps.)
#echo off
:1
echo
echo
echo
echo -
echo
cls
echo
echo
echo /
echo
cls
echo
echo
echo I
echo
cls
echo
echo
echo \
echo
cls
echo
echo
echo -
echo
IF %timer%
goto 1
pause
Really sorry if it's already been asked; I just can't seem to find what I'm looking for. Also, it's very possible this could just be a simple command, in which case i apologise again.
There's a couple of errors with your code as it stands.
The major one is
IF %timer%
goto 1
Batch is a ver old-fashioned language and is very particular about syntax. You can get a syntax description by typing
if /? |more
at the prompt. (replace if with the keyword you desire)
if requires
if string1==string2 thingtodoiftrue
Over the years, the syntax has been expanded, still maintaining the old version for compatibility so that the general form is now
if string1==string2 (thingtodoiftrue) else (thingtodoiffalse)
where == may now be a number of operators (equ, neq, ...); you can add a /i switch to make the strings case-insensitive, or a not keyword to reverse the sense. The parentheses are optional if the thingtodo is a single statement.
There are some quirky syntax requirements however. Either thingtodoiftrue or ( must be on the same physical line as the if keyword. The sequence ) else ( must all be on one physical line (if it's used)
As for performing some variety of count using your original structure, there are many ways. Here's one:
#echo off
set count=15
:1
....
set /a count=count-1
IF %count% neq 0 goto 1
pause
This may be what you are looking for. This runs the code 10000 times and it could be modified according to your need.
#echo off
for /l %%i in (1,1,10000) do (
echo -
cls
echo /
cls
echo I
cls
echo \
cls
)
Cheers, G
#echo off
setlocal enableextensions disabledelayedexpansion
rem Get a 0x13 character (carriage return) char inside a variable
for /f %%a in ('copy /Z "%~f0" nul') do set "CR=%%a"
rem Spin the line
setlocal enabledelayedexpansion
for /l %%a in (1 1 5000) do for %%b in (- \ ^| /) do (
set /a "pct=%%a / 50"
<nul set /p ".=%%b !pct!%% !cr!"
)
endlocal
The basic idea is to output the spinning element, followed by the percentage. To keep all the output in the same line
<nul set /p "var=outputtext"
is used. This echoes the indicated text to console, waits for input, that is readed from nul, ending the prompt wait and continues without moving the cursor to the next line. There is no line feed
Now, it is necessary overwrite this line with the new information each time. To do it, it is necessary to get a carriage return character into a variable. For it, the for /f is used, retrieving the needed carriage return character from the output of the copy command.
note: the disabledelayedexpansion is necessary in this step to avoid problems if the full path/filename of the batch file (%~f0) contains any exclamation character.
The remaining code just iterates painting the corresponding character in the list, calculating the percentage to output and printing all to the console, without the line feed (<nul set /p ...) but with an aditional carriage return that moves the cursor to the start of the line. So, the next output will overwrite the previous one
note: in this case, enabledelayedexpansion is needed to get access to the content of the changed percentage variable and to output the carriage return. Without delayed expansion active, the parser removes the carriage return from the output.