Costa Bruno
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Arc Attorney ![](https://i.imgur.com/844K9B7.png) <div align="center">Costa Bruno, Izzo Valentino et Lopes Da Silva Diogo - Le Callennec Benoît, Rizzotti Aïcha, Senn Julien</div> <div align="center">P2, HE-Arc, 15.06.2021</div> <div style="page-break-after: always;"></div> ![](https://i.imgur.com/7oCy6SH.jpg) ## Résumé Ce document regroupe toutes les informations sur les procédés effectués pour créer le jeu Arc Attorney en Java. De l'introduction à la conclusion, chaque paragraphe permettra de mieux comprendre son fonctionnement. Arc Attorney est un jeu multijoueur de débat sous forme de chat. Un cas est présenté aux joueurs et le but de la partie est de prouver l'innocence ou la culpabilité d'un suspect. L'avocat de la défense devra proposer des arguments solides tout en déjouant ceux du procureur pour ainsi sauver l'âme en peine. Quant au procureur, il devra faire son possible pour accuser et mettre en péril le sort du suspect. Finalement, c'est le juge qui possédera le dernier mot. Il donnera un verdict et dépendant de celui-ci, un des deux joueurs remportera la partie. L'application fonctionne correctement, la connexion au serveur se fait aussi bien localement qu'à distance et une partie peut être déroulée sans problème. Le [cahier des charges](https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/1-Cahier-des-charges) est ainsi concrètement rempli. Des fonctionnalités de gestion de preuves et de questionnement de témoins sont encore nécessaires à être implémentées. <div style="page-break-after: always;"></div> [TOC] <div style="page-break-after: always;"></div> ## 1. Introduction Ce rapport permet au lecteur de comprendre comment le jeu Arc Attorney a été conçu de la première ligne de code, au dernier placement de bouton. ### 1.1 Mise en contexte Ce projet a été réalisé dans le cadre du module de deuxième année, le P2. La durée du développement s'est déroulée sur le second semestre de 2020-2021. L'application sera entièrement conçue avec le framework LibGDX en Java. ### 1.2 Le sujet du projet Le projet est très grandement inspiré du jeu de Nintendo "Ace Attorney" qui suit le héros, Phoenix avocat de la défense, dans un système juridique américain. Ce jeu est totalement solo et nous avons voulu ajouter une dimension multijoueur tout en respectant le jeu original via les animations, les objections, les personnages, etc. ### 1.3 Les détails du jeu Le but du jeu est d'incarner le juge, le procureur ou l'avocat de la défense. - Le juge est l'arbitre du jeu, c'est lui qu'il faut convaincre et qui va décider qui remporte la partie. - Le procureur doit prouver que l'accusé est bien coupable, démonter les arguments de l'avocat de la défense afin d'avoir un verdict coupable. - L'avocat de la défense, lui, doit prouver que l'accusé est innocent, il doit apporter des arguments allant dans son sens et selon le témoignage de l'accusé et/ou des potentiels témoins. ### 1.4 Cahier des charges En tant que propre client, le cahier des charges a été rédigé par le groupe et validé par les enseignants responsables du module. #### 1.4.1 Objectifs du cahier des charges L'objectif principal du [cahier des charges](https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/1-Cahier-des-charges) est d'apprendre à utiliser des interfaces graphiques via Swing dans un premier temps bien que, par la suite, le groupe a décidé de migrer vers LibGDX. De plus, l'utilisation d'un schéma client-serveur en Java via les sockets a été ajoutée aux objectifs. Quant aux objectifs du projet, ils sont mis en formes dans le [cahier des charges](https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/1-Cahier-des-charges) mis en annexe. ### 1.5 Le plan d'attaque Le projet est d'abord passé par une phase d'analyse où plusieurs réflexions sur l'architecture et la forme du projet ont été mises en place. Puis une phase de planification afin de savoir quelles seraient les tâches liées au projet et combien de temps elles prendraient, pour ensuite schématiser son fonctionnement et ainsi comprendre comment l'application fonctionnera lors de la [conception](https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/4-Conception). Finalement, la phase de réalisation du dit projet. ## 2. Analyse L'analyse du projet est la première phase décisive dans la création d'une application. C'est ici qu'il va falloir établir les "règles" du logiciel, comment il va fonctionner et ce qu'il faudra implémenter. ### 2.1 Discussions entre les membres du groupe La première étape était de mettre à jour la compréhension du projet au niveau du groupe, pour que chaque membre sache comment le jeu allait être et quelles seraient ses fonctionnalités. Un "brainstorming" général a alors été fait pour noter les différentes visions et idées sur le déroulement du programme. Comme Arc Attorney est un jeu du genre visual novel, de multiples hypothèses ont donc été créées sur le suivi du programme en s'inspirant de la célèbre série de jeu vidéo "Ace Attorney", mais en multijoueur. L'objectif du jeu n'est pas de faire en sorte que le joueur suive une histoire de façon linéaire, mais que les joueurs créent eux-mêmes leur histoire avec leur ingéniosité et créativité. ### 2.2 Création de croquis des interfaces graphiques Pour mieux se représenter les besoins de l'utilisateur quant à l'interface graphique, des maquettes basiques ont été conçues. De celles-ci en est ressortie l'idée principale que le logiciel devait avoir 3 fenêtres principales. Quelques autres popups ont été designés pour servir à l'utilisateur. #### 2.2.1 Le menu de connexion ![](https://i.imgur.com/QS7mlRk.png) <div align="center">Fig.1 Écran de connexion</div> #### 2.2.2 Le lobby ![](https://i.imgur.com/MKWoQQM.png) <div align="center">Fig.2 Lobby</div> #### 2.2.3 Le jeu côté avocat ![](https://i.imgur.com/4EWKg0p.png) <div align="center">Fig.3 Interface de jeu Avocat</div> #### 2.2.4 Le jeu côté juge ![](https://i.imgur.com/EjzRgUB.png) <div align="center">Fig.4 Interface de jeu Juge</div> ### 2.3 Scénarii Pour but d'approfondir la recherche, quelques scénarii ont été simulés sur papier et un spécifiquement est ressorti comme une partie "normale" #### 2.3.1 Scénario I - Un utilisateur crée la partie et les deux autres la rejoignent. - Ils arrivent sur un lobby - L'hôte peut changer le cas courant - Tant que les utilisateurs n'ont pas tous choisi leur rôle et qu'ils n'ont pas tous mis prêt, l'hôte ne peut pas lancer la partie. - Une fois la partie lancée, le juge commence avec un petit résumé de l'affaire pour que tout le monde soit au courant. - C'est aux avocats de prendre les devants et d'émettre leurs arguments quant à l'innocence ou la culpabilité de l'accusé. - Pour soutenir ses arguments, un avocat peut présenter une preuve. - Une preuve est récoltée sous forme de texte pour appuyer ses dires. - Un joueur peut à tout moment consulter l'historique des messages. - Un avocat peut émettre une objection pour instantanément contredire ce que dit son opposant. - Le juge peut accepter ou non l'objection. - Si acceptée, l'objection se joue - Si refusée, rien ne se passe. - Une fois convaincu, le juge décide de qui gagne et finit la partie. - Les joueurs peuvent décider de quitter le jeu ou recommencer - S’ils décident de recommencer alors ils retournent sur l'interface de connexion. ### 2.4 Spécifications Une phase très importante de l'analyse est la [spécification](https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/2-Sp%C3%A9cification). C'est ici que le groupe va documenter tous les détails du projet et, de manière générale, ses fonctionnalités. #### 2.4.1 Personnages Le noyau du programme se base sur ses joueurs et donc, ses personnages. L'avocat de la défense, le procureur et le juge forment le système du jeu. En effet, comme dit précédemment, leur ingéniosité et leur créativité vont faire avancer la partie. Sans ces éléments, le jeu n'existe pas. ##### 2.4.1.1 Avocats Les avocats doivent prouver l'innocence ou la culpabilité de l'accusé et pour se faire possède la capacité d'interagir avec les autres joueurs via discussions, objections et mentions de preuves. ##### 2.4.1.2 Juge Le juge doit arbitrer la partie, c'est lui que les autres joueurs doivent convaincre pour résoudre l'affaire. Il peut donner un verdict une fois convaincu pour mettre fin à la partie et ainsi condamner ou innocenter l'accusé. ##### 2.4.1.3 Témoins Il y a des personnages supplémentaires qui ne sont pas jouables qui serait le client ainsi que des témoins. Ils ont des informations clefs pour réussir à démêler le vrai du faux et aident à orienter les joueurs. #### 2.4.2 Tâches bonus Le groupe avait également prévu une tâche bonus, à faire si le temps le permet. Le choix s'est porté sur l'utilisation d'un arbre de décision pour les témoins, qui permettrait une interaction plus complexe entre les joueurs et les personnages non joueurs. ## 3. Planification La planification est là pour séparer le projet en tâches et les placer dans une échelle temporelle. Ce procédé est fait pour estimer quelles seraient les tâches prenant plus ou moins de temps. Ainsi, une fois préparées dans un échéancier, un procédé, une marche à suivre se démontre en suivant un ordre de priorité des tâches. Le support choisi pour cet échéancier est le [GANTT](https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/blob/master/Documentation/Gantt_-_Arc_Attorney.xlsx). De plus, pour ce qui est de la communication, le groupe a décidé d'utiliser Discord. ### 3.1 GANTT, Gitlab & Discord Le [GANTT](https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/blob/master/Documentation/Gantt_-_Arc_Attorney.xlsx) a été un des premiers documents qui a été préparé. En effet, il nous permet d'organiser les différentes opérations à faire et de nous rendre compte de la taille de notre projet et du nombre de tâches qui doivent être réalisées. De plus, nous pouvons y entrer un suivi des heures passées sur les différentes tâches du projet. Sur la forge de l'école, les fonctionnalités des *issues*, *boards* et *milestones* ont été exploitées. Grâce à celles-ci, nous avons pu mettre en évidence les tâches finies, en cours et à faire de manière ergonomique. Puisque nous voulions un projet centré en un endroit, l'emploi du Wiki de gitlab a été utilisé pour rédiger la documentation, les journaux de travail ainsi que les différents documents liés au projet. Finalement, Discord était et est toujours l'outil de communication préféré du groupe. Un serveur a donc été créé pour accueillir nos conversations et les différents partages de fichiers. ### 3.2 Retard Au cours du projet, le groupe a été pris de court par la quantité de travail à accomplir dans l'ensemble des cours du deuxième semestre de deuxième année. Ce qui a fait que le groupe a dans un premier temps vu la charge de travail un peu à la baisse dans le projet. Bien que tout n'a pas pu être fait, le groupe a réussi à rattraper beaucoup de retard en augmentant le rythme de travail sur le P2. ### 3.3 Améliorations du planning C'est après une analyse du planning que nous avons trouvé plusieurs facteurs permettant d'améliorer une future gestion de projet : - Meilleure analyse des tâches à effectuer et du temps qu'elles demandent pour les finir - Meilleure répartition des tâches ## 4. Conception Après la planification, il faut maintenant définir en détail les bases de l'architecture du logiciel ainsi que ses différentes classes et fonctions. Une maquette pour une interface graphique, un schéma de classe pour une fonctionnalité, tout est revisité pour avoir un plan d'attaque concret et simple d'exécution. ### 4.1 Langages et/ou frameworks utilisés Le langage à utiliser est le Java et comme le groupe a eu des soucis avec Swing, il a été décidé d'utiliser le framework LibGDX qui permet de créer des interfaces graphiques plus souples. #### 4.1.1 Outils externes et librairies Les outils utilisés sont : * Texture packer, un outil utilisé pour créer des packs de texture, il a servi pour créer les fichiers utiles à l'animation. * Gson, une librairie qui permet de transformer une classe Java sous forme json et vice-versa. ### 4.2 Diagramme UML L'architecture du programme est la partie la plus importante de la [conception](https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/4-Conception), c'est ici que les premières bases de code se mettent en place. Nous avons donc créé un diagramme UML représentant la structure générale du logiciel. Ce diagramme est séparé en deux parties, la première est l'ensemble de la partie graphique et la seconde regroupe les classes de la partie modèle. Ces deux parties sont liées à la classe principale "ArcAttorney" qui représente le jeu en lui-même. #### 4.2.1 Partie graphique Dans le package *screens* se trouvent toutes les classes représentant les interfaces graphiques et leurs fonctionnalités. Chaque étape graphique du jeu est une classe qui hérite de Screen afin de pouvoir afficher des menus, des boutons et autres. 1. ConnectionScreen * La ConnectionScreen est la fenêtre au début du jeu, c'est sur celle-ci que l'on va créer une partie ou se connecter. 2. LobbyScreen * Le LobbyScreen est une fenêtre de lobby où les joueurs pourront changer de rôle avant le début de la partie. De plus, l'hôte pourra changer l'affaire choisie. 3. EndScreen * C'est le menu de fin, une fois que la partie est terminée, c'est ici que l'utilisateur arrivera. 4. Interface de jeu 1. GameUiJudge * Si l'utilisateur possède le rôle de juge, son interface sera une GameUiJudge qui possède des actions uniques au juge. 2. GameUiAP * Si l'utilisateur possède soit le rôle d'avocat de la défense, soit le rôle de procureur, son interface sera une GameUiAP. Les deux types d'interfaces de jeu possèdent des éléments tels que : * MessageArea : Une zone qui va afficher les messages des joueurs de manière spécifique. * DisplayHolder : La zone qui contient l'animation du personnage. * History : Une fenêtre de dialogue qui affiche l'historique des messages envoyés depuis le début de la partie. #### 4.2.2 Partie modèle La partie modèle, elle, possède l'architecture logique du projet, là où les calculs et les lignes de code importantes du programme vont se faire. 1. Player * C'est la classe du client. C'est le Player qui va se connecter au serveur, communiquer et recevoir des données vers et en provenance de celui-ci. 2. PlayerThread * La classe PlayerThread va servir d'écoute pour le joueur, c'est elle qui va recevoir les informations provenant du serveur. 3. Server * C'est la classe du serveur. C'est lui qui va retransmettre aux clients les informations qu'il a reçues. 4. ServerThread * Comme pour le PlayerThread, c'est une classe qui va servir d'écoute pour le serveur. Toutes les informations provenant des clients passent par ici. 5. Role * Une énumération qui définit le rôle d'un joueur. 6. MessageListener * Un listener qui va réagir à chaque fois qu'un joueur reçoit un message. 7. Message * La classe Message est la classe qui contient les informations qui vont être envoyées aux autres clients. 8. MessageType * Une énumération qui définit le type principal du message. Si c'est un message de modification de données du lobby ou un message de discussion du chat. 9. SubMessageType * Cette énumération est utilisée pour définir un sous-type aux deux autres types principaux. Par exemple, un sous-type indiquant un changement de rôle lors de la phase de lobby. 10. Case * Elle va contenir les informations concernant l'affaire sur laquelle les joueurs vont débattre. ### 4.3 Architecture réseau Comme l'application se base entièrement sur un serveur et des clients, l'architecture de ce système est ainsi le fondement du bon fonctionnement du jeu. Le groupe étant inexpérimenté, des recherches ont en premier lieu été faites et c'est après avoir trouvé et comparé diverses façons de faire que cette solution a été choisie. #### 4.3.1 Serveur broadcast Le jeu a besoin d'un serveur procurant uniquement une fonctionnalité de redirection de messages en broadcast. À chaque message reçu de la part d'un client, il va le rediriger à tous ses clients. Son fonctionnement est simple, le serveur possède deux threads : 1. Un de connexion, qui va écouter à chaque instant si un client tente de se connecter au serveur, et ce jusqu'à la mort du serveur. 2. Un autre d'écoute, qui lui va écouter les données provenant des clients pour ensuite les retransmettre. Ce thread d'écoute va être une instance d'une classe nommée ServerThread #### 4.3.2 Client Le client va être le joueur se connectant au serveur. Il va posséder les éléments nécessaires pour participer au jeu ainsi que les fonctions et caractéristiques lui permettant la connexion et la communication avec le serveur. Il ne possède qu'un seul thread d'écoute du nom de PlayerThread, c'est lui qui va récupérer les informations provenant du serveur pour ensuite les traiter. C'est par l'interface graphique que le client va envoyer des données au serveur, en envoyant un message ou en se mettant prêt lors de la phase de lobby par exemple. #### 4.3.3 Communication Pour simplifier la communication, du texte simple ne suffit pas. La classe message a alors été créée pour ainsi contenir toutes les informations nécessaires. Le contenu d'un message, le rôle de l'expéditeur ou encore sa position dans le lobby seront ainsi envoyés de via cet objet qui lui-même sera transformé sous forme json. ## 5. Réalisation C'est ici que la création et l'implémentation du projet, de l'architecture réseau aux fonctionnalités du jeu, sont expliquées en détail. ### 5.1 Architecture réseau Comme dit précédemment, l'architecture réseau est la base du programme. C'est par ses classes que toutes les autres vont passer pour communiquer et produire des changements dans l'interface graphique et dans le déroulement du jeu. La technologie des sockets a été choisie pour créer et gérer cettedite architecture. Ainsi, c'est dans cette section que son fonctionnement sera expliqué et détaillé. #### 5.1.1 Serveur broadcast ##### 5.1.1.1 Server Le serveur va en tout premier lieu créer un "ServerSocket", c'est le serveur qui va accueillir les connexions et les requêtes provenant des sockets clients. Le serveur possède plusieurs valeurs et caractéristiques importantes : tout d'abord un tableau contenant trois threads d'écoute "ServerThread" qui représentent les clients, un entier indiquant le nombre de clients connectés et finalement un tableau regroupant toutes les informations des clients une fois connectés au lobby. De plus, toutes les communications passent par des I/O tels qu'un BufferedReader et un PrintWriter. Une fois créé, il va lancer le thread d'écoute de connexion pour permettre à chaque nouveau socket client de correctement se lier au socket serveur. ```java= // Server creation ... try { System.out.println("Liaison au port " + port + ", veuillez patienter ..."); // Launch the server to the specified port this.serverSocket = new ServerSocket(port); System.out.println("Serveur lancé : " + serverSocket); // Start the connection thread start(); } catch (IOException ioe) { System.out.println("Ne peut pas se lier au port " + port + " : " + ioe.getMessage()); ... ``` À la création de l'objet Server, le ServerSocket est créé et le thread de connexion est également lancé. ```java= public void start() { if (this.connectionThread == null) { this.connectionThread = new Thread(new Runnable() { @Override public void run() { while (connectionThread != null && connectionBoolean.get()) { try { System.out.println("En attente d'un client ... "); // Wait for connections addThread(serverSocket.accept()); } catch (IOException ioe) { System.out.println("Le serveur n'a pas pu accepter la connexion : " + ioe); stop(); } } } }); this.connectionThread.start(); } } ``` Ici, le thread va continuellement attendre des connexions via la fonction bloquante "serverSocket.accept()". Dès qu'une connexion se fait, la fonction va retourner le socket client pour le traiter dans la méthode "addThread()" qui, elle, va créer un thread d'écoute, l'ajouter au tableau de client et traiter les différentes informations importantes. ```java= private void addThread(Socket socket) { if (clientCount < clientsTab.length) { System.out.println("Client accepté : " + socket); clientsTab[clientCount] = new ServerThread(socket, this, clientCount); try { clientsTab[clientCount].open(); clientsTab[clientCount].start(); clientCount++; } catch (IOException ioe) { System.out.println("Erreur lors de l'ouverture du thread"); } } else { System.out.println("Client refusé : taille maximum atteinte - " + clientCount); } } ``` Finalement, le serveur possède une fonction de traitement et de *broadcast* de données reçues. Cette fonction va d'abord vérifier la donnée reçue avant de la rediriger. En effet, si le message indique : - Que le client quitte le jeu. - Le serveur doit gérer la déconnexion de celui-ci. - Change le sous-type du message pour indiquer aux autres joueurs qu'un client quitte le jeu. - Que c'est un changement dans le lobby. - Le serveur doit gérer ces changements. Pour ensuite rediriger ce message à tous les clients connectés. ```java= if(data.getsType() == SubMessageType.QUITTING) { clientsTab[findClient(id)].send(gson.toJson(data)); // If the client want to disconnect, send the quitting data to him remove(id); // Set the message data as -> someone is quitting data = new Message(null, id, data); data.setType(SubMessageType.SOMEONE_IS_QUITTING); } else if (data.getmType() == MessageType.LOBBY) { // Add informations to the array so the next client to connect can retrieve it lobbyData[id] = data; if (data.getsType() == SubMessageType.ENTERING_LOBBY) { // If the client is entering the lobby, it's collecting the data in the serverto refresh its UI data = new Message(gson.toJson(this.lobbyData), id, data); } else if(data.getsType() != SubMessageType.AFFAIR_CHANGE) { // Send the changes to the other clients data = new Message(null, id, data); } } // Serialize the data received and send it to the clients for (int i = 0; i < clientCount; i++) { clientsTab[i].send(gson.toJson(data)); } ``` ##### 5.1.1.2 ServerThread Le ServerThread est l'oreille du serveur, c'est elle qui va lui donner les informations entendues. À sa création, il reçoit un socket client qui est finalement le client auquel ce thread est lié. Ce thread possède deux fonctions importantes : 1. Sa fonction *run* qui tourne tant que le serveur est vivant et récupère les informations provenant du client. ```java= try { // Listen data from the client and give it to the server so it can broadcast it mainServer.handle(this.id, input.readLine()); } catch (IOException ioe) { System.out.println(this.id + " n'arrive pas à lire le message " + ioe.getMessage()); mainServer.remove(this.id); stopThread(); } ``` 2. La fonction *send* qui permet d'envoyer une donnée au client. ```java= try { output.println(msg); } catch (Exception ioe) { System.out.println(this.id + " n'arrive pas à envoyer de message " + ioe.getMessage()); mainServer.remove(this.id); } ``` #### 5.1.2 Client ##### 5.1.2.1 Player Le client possède un socket lié au serveur, un thread d'écoute PlayerThread ainsi qu'une liste d'abonnés "Listener" pour ce qui est de la communication. Comme autres attributs, il possède un pseudo et un rôle. À sa création, le socket est initialisé et essaie de se connecter au serveur via son adresse IP et son port pour finalement ouvrir ses flux d'écoute et d'envoi de messages. ```java= try { // Connect to the server this.socket = new Socket(serverAddress, serverPort); System.out.println("Connecté : " + socket); // Open the Output to the server and create the listening server open(); } catch (UnknownHostException uhe) { System.out.println("Hôte inconnu ou inatteignable : " + uhe.getMessage()); throw(uhe); } catch (IOException ioe) { System.out.println("Problème de connexion : " + ioe.getMessage()); throw(ioe); } ``` Lors de l'ouverture des flux, le PlayerThread est initialisé et lancé pour écouter les informations provenant du serveur. Mais c'est via deux fonctions principales que le client va communiquer : 1. La fonction *handle*, qui permet de traiter les données reçues par le serveur en appelant les différents abonnés *listener* ```java= // Receive the data from the server public void handle(String msg) { // Deserialize the data received Gson gson = new Gson(); Message data = gson.fromJson(msg, Message.class); if (data.getsType() == SubMessageType.QUITTING) { System.out.println("Déconnexion du client : " + this.pseudo); stop(); } else { // Send the data to all the "listeners" triggerUiChanges(data); } } ``` 2. La fonction *send* qui, elle, permet d'envoyer un message au serveur et ainsi à tous les autres clients. ```java= public void send(Message message) { Gson gson = new Gson(); String data = gson.toJson(message); // Send the data to the server output.println(data); } ``` ##### 5.1.2.2 PlayerThread Le PlayerThread fonctionne exactement comme le ServerThread, c'est un thread d'écoute qui va récupérer les informations provenant d'une source pour ensuite les envoyer à un élément qui va traiter cesdites informations. Dans ce cas-ci, c'est du serveur que proviennent les informations et c'est le client qui va les traiter. ##### 5.1.2.3 MessageListener L'interface MessageListener est utilisée pour déclencher un événement à chaque fois que sa fonction *messageReceived()* est appelée. Le Player possède une liste d'abonnés qui vont recevoir cet événement à chaque fois que la fonction *triggerUiChanges()* est appelée, cette fonction va contacter tous les abonnés et va lancer la fonction *messageReceived()* qui va transmettre le message reçu. ```java= // When a message is received, the ui changes accordingly private synchronized void triggerUiChanges(Message data) { for (MessageListener msgListener : this.listMessageListener) { msgListener.messageReceived(data); } } ``` Cette fonction, *triggerUiChanges()*, est appelée lorsque le client reçoit un message, ainsi, à chaque fois que le client reçoit une nouvelle information, les abonnés recevront également ce message pour modifier l'UI ou certaines données selon sa nature. Par exemple, lorsque l'UI abonnée est le lobby, certaines modifications vont être faites pour les différents clients. #### 5.1.3 Message et types de message Message est la classe "conteneur" qui va être envoyée aux autres clients. Elle contient : * Le pseudo de l'expéditeur * Le contenu du message * La preuve mise dans le message * Le rôle de l'expéditeur * Le type du message * Le sous-type du message * La position du client dans le lobby ##### 5.1.3.1 Type de message Le message possède deux types, un type lobby qui définit que le message sera lié à la gestion du lobby (connexion, modification, lancement de partie) et le type chat qui définit le fait que le message fera partie du jeu, lorsque les clients discutent entre eux. ##### 5.1.3.2 Sous-type de message Il existe plusieurs sous-types utilisés pour différents cas : * NORMAL => indiquant que c'est un simple message * OBJECTION => indiquant que le message est une objection * VERDICT => un message de verdict, qui indique la fin de partie * REFUSE_OBJECTION => indiquant que l'objection est refusée * ACCEPT_OBJECTION => indiquant que l'objection est acceptée * ROLE_CHANGE => un changement de rôle dans le lobby * READY => un joueur est prêt dans le lobby * NOT_READY => un joueur n'est pas prêt dans le lobby * ENTERING_LOBBY => un client entre dans le lobby * AFFAIR_CHANGE => l'affaire a été changée par l'hôte de la partie * LAUNCHING_GAME => l'hôte de la partie lance le jeu * QUITTING => un client souhaite quitter la partie * SOMEONE_IS_QUITTING => indique qu'un autre client a quitté la partie ### 5.2 Fenêtre de connexion Cette interface permet à l'utilisateur de créer ou rejoindre une partie d'Arc Attorney. Il a aussi accès à la fenêtre de dialogue "À propos". #### 5.2.1 Démarrage d'une partie Pour les joueurs qui veulent rejoindre une partie, ils doivent, comme l'hôte, donner leur pseudo et l'adresse IP de l'hôte. Pour créer la partie, l'option de création de partie doit être cocher par l'utilisateur et le bouton "Rejoindre" se change en "Créer une partie", ce qui va aussi retirer le champ d'entrée de l'adresse IP. ##### 5.2.1.1 Rejoindre une partie Si l'utilisateur rejoint une partie, un object client sera créé et la variable "server" sera mise à *null*. C'est ici que le client se connectera au serveur et fera le changement : Menu de connexion ==> Lobby ```java= // Create the client try { game.server = null; game.client = new Player(textFieldIp.getText(), 5000, textFieldPseudo.getText(), Role.UNROLLED); game.lobbyScreen.setHostRole(false); game.lobbyScreen.listenerControl(); game.client.send(new Message(game.client.getPseudo(), null, null, game.client.getRole(), MessageType.LOBBY, SubMessageType.ENTERING_LOBBY)); // Go to the lobby game.setScreen(game.lobbyScreen); } catch (UnknownHostException ioe) { labelError.setText("L'hote est introuvable."); } catch (IOException ioe) { labelError.setText("Un probleme de connexion est apparu."); } ``` ##### 5.2.1.1 Créer une partie Si l'utilisateur créer une partie, la variable "server" sera un objet Server et un client s'y connectera. De plus, le changement d'interface se fera de la même manière qu'avec un client rejoignant le serveur. ```java= game.server = new Server(5000); try { game.client = new Player("127.0.0.1", 5000, textFieldPseudo.getText(), Role.UNROLLED); game.lobbyScreen.setHostRole(true); game.lobbyScreen.listenerControl(); game.client.send(new Message(game.client.getPseudo(), null, null, game.client.getRole(), MessageType.LOBBY, SubMessageType.ENTERING_LOBBY)); // Go to the lobby game.setScreen(game.lobbyScreen); } catch (UnknownHostException ioe) { labelError.setText("L'hote est introuvable."); } catch (IOException ioe) { labelError.setText("Un probleme de connexion est apparu."); } ``` #### 5.2.2 Erreur(s) Des messages d'erreurs peuvent apparaître à l'écran si les joueurs oublient de renseigner leur pseudo ou si les joueurs tentent de rejoindre une partie inexistante. #### 5.2.3 À Propos Dans cette fenêtre de connexion, les joueurs ont aussi accès à une fenêtre de dialogue "À propos" qui est accessible via un bouton sur la fenêtre. On retrouve les informations utiles pour connaître le nom de l'école et les noms des créateurs. ### 5.3 Table Pour positionner les éléments sur une *Screen* libGDX, comme des *labels*, des *textfield* ou des boutons. On peut utiliser la classe *Table* de la même libraire. Cette *Table* est comme un tableau. On va pouvoir ajouter des éléments ligne par ligne. On peut choisir aussi des marges entre chaque ligne, le nombre de colonnes que doit prendre un élément, la largeur et hauteur. Voici un échantillon de code d'une disposition d'une *Table*. ```java= ... table.add(this.imageLogo).colspan(2).padBottom(30).width(100).height(100); table.row(); table.add(this.labelPSeudo).padBottom(30); table.add(this.textFieldPseudo).padBottom(30); table.row(); ... ``` ### 5.4 Skin Les *skins* dans libGDX sont des apparences qu'on peut donner à nos éléments graphiques. On peut soit créer nos propres *skins* qui sont des fichiers "JSON" et "atlas" ou alors utiliser ceux que propose [libGDX](https://github.com/czyzby/gdx-skins). Le projet utilise le *skin* par défaut de libDGX. ### 5.5 Fenêtre de lobby C'est ici que les joueurs vont pouvoir attendre la configuration de la partie et les autres joueurs. L'hôte de la partie peut sélectionner l'affaire qui va être jouée. Les autres joueurs peuvent sélectionner leur rôle via une *combobox* avec tous les rôles et se mettre prêts via une *checkbox*. Les éléments graphiques de la fenêtre sont disposés via une *Table* comme pour la fenêtre de [connexion](#53-Table). #### 5.5.1 Gestion des clients dans le lobby Chaque fois qu'un client entre dans le lobby, ses informations (son pseudo, sa position dans le lobby et son rôle) sont mises à jour dans le serveur. De même lorsqu'il modifie son rôle ou lorsqu'il change son état en prêt ou non-prêt. Ainsi, lorsqu'une nouvelle personne entre, les informations concernant les autres joueurs sont directement mises à jour. De plus, à chaque changement, un message est envoyé aux autres clients pour qu'ils mettent à jour l'interface de leur côté. #### 5.5.2 Erreur(s) L'hôte de la partie va avoir des erreurs affichées sur son écran s'il veut démarrer la partie alors que les joueurs ne sont pas tous prêts et que les rôles ne sont pas répartis correctement. ### 5.6 Case La classe *Case* permet de créer une affaire scénarisée pour le jeu. Chaque *Case* doit avoir une image correspondante à la tête de l'accusé, une description qui explique pourquoi la personne est jugée, un résumé que le juge va prononcer en début de partie et un index qui sert d'*id* pour différencier les affaires. La description va être utilisée dans la [fenêtre de lobby](#55-Fenêtre-de-lobby) pour que les joueurs puissent prendre connaissance de l'affaire et pourquoi la personne est accusée. ### 5.7 Holder Le *holder* va contenir les différentes animations et positions de ces dernières. Ces animations sont récupérées depuis des atlas. #### 5.7.1 Texture et Atlas Le texture packer est un exécutable utilisé par LibGDX pour créer des atlas. Il crée un *texture sheet* de toutes les images et dans un fichier texte (en .atlas) il va indiquer à l'animation quelle image prendre dans quel ordre et où elle se trouve dans le PNG qui pack toutes les images. ### 5.8 MessageArea Le *MessageArea* est une classe qui va simplement écrire un caractère tous les ticks du timer pour créer une animation comparable à celle du jeu Ace Attorney. De plus la classe va aussi jouer un son "blip" pour ajouter un petit effet. ### 5.9 GameUI C'est la classe principale du jeu (GameUIAP pour les avocats et GameUIJudge pour le juge). Elle contient une image pour représenter la court avec par-dessus un batch qui dessinera l'animation contenue dans le Holder. #### 5.9.1 TextArea Le TextArea dans lequel le joueur peut écrire, a des fonctionnalités limitées pour éviter de casser l'UI. 100 caractères maximum et impossible de faire de retour à la ligne. Lorsque la touche *enter* est pressée, le message est envoyé en enlevant le retour à la ligne. Lorsque le joueur n'a pas le focus sur le TextArea il peut quand même envoyer le message via la touche *enter*. #### 5.9.2 Bouton Send La touche *enter* va activer le bouton pour envoyer le message. Ce que ce bouton fait c'est qu'il récupère le texte du textArea, le clear et envoie le texte récupéré au serveur en indiquant son pseudo, son rôle, etc. #### 5.9.3 Bouton Objection - GameUIAP Ce bouton fait le même travail que le send à la différence qu'il ne peut être utilisé que toutes les 30 secondes, car lui peut être utilisé même quand un message est encore en train de s'afficher. #### 5.9.4 Bouton Verdict - GameUIJudge Lorsque ce bouton est pressé, une fenêtre de dialogue arrive avec un choix pour le verdict. Une fois que le juge a choisi, un message est envoyé au serveur indiquant un verdict. #### 5.9.4 Réception de messages Quand un message est reçu, on va vérifier s'il est bien de type chat. Si c'est le cas on vérifie si c'est un message normal, objection ou un verdict. - Dans les deux premiers cas, le bouton send sera désactivé le temps que le message finisse de s'afficher pour éviter le spam. - Les deux afficheront l'animation du joueur correspondant. - L'objection aura une animation supplémentaire pour montrer que c'est une objection. - Lorsque c'est un verdict, on vérifie le résultat (true ou false) et en fonction on affiche le EndScreen avec le texte de qui a gagné. - On peut aussi recevoir un message indiquant qu'un joueur a quitté, si c'est le cas on ferme l'application. ### 5.10 Historique des messages La classe "History" permet de créer et d'afficher une fenêtre de dialogue qui va afficher l'historique des messages envoyés par les joueurs durant la partie. Cette fenêtre est ouvrable via un bouton à droite de leur écran. Elle est utile pour les utilisateurs dans le cas où ils aimeraient revoir d'anciens messages pour se rappeler des dires d'un joueur. #### 5.10.1 Création Cette fenêtre de dialogue hérite de la classe "Dialog" de libGDX. Elle est *movable*, ce qui veut dire que l'utilisateur peut la déplacer. Elle n'est pas modale, le joueur peut continuer d'utiliser les autres fonctionnalités de l'application. #### 5.10.2 Ouverture Quand l'utilisateur clique sur le bouton historique, il faut modifier le paramètre "Gdx.input.setInputProcessor(stageDialog)". Avec en paramètre le "stage" correspondant à la dialogue, car sinon l'utilisateur ne pourra pas la fermer avec un clic sur le bouton retour. #### 5.10.3 Ajout des messages L'ajout de message se fait via une méthode qui est appelée par la GUI qui prend en argument un message. Avec ce message, la méthode crée un *label* qui va être sauvegardé à la fin d'une *Queue List*. Ensuite, la fenêtre de dialogue affiche son message et fait défiler pour que l'utilisateur voie le dernier message. ### 5.11 Fenêtre de fin de partie Quand le juge aura rendu son verdict, tous les joueurs seront redirigés sur la fenêtre de fin de partie. Cela va informer les joueurs du choix du juge en affichant si c'est le procureur ou l'avocat de la défense qui a gagné. Chaque joueur a la possibilité de quitter l'application ou de recommencer une partie qui les redirige directement sur la fenêtre de connexion. ## 6. Tests L'application a été testée par un membre de l'équipe. Ces tests permettent de savoir si le jeu est stable ou non et de trouver des bugs. Par manque de temps, ces bugs ne pourront pas être résolus, c'est pourquoi uniquement des hypothèses sur leurs réparations seront rédigées. * Bugs trouvés 1. Si une application hôte plante ou se ferme mal. Le serveur local tourne toujours et avec une autre instance on peut rejoindre une partie alors que l'application hôte est fermée. Il est nécessaire de kill l'application dans le gestionnaire des tâches windows. 2. Si les joueurs décident d'envoyer chacun une pléthore de messages, il se peut qu'une des applications aille planter ou alors que l'un des clients ne puisse plus rien faire. 3. Le raccourci *enter* n'est pas désactivé lors d'une réception de message et peut donc être spammé * Hypothèse 1. Lors d'un crash de l'application, le serveur et le client ne sont pas tués. C'est pourquoi ils tournent toujours en tant que processus de fond. Pour remédier à ce problème, il faudrait appeler la mort du serveur et des clients lorsque l'application se fait fermer de manière inopinée. 2. Lors d'un envoi trop régulier de données, le serveur n'arrive plus à gérer le broadcast et se ferme de force. Ce qui peut causer une fermeture d'application ou un stop des actions requérant le serveur. Pour régler ce problème, il faudrait mettre en place un système de buffer (BlockingQueue par exemple) afin de mieux traiter l'afflux de message. Dans le cas où on ne cherche pas à créer des bugs ou à en trouver, l'application permet de jouer au jeu de façon normale sans problème. ## 7. Résultats Dans ce projet, le [cahier des charges](https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/1-Cahier-des-charges) à été en majeure partie respecté, même si des points importants dans un déroulement normal et propre du jeu manquent. Notamment les preuves qui n'ont pas été implémentées ainsi que les interactions avec témoins / client. Le refus ou la validation de l'objection par le juge ne l'ont également pas été. Au-delà des points mentionnés plus haut, l'ajout de l'arbre de choix serait un atout majeur qui rendrait l'interaction et l'avancement dans l'affaire beaucoup plus dynamique. De plus, la résolution de bug et l'optimisation des interfaces graphiques et des phases de jeu rendraient l'expérience de jeu plus agréable. ### 7.1 Résumé des objectifs du module 1. Création d'une application graphique via Java - Le jeu Arc Attorney est une application graphique simple et fonctionnelle bien que des améliorations sont les bienvenues. L'objectif est pratiquement atteint. 2. Gérer un projet à plusieurs (planning, suivi, analyse ...) - Malgré un problème de retard, le planning et les différentes tâches de la gestion du projet ont été gérés au mieux possible. 3. Rendre un programme stable - L'application est stable, mais possède un seul bug capable de faire planter l'application. Cependant, ce bug est difficile à déclencher lors d'une partie normale. ### 7.2 Résumé des objectifs du projet 1. Créer des interfaces agréables à utiliser avec LibGDX, car Swing est trop rigide. - L'objectif est presque atteint, car l'interface du jeu n'est pas très belle et souple. 2. Jeu en multijoueur avec serveur. - Objectif rempli, 3 joueurs participent à ce jeu. 3. Trois rôles différents avec chacun leur propre objectif. - Objectif rempli, le juge, l'avocat de la défense et le procureur sont implémentés. 4. Jeu interactif avec une histoire de base développée par les joueurs. - Objectif rempli, une histoire de base est donnée et les joueurs doivent d'eux-mêmes développer le cours du procès. 5. Interactions avec des témoins / client pour aider les joueurs dans leur avancée. - Objectif non atteint, la gestion de témoins n'a pas été implémentée. 6. Objection des avocats pour dynamiser le jeu. - Objectif rempli, les avocats peuvent interrompre n'importe qui en émettant une objection. 7. Système de preuve pour que les joueurs puissent appuyer leurs propos. - Objectif non atteint, il n'y a pas d'implémentation de preuves. 8. Historique pour que les joueurs puissent relire des événements passés. - Objectif rempli, les joueurs peuvent appuyer sur un bouton pour voir l'historique des messages ## 8. Conclusion Pour conclure ce projet P2 de deuxième semestre, nous sommes, malgré les problèmes de planning, arrivés à remplir une majorité du [cahier des charges](https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/1-Cahier-des-charges). Le jeu Arc Attorney est stable bien que des bugs restent présents. Malgré une optimisation imparfaite et que certains éléments qui apporteraient une meilleure jouabilité pour les joueurs manquent, le jeu reste totalement jouable et terminable. Ceci dit, les objectifs du cours ont, eux, été majoritairement remplis. ## 9. Bibliographie [1] Catalin Munteanu, 25.02.18, https://www.catalinmunteanu.com/design-custom-dialog-libgdx/ [2] Creating a simple Chat Client/Server Solution, wachsmut, http://pirate.shu.edu/~wachsmut/Teaching/CSAS2214/Virtual/Lectures/chat-client-server.html [3] Socket java : Créer une application de chat Client/Serveur, Codeur Java, http://www.codeurjava.com/2014/11/java-socket-clientserveur-mini-chat.html [4] Socket programming in Java: A tutorial, Steven Haines, 08.01.2020, https://www.infoworld.com/article/2853780/socket-programming-for-scalable-systems.html [5] Multi-Client Chat Server using Sockets and Threads in Java, Amit Gyawali, 18.11.20, https://gyawaliamit.medium.com/multi-client-chat-server-using-sockets-and-threads-in-java-2d0b64cad4a7 [6] Mans Gezelius, https://golen.nu/portal/phoenix/ [7] Phoenix_Wright GIF, aceattorney.fandom, https://aceattorney.fandom.com/wiki/Phoenix_Wright_-SpriteGallery?file=PhoenixandDocument2.gif [8] Juge GIF, aceattorney.fandom, https://aceattorney.fandom.com/wiki/Judge_-SpriteGallery?file=JudgeStern2.gif [9] Miles Edgeworth GIF, aceattorney.fandom, https://aceattorney.fandom.com/wiki/Miles_Edgeworth_-_Sprite_Gallery?file=AA_Miles_Edgeworth_Court_Smirking_1.gif [10] Objection GIF, Medli, http://fanaru.com/ace-attorney/image/210447/co-coffe-gif/ [11] Hammer Logo, FreePick, FlatIcon.com, https://www.freepik.com ## 10. Annexes [1] Cahier des charges, https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/1-Cahier-des-charges [2] Spécification, https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/2-Sp%C3%A9cification [3] Conception, https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/4-Conception [4] Journal de travail, Diogo Lopes Da Silva, https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/Journal-de-travail-Bruno-Costa [5] Journal de travail, Costa Bruno, https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/Journal-de-travail-Bruno-Costa [5] Journal de travail, Izzo Valentino, https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/wikis/Journal-de-travail-Valentino-Izzo [6] GANTT, Arc Attorney, https://gitlab-etu.ing.he-arc.ch/isc/2021/projet-p2-java/arc-attorney/arc-attorney/-/blob/master/Documentation/Gantt_-_Arc_Attorney.xlsx

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully