Trieur à mobs [Vanilla] [CC] [Life is peripheral]

Voir le sujet précédent Voir le sujet suivant Aller en bas

Trieur à mobs [Vanilla] [CC] [Life is peripheral]

Message par skypop le Jeu 1 Sep - 3:01

Trieur à mobs [Vanilla] [CC] [Life is peripheral]

Ce tuto se divise en 3 parts, et consiste en l'adaptation d'un design fonctionnel en vanilla pour intégrer les fonctionnalités de computercraft, et en complément comment peaufiner son concept par l'intermédiaire d'interfaces du mod Life is peripheral.

Problématique :

Exploiter un spawner à zombies, et pouvoir en extraire les zombies villageois du reste.
Le trieur à mob doit pouvoir s'intégrer à une usine à zombie standard. A cet effet, le trieur sera raccordé à l'usine par l'intermédiaire de canaux et ascenseurs à eau.

Screenshots : Réseau de canaux et ascenseur à eau:




Spawner et chambre de génération

De tous les spawners, celui à zombie est probablement celui qui a la production la plus variée. En plus des zombies standards et des zombies villageois, il faut s'attendre à ce que le circuit reçoive des demi-portions, à savoir : mini zombies, ainsi que des mini zombies à cheval sur un poulet, et donc aussi des poulets. Ces demi portions ne sont à priori pas exploitables, et pourraient faire disfonctionner les canaux, ou s'accumuler et causer des lags. Ma solution à ça, est de les détruire dès que possible.
Ces 3 types de mobs ont la particularité de mesurer moins de 2 blocs de haut, c'est tout le problème, mais également la solution. Au bout du premier canal (celui qui extrait les mobs de la chambre de spawn, et au pied du premier ascenseur, il s'agit de construire un second canal mesurant 1,5 blocs de haut. Ainsi, les mobs mesurant 2 blocs ne pourront pas y rentrer et seront aspirés par l'ascenseur. Les mini zombies, les poulets, et les deux l'un sur l'autre, passeront par ce canal. Il suffit ensuite de les mener vers un grinder, ou simplement une fosse avec de la lave au fond.

Screenshots : Tri des demi-portions:




Voici un schéma de l'ensemble du dispositif.
Plan : Chambre de spawn, canal d'extraction, ascenseur à mob, tri des demi-portions:



NB : un spawner a un délai de production aléatoire entre 10 et 40 secondes. à chaque délai, s'il se trouve 6 mobs (ou plus) dans les environs proches du spawner, la génération est reportée au délai suivant. Pour être optimal, il s'agit de parvenir à éloigner suffisamment les mobs du spawner en moins de 10 secondes.
Cette zone s'étend à 4 blocs au dessus et au dessous du spawner, et 8 blocs autour du spawner sur le plan horizontal (voir schéma ci-dessous), c'est à dire un volume de 17x9x17 blocs ayant le spawner en son centre.
La solution la plus simple étant de creuser la chambre de spawn avec assez de profondeur pour faire tomber les mobs en dessous de cette zone? Assez profonde pour empêcher qu'un mob puisse faire entrer sa tête dans cette zone en sautant. (c'est à dire une profondeur d'au moins 8 blocs sous le spawner)
Si le spawner se situe trop bas pour permettre de creuser une chambre si profonde, il faut opter par une évacuation directe par un des côtés. Creuser la chambre d'une profondeur minimale pour qu'un mob ne puisse pas se cogner la tête sur le spawner en sautant. (c'est à dire une profondeur de 4 blocs sous le spawner) Normalement, si les courants d'eau au fond de la chambre, et l'entrée du canal d'extraction sont bien agencé, les mobs devraient être évacués assez vite pour ne pas ralentir la cadence du spawner.

Schéma : Les différents périmètres d'un spawner:



NB : Les spawners sont indépendants du mob-cap général (la limitation du nombre de mobs dans le monde). Si l'on extrait les mobs de la zone de modération, il faut s'assurer d'éviter de les accumuler en trop grand nombre. Le système d'extraction sert à court-circuiter le mob-cap propre au spawner (limite de 6 mobs dans la zone de modération). Ainsi le spawner est capable de produire des mobs jusqu'à ce que leur accumulation fasse planter le serveur.
L'enjeu d'une usine à mob est donc d'en générer un maximum, et d'en avoir un nombre minimum dans le système. Il s'agit donc de les traiter rapidement, et de les rediriger vers un grinder dès que possible.

Trier les mobs individuellement

Il est basé sur l'excellent design vanilla présenté par Monsieur Touf, dans cette vidéo :
https://youtu.be/fKSos00KWAY?t=7m36s

Il s'agit d'un trieur à villageois. Ils sont répartis dans des cellules individuelles. Chaque cellule est équipée d'un bouton en façade, qui enclenché vidange la cellule.
Il conviendrait tout à fait au tri de zombies, entre ceux qui pourraient être rendus à l'état de villageois, et le reste. L'idée est de conserver les fonctionnalités vanilla du design, mais de les coupler à une interface Computer Craft pour automatiser le traitement.

Adaptation du trieur

Après une série de test, j'en suis venu à modifier plusieurs éléments de ce design :

  • Supprimer la liane. Elle n'est pas indispensable, car la chute n'est pas assez importante pour représenter un risque mortel pour le mob. D'autre part, le ralentissement du mob par cette liane augment le risque que deux mobs tombent dans la même cellule. Le ralentissement risque aussi de provoquer des évaluations erronées par le programme de tri automatique.
  • Remplacer bloc situé sous les pieds du mob, par un bloc "plein". Du moins, un bloc qui permet de poser des rails dessus, et qui reste amovible pour un piston. Utiliser des rails et un minecart est un très bon moyen de déplacer un mob. Ainsi, si l'on souhaite extraire un mob du système pour le conserver en lieux sûr, il suffira de placer des rails sous ses pieds et pousser un minecart vers lui.
    Car je ne recommanderais pas de laisser un mob trop longtemps dans le système. On ne peut exclure une fausse manip, un glitch ou un bug du programme qui déclencherait la vidange de la cellule. Si l'on souhaite conserver durablement un mob, il vaut mieux l'extraire du système, sinon s'assurer qu'il soit verrouillé (par exemple, en retirant le piston).
  • Placer une redstone lamp derrière le mon. Cela permet d'éclairer suffisamment la cellule. La redstone adjacente est alimentée par défaut, donc la lampe restera allumée, et clignotera durant la vidange. Si la lampe est éteinte, cela signalera un défaut du circuit.
  • Combler les côtés de la cellule par du verre. Afin de garder en vue l'intégralité du dispositif. Il est très utile de pouvoir visualiser la fence gate située en haut de la cellule, pour vérifier son état ouvert ou fermé.
  • Pour la mémoire, il s'agit de deux droppers faisant face l'un vers l'autre. Elle définit l'état de la fence gate. Si l'item est dans le dropper de droite, la fence gate est alimentée, donc fermée. Dans le dropper de gauche, la fence gate n'est plus alimentée, donc ouverte (à priori, sauf si quelqu'un la fermé manuellement)
  • Le bouton en façade devrait être en pierre. Car un bouton en bois peut être déclenché si une flèche se plante dessus. Un bouton en pierre garanti que cette éventualité ne se produira pas.

Screenshots::









Ajout d'une interface Computer Craft

L'enjeu est de pouvoir contrôler les fonctionnalités d'origine du dispositif, sans interférer avec son interface vanilla.
En fin de compte il s'agit de deux actions à mener en même temps :
- désactiver le piston
- déclencher le dropper de droite (reset la mémoire, ouvre la fence gate)

Étant donné que l'interface vanilla repose sur un signal inversé, pour désactiver le piston j'ai opté pour la solution de couper son fil d'alimentation en poussant un bloc avec un second piston. Ce second piston sera alimenté par un simple computer, et le signal redstone sera également envoyé dans le dropper, et dans le bloc sur lequel est accroché une torche de redstone (pour s'assurer qu'elle soit éteinte quand le signal sera envoyé au dropper)
Il s'agit donc d'un sticky piston, au bout duquel sont collés un slime block est un bloc solide (capable de couper un signal redstone)
Les deux fours font offices de simples blocs, à ceci qu'il ne s'accrocheront pas au slime block durant ses mouvements.
Le repeater paramétré à 1 tick, sert principalement à ce que les deux fils de redstone restent parallèle (ne se connectent pas).
Sous le computer, prévoir un wired modem, ainsi que des câbles. Cela reliera en réseau les computers contrôlant chaque cellules. veillez juste à connecter chaque modem : clique droit dessus, la prise passe au rouge, une notification s'affiche dans le chat précisant le nom de périphérique et son état (connecté ou déconnecté). Songez à noter pour chaque computer son nom de périphérique (du style "computer_21"). Ce sera utile ensuite pour les contrôler individuellement depuis un ordinateur maître.

L'ordinateur maître

Une fois le trieur construit, il est d'ores et déjà opérationnel en pur vanilla et peut être commandé manuellement.
Une fois installé pour chaque cellule son interface Computer Craft, et que celles-ci sont reliées et connectés dans un wired network (réseau filaire), il s'agit de prolonger le fil de ce réseau vers l'emplacement de votre computer maître. C'est là où sera centralisé le programme et les opérations.
Cette installation, en plus du computer, comprend 3 périphériques :
  • Un Entity Detector (du mod Life is peripheral), qui servira à évaluer les mobs contenus dans chaque cellules
  • Un World Interface (du mod Life is peripheral), qui sera utile à fin de vérifications d'usage, sans avoir à recourir à des circuits de redstone supplémentaires.
  • Un Disk Drive (Computer Craft), comportant un Floppy Disk.

Pour ces 3 périphériques ainsi que le computer maître, veillez à les connecter à un wired modem, et les relier au réseau.

Contrôle des interfaces de chaque cellules, et Disk Drive

Il serait possible d'installer un programme sur chacun des ordinateurs, mais ça n'est pas nécessaire. Le rôle de chaque ordinateur composant l'interface de chaque cellule est le même :
Envoyer une impulsion de redstone pour vidanger la cellule.
Il s'agit grosso-modo d'une torche de redstone qu'on pourrait commander à distance depuis un autre ordinateur.

La solution que j'ai trouvé, est d'utiliser le même fichier comme programme pour chacun de ces computers. Et c'est possible, selon le principe qu'un programme nommé "startup" sur un disque est exécute en priorité sur un ordinateur dès son allumage. Comme le réseau comprend un Disk Drive, si le disque contenu détient un programme startup, tous les ordinateurs du réseau l'exécuteront au démarrage.
Précaution oblige, avant d'entamer ce programme, il faut faire en sorte que l'ordinateur maître du dispositif ne suive pas cette directive. Il faut qu'il n'exécute pas le programme startup du disk à son démarrage. Pour cela, exécutez cette commande :
Code:
set shell.allow_disk_startup false
Pour tous les autres ordinateurs du réseau, ce paramètre est à true (par défaut)

Enfin, le programme startup à enregistrer sur le disque est très simple :
Code:
shell.run("rs pulse top 1 1.6")
os.shutdown()
Exécuter le programme "redstone" (rs) pour emmètre une impulsion d'une période de 16 ticks sur le dessus du computer (période de 16 ticks : 8 ticks on, 8 ticks off).
redstone pulse <side> <count> <period>
Une fois que c'est fait, faire en sorte que l'ordinateur s'éteigne de lui même.

Ainsi, depuis l'ordinateur maître, il suffit d'utiliser l'API "peripheral", d'indiquer le nom de l'ordinateur dans le réseau et l'allumer :
Code:
peripheral.call("computer_21","turnOn")
peripheral.call(name, methodName, param1, param2, etc)

Bien que ce soit très simple, ça peut demander quelques précautions :

Si l'ordinateur est déjà allumé au moment où le maître lui ordonne de se déclencher, l'instruction serait sans effet.
On peut perfectionner ce principe, en faisant en sorte que l'ordinateur maître force le ré-allumage si nécessaire.
Code:
local function resetCell(computer_id)
   local computer = peripheral.wrap(computer_id)
   if computer.isOn() then
      computer.shutdown()
   end
   computer.turnOn()
end
L'argument "computer_id" de cette fonction est le nom de périphérique de l'ordinateur concerné (par exemple "computer_21")
Ce coups-ci, comme il y aura plusieurs recours à différentes méthodes, j'assigne le périphérique à la variable "computer" par l'intermédiaire de peripheral.wrap( name ).
Ensuite, si l'ordinateur est déjà allumé (isOn), on lui ordonne de s'éteindre immédiatement (shutdown). Il n'y a plus qu'à le rallumer (turnOn)

En l'état actuel, il y a un dernier souci. Si quelqu'un allume un des ordinateurs (simplement en cliquant dessus) cela lancerait le programme startup du disque, et donc la vidange de la cellule. Pour prévenir ce cas de figure, il s'agirait pour l'ordinateur de vérifier que l'allumage est un ordre du computer maître. Or les possibilité offertes par l'API peripheral sont plutôt limitées et ne permettraient pas d'assurer cette vérification. J'ai opté pour une solution simple, basée sur le World Interface. J'y reviendrais après avoir détaillé ce périphérique.

Vérifier des éléments du système avec le World Interface

Ce périphérique du mod Life is peripheral, permet de recueillir plusieurs informations globales du serveur, ainsi que des informations locales à l'environnement basé sur les coordonnées X,Y,Z.
Pour obtenir le détail des méthodes disponibles, vous pouvez passer par lua en ligne de commande (programme lua)
Assignez le périphérique connecté à une variable :
Code:
wi = peripheral.find("WorldInterface")
Ensuite, pour lister les méthodes :
Code:
wi.getMethods()
Pour plus de détail : (en argument passez le nom de la méthode dont vous voulez le détail)
Code:
wi.getMethods("getMethods")

Voici la liste des méthodes disponibles :

  • getBiome :
    Indique le type de biome local, sinon celui aux coordonnées x,y,z passées en argument. Exemple de retour : "Desert"
  • getWeather :
    Information sur la météo. Retourne "Rain" pour un temps pluvieux, "Thunder" en cas d'orages, ou "Clear" en cas de beau temps
  • getBlockInfos :
    Renvoi toutes les informations du bloc se trouvant aux coordonnées indiquées par les arguments X,Y,Z. Renvoi une table, et dans le détail, je vous recommande de vous référer à un wiki sérieusement documenté, notamment sur les data-values, data-tags, block-states etc. Pour ma part je me réfère au wiki http://minecraft.gamepedia.com/ qui est assez complet et précis (enfin il vaut mieux quand même toujours vérifier)
  • setListeningRange :
    Je suppose que c'est en lien avec l'event "sound_played"... Par défaut il est à 16.
  • getRealDate :
    Renvoi la date/heure du serveur (temps IRL), dans un format en anglais non standard. Exemple : "Wed Aug 31 18:29:29 CEST 2016". (Un format standard serait plutôt : "2016-08-31 18:29:29 CEST", enfin on peut toujours faire avec)
  • getPlayerList :
    [i]Renvoi la liste des joueurs connectés. C'est à dire, une table contenant chaque pseudo.
  • getMethods :
    [i]Affiche la liste des méthodes, et information au sujet de l'event "sound_played". Passez en argument le nom d'une méthode pour avoir tout le détail.



Dans le cas du trieur à mob, je n'utiliserai que la méthode getBlockInfos(). C'est une fonctionnalité assez ultime, comparable à turtle.inspect() sauf sur la portée et la quantité de données renvoyées. On obtient absolument tout le détail d'un bloc, où qu'il soit dans le monde. Bref c'est à la fois très cool et très cracké. Personnellement je trouverais plus raisonnable de réduire sa portée à 1 ou 2 chunk, mais trêve de hors-sujet.

Avec getBlockInfos, prenant comme argument les coordonnées du fence gate, on pourrait savoir si celle-ci est alimentée. On pourrait aussi le supposer en vérifiant la mémoire vanilla du dispositif (les deux droppers face à face), ou de la sortie du comparateur. En principe, quand une porte est alimentée en redstone elle s'ouvre, et se referme dès qu'on coupe l'alimentation. Ce serait fiable sur une porte en fer ou trap-door en fer, car on ne peut les manipuler manuellement.
L'intérêt de le vérifier indépendamment de l'état de la redstone, est de parer aux erreurs de manipulation. Vous pourriez avoir besoin de passer dans le canal et peut être manipuler un fence gate pour sortir, ou même je ne sais quel lag ou block update qui tourne de travers... Quand bien même, en recourant à getBlockInfos pour vérifier l'état de cette porte, le programme ne sera pas déstabilisé.

Voici un exemple de table retournée sur les coordonnées d'un fence gate :
Code:
{
  powerStrength = 0,
  chunkZ = 37,
  chunkX = -48,
  opacity = 0,
  lightLevel = 13,
  isChunkLoaded = true,
  isPowered = false,
  biome = "Desert",
  slipperiness = 0.6,
  metadata = 0,
  datatags = {},
  blockName = "minecraft:fence_gate",
  light = 0.61904764,
}

C'est là qu'il est utile de se référer à une bonne documentation. La table de cet exemple est assez courte, mais certains blocs peuvent révéler plus d'une centaine de données, parfois dans des tables imbriquées...
Voici une page détaillant les data-values des fence gates : http://minecraft.gamepedia.com/Fence_Gate#Data_values
On comprend donc que la propriété isPowered révèle si la porte est oui ou non alimentée par redstone.
Quant à savoir si la porte est ouverte ou fermée, indépendamment de l'alimentation de redstone, il faut évaluer la propriété "metadata". Celle-ci confond l'information sur l'orientation et le statut ouvert ou fermé. En fait les deux valeurs s'additionnent, donc si la valeur de metadata est supérieure ou égale à 4, à priori elle est ouverte, sinon elle est fermée.
On peut coder ça de cette façon :
Code:
function isGateOpen(x,y,z)
   blockDatas = wi.getGlockInfos(x,y,z)
   --Il vaut mieux s'assurer que le block est celui qu'on attend
   if blockDatas.blockName == "minecraft:fence_gate"
   or blockDatas.blockName == "minecraft:spruce_fence_gate"
   or blockDatas.blockName == "minecraft:birch_fence_gate"
   or blockDatas.blockName == "minecraft:jungle_fence_gate"
   or blockDatas.blockName == "minecraft:dark_oak_fence_gate"
   or blockDatas.blockName == "minecraft:acacia_fence_gate"
   then
      --On évalue le statut ouvert ou fermé
      --Renverra true si la porte est ouverte, sinon false
      return blockDatas.metadata >= 4
   else
      error(string.format([[Le bloc "%s" aux coordonnées x:%d, y:%d, z:%d n'est pas un fence gate.]], blockDatas.blockName, x, y, z),2)
      exit()
   end
end

Cette fonction sera utile pour vérifier si une cellule est ouverte ou fermée, en complément d'information en cas de présence ou non d'un mob ans la cellule, on pourrait évaluer le besoin de reset cette partie du système. C'est à dire, éviter qu'une cellule vide reste fermée, ce qui l'empêcherait de réceptionner un mob, et donc réduire la capacité de tri du système.

Enfin, un autre usage du World Interface, serait de permettre aux computers de s'assurer que leur commande de mise en route est demandée par l'ordinateur maître.
L'enjeu principal est de différencier le cas d'un allumage manuel (clic sur le computer), d'un allumage commandé. A cet effet, j'ai placé au dessus du computer maître une redstone lamp. Lorsque le maître commande un allumage, il allume au préalable cette lampe. L'autre ordinateur, avant de déclencher une impulsion de redstone, vérifiera si cette lampe est allumée. Si c'est bien le cas, l'impulsion sera lancée, sinon l'ordinateur s'éteint immédiatement.
Ce principe n'est pas complètement infaillible, mais bien assez efficace.

Ainsi je code la fonction qui commande l'allumage :
Code:
local function resetCell(computer_id)
   --allumage de la lampe
   rs.setOutput("top",true)
   local computer = peripheral.wrap(computer_id)
   --court délai pour laisser le temps à l'update de la redstone
   sleep(.1)
   if computer.isOn() then computer.shutdown() end
   computer.turnOn()
   --Rajout d'un délai, pour s'assurer que l'ordinateur ciblé s'est allumé
   while computer.isOn() do
      sleep(.1)
   end
   --Dernier délai pour s'assurer que le programme de l'ordinateur ciblé s'est terminé
   --et que l'ordinateur s'est éteint
   repeat
      sleep(.1)
   until not computer.isOn()
   --extinction de la lampe
   rs.setOutput("top",false)
end

Du côté du programme "startup" sur le disque :
Code:
--Assignation du périphérique
local wi = peripheral.find("WorldInterface")
-- Ici j'ai instruit les coordonnées de la lampe x:-759, y:61, z:603
local lamp = wi.getBlockInfos(-759,61,603)
if lamp.blockName=="minecraft:lit_redstone_lamp"
-- Vérification supplémentaire, si la lampe est bien alimentée par un courant de redstone
and lamp.isPowered then
   shell.run("rs pulse top 1 1.6")
end
os.shutdown()

NB: Une redstone lamp n'a pas le même blockName selon qu'elle soit allumée ou éteinte
Allumée, le blockName est "minecraft:lit_redstone_lamp", éteinte c'est "minecraft:redstone_lamp"
Réf: http://minecraft.gamepedia.com/Redstone_Lamp#Data_values

Désormais on a tout le nécessaire pour commander la vidange des cellules à distance, depuis l'ordinateur maître. Maintenant pour permettre l'automatisation du système, il s'agit de contrôler les mobs, lorsqu'ils se retrouvent dans les cellules.

Référence ment des cellules

Il s'agit de recenser et rassembler dans une table chaque noms de périphérique des ordinateurs, et les coordonnées X, Y et Z de la cellule qui en dépendent.
Pour cela je préfère noter ces coordonnées dans un fichier à part, car c'est plus facile à éditer qu'au milieu d'un programme. Cela prend la forme d'un API, ce qui rend la table très facile à charger ensuite par le programme principal.

Dans un fichier nommé "cell"
Code:
computer_2 = {x=-754,y=60,z=609}

computer_3 = {x=-751,y=60,z=609}

computer_4 = {x=-748,y=60,z=609}

computer_5 = {x=-745,y=60,z=609}

computer_1 = {x=-742,y=60,z=609}

"computer_1" à "computer_5" sont les noms de périphériques des ordinateurs. S'ils sont dans le désordre, c'est parce que je n'ai pas connecté leur modem dans le bon ordre. C'est pourquoi je vous recommande de les noter dès leur connexion, quitte à l'écrire sur une pancarte posée à côté.
Les coordonnées sont celle d'un bloc vide, celui juste au dessus de la trappe de vidange. Là où se trouveront les pieds du mob. Pour le vérifier facilement, appuyez sur la touche "F3" pour afficher tout les détails de minecraft. Parmi les lignes qui s'affichent en haut à gauche se trouvent plusieurs concernant les coordonnées. Visez avec votre curseur le bloc de la trappe, et référez vous à la ligne "Looking at:", notez ces coordonnée en ajoutant 1 à la coordonnée Y.

Pour charger ces coordonnées depuis le programme principal il suffit de faire :
Code:
os.loadAPI("/cell")
Pour les employer il suffit de faire :
Code:
cell.computer_1.x
cell.computer_1.y
cell.computer_1.z

Si vous codez directement sur Computer Craft, vous y serez aidé par l'auto-completion.

Pour parcourir l'ensemble de ces ordinateurs dans une boucle, il faudra recourir à for de cette manière :
Code:
local computer,pos
for computer,pos in pairs(cell) do
   --Nom du périphérique
   print(computer)
   --Coordonnées X Y Z
   print("X:"..pos.x)
   print("Y:"..pos.y)
   print("Z:"..pos.z)
end

Entity Detector

Il s'agit du second périphérique apporté par le mod Life is peripheral. Dans les grandes lignes, il permet d'obtenir des informations sur tout ce qui n'est pas un bloc. Notamment les mobs, mais pas seulement : http://minecraft.gamepedia.com/Entity#Types_of_entities
Enfin, dans le cas du trieur, il s'agira principalement d'analyser des zombies, et c'est là le coeur du programme.

Ce périphérique dispose de plusieurs méthodes que l'on peut lister par l'intermédiaire de getMethods (même principe de documentation que pour le World Interface)

  • getEntityList :
    Prend comme argument un rayon, et les coordonnées X, Y et Z de la zone à analyser. Elle renvoi une table dans laquelle vous trouverez un série de tables détaillant chaque entité trouvée dans le rayon du point visé. Par exemple, getEntityList(10, 23, 62, 25) vous obtiendrez la liste de toutes les entités présentes dans un rayon de 10 blocs autour des coordonnées X:23, Y:62, Z:25
  • getPlayerDetail :
    Si un joueur est connecté, vous n'aurez rien à lui demander, vous saurez déjà tout sur lui... Razz
  • [b]getMethods
  • :
    Même principe que celle du World Interface(/i]


Je vais donc recourir à getEntityList, afin de scanner individuellement chaque cellules.
L'idée est la suivante :

  • Si la cellule est vide, vérifier que le fence gate est bien ouvert pour accueillir le prochain mob
  • S'il y a un zombie, voir s'il peut être transformé en villageois
  • Si on ne peut rien faire de ce zombie, vidanger la cellule
  • S'il y a plus d'un mob dans la cellule, voir ce qu'on peut y faire...


Pour simplifier cette tâche, voici une fonction qui se chargera de traiter les données renvoyées par getEntityList.
NB: Si aune entité n'est détecté, le retour sera une table vide, il ne sera pas false, il ne sera pas nil
NB: Si une seule entité est détecté, le retour sera malgré tout une liste, une liste avec un seul élément.

Les mobs peuvent contenir une vaste quantité de données (plus d'une centaine), pour s'y retrouver et savoir trouver l'information qui intéresse, je vous recommande de vous référer à une bonne documentation. Réf: http://minecraft.gamepedia.com/Zombie#Data_values
Cependant il y a eu pas mal de changements depuis la version 1.10, et il s'en prépare d'autres pour la 1.11.
Le plus fiable est de faire un essai en ligne de commande (programme lua) et parcourir les données renvoyées, ou s'arranger pour enregistrer la table dans un fichier pour l'examiner plus facilement.

Voici un exemple de données renvoyées, testé sur un zombie villageois :
Code:
{
  {
    isInLava = false,
    lookX = -0.1989441,
    motionY = -0.0784,
    fireImmunity = false,
    yaw = 171.8619,
    datatags = {
      Dimension = 0,
      UUIDMost = 1642676981,
      HurtTime = 0,
      Leashed = 0,
      ConversionTime = -1,
      UUIDLeast = 471436284,
      AbsorptionAmount = 0,
      OnGround = 1,
      id = "Zombie",
      Air = 300,
      FallDistance = 0,
      DropChances = {},
      Health = 17,
      HealF = 17,
      Invulnerable = 0,
      Equipment = {
        {},
        {},
        {},
        {},
        {},
      },
      IsVillager = 1,
      Pos = {},
      CanBreakDoors = 1,
      HurtByTimestamp = 0,
      Fire = -1,
      CanPickUpLoot = 0,
      PersistenceRequired = 0,
      Attributes = {
        {
          Base = 20,
          Name = "generic.maxHealth",
        },
        {
          Name = "generic.knockbackResistance",
          Base = 0,
          Modifiers = {
            {
              Operation = 0,
              UUIDLeast = 1183418336,
              Amount = 0,
              UUIDMost = -1152825247,
              Name = "Random spawn bonus",
            },
          },
        },
        {
          Base = 0,
          Name = "generic.movementSpeed",
        },
        {
          Name = "generic.followRange",
          Base = 35,
          Modifiers = {
            {
              Operation = 1,
              UUIDLeast = 2007326096,
              Amount = 0,
              UUIDMost = -396997616,
              Name = "Random spawn bonus",
            },
          },
        },
        {
          Base = 3,
          Name = "generic.attackDamage",
        },
        {
          Base = 0,
          Name = "zombie.spawnReinforcements",
        },
      },
      PortalCooldown = 0,
      Rotation = {},
      DeathTime = 0,
      Motion = {},
    },
    distance = "0,84",
    outsideBorder = false,
    health = 17,
    pitch = 0,
    onFire = false,
    isInvisible = false,
    isRiding = false,
    onGround = true,
    explosionImmunity = false,
    name = "Zombie",
    isSprinting = false,
    chunkCoordY = 3,
    canBePushed = true,
    chunkCoordZ = 38,
    isInWater = false,
    motionZ = 0,
    canBePushedByWater = true,
    lookZ = 0.98001087,
    maxInPortalTime = 0,
    isSilent = false,
    isAlive = true,
    lookY = 0,
    isSneaking = false,
    type = "EntityZombie",
    portalCooldown = 300,
    motionX = 0,
    y = "60.0",
    x = "-753.3000000119209",
    UUID = "825f821f-61e9-42f5-926f-bd691c198bfc",
    customNameTag = "",
    z = "609.4636590586986",
    hasCustomName = false,
    headYaw = 731.4791,
    chunkCoordX = -48,
    isWet = false,
  },
}

C'est ainsi que j'ai pu trouver la propriété "isVillager" dans la table "datatags".
Pour savoir s'il s'agit d'un zombie, il y a plusieurs options : "name", "type", ou encore "id" dans "datatags". J'ai choisi de me baser sur "type"
Ainsi pour accéder à ces informations :
Code:
local isZombie = entityList[1].type == "EntityZombie"
local isVillager = entityList[1].datatags.isVillager == 1

Sachant lire ces deux informations, je peux développer la fonction qui évaluera les mobs dans chaque cellules :
Code:

ed = peripheral.find("EntityDetector")

local function getCellInfos(pos)
   local entityList = ed.getEntityList(1.5,pos.x,pos.y,pos.z)
   if not entityList or #entityList==0 then
      return false
   elseif #entityList>1 then
      return #entityList
   elseif entityList[1].type=="EntityZombie" then
      return 1, entityList[1].type, entityList[1].datatags.IsVillager
   else
      return 1, entityList[1].type
   end
end
J'ai défini le rayon à 1,5 bloc, car certaines fois les mobs rebondissent en tombant dans leur cellule. Quand ce rayon était à 1, les mobs qui rebondissaient passaient à travers le scan, et laissait croire que la cellule était vide et fermée (provoquant la vidange).

S'il aucune entité a été détectée, la fonction renvoi false
Si le compte de la table (#entityList) dépasse 1, c'est qu'il y a plus d'un mob. Je développerais cette partie plus tard. Dans ce cas la fonction renvoi le compte.
Sinon il y a un mob dans la cellule. Et si c'est un zombie, je renvoi le compte (1), le type d'entité, et l'état de du tag "isVillage"
S'il y a un mob qui n'est pas un zombie, je renvoi le compte (1) et le type d'entité.

Pour traiter chaque cellule l'une après l'autre (dans une boucle for) :
Code:
for computer,pos in pairs(cell) do
   local dump = false
   local _entity,_type,_isVillager = getCellInfos(pos)
   if not _entity then
      --Check if need reset
      dump = not isGateOpen(pos)
   elseif _entity>1 then
      --Trop de mobs
      dump = true
   elseif _type=="EntityZombie" then
      dump = not _isVillager
   else
      dump = true
   end
   if dump then
      resetCell(computer,pos)
   end
end

La variable "dump" me servira à évaluer la nécessité de vidanger la cellule.
Donc s'il n'y a pas d'entités détecté, dump prend la valeur retournée par la fonction isGateOpen(pos) [i](entre temps je l'ai remanié, elle tiens compte que le fence gate se situe aux même coordonnées X et Z, et Y+5)

Si la porte est fermée, isGateOpen() renvoi false. "not" inverse l'évaluation, et passera la variable "dump" à true. Une vidange sera donc déclenchée pour reset la cellule.
S'il y a plus d'un mob de détecté, la cellule est vidangée. Je ne cois pas qu'on puisse faire quelque chose de deux mobs superposés, donc autant vidanger et retenter le coups au tour suivant.
Si l'entité est un zombie, et que c'est un villageois, "dump" sera passé à false. Si c'est un zombie standard, elle est passée à true (donc vidange)
Si c'est un autre type d'entité, vidange.

Relativement efficace, néanmoins quelques soucis.

J'ai constaté que le cas de deux entités détectées pouvait se produire alors qu'il n'y avait qu'un seul mob dans la cellule. Alors, je m'étais rapproché de la cellule au plus près. La seconde entité compté était donc celle de mon avatar. J'ai donc revu la fonction getCellInfos() pour exclure ce cas de figure.
Également, il faut prendre en compte les entités de villageois. Qui une fois les zombies soignés apparaîtrons dans les cellules. Il s'agirait de ne pas les vidanger...

Bref, les grandes lignes sont posées. à priori le tutoriel pourrait s'arrêter là, même si le projet peut se poursuivre bien plus loin :
- Établir une "wanted list" des carrières des villageois (pour vidanger automatiquement les villageois dont on se désintéresse de leur business)
- Un algorithme d'évaluation des échanges, pour un tri approfondi pour ne conserver que le commerce le plus avantageux.
- Un affichage sur moniteur de l'état des cellules, signaler l'apparition d'un nouveau villageois, ou si toutes les cellules sont occupées
- Installer un retour dans l'espace AFK à proximité du spawner
- Un contrôle pour activer/désactiver le tri automatique, ou bien verrouiller une cellule pour éviter toute vidange accidentelle
Je développe un programme complet en parallèle, dont j'ai extrait quelques fonctions présentées ici à titre d'illustration, en version simplifiées.

Je ne sais pas si je poursuivrai ce tutoriel, ou si je publierai le code source final de mon programme.
Disons que j'attends de voir les retours, les question et les apports au topic.


Dernière édition par skypop le Ven 2 Sep - 1:26, édité 2 fois
avatar
skypop

Messages : 95
Date d'inscription : 25/07/2016

Revenir en haut Aller en bas

Re: Trieur à mobs [Vanilla] [CC] [Life is peripheral]

Message par Shiranuit le Jeu 1 Sep - 11:44

Merci pour ton tuto même si certaines données sont quelques peut erronée désormais suite au Patch côté serveur qui modifie certaines fonctions des périphériques et ce qu'elles retournent
avatar
Shiranuit

Messages : 164
Date d'inscription : 02/04/2016
Age : 17

Revenir en haut Aller en bas

Re: Trieur à mobs [Vanilla] [CC] [Life is peripheral]

Message par skypop le Jeu 1 Sep - 13:38

Tu veux dire par là qu'il y a des différences entre la version sur le serveur, et la version sur une partie solo ?
Ou simplement d'une mise à jour du mod ?
Enfin si tu peux préciser quelles données / fonctions sot concernées, et comment, ça aiderait bien. à fortiori, la seule source d'information que j'ai pour l'instant, c'est les retours de getMethods(), et la méthode empirique.

J'y serais de toute façon confronté, étant donné que la finalité est d'installer ce trieur sur le serveur. Je tâcherais de corriger ponctuellement.
avatar
skypop

Messages : 95
Date d'inscription : 25/07/2016

Revenir en haut Aller en bas

Re: Trieur à mobs [Vanilla] [CC] [Life is peripheral]

Message par Kuruyia le Jeu 1 Sep - 17:51

Superbe tutoriel, très bien présenté, écrit et surtout complet, bravo à toi !
avatar
Kuruyia

Messages : 83
Date d'inscription : 10/04/2016
Age : 16
Localisation : Glitch City

Revenir en haut Aller en bas

Re: Trieur à mobs [Vanilla] [CC] [Life is peripheral]

Message par skypop le Ven 2 Sep - 1:39

Merci arc13, et pourtant je suis à 66% de la complétion que j'espérais à la base.

Je viens de tenter d'éditer une petite faute de frappe, et k@*§#!! de forum m'a coupé la moitié du post... Rolling Eyes
Heureusement, que j'ai une copie du document d'origine.
Du coups, pour les compléments, ça se fera dans de nouveaux posts. C'est dommage, ça perd en continuité, mais j'ai pas envie de rager contre un site web.

Bref, premier complément :

Sur le serveur, les données de la méthode getBlockInfos du World Interface sont réduites à l'essentiel.
Une méthode est rajoutée : getBlockDatatags, même arguments, qui renvoi tout le reste des données.
La table qui est retournée en partie solo, est donc partagée et réparties sur deux méthodes sur le serveur.

Même principe pour la méthode getEntityList du Entity Detector, la quantité de données est réduites.
Pour avoir le même retour qu'en partie solo, il faut recourir à la méthode getEntityListAdvanced (mêmes arguments)
avatar
skypop

Messages : 95
Date d'inscription : 25/07/2016

Revenir en haut Aller en bas

Re: Trieur à mobs [Vanilla] [CC] [Life is peripheral]

Message par Contenu sponsorisé


Contenu sponsorisé


Revenir en haut Aller en bas

Voir le sujet précédent Voir le sujet suivant Revenir en haut


 
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum