Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
tutoriel:script_shell [Le 20/02/2022, 09:00] 88.170.129.244 [Les arguments en ligne de commande] |
tutoriel:script_shell [Le 29/02/2024, 12:55] (Version actuelle) Amiralgaby ancienne révision (Le 11/09/2022, 12:20) restaurée |
||
---|---|---|---|
Ligne 3: | Ligne 3: | ||
---- | ---- | ||
- | loadstring(game:HttpGet("https://raw.githubusercontent.com/wally-rblx/funky-friday-autoplay/main/main.lua",true))() | + | ====== Introduction aux scripts shell ====== |
+ | |||
+ | Un script shell permet d'automatiser une série d'opérations. Il se présente sous la forme d'un fichier contenant une ou plusieurs commandes qui seront exécutées de manière séquentielle. | ||
+ | <code bash> | ||
+ | #!/bin/bash | ||
+ | # This script will take an animated GIF and delete every other frame | ||
+ | # Accepts two parameters: input file and output file | ||
+ | # Usage: ./<scriptfilename> input.gif output.gif | ||
+ | |||
+ | # Make a copy of the file | ||
+ | cp "$1" "$2" | ||
+ | |||
+ | # Get the number of frames | ||
+ | numframes=$(gifsicle --info "$1" \ | ||
+ | | grep --perl-regexp --only-matching '\d+ images' \ | ||
+ | | grep --perl-regexp --only-matching '\d+') | ||
+ | |||
+ | # Deletion | ||
+ | let i=0 | ||
+ | while test $i -lt $numframes | ||
+ | do | ||
+ | rem=$(( $i % 2 )) | ||
+ | |||
+ | if test $rem -eq 0 | ||
+ | then | ||
+ | gifsicle "$2" --delete "#"$(($i/2)) -o "$2" | ||
+ | fi | ||
+ | |||
+ | let i=i+1 | ||
+ | done | ||
+ | </code> | ||
=====Pour faire qu'un script soit exécutable ===== | =====Pour faire qu'un script soit exécutable ===== | ||
Ligne 9: | Ligne 39: | ||
Votre script est un simple fichier texte, par défaut il s'ouvre donc avec l'éditeur de texte défini par défaut (ex : [[:gedit|Gedit]] dans une session Unity ou Gnome).\\ | Votre script est un simple fichier texte, par défaut il s'ouvre donc avec l'éditeur de texte défini par défaut (ex : [[:gedit|Gedit]] dans une session Unity ou Gnome).\\ | ||
- | Pour qu'il soit autorisé à se lancer en tant que programme, il faut modifier ses propriétés.\\ | + | Pour qu'il soit autorisé à se lancer en tant que programme, il faut modifier ses propriétés. |
Pour cela faites un clic droit sur son icône, et dans l'onglet "Permissions" des "Propriétés", cocher la case //"autoriser l'exécution du fichier comme un programme"//. | Pour cela faites un clic droit sur son icône, et dans l'onglet "Permissions" des "Propriétés", cocher la case //"autoriser l'exécution du fichier comme un programme"//. | ||
Ligne 48: | Ligne 78: | ||
Ajouter un répertoire au PATH peut donc être très pratique. Par convention, ce répertoire s'appelle **bin** et se place dans votre répertoire personnel. Si votre répertoire personnel est /home/toto, ce répertoire sera donc **/home/toto/bin**. | Ajouter un répertoire au PATH peut donc être très pratique. Par convention, ce répertoire s'appelle **bin** et se place dans votre répertoire personnel. Si votre répertoire personnel est /home/toto, ce répertoire sera donc **/home/toto/bin**. | ||
- | Pour pouvoir utiliser mes scripts en tapant directement leur nom (sans le "./") depuis n'importe quel répertoire de mon ordinateur, il me suffit d'indiquer au shell de chercher aussi dans ce nouveau dossier en l'ajoutant au PATH. | + | Pour pouvoir utiliser vos scripts en tapant directement leur nom (sans le "./") depuis n'importe quel répertoire de votre ordinateur, il vous suffit d'indiquer au shell de chercher aussi dans ce nouveau dossier en l'ajoutant au PATH. |
Pour ceci, il suffit de faire : | Pour ceci, il suffit de faire : | ||
export PATH=$PATH:$HOME/bin | export PATH=$PATH:$HOME/bin | ||
Ligne 74: | Ligne 104: | ||
* ksh (<=> ksh88 sur Solaris et équivaut à ksh93 sur les autres UNIX/Linux cf.[[http://en.wikipedia.org/wiki/Korn_shell#History|Korn shell History]]): shells korn écrits par David Korn, pdksh (Public Domain Korn Shell <=> ksh88) ; | * ksh (<=> ksh88 sur Solaris et équivaut à ksh93 sur les autres UNIX/Linux cf.[[http://en.wikipedia.org/wiki/Korn_shell#History|Korn shell History]]): shells korn écrits par David Korn, pdksh (Public Domain Korn Shell <=> ksh88) ; | ||
* rc : shell C, lui aussi conçu par le projet GNU ; | * rc : shell C, lui aussi conçu par le projet GNU ; | ||
- | * tclsh : shell utilisant Tcl ; | + | * tclsh : shell utilisant Tcl ; |
- | * wish : shell utilisant Tk . | + | * wish : shell utilisant Tk . |
Il existe bien entendu beaucoup d'autres types de shells.\\ Pour savoir quel type de shell est présent sur une machine, aller dans un terminal et taper la commande **ps**. | Il existe bien entendu beaucoup d'autres types de shells.\\ Pour savoir quel type de shell est présent sur une machine, aller dans un terminal et taper la commande **ps**. | ||
Ligne 83: | Ligne 113: | ||
===== Les variables ===== | ===== Les variables ===== | ||
Il faut savoir que en bash les variables sont toutes des chaînes de caractères.\\ | Il faut savoir que en bash les variables sont toutes des chaînes de caractères.\\ | ||
- | Cela dépendra de son USAGE, pour une opération arithmétique prochaine voir : let ma_variable sinon pour conserver une valeur : | + | Cela dépendra de son USAGE, pour une opération arithmétique prochaine voir : let ma_variable sinon pour conserver une valeur : |
il suffit de lui donner un nom et une valeur avec l'affectation égale : | il suffit de lui donner un nom et une valeur avec l'affectation égale : | ||
<code bash> | <code bash> | ||
Ligne 151: | Ligne 181: | ||
Exemple: un sleep interactif pour illustrer $! (Cf. [[:tutoriel:script_shell#les_fonctions|les fonctions]]).\\ | Exemple: un sleep interactif pour illustrer $! (Cf. [[:tutoriel:script_shell#les_fonctions|les fonctions]]).\\ | ||
- | Pour déclarer un tableau, plusieurs méthodes :\\ | + | Pour déclarer un tableau, plusieurs méthodes : |
première méthode (compatible bash, zsh, et ksh93 mais pas ksh88, ni avec dash, qui est lancé par "sh") : | première méthode (compatible bash, zsh, et ksh93 mais pas ksh88, ni avec dash, qui est lancé par "sh") : | ||
<code bash>tab=("John Smith" "Jane Doe")</code> | <code bash>tab=("John Smith" "Jane Doe")</code> | ||
Ligne 185: | Ligne 215: | ||
renverront la même réponse. | renverront la même réponse. | ||
- | NB2 : les tableaux sont séparés par un séparateur défini : l'IFS. | + | NB2 : les tableaux sont séparés par un séparateur défini : l'IFS. |
- | Par défaut l'IFS est composé des trois caractères : $' \t\n' soit espace, tabulation, saut de ligne. | + | Par défaut l'IFS est composé des trois caractères : $' \t\n' soit espace, tabulation, saut de ligne. |
Il peut être forcé sur un autre caractère. | Il peut être forcé sur un autre caractère. | ||
<code bash>IFS=$SEPARATEUR</code> | <code bash>IFS=$SEPARATEUR</code> | ||
Ligne 194: | Ligne 224: | ||
* un caractère spécial : ($'\t' : tabulation, $'\n' : saut de ligne,...) | * un caractère spécial : ($'\t' : tabulation, $'\n' : saut de ligne,...) | ||
- | --[[ | + | ==== Les arguments en ligne de commande ==== |
- | Change logs: | + | |
- | 1/14/22 | + | Pour passer des arguments en ligne de commande c'est encore une fois très simple. Chaque argument est numéroté et ensuite on l'appelle par son numéro : |
- | * first update of the new year! | + | |
- | * added a settings saving / loading system. | + | |
- | * added some save manager code for this ui library, not all option types are supported or tested. | + | |
- | 12/5/21 | + | ./test.sh powa noplay |
- | * Fixed issues with noteTime calculation, causing some songs like Triple Trouble to break. Report bugs as always | + | |
- | 11/9/21 | + | Voici notre test.sh |
- | + Added support for new modes (9Key for example) | + | <code bash> |
+ | #!/bin/sh | ||
+ | echo $3 | ||
+ | echo $2 | ||
+ | </code> | ||
- | 9/26/21 | + | Notez que $0 est le nom du fichier. |
- | + Added 'Unload' | + | |
- | * Fixed issues with accuracy. | + | |
- | 9/25/21 (patch 1) | + | ''shift'' est une commande très pratique lorsque vous traitez des arguments en ligne de commande. Elle permet de faire "défiler" les arguments ($0, $1, $2, …). C'est à dire que le contenu de $1 passe dans $0, celui de $2 dans $1 et ainsi de suite. Il est tout à fait possible de traiter les arguments avec ''for i in $*; do'' mais lorsque vous aurez des options du style ''–title "mon_titre"'' il sera très laborieux de récupérer la valeur ''"mon_titre"''. |
- | * Added a few sanity checks | + | |
- | * Fixed some errors | + | |
- | * Should finally fix invisible notes (if it doesnt, i hate this game) | + | |
- | 9/25/21 | + | Voici un exemple de script où vous devez vous souvenir de ce que vous avez écrit (un petit jeu de mémoire, quoi) : |
- | * Code refactoring. | + | <code bash> |
- | * Fixed unsupported exploit check | + | #!/bin/sh |
- | * Implemented safer URL loading routine. | + | clear # Un peu facile si la commande reste au dessus :-) |
- | * Tweaked autoplayer (implemented hitbox offset, uses game code to calculate score and hit type now) | + | until [ $# = 0 ] |
- | + | do | |
- | 9/19/21 | + | echo -n "Taper l'option suivante : " |
- | * Miss actually ignores the note. | + | read Reslt |
- | + | if [ "$Reslt" = "$1" ]; then | |
- | 8/20/21 | + | echo "Bien joué !" |
- | ! This update was provided by Sezei (https://github.com/greasemonkey123/ff-bot-new) | + | else |
- | * I renamed some stuff and changed their default 'Autoplayer bind' | + | echo "Non mais quand même !!! C'ÉTAIT $1 ET NON PAS $Reslt PETIT FRIPON !!!" |
- | + | sleep 3 # Juste pour le fun du script qui rage ;-p | |
- | + Added 'Miss chance' | + | echo "Donc je te bannis de ubuntu-fr.org ! Et toc !! Tu ne peux rien contre moi !!!" |
- | + Added 'Release delay' (note: higher values means a higher chance to miss) | + | exit 1 |
- | + Added 'Autoplayer bind' | + | fi |
- | * Added new credits | + | shift # On défile |
- | * Made folder names more clear | + | done |
- | + | echo "Vous avez réussi !" | |
- | 8/2/21 | + | </code> |
- | ! KRNL has since been fixed, enjoy! | + | |
- | + | ||
- | + Added 'Manual' mode which allows you to force the notes to hit a specific type by holding down a keybind. | + | |
- | * Switched fastWait and fastSpawn to Roblox's task libraries | + | |
- | * Attempted to fix 'invalid key to next' errors | + | |
- | + | ||
- | 5/12/21 | + | |
- | * Attempted to fix the autoplayer missing as much. | + | |
- | + | ||
- | 5/16/21 | + | |
- | * Attempt to fix invisible notes. | + | |
- | * Added hit chances & an autoplayer toggle | + | |
- | ! Hit chances are a bit rough but should work. | + | |
- | + | ||
- | Information: | + | |
- | Officially supported: Synapse X, Script-Ware, KRNL, Fluxus | + | |
- | Needed functions: setthreadcontext, getconnections, getgc, getloaodedmodules | + | |
- | + | ||
- | You can find contact information on the GitHub repository (https://github.com/wally-rblx/funky-friday-autoplay) | + | |
- | --]] | + | |
- | + | ||
- | local start = tick() | + | |
- | local client = game:GetService('Players').LocalPlayer; | + | |
- | local set_identity = (type(syn) == 'table' and syn.set_thread_identity) or setidentity or setthreadcontext | + | |
- | + | ||
- | local function fail(r) return client:Kick(r) end | + | |
- | + | ||
- | -- gracefully handle errors when loading external scripts | + | |
- | -- added a cache to make hot reloading a bit faster | + | |
- | + | ||
- | local usedCache = shared.__urlcache and next(shared.__urlcache) ~= nil | + | |
- | + | ||
- | shared.__urlcache = shared.__urlcache or {} | + | |
- | local function urlLoad(url) | + | |
- | local success, result | + | |
- | + | ||
- | if shared.__urlcache[url] then | + | |
- | success, result = true, shared.__urlcache[url] | + | |
- | else | + | |
- | success, result = pcall(game.HttpGet, game, url) | + | |
- | end | + | |
- | + | ||
- | if (not success) then | + | |
- | return fail(string.format('Failed to GET url %q for reason: %q', url, tostring(result))) | + | |
- | end | + | |
- | + | ||
- | local fn, err = loadstring(result) | + | |
- | if (type(fn) ~= 'function') then | + | |
- | return fail(string.format('Failed to loadstring url %q for reason: %q', url, tostring(err))) | + | |
- | end | + | |
- | + | ||
- | local results = { pcall(fn) } | + | |
- | if (not results[1]) then | + | |
- | return fail(string.format('Failed to initialize url %q for reason: %q', url, tostring(results[2]))) | + | |
- | end | + | |
- | + | ||
- | shared.__urlcache[url] = result | + | |
- | return unpack(results, 2) | + | |
- | end | + | |
- | + | ||
- | -- attempt to block imcompatible exploits | + | |
- | -- rewrote because old checks literally did not work | + | |
- | if type(set_identity) ~= 'function' then return fail('Unsupported exploit (missing "set_thread_identity")') end | + | |
- | if type(getconnections) ~= 'function' then return fail('Unsupported exploit (missing "getconnections")') end | + | |
- | if type(getloadedmodules) ~= 'function' then return fail('Unsupported exploit (misssing "getloadedmodules")') end | + | |
- | if type(getgc) ~= 'function' then return fail('Unsupported exploit (misssing "getgc")') end | + | |
- | + | ||
- | local library = urlLoad("https://raw.githubusercontent.com/wally-rblx/uwuware-ui/main/main.lua") | + | |
- | local akali = urlLoad("https://gist.githubusercontent.com/wally-rblx/e010db020afe8259048a0c3c7262cdf8/raw/76ae0921ac9bd3215017e635d2c1037a37262240/notif.lua") | + | |
- | + | ||
- | local httpService = game:GetService('HttpService') | + | |
- | + | ||
- | local framework, scrollHandler | + | |
- | local counter = 0 | + | |
- | + | ||
- | while true do | + | |
- | for _, obj in next, getgc(true) do | + | |
- | if type(obj) == 'table' and rawget(obj, 'GameUI') then | + | |
- | framework = obj; | + | |
- | break | + | |
- | end | + | |
- | end | + | |
- | + | ||
- | for _, module in next, getloadedmodules() do | + | |
- | if module.Name == 'ScrollHandler' then | + | |
- | scrollHandler = module; | + | |
- | break; | + | |
- | end | + | |
- | end | + | |
- | + | ||
- | if (type(framework) == 'table') and (typeof(scrollHandler) == 'Instance') then | + | |
- | break | + | |
- | end | + | |
- | + | ||
- | counter = counter + 1 | + | |
- | if counter > 6 then | + | |
- | fail(string.format('Failed to load game dependencies. Details: %s, %s', type(framework), typeof(scrollHandler))) | + | |
- | end | + | |
- | wait(1) | + | |
- | end | + | |
- | + | ||
- | local runService = game:GetService('RunService') | + | |
- | local userInputService = game:GetService('UserInputService') | + | |
- | local virtualInputManager = game:GetService('VirtualInputManager') | + | |
- | + | ||
- | local random = Random.new() | + | |
- | + | ||
- | local task = task or getrenv().task; | + | |
- | local fastWait, fastSpawn = task.wait, task.spawn; | + | |
- | + | ||
- | -- firesignal implementation | + | |
- | -- hitchance rolling | + | |
- | local fireSignal, rollChance do | + | |
- | -- updated for script-ware or whatever | + | |
- | -- attempted to update for krnl | + | |
- | + | ||
- | function fireSignal(target, signal, ...) | + | |
- | -- getconnections with InputBegan / InputEnded does not work without setting Synapse to the game's context level | + | |
- | set_identity(2) | + | |
- | local didFire = false | + | |
- | for _, signal in next, getconnections(signal) do | + | |
- | if type(signal.Function) == 'function' and islclosure(signal.Function) then | + | |
- | local scr = rawget(getfenv(signal.Function), 'script') | + | |
- | if scr == target then | + | |
- | didFire = true | + | |
- | pcall(signal.Function, ...) | + | |
- | end | + | |
- | end | + | |
- | end | + | |
- | -- if not didFire then fail"couldnt fire input signal" end | + | |
- | set_identity(7) | + | |
- | end | + | |
- | + | ||
- | -- uses a weighted random system | + | |
- | -- its a bit scuffed rn but it works good enough | + | |
- | + | ||
- | function rollChance() | + | |
- | if (library.flags.autoPlayerMode == 'Manual') then | + | |
- | if (library.flags.sickHeld) then return 'Sick' end | + | |
- | if (library.flags.goodHeld) then return 'Good' end | + | |
- | if (library.flags.okayHeld) then return 'Ok' end | + | |
- | if (library.flags.missHeld) then return 'Bad' end | + | |
- | + | ||
- | return 'Bad' -- incase if it cant find one | + | |
- | end | + | |
- | + | ||
- | local chances = { | + | |
- | { type = 'Sick', value = library.flags.sickChance }, | + | |
- | { type = 'Good', value = library.flags.goodChance }, | + | |
- | { type = 'Ok', value = library.flags.okChance }, | + | |
- | { type = 'Bad', value = library.flags.badChance }, | + | |
- | { type = 'Miss' , value = library.flags.missChance }, | + | |
- | } | + | |
- | + | ||
- | table.sort(chances, function(a, b) | + | |
- | return a.value > b.value | + | |
- | end) | + | |
- | + | ||
- | local sum = 0; | + | |
- | for i = 1, #chances do | + | |
- | sum += chances[i].value | + | |
- | end | + | |
- | + | ||
- | if sum == 0 then | + | |
- | -- forgot to change this before? | + | |
- | -- fixed 6/5/21 | + | |
- | + | ||
- | return chances[random:NextInteger(1, #chances)].type | + | |
- | end | + | |
- | + | ||
- | local initialWeight = random:NextInteger(0, sum) | + | |
- | local weight = 0; | + | |
- | + | ||
- | for i = 1, #chances do | + | |
- | weight = weight + chances[i].value | + | |
- | + | ||
- | if weight > initialWeight then | + | |
- | return chances[i].type | + | |
- | end | + | |
- | end | + | |
- | + | ||
- | return 'Sick' -- just incase it fails? | + | |
- | end | + | |
- | end | + | |
- | + | ||
- | + | ||
- | local function notify(text, duration) | + | |
- | return akali.Notify({ | + | |
- | Title = 'Funky friday autoplayer', | + | |
- | Description = text, | + | |
- | Duration = duration or 1, | + | |
- | }) | + | |
- | end | + | |
- | + | ||
- | library.notify = notify | + | |
- | + | ||
- | -- save manager | + | |
- | local saveManager = {} do | + | |
- | local defaultSettings = [[{"Funky Friday":{"goodChance":{"value":0,"type":"slider"},"badChance":{"value":0,"type":"slider"},"okChance":{"value":0,"type":"slider"},"autoPlayer":{"state":false,"type":"toggle"},"goodBind":{"key":"Two","type":"bind"},"sickChance":{"value":100,"type":"slider"},"okBind":{"key":"Three","type":"bind"},"sickBind":{"key":"One","type":"bind"},"Menu toggle":{"key":"Delete","type":"bind"},"secondaryPressMode":{"state":false,"type":"toggle"},"autoDelay":{"value":50,"type":"slider"},"autoPlayerToggle":{"key":"End","type":"bind"},"badBind":{"key":"Four","type":"bind"},"autoPlayerMode":{"value":"Chances","type":"list"},"missChance":{"value":0,"type":"slider"}}}]] | + | |
- | local optionTypes = { | + | |
- | toggle = { | + | |
- | Save = function(option) | + | |
- | return { type = 'toggle', state = option.state } | + | |
- | end, | + | |
- | Load = function(option, data) | + | |
- | option:SetState(data.state) | + | |
- | end | + | |
- | }, | + | |
- | bind = { | + | |
- | Save = function(option) | + | |
- | return { type = 'bind', key = option.key } | + | |
- | end, | + | |
- | Load = function(option, data) | + | |
- | option:SetKey(data.key) | + | |
- | end | + | |
- | }, | + | |
- | slider = { | + | |
- | Save = function(option) | + | |
- | return { type = 'slider', value = option.value } | + | |
- | end, | + | |
- | Load = function(option, data) | + | |
- | option:SetValue(data.value) | + | |
- | end, | + | |
- | }, | + | |
- | color = { | + | |
- | Save = function(option) | + | |
- | return { type = 'color', color = option.color:ToHex() } | + | |
- | end, | + | |
- | Load = function(option, data) | + | |
- | option:SetValue(Color3.fromHex(data.color)) | + | |
- | end | + | |
- | }, | + | |
- | list = { | + | |
- | Save = function(option) | + | |
- | return { type = 'list', value = option.value } | + | |
- | end, | + | |
- | Load = function(option, data) | + | |
- | option:SetValue(data.value) | + | |
- | end | + | |
- | }, | + | |
- | } | + | |
- | + | ||
- | local function recurseLibraryOptions(root, callback) | + | |
- | for _, option in next, root do | + | |
- | if option.type == 'folder' then | + | |
- | recurseLibraryOptions(option.options, callback) | + | |
- | else | + | |
- | callback(option) | + | |
- | end | + | |
- | end | + | |
- | end | + | |
- | + | ||
- | function saveManager:SaveConfig(name) | + | |
- | local data = {} | + | |
- | + | ||
- | for _, window in next, library.windows do | + | |
- | if window.title == 'Configs' then continue end | + | |
- | + | ||
- | local storage = {} | + | |
- | data[window.title] = storage | + | |
- | + | ||
- | recurseLibraryOptions(window.options, function(option) | + | |
- | local parser = optionTypes[option.type] | + | |
- | if parser then | + | |
- | storage[option.flag] = parser.Save(option) | + | |
- | end | + | |
- | end) | + | |
- | end | + | |
- | + | ||
- | local s, err = pcall(writefile, 'funky_friday_autoplayer\\configs\\' .. name, httpService:JSONEncode(data)) | + | |
- | if not s then | + | |
- | library.notify(string.format('Failed to save config %q because %q', name, err), 2) | + | |
- | if err == 'invalid extension' then | + | |
- | library.notify('Try adding a file extension after your config name. ex: ".json", ".txt", ".dat"', 2) | + | |
- | end | + | |
- | return | + | |
- | end | + | |
- | + | ||
- | library.refreshConfigs() | + | |
- | end | + | |
- | + | ||
- | function saveManager:LoadConfig(name) | + | |
- | local data | + | |
- | if name == 'default' then | + | |
- | data = defaultSettings | + | |
- | else | + | |
- | data = readfile('funky_friday_autoplayer\\configs\\' .. name) | + | |
- | end | + | |
- | + | ||
- | local success, data = pcall(function() return httpService:JSONDecode(data) end) | + | |
- | if not success then | + | |
- | return library.notify(string.format('Failed to load config %q because %q', name, data)) | + | |
- | end | + | |
- | + | ||
- | for _, window in next, library.windows do | + | |
- | if window.title == 'Configs' then continue end | + | |
- | + | ||
- | local storage = data[window.title] | + | |
- | if not storage then continue end | + | |
- | + | ||
- | recurseLibraryOptions(window.options, function(option) | + | |
- | local parser = optionTypes[option.type] | + | |
- | if parser then | + | |
- | parser.Load(option, storage[option.flag]) | + | |
- | end | + | |
- | end) | + | |
- | end | + | |
- | end | + | |
- | + | ||
- | end | + | |
- | + | ||
- | -- autoplayer | + | |
- | local chanceValues do | + | |
- | chanceValues = { | + | |
- | Sick = 96, | + | |
- | Good = 92, | + | |
- | Ok = 87, | + | |
- | Bad = 75, | + | |
- | } | + | |
- | + | ||
- | local keyCodeMap = {} | + | |
- | for _, enum in next, Enum.KeyCode:GetEnumItems() do | + | |
- | keyCodeMap[enum.Value] = enum | + | |
- | end | + | |
- | + | ||
- | if shared._unload then | + | |
- | pcall(shared._unload) | + | |
- | end | + | |
- | + | ||
- | library.threads = {} | + | |
- | function shared._unload() | + | |
- | if shared._id then | + | |
- | pcall(runService.UnbindFromRenderStep, runService, shared._id) | + | |
- | end | + | |
- | + | ||
- | if library.open then | + | |
- | library:Close() | + | |
- | end | + | |
- | + | ||
- | library.base:ClearAllChildren() | + | |
- | library.base:Destroy() | + | |
- | + | ||
- | for i = 1, #library.threads do | + | |
- | coroutine.close(library.threads[i]) | + | |
- | end | + | |
- | end | + | |
- | + | ||
- | shared._id = httpService:GenerateGUID(false) | + | |
- | runService:BindToRenderStep(shared._id, 1, function() | + | |
- | if (not library.flags.autoPlayer) then return end | + | |
- | if typeof(framework.SongPlayer.CurrentlyPlaying) ~= 'Instance' then return end | + | |
- | if framework.SongPlayer.CurrentlyPlaying.ClassName ~= 'Sound' then return end | + | |
- | + | ||
- | local arrows = {} | + | |
- | for _, obj in next, framework.UI.ActiveSections do | + | |
- | arrows[#arrows + 1] = obj; | + | |
- | end | + | |
- | + | ||
- | local count = framework.SongPlayer:GetKeyCount() | + | |
- | local mode = count .. 'Key' | + | |
- | + | ||
- | local arrowData = framework.ArrowData[mode].Arrows | + | |
- | + | ||
- | for idx = 1, #arrows do | + | |
- | local arrow = arrows[idx] | + | |
- | if type(arrow) ~= 'table' then | + | |
- | continue | + | |
- | end | + | |
- | + | ||
- | local ignoredNoteTypes = { Death = true, ['Pea Note'] = true } | + | |
- | + | ||
- | if type(arrow.NoteDataConfigs) == 'table' then | + | |
- | if ignoredNoteTypes[arrow.NoteDataConfigs.Type] then | + | |
- | continue | + | |
- | end | + | |
- | end | + | |
- | + | ||
- | if (arrow.Side == framework.UI.CurrentSide) and (not arrow.Marked) and framework.SongPlayer.CurrentlyPlaying.TimePosition > 0 then | + | |
- | local indice = (arrow.Data.Position % count) | + | |
- | local position = indice .. '' | + | |
- | + | ||
- | if (position) then | + | |
- | local hitboxOffset = 0 do | + | |
- | local settings = framework.Settings; | + | |
- | local offset = type(settings) == 'table' and settings.HitboxOffset; | + | |
- | local value = type(offset) == 'table' and offset.Value; | + | |
- | + | ||
- | if type(value) == 'number' then | + | |
- | hitboxOffset = value; | + | |
- | end | + | |
- | + | ||
- | hitboxOffset = hitboxOffset / 1000 | + | |
- | end | + | |
- | + | ||
- | local songTime = framework.SongPlayer.CurrentTime do | + | |
- | local configs = framework.SongPlayer.CurrentSongConfigs | + | |
- | local playbackSpeed = type(configs) == 'table' and configs.PlaybackSpeed | + | |
- | + | ||
- | if type(playbackSpeed) ~= 'number' then | + | |
- | playbackSpeed = 1 | + | |
- | end | + | |
- | + | ||
- | songTime = songTime / playbackSpeed | + | |
- | end | + | |
- | + | ||
- | local noteTime = math.clamp((1 - math.abs(arrow.Data.Time - (songTime + hitboxOffset))) * 100, 0, 100) | + | |
- | + | ||
- | local result = rollChance() | + | |
- | arrow._hitChance = arrow._hitChance or result; | + | |
- | + | ||
- | local hitChance = (library.flags.autoPlayerMode == 'Manual' and result or arrow._hitChance) | + | |
- | if hitChance ~= "Miss" and noteTime >= chanceValues[arrow._hitChance] then | + | |
- | fastSpawn(function() | + | |
- | arrow.Marked = true; | + | |
- | local keyCode = keyCodeMap[arrowData[position].Keybinds.Keyboard[1]] | + | |
- | + | ||
- | if library.flags.secondaryPressMode then | + | |
- | virtualInputManager:SendKeyEvent(true, keyCode, false, nil) | + | |
- | else | + | |
- | fireSignal(scrollHandler, userInputService.InputBegan, { KeyCode = keyCode, UserInputType = Enum.UserInputType.Keyboard }, false) | + | |
- | end | + | |
- | + | ||
- | if arrow.Data.Length > 0 then | + | |
- | fastWait(arrow.Data.Length + (library.flags.heldDelay / 1000)) | + | |
- | else | + | |
- | fastWait(library.flags.autoDelay / 1000) | + | |
- | end | + | |
- | + | ||
- | if library.flags.secondaryPressMode then | + | |
- | virtualInputManager:SendKeyEvent(false, keyCode, false, nil) | + | |
- | else | + | |
- | fireSignal(scrollHandler, userInputService.InputEnded, { KeyCode = keyCode, UserInputType = Enum.UserInputType.Keyboard }, false) | + | |
- | end | + | |
- | + | ||
- | arrow.Marked = nil; | + | |
- | end) | + | |
- | end | + | |
- | end | + | |
- | end | + | |
- | end | + | |
- | end) | + | |
- | end | + | |
- | + | ||
- | -- menu | + | |
- | + | ||
- | local windows = { | + | |
- | autoplayer = library:CreateWindow('Autoplayer'), | + | |
- | customization = library:CreateWindow('Customization'), | + | |
- | configs = library:CreateWindow('Configs'), | + | |
- | misc = library:CreateWindow('Miscellaneous') | + | |
- | } | + | |
- | + | ||
- | local folder = windows.autoplayer:AddFolder('Main') do | + | |
- | local toggle = folder:AddToggle({ text = 'Autoplayer', flag = 'autoPlayer' }) | + | |
- | + | ||
- | folder:AddToggle({ text = 'Secondary press mode', flag = 'secondaryPressMode', callback = function() | + | |
- | if library.flags.secondaryPressMode then | + | |
- | library.notify('Only enable "Secondary press mode" if the main autoplayer does not work! It may cause issues or not be as accurate!') | + | |
- | end | + | |
- | end }) -- alternate mode if something breaks on krml or whatever | + | |
- | folder:AddLabel({ text = "Enable if autoplayer breaks" }) | + | |
- | + | ||
- | -- Fixed to use toggle:SetState | + | |
- | folder:AddBind({ text = 'Autoplayer toggle', flag = 'autoPlayerToggle', key = Enum.KeyCode.End, callback = function() | + | |
- | toggle:SetState(not toggle.state) | + | |
- | end }) | + | |
- | + | ||
- | folder:AddDivider() | + | |
- | folder:AddList({ text = 'Autoplayer mode', flag = 'autoPlayerMode', values = { 'Chances', 'Manual' } }) | + | |
- | end | + | |
- | + | ||
- | local folder = windows.customization:AddFolder('Hit chances') do | + | |
- | folder:AddSlider({ text = 'Sick %', flag = 'sickChance', min = 0, max = 100, value = 100 }) | + | |
- | folder:AddSlider({ text = 'Good %', flag = 'goodChance', min = 0, max = 100, value = 0 }) | + | |
- | folder:AddSlider({ text = 'Ok %', flag = 'okChance', min = 0, max = 100, value = 0 }) | + | |
- | folder:AddSlider({ text = 'Bad %', flag = 'badChance', min = 0, max = 100, value = 0 }) | + | |
- | folder:AddSlider({ text = 'Miss %', flag = 'missChance', min = 0, max = 100, value = 0 }) | + | |
- | end | + | |
- | + | ||
- | local folder = windows.customization:AddFolder('Timing') do | + | |
- | folder:AddSlider({ text = 'Release delay (ms)', flag = 'autoDelay', min = 0, max = 500, value = 20 }) | + | |
- | folder:AddSlider({ text = 'Held delay (ms)', flag = 'heldDelay', min = -20, max = 100, value = -20 }) | + | |
- | end | + | |
- | + | ||
- | local folder = windows.customization:AddFolder('Keybinds') do | + | |
- | folder:AddBind({ text = 'Sick', flag = 'sickBind', key = Enum.KeyCode.One, hold = true, callback = function(val) library.flags.sickHeld = (not val) end, }) | + | |
- | folder:AddBind({ text = 'Good', flag = 'goodBind', key = Enum.KeyCode.Two, hold = true, callback = function(val) library.flags.goodHeld = (not val) end, }) | + | |
- | folder:AddBind({ text = 'Ok', flag = 'okBind', key = Enum.KeyCode.Three, hold = true, callback = function(val) library.flags.okayHeld = (not val) end, }) | + | |
- | folder:AddBind({ text = 'Bad', flag = 'badBind', key = Enum.KeyCode.Four, hold = true, callback = function(val) library.flags.missHeld = (not val) end, }) | + | |
- | end | + | |
- | + | ||
- | if type(readfile) == 'function' and type(writefile) == 'function' and type(makefolder) == 'function' and type(isfolder) == 'function' then | + | |
- | if not isfolder('funky_friday_autoplayer\\configs') then | + | |
- | makefolder('funky_friday_autoplayer') | + | |
- | makefolder('funky_friday_autoplayer\\configs') | + | |
- | end | + | |
- | + | ||
- | local window = windows.configs do | + | |
- | window:AddBox({ text = 'Config name', value = '', flag = 'configNameInput' }) | + | |
- | library._configList = window:AddList({ text = 'Config list', values = { 'default' }, flag = 'configList' }) | + | |
- | + | ||
- | window:AddButton({ text = 'Save config', callback = function() | + | |
- | local name = library.flags.configNameInput | + | |
- | if name:gsub(' ', '') == '' then | + | |
- | return notify('Failed to save. [invalid config name]', 3) | + | |
- | end | + | |
- | + | ||
- | saveManager:SaveConfig(name) | + | |
- | end }) | + | |
- | + | ||
- | window:AddButton({ text = 'Load config', callback = function() | + | |
- | local name = library.flags.configList | + | |
- | + | ||
- | if name:gsub(' ', '') == '' then | + | |
- | return notify('Failed to load. [invalid config name]', 3) | + | |
- | end | + | |
- | + | ||
- | if not isfile('funky_friday_autoplayer\\configs\\' .. name) then | + | |
- | return notify('Failed to load. [config does not exist]', 3) | + | |
- | end | + | |
- | + | ||
- | saveManager:LoadConfig(name) | + | |
- | end }) | + | |
- | + | ||
- | window:AddDivider() | + | |
- | + | ||
- | function library.refreshConfigs() | + | |
- | for _, value in next, library._configList.values do | + | |
- | if value == 'default' then continue end | + | |
- | library._configList:RemoveValue(tostring(value)) | + | |
- | end | + | |
- | + | ||
- | local files = listfiles('funky_friday_autoplayer\\configs') | + | |
- | for i = 1, #files do | + | |
- | files[i] = files[i]:gsub('funky_friday_autoplayer\\configs\\', '') | + | |
- | library._configList:AddValue(files[i]) | + | |
- | end | + | |
- | + | ||
- | if files[1] then | + | |
- | library._configList:SetValue(files[1]) | + | |
- | else | + | |
- | library._configList:SetValue('default') | + | |
- | end | + | |
- | end | + | |
- | + | ||
- | window:AddButton({ text = 'Refresh configs', callback = library.refreshConfigs }) | + | |
- | end | + | |
- | task.delay(1, library.refreshConfigs) | + | |
- | else | + | |
- | notify('Failed to create configs window due to your exploit missing certain file functions.', 2) | + | |
- | end | + | |
- | + | ||
- | local folder = windows.misc:AddFolder('Credits') do | + | |
- | folder:AddLabel({ text = 'Jan - UI library' }) | + | |
- | folder:AddLabel({ text = 'wally - Script' }) | + | |
- | folder:AddLabel({ text = 'Sezei - Contributor'}) | + | |
- | folder:AddLabel({ text = 'aKinlei - Notifications'}) | + | |
- | end | + | |
- | + | ||
- | windows.misc:AddLabel({ text = 'Version 1.9a' }) | + | |
- | windows.misc:AddLabel({ text = 'Updated 12/11/21' }) | + | |
- | windows.misc:AddLabel({ text = 'i fixed stuff at 3 am' }) | + | |
- | + | ||
- | windows.misc:AddDivider() | + | |
- | windows.misc:AddButton({ text = 'Unload script', callback = function() | + | |
- | shared._unload() | + | |
- | library.notify('Successfully unloaded script!', 2) | + | |
- | end }) | + | |
- | + | ||
- | windows.misc:AddButton({ text = 'Copy discord', callback = function() | + | |
- | if pcall(setclipboard, "https://wally.cool/discord") then | + | |
- | library.notify('Successfully copied discord', 2) | + | |
- | end | + | |
- | end }) | + | |
- | + | ||
- | windows.misc:AddDivider() | + | |
- | windows.misc:AddBind({ text = 'Menu toggle', key = Enum.KeyCode.Delete, callback = function() library:Close() end }) | + | |
- | library:Init() | ||
- | library.notify(string.format('Loaded script in %.4f second(s)!\nUsed Http cache: %s', tick() - start, tostring(usedCache)), 3) | ||
==== L'arithmétique ==== | ==== L'arithmétique ==== | ||
<code bash>(( variable = 2 + $autre_var * 5 ))</code> | <code bash>(( variable = 2 + $autre_var * 5 ))</code> | ||
Ligne 1249: | Ligne 694: | ||
</code> | </code> | ||
| | ||
- | Comme vous l'avez sans doute remarqué, quand on appelle la fonction, on exécute simplement ce qu'on lui a défini au début, dans notre exemple, echo... et ls -l, on peut donc faire exécuter n'importe quoi à une fonction. | + | Comme vous l'avez sans doute remarqué, quand on appelle la fonction, on exécute simplement ce qu'on lui a défini au début, dans notre exemple, echo... et ls -l, on peut donc faire exécuter n'importe quoi à une fonction. |
Les fonctions peuvent être définies n'importe où dans le code du moment qu'elles sont définies avant d'être utilisées. Même si en bash les variables sont globales, il est possible de les déclarer comme locales au sein d'une fonction en la précédant du mot clé local: local ma_fonction . | Les fonctions peuvent être définies n'importe où dans le code du moment qu'elles sont définies avant d'être utilisées. Même si en bash les variables sont globales, il est possible de les déclarer comme locales au sein d'une fonction en la précédant du mot clé local: local ma_fonction . | ||
Ligne 1386: | Ligne 831: | ||
===== Exemples et exercices ===== | ===== Exemples et exercices ===== | ||
- | Comme indiqué dans la [[script_shell#liens|section liens]] de cette page, de très bon exemples et exercices illustrent le cours disponible sur cette page :\\ | + | Comme indiqué dans la [[script_shell#liens|section liens]] de cette page, de très bon exemples et exercices illustrent le cours disponible sur cette page : |
- | [[http://abs.traduc.org/abs-5.3-fr/apm.html|Guide avancé d'écriture des scripts Bash - Une exploration en profondeur de l'art de la programmation shell]] | + | [[https://abs.traduc.org/abs-5.3-fr/apm.html|Guide avancé d'écriture des scripts Bash - Une exploration en profondeur de l'art de la programmation shell]] |
Aux structures décrites ci-dessus, il est nécessaire, pour réaliser des scripts poussés, de connaître les commandes shell les plus usitées.\\ Vous en trouverez une présentation sur cette autre page du wiki : [[projets:ecole:scripting:initiation_au_shell]] . | Aux structures décrites ci-dessus, il est nécessaire, pour réaliser des scripts poussés, de connaître les commandes shell les plus usitées.\\ Vous en trouverez une présentation sur cette autre page du wiki : [[projets:ecole:scripting:initiation_au_shell]] . | ||
- | La programmation de script shell étant ouverte à tous, cela permet de bénéficier de nombreux scripts pour des applications très variées ; cependant, **la plupart sont proposés sans aucune garantie**.\\ | + | La programmation de script shell étant ouverte à tous, cela permet de bénéficier de nombreux scripts pour des applications très variées ; cependant, **la plupart sont proposés sans aucune garantie**. |
Vous pourrez trouver une liste de scripts pouvant servir d'exemples sur la page [[:scripts_utiles|scripts utiles]] du wiki. | Vous pourrez trouver une liste de scripts pouvant servir d'exemples sur la page [[:scripts_utiles|scripts utiles]] du wiki. | ||
- | Une fois vos armes faites, proposez vos contributions sur le topic du forum [[http://forum.ubuntu-fr.org/viewtopic.php?id=204074|[VOS SCRIPTS UTILES]]] et rajoutez un lien dans la page du wiki ci-dessus. | + | Une fois vos armes faites, proposez vos contributions sur le topic du forum [[https://forum.ubuntu-fr.org/viewtopic.php?id=204074|[VOS SCRIPTS UTILES]]] et rajoutez un lien dans la page du wiki ci-dessus. |
===== L'art d'écrire un script ===== | ===== L'art d'écrire un script ===== | ||
Ligne 1445: | Ligne 890: | ||
===== Liens ===== | ===== Liens ===== | ||
- | * (fr) http://marcg.developpez.com/ksh/ : Pour ceux qui souhaitent aller plus loin dans la conception de script shell. | + | * (fr) https://marcg.developpez.com/ksh/ : Pour ceux qui souhaitent aller plus loin dans la conception de script shell. |
- | * (fr) [[http://abs.traduc.org/abs-fr/|Guide avancé d'écriture des scripts Bash]] : Un très bon tutoriel concernant la réalisation du script shell. C'est l'un des plus complets et les mieux détaillés disponibles en français. Il contient également [[http://abs.traduc.org/abs-5.3-fr/apa.html|des exemples de script complets]], une [[http://abs.traduc.org/abs-5.3-fr/apb.html|carte de référence]] (variables, tests...). Ce site est un site qui vaut réellement le détour pour tous ceux qui cherchent à créer des scripts complets en utilisant au mieux les performances du shell. | + | * (fr) [[https://abs.traduc.org/abs-fr/|Guide avancé d'écriture des scripts Bash]] : Un très bon tutoriel concernant la réalisation du script shell. C'est l'un des plus complets et les mieux détaillés disponibles en français. Il contient également [[http://abs.traduc.org/abs-5.3-fr/apa.html|des exemples de script complets]], une [[http://abs.traduc.org/abs-5.3-fr/apb.html|carte de référence]] (variables, tests...). Ce site est un site qui vaut réellement le détour pour tous ceux qui cherchent à créer des scripts complets en utilisant au mieux les performances du shell. |
* (fr) https://openclassrooms.com/courses/reprenez-le-controle-a-l-aide-de-linux : Un tutoriel très complet pour linux qui comporte quelques parties sur la réalisation de scripts bash. | * (fr) https://openclassrooms.com/courses/reprenez-le-controle-a-l-aide-de-linux : Un tutoriel très complet pour linux qui comporte quelques parties sur la réalisation de scripts bash. | ||
* (en) [[http://www.ibm.com/developerworks/library/l-bash-parameters.html|Bash parameters and parameter expansions]]. En anglais mais contient de nombreux exemples concernant la gestion et l'analyse des paramètres. | * (en) [[http://www.ibm.com/developerworks/library/l-bash-parameters.html|Bash parameters and parameter expansions]]. En anglais mais contient de nombreux exemples concernant la gestion et l'analyse des paramètres. | ||
* (fr ) [[ftp://ftp-developpez.com/eric-sanchis/IntroProgBash.pdf|Introduction à Bash]] | * (fr ) [[ftp://ftp-developpez.com/eric-sanchis/IntroProgBash.pdf|Introduction à Bash]] | ||
- | * (fr ) [[http://www.scotchlinux.tuxfamily.org/]] exemples de scripts bash, quelques trucs utiles ( fonctions, fonctions comme paramètres... ) | + | * (fr ) [[http://www.scotchlinux.tuxfamily.org/]] exemples de scripts bash, quelques trucs utiles ( fonctions, fonctions comme paramètres... ) |
* (en) [[https://www.shellcheck.net/]] Permet de corriger la syntaxe du script (parenthèse oubliée, graphie incorrecte d'une commande, un "if" sans son "fi", un "while sans son "do" ou son "done", etc...). | * (en) [[https://www.shellcheck.net/]] Permet de corriger la syntaxe du script (parenthèse oubliée, graphie incorrecte d'une commande, un "if" sans son "fi", un "while sans son "do" ou son "done", etc...). | ||