• TutorielJava
  • [JAVA] Parseur socket orienté objet (bonnes pratiques)

Pour faire un bon parseur (et plus généralement un bon logiciel) il faut bien découpler toutes les parties logicielles en composants simples et réutilisables.

L'approche orientée objet est actuellement la meilleure que je connaisse (oubliez AutoIt) !

Pour faire quelque chose de propre :

- on créé une interface définissant le contrat à remplir,

- on implémente cette interface avec une (ou plusieurs classes) abstraites pour définir le code commun à toutes les implémentations,

- on hérite de la classe abstraite avec une classe concrète pour implémenter le spécifique (ce que l'on veut faire)

Donc, pour ce tuto ce sera Java, vous pourrez porter ça sous C# ou encore plus facilement sous J#, les amateurs de C++ peuvent aussi profiter du voyage.

Je pars du principe que vous :

- connaissez un peu le jargon technique,

- savez vous connecter, lire et écrire le flux réseau.

De toutes façon, vous poserez des questions et la connexion ça ressemble à ce pseudo code:


Socket socket = new Socket("xxx.xxx.xxx.xxx", 443);
while(isRunning) {
	String line = socket.readLineTerminéeParNull();
	faireQuelqueChoseAvec(line);
}

A plusieurs reprises, je parle de ligne : cette ligne est la chaine de caractères reçue du serveur et qui se termine par un caractère NULL ('\0' ou chr$(0) ou toute autre notation spécifique à votre language).

Le traitement se fait toujours ligne par ligne.

Nous allons donc commencer par créer une classe ProtocolProcessor contenant une methode process() qui va s'occuper de l'aiguillage.

Par exemple :


public class ProtocolProcessor {
	/**
	 * Pré-traitement de la ligne reçue du serveur
	 * @param sData : la ligne reçue
	 * @throws IOException si une exception d'entrée/sortie à lieu (coupure réseau par exemple)
	 */
	public void process(String sData) throws IOException {
		// Premier caractère de la ligne
		char type = sData.charAt(0);
		// Deuxième caractère de la ligne
		char action = sData.charAt(1);
		boolean hasError = false;
		try {
			// Troisième caractère de la ligne
			hasError = sData.charAt(2) == 'E';
		} catch (StringIndexOutOfBoundsException e) {
		}
		// Traitement par lui même
		this.postProcess(type, action, hasError, sData);
	}
}

Comme on vient de le voir la méthode process() est appelée pour chaque ligne et elle sous-traite le fonctionnement réel à postProcess() dont voici le début.


	/**
	 * Traitement de la ligne reçue du serveur
	 * @param sType : Type de message
	 * @param sAction : Sous type ou action
	 * @param bError : si vrai ; le message contient une erreur. 
	 * @param sData ligne reçue du serveur dans son intégralité (sauf le NULL terminateur)
	 * @throws IOException si une exception d'entrée/sortie à lieu (coupure réseau par exemple)
	 */
	private void postProcess(char sType, char sAction, boolean bError, String sData) throws IOException {
		switch (sType) {
		case 'H': {
			switch (sAction) {
			case 'C': {
				this.aks.onHelloConnectionServer(sData.substring(2));
				break;
			}
			case 'G': {
				this.aks.onHelloGameServer(sData.substring(2));
				break;
			}
			default: {
				this.aks.disconnect(false, true);
			}
			} // End of switch
			break;
		}
		//Les autres cas sont traités ci-après, pour la lisibilité, je les ai supprimés
		}
	}

Comme on peut le voir, les messages sont constitués de 2 ou 3 lettres au minimum et on aiguille une première fois sur la première lettre, puis un deuxième fois sur

la 2è lettre.

Les messages sont regroupés de manière logique par la première lettre.

Dans ce bout de code je viens d'intégrer un nouvel objet que j'ai nommé aks (this.aks est en Java une notation qui permet de signifier que aks est une instance

de notre classe ProtocolProcessor). Cet objet est passé au constructeur de la classe. J'ai nommé la classe de cet objet ProcessorListener car elle va "écouter"

le parseur et effectuer les actions correspondantes à chaque cas (ceux qui connaissent les parseurs XML de type SAX ne seront pas dépaysés).

Ci-dessous la déclaration de la classe ProtocolProcessor avec le constructeur prenant en argument le ProcessorListener.


public class ProtocolProcessor {

	private ProcessorListener aks;
	private Logger logger = Logger.getLogger(ProtocolProcessor.class);

	public ProtocolProcessor(ProcessorListener oAKS) {
		aks = oAKS;
	}

La classe complete

Cliquez pour révéler Cliquez pour masquer


package dofus.aks;

import java.io.IOException;

import org.apache.log4j.Logger;

import dofus.aks.processor.listeners.ProcessorListener;

public class ProtocolProcessor {

	private ProcessorListener aks;
	private Logger logger = Logger.getLogger(ProtocolProcessor.class);

	public ProtocolProcessor(ProcessorListener oAKS/* , int oAPI */) {
		aks = oAKS;
	}

	/**
	 * Pré-traitement de la ligne reçue du serveur
	 * @param sData : la ligne reçue
	 * @throws IOException si une exception d'entrée/sortie à lieu (coupure réseau par exemple)
	 */
	public void process(String sData) throws IOException {
		char type = sData.charAt(0);
		char action = sData.charAt(1);
		boolean hasError = false;
		try {
			hasError = sData.charAt(2) == 'E';
		} catch (StringIndexOutOfBoundsException e) {
		}
		this.postProcess(type, action, hasError, sData);
	}

	/**
	 * Traitement de la ligne reçue du serveur
	 * @param sType : Type de message
	 * @param sAction : Sous type ou action
	 * @param bError : si vrai ; le message contient une erreur. 
	 * @param sData ligne reçue du serveur dans son intégralité (sauf le NULL terminateur)
	 * @throws IOException si une exception d'entrée/sortie à lieu (coupure réseau par exemple)
	 */
	private void postProcess(char sType, char sAction, boolean bError, String sData) throws IOException {
		switch (sType) {
		case 'H': {
			switch (sAction) {
			case 'C': {
				this.aks.onHelloConnectionServer(sData.substring(2));
				break;
			}
			case 'G': {
				this.aks.onHelloGameServer(sData.substring(2));
				break;
			}
			default: {
				this.aks.disconnect(false, true);
			}
			} // End of switch
			break;
		}
		case 'p': {
			this.aks.onPong();
			break;
		}
		case 'q': {
			this.aks.onQuickPong();
			break;
		}
		case 'r': {
			this.aks.onResponsePong(sData.substring(5));
			break;
		}
		case 'M': {
			this.aks.onServerMessage(sData.substring(1));
			break;
		}
		case 'k': {
			this.aks.onServerWillDisconnect();
			break;
		}
		case 'B': {
			switch (sAction) {
			case 'N': {
				return;
			}
			case 'A': {
				switch (sData.charAt(2)) {
				case 'T': {
					this.aks.getBasics().onAuthorizedCommand(true, sData.substring(3));
					break;
				}
				case 'L': {
					this.aks.getBasics().onAuthorizedLine(sData.substring(3));
					break;
				}
				case 'P': {
					this.aks.getBasics().onAuthorizedCommandPrompt(sData.substring(3));
					break;
				}
				case 'C': {
					this.aks.getBasics().onAuthorizedCommandClear();
					break;
				}
				case 'E': {
					this.aks.getBasics().onAuthorizedCommand(false);
					break;
				}
				case 'I': {
					if (sData.charAt(3) != 'O') {
						break;
					} // end if
					this.aks.getBasics().onAuthorizedInterfaceOpen(sData.substring(4));
					// TODO que veut dire ce double break en AS ?
					// break;
					this.aks.getBasics().onAuthorizedInterfaceClose(sData.substring(4));
					break;
				}
				} // End of switch
				break;
			}
			case 'T': {
				this.aks.getBasics().onReferenceTime(sData.substring(2));
				break;
			}
			case 'D': {
				this.aks.getBasics().onDate(sData.substring(2));
				break;
			}
			case 'W': {
				this.aks.getBasics().onWhoIs(!bError, sData.substring(3));
				break;
			}
			case 'P': {
				this.aks.getBasics().onSubscriberRestriction(sData.substring(2));
				break;
			}
			case 'C': {
				this.aks.getBasics().onFileCheck(sData.substring(2));
				break;
			}
			case 'p': {
				this.aks.getBasics().onAveragePing(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'A': {
			switch (sAction) {
			case 'c': {
				this.aks.getAccount().onCommunity(sData.substring(2));
				break;
			}
			case 'd': {
				this.aks.getAccount().onDofusPseudo(sData.substring(2));
				break;
			}
			case 'l': {
				this.aks.getAccount().onLogin(!bError, sData.substring(3));
				break;
			}
			case 'L': {
				this.aks.getAccount().onCharactersList(!bError, sData.substring(3));
				break;
			}
			case 'x': {
				this.aks.getAccount().onServersList(!bError, sData.substring(3));
				break;
			}
			case 'A': {
				this.aks.getAccount().onCharacterAdd(!bError, sData.substring(3));
				break;
			}
			case 'T': {
				this.aks.getAccount().onTicketResponse(!bError, sData.substring(3));
				break;
			}
			case 'X': {
				this.aks.getAccount().onSelectServer(!bError, true, sData.substring(3));
				break;
			}
			case 'Y': {
				this.aks.getAccount().onSelectServer(!bError, false, sData.substring(3));
				break;
			}
			case 'S': {
				this.aks.getAccount().onCharacterSelected(!bError, sData.substring(4));
				break;
			}
			case 's': {
				this.aks.getAccount().onStats(sData.substring(2));
				break;
			}
			case 'N': {
				this.aks.getAccount().onNewLevel(sData.substring(2));
				break;
			}
			case 'R': {
				this.aks.getAccount().onRestrictions(sData.substring(2));
				break;
			}
			case 'H': {
				this.aks.getAccount().onHosts(sData.substring(2));
				break;
			}
			case 'r': {
				this.aks.getAccount().onRescue(!bError);
				break;
			}
			case 'g': {
				this.aks.getAccount().onGiftsList(sData.substring(2));
				break;
			}
			case 'G': {
				this.aks.getAccount().onGiftStored(!bError);
				break;
			}
			case 'q': {
				this.aks.getAccount().onQueue(sData.substring(2));
				break;
			}
			case 'f': {
				this.aks.getAccount().onNewQueue(sData.substring(2));
				break;
			}
			case 'V': {
				this.aks.getAccount().onRegionalVersion(sData.substring(2));
				break;
			}
			case 'P': {
				this.aks.getAccount().onCharacterNameGenerated(!bError, sData.substring(3));
				break;
			}
			case 'K': {
				this.aks.getAccount().onKey(sData.substring(2));
				break;
			}
			case 'Q': {
				this.aks.getAccount().onSecretQuestion(sData.substring(2));
				break;
			}
			case 'D': {
				this.aks.getAccount().onCharacterDelete(!bError, sData.substring(3));
				break;
			}
			case 'M': {
				switch (sData.charAt(2)) {
				case '?': {
					this.aks.getAccount().onCharactersMigrationAskConfirm(sData.substring(3));
					break;
				}
				default: {
					this.aks.getAccount().onCharactersList(!bError, sData.substring(3), true);
					break;
				}
				} // End of switch
				break;
			}
			case 'F': {
				this.aks.getAccount().onFriendServerList(sData.substring(2));
				break;
			}
			case 'm': {
//				if (!CONFIG.isStreaming) {
//					this.aks.getAccount().onMiniClipInfo();
//				} else {
					int value;
					try {
						value = Integer.parseInt("" + sData.charAt(2), 10);
					} catch (Exception e) {
						value = 3;
					}
					logger.warn("GoToCongratulation : " + value + " Not implemented");
//				}
				break;
			}
			} // End of switch
			break;
		}
		case 'G': {
			switch (sAction) {
			case 'C': {
				this.aks.getGame().onCreate(!bError, sData.substring(4));
				break;
			}
			case 'J': {
				this.aks.getGame().onJoin(sData.substring(3));
				break;
			}
			case 'P': {
				this.aks.getGame().onPositionStart(sData.substring(2));
				break;
			}
			case 'R': {
				this.aks.getGame().onReady(sData.substring(2));
				break;
			}
			case 'S': {
				this.aks.getGame().onStartToPlay();
				break;
			}
			case 'E': {
				this.aks.getGame().onEnd(sData.substring(2));
				break;
			}
			case 'M': {
				this.aks.getGame().onMovement(sData.substring(3));
				break;
			}
			case 'c': {
				this.aks.getGame().onChallenge(sData.substring(2));
				break;
			}
			case 't': {
				this.aks.getGame().onTeam(sData.substring(2));
				break;
			}
			case 'V': {
				this.aks.getGame().onLeave(true, sData.substring(2));
				break;
			}
			case 'f': {
				this.aks.getGame().onFlag(sData.substring(2));
				break;
			}
			case 'I': {
				switch (sData.charAt(2)) {
				case 'C': {
					this.aks.getGame().onPlayersCoordinates(sData.substring(4));
					break;
				}
				case 'E': {
					this.aks.getGame().onEffect(sData.substring(3));
					break;
				}
				case 'e': {
					this.aks.getGame().onClearAllEffect(sData.substring(3));
					break;
				}
				case 'P': {
					this.aks.getGame().onPVP(sData.substring(3), false);
					break;
				}
				} // End of switch
				break;
			}
			case 'D': {
				switch (sData.charAt(2)) {
				case 'M': {
					this.aks.getGame().onMapData(sData.substring(4));
					break;
				}
				case 'K': {
					this.aks.getGame().onMapLoaded();
					break;
				}
				case 'C': {
					this.aks.getGame().onCellData(sData.substring(3));
					break;
				}
				case 'Z': {
					this.aks.getGame().onZoneData(sData.substring(3));
					break;
				}
				case 'O': {
					this.aks.getGame().onCellObject(sData.substring(3));
					break;
				}
				case 'F': {
					this.aks.getGame().onFrameObject2(sData.substring(4));
					break;
				}
				case 'E': {
					this.aks.getGame().onFrameObjectExternal(sData.substring(4));
					break;
				}
				} // End of switch
				break;
			}
			case 'd': {
				switch (sData.charAt(3)) {
				case 'K': {
					this.aks.getGame().onFightChallengeUpdate(sData.substring(4), true);
					break;
				}
				case 'O': {
					this.aks.getGame().onFightChallengeUpdate(sData.substring(4), false);
					break;
				}
				default: {
					this.aks.getGame().onFightChallenge(sData.substring(2));
					break;
				}
				} // End of switch
				break;
			}
			case 'A': {
				switch (sData.charAt(2)) {
				case 'S': {
					this.aks.getGameActions().onActionsStart(sData.substring(3));
					break;
				}
				case 'F': {
					this.aks.getGameActions().onActionsFinish(sData.substring(3));
					break;
				}
				default: {
					this.aks.getGameActions().onActions(sData.substring(2));
				}
				} // End of switch
				break;
			}
			case 'T': {
				switch (sData.charAt(2)) {
				case 'S': {
					this.aks.getGame().onTurnStart(sData.substring(3));
					break;
				}
				case 'F': {
					this.aks.getGame().onTurnFinish(sData.substring(3));
					break;
				}
				case 'L': {
					this.aks.getGame().onTurnlist(sData.substring(4));
					break;
				}
				case 'M': {
					this.aks.getGame().onTurnMiddle(sData.substring(4));
					break;
				}
				case 'R': {
					this.aks.getGame().onTurnReady(sData.substring(3));
					break;
				}
				} // End of switch
				break;
			}
			case 'X': {
				this.aks.getGame().onExtraClip(sData.substring(2));
				break;
			}
			case 'o': {
				this.aks.getGame().onFightOption(sData.substring(2));
				break;
			}
			case 'O': {
				this.aks.getGame().onGameOver();
				break;
			}
			} // End of switch
			break;
		}
		case 'c': {
			switch (sAction) {
			case 'M': {
				this.aks.getChat().onMessage(!bError, sData.substring(3));
				break;
			}
			case 's': {
				this.aks.getChat().onServerMessage(sData.substring(2));
				break;
			}
			case 'S': {
				this.aks.getChat().onSmiley(sData.substring(2));
				break;
			}
			case 'C': {
				this.aks.getChat().onSubscribeChannel(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'D': {
			switch (sAction) {
			case 'A': {
				this.aks.getDialog().onCustomAction(sData.substring(2));
				break;
			}
			case 'C': {
				this.aks.getDialog().onCreate(!bError, sData.substring(3));
				break;
			}
			case 'Q': {
				this.aks.getDialog().onQuestion(sData.substring(2));
				break;
			}
			case 'V': {
				this.aks.getDialog().onLeave();
				break;
			}
			case 'P': {
				this.aks.getDialog().onPause();
				break;
			}
			} // End of switch
			break;
		}
		case 'I': {
			switch (sAction) {
			case 'M': {
				this.aks.getInfos().onInfoMaps(sData.substring(2));
				break;
			}
			case 'C': {
				this.aks.getInfos().onInfoCompass(sData.substring(2));
				break;
			}
			case 'H': {
				this.aks.getInfos().onInfoCoordinatespHighlight(sData.substring(2));
				break;
			}
			case 'm': {
				this.aks.getInfos().onMessage(sData.substring(2));
				break;
			}
			case 'Q': {
				this.aks.getInfos().onQuantity(sData.substring(2));
				break;
			}
			case 'O': {
				this.aks.getInfos().onObject(sData.substring(2));
				break;
			}
			case 'L': {
				switch (sData.charAt(2)) {
				case 'S': {
					this.aks.getInfos().onLifeRestoreTimerStart(sData.substring(3));
					break;
				}
				case 'F': {
					this.aks.getInfos().onLifeRestoreTimerFinish(sData.substring(3));
					break;
				}
				} // End of switch
				break;
			}
			} // End of switch
			break;
		}
		case 'S': {
			switch (sAction) {
			case 'L': {
				switch (sData.charAt(2)) {
				case 'o': {
					this.aks.getSpells().onChangeOption(sData.substring(3));
					break;
				}
				default: {
					this.aks.getSpells().onList(sData.substring(2));
					break;
				}
				} // End of switch
				break;
			}
			case 'U': {
				this.aks.getSpells().onUpgradeSpell(!bError, sData.substring(3));
				break;
			}
			case 'B': {
				this.aks.getSpells().onSpellBoost(sData.substring(2));
				break;
			}
			case 'F': {
				this.aks.getSpells().onSpellForget(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'O': {
			switch (sAction) {
			case 'a': {
				this.aks.getItems().onAccessories(sData.substring(2));
				break;
			}
			case 'D': {
				this.aks.getItems().onDrop(!bError, sData.substring(3));
				break;
			}
			case 'A': {
				this.aks.getItems().onAdd(!bError, sData.substring(3));
				break;
			}
			case 'C': {
				this.aks.getItems().onChange(sData.substring(3));
				break;
			}
			case 'R': {
				this.aks.getItems().onRemove(sData.substring(2));
				break;
			}
			case 'Q': {
				this.aks.getItems().onQuantity(sData.substring(2));
				break;
			}
			case 'M': {
				this.aks.getItems().onMovement(sData.substring(2));
				break;
			}
			case 'T': {
				this.aks.getItems().onTool(sData.substring(2));
				break;
			}
			case 'w': {
				this.aks.getItems().onWeight(sData.substring(2));
				break;
			}
			case 'S': {
				this.aks.getItems().onItemSet(sData.substring(2));
				break;
			}
			case 'K': {
				this.aks.getItems().onItemUseCondition(sData.substring(2));
				break;
			}
			case 'F': {
				this.aks.getItems().onItemFound(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'F': {
			switch (sAction) {
			case 'A': {
				this.aks.getFriends().onAddFriend(!bError, sData.substring(3));
				break;
			}
			case 'D': {
				this.aks.getFriends().onRemoveFriend(!bError, sData.substring(3));
				break;
			}
			case 'L': {
				this.aks.getFriends().onFriendsList(sData.substring(3));
				break;
			}
			case 'S': {
				this.aks.getFriends().onSpouse(sData.substring(2));
				break;
			}
			case 'O': {
				this.aks.getFriends().onNotifyChange(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'i': {
			switch (sAction) {
			case 'A': {
				this.aks.getEnemies().onAddEnemy(!bError, sData.substring(3));
				break;
			}
			case 'D': {
				this.aks.getEnemies().onRemoveEnemy(!bError, sData.substring(3));
				break;
			}
			case 'L': {
				this.aks.getEnemies().onEnemiesList(sData.substring(3));
				break;
			}
			} // End of switch
			break;
		}
		case 'K': {
			switch (sAction) {
			case 'C': {
				this.aks.getKey().onCreate(sData.substring(3));
				break;
			}
			case 'K': {
				this.aks.getKey().onKey(!bError);
				break;
			}
			case 'V': {
				this.aks.getKey().onLeave();
				break;
			}
			} // End of switch
			break;
		}
		case 'J': {
			switch (sAction) {
			case 'S': {
				this.aks.getJob().onSkills(sData.substring(3));
				break;
			}
			case 'X': {
				this.aks.getJob().onXP(sData.substring(3));
				break;
			}
			case 'N': {
				this.aks.getJob().onLevel(sData.substring(2));
				break;
			}
			case 'R': {
				this.aks.getJob().onRemove(sData.substring(2));
				break;
			}
			case 'O': {
				this.aks.getJob().onOptions(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'E': {
			switch (sAction) {
			case 'R': {
				this.aks.getExchange().onRequest(!bError, sData.substring(3));
				break;
			}
			case 'K': {
				this.aks.getExchange().onReady(sData.substring(2));
				break;
			}
			case 'V': {
				this.aks.getExchange().onLeave(!bError, sData.substring(2));
				break;
			}
			case 'C': {
				this.aks.getExchange().onCreate(!bError, sData.substring(3));
				break;
			}
			case 'c': {
				this.aks.getExchange().onCraft(!bError, sData.substring(3));
				break;
			}
			case 'M': {
				this.aks.getExchange().onLocalMovement(!bError, sData.substring(3));
				break;
			}
			case 'm': {
				this.aks.getExchange().onDistantMovement(!bError, sData.substring(3));
				break;
			}
			case 'r': {
				this.aks.getExchange().onCoopMovement(!bError, sData.substring(3));
				break;
			}
			case 'p': {
				this.aks.getExchange().onPayMovement(!bError, sData.substring(2));
				break;
			}
			case 's': {
				this.aks.getExchange().onStorageMovement(!bError, sData.substring(3));
				break;
			}
			case 'i': {
				this.aks.getExchange().onPlayerShopMovement(!bError, sData.substring(3));
				break;
			}
			case 'W': {
				this.aks.getExchange().onCraftPublicMode(sData.substring(2));
				break;
			}
			case 'e': {
				this.aks.getExchange().onMountStorage(sData.substring(2));
				break;
			}
			case 'f': {
				this.aks.getExchange().onMountPark(sData.substring(2));
				break;
			}
			case 'w': {
				this.aks.getExchange().onMountPods(sData.substring(2));
				break;
			}
			case 'L': {
				this.aks.getExchange().onList(sData.substring(2));
				break;
			}
			case 'S': {
				this.aks.getExchange().onSell(!bError);
				break;
			}
			case 'B': {
				this.aks.getExchange().onBuy(!bError);
				break;
			}
			case 'q': {
				this.aks.getExchange().onAskOfflineExchange(sData.substring(2));
				break;
			}
			case 'H': {
				switch (sData.charAt(2)) {
				case 'S': {
					this.aks.getExchange().onSearch(sData.substring(3));
					break;
				}
				case 'L': {
					this.aks.getExchange().onBigStoreTypeItemsList(sData.substring(3));
					break;
				}
				case 'M': {
					this.aks.getExchange().onBigStoreTypeItemsMovement(sData.substring(3));
					break;
				}
				case 'l': {
					this.aks.getExchange().onBigStoreItemsList(sData.substring(3));
					break;
				}
				case 'm': {
					this.aks.getExchange().onBigStoreItemsMovement(sData.substring(3));
					break;
				}
				case 'P': {
					this.aks.getExchange().onItemMiddlePriceInBigStore(sData.substring(3));
					break;
				}
				} // End of switch
				break;
			}
			case 'J': {
				this.aks.getExchange().onCrafterListChanged(sData.substring(2));
				break;
			}
			case 'j': {
				this.aks.getExchange().onCrafterReference(sData.substring(2));
				break;
			}
			case 'A': {
				this.aks.getExchange().onCraftLoop(sData.substring(2));
				break;
			}
			case 'a': {
				this.aks.getExchange().onCraftLoopEnd(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'h': {
			switch (sAction) {
			case 'L': {
				this.aks.getHouses().onList(sData.substring(2));
				break;
			}
			case 'P': {
				this.aks.getHouses().onProperties(sData.substring(2));
				break;
			}
			case 'X': {
				this.aks.getHouses().onLockedProperty(sData.substring(2));
				break;
			}
			case 'C': {
				this.aks.getHouses().onCreate(sData.substring(3));
				break;
			}
			case 'S': {
				this.aks.getHouses().onSell(!bError, sData.substring(3));
				break;
			}
			case 'B': {
				this.aks.getHouses().onBuy(!bError, sData.substring(3));
				break;
			}
			case 'V': {
				this.aks.getHouses().onLeave();
				break;
			}
			case 'G': {
				this.aks.getHouses().onGuildInfos(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 's': {
			switch (sAction) {
			case 'L': {
				this.aks.getStorages().onList(sData.substring(2));
				break;
			}
			case 'X': {
				this.aks.getStorages().onLockedProperty(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'e': {
			switch (sAction) {
			case 'U': {
				this.aks.getEmotes().onUse(!bError, sData.substring(3));
				break;
			}
			case 'L': {
				this.aks.getEmotes().onList(sData.substring(2));
				break;
			}
			case 'A': {
				this.aks.getEmotes().onAdd(sData.substring(2));
				break;
			}
			case 'R': {
				this.aks.getEmotes().onRemove(sData.substring(2));
				break;
			}
			case 'D': {
				this.aks.getEmotes().onDirection(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'd': {
			switch (sAction) {
			case 'C': {
				this.aks.getDocuments().onCreate(!bError, sData.substring(3));
				break;
			}
			case 'V': {
				this.aks.getDocuments().onLeave();
				break;
			}
			} // End of switch
			break;
		}
		case 'g': {
			switch (sAction) {
			case 'n': {
				this.aks.getGuild().onNew();
				break;
			}
			case 'C': {
				this.aks.getGuild().onCreate(!bError, sData.substring(3));
				break;
			}
			case 'S': {
				this.aks.getGuild().onStats(sData.substring(2));
				break;
			}
			case 'I': {
				switch (sData.charAt(2)) {
				case 'G': {
					this.aks.getGuild().onInfosGeneral(sData.substring(3));
					break;
				}
				case 'M': {
					this.aks.getGuild().onInfosMembers(sData.substring(3));
					break;
				}
				case 'B': {
					this.aks.getGuild().onInfosBoosts(sData.substring(3));
					break;
				}
				case 'F': {
					this.aks.getGuild().onInfosMountPark(sData.substring(3));
					break;
				}
				case 'T': {
					switch (sData.charAt(3)) {
					case 'M': {
						this.aks.getGuild().onInfosTaxCollectorsMovement(sData.substring(4));
						break;
					}
					case 'P': {
						this.aks.getGuild().onInfosTaxCollectorsPlayers(sData.substring(4));
						break;
					}
					case 'p': {
						this.aks.getGuild().onInfosTaxCollectorsAttackers(sData.substring(4));
						break;
					}
					} // End of switch
					break;
				}
				case 'H': {
					this.aks.getGuild().onInfosHouses(sData.substring(3));
					break;
				}
				} // End of switch
				break;
			}
			case 'J': {
				switch (sData.charAt(2)) {
				case 'E': {
					this.aks.getGuild().onJoinError(sData.substring(3));
					break;
				}
				case 'R': {
					this.aks.getGuild().onRequestLocal(sData.substring(3));
					break;
				}
				case 'r': {
					this.aks.getGuild().onRequestDistant(sData.substring(3));
					break;
				}
				case 'K': {
					this.aks.getGuild().onJoinOk(sData.substring(3));
					break;
				}
				case 'C': {
					this.aks.getGuild().onJoinDistantOk();
					break;
				}
				} // End of switch
				break;
			}
			case 'V': {
				this.aks.getGuild().onLeave();
				break;
			}
			case 'K': {
				this.aks.getGuild().onBann(!bError, sData.substring(3));
				break;
			}
			case 'H': {
				this.aks.getGuild().onHireTaxCollector(!bError, sData.substring(3));
				break;
			}
			case 'A': {
				this.aks.getGuild().onTaxCollectorAttacked(sData.substring(2));
				break;
			}
			case 'T': {
				this.aks.getGuild().onTaxCollectorInfo(sData.substring(2));
				break;
			}
			case 'U': {
				this.aks.getGuild().onUserInterfaceOpen(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'W': {
			switch (sAction) {
			case 'C': {
				this.aks.getWaypoints().onCreate(sData.substring(2));
				break;
			}
			case 'V': {
				this.aks.getWaypoints().onLeave();
				break;
			}
			case 'U': {
				this.aks.getWaypoints().onUseError();
				break;
			}
			case 'c': {
				this.aks.getSubway().onCreate(sData.substring(2));
				break;
			}
			case 'v': {
				this.aks.getSubway().onLeave();
				break;
			}
			case 'u': {
				this.aks.getSubway().onUseError();
				break;
			}
			case 'p': {
				this.aks.getSubway().onPrismCreate(sData.substring(2));
				break;
			}
			case 'w': {
				this.aks.getSubway().onPrismLeave();
				break;
			}
			} // End of switch
			break;
		}
		case 'a': {
			switch (sAction) {
			case 'l': {
				this.aks.getSubareas().onList(sData.substring(3));
				break;
			}
			case 'm': {
				this.aks.getSubareas().onAlignmentModification(sData.substring(2));
				break;
			}
			case 'M': {
				this.aks.getConquest().onAreaAlignmentChanged(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'C': {
			switch (sAction) {
			case 'I': {
				switch (sData.charAt(2)) {
				case 'J': {
					this.aks.getConquest().onPrismInfosJoined(sData.substring(3));
					break;
				}
				case 'V': {
					this.aks.getConquest().onPrismInfosClosing(sData.substring(3));
				}
				} // End of switch
				break;
			}
			case 'B': {
				this.aks.getConquest().onConquestBonus(sData.substring(2));
				break;
			}
			case 'A': {
				this.aks.getConquest().onPrismAttacked(sData.substring(2));
				break;
			}
			case 'S': {
				this.aks.getConquest().onPrismSurvived(sData.substring(2));
				break;
			}
			case 'D': {
				this.aks.getConquest().onPrismDead(sData.substring(2));
				break;
			}
			case 'P': {
				this.aks.getConquest().onPrismFightAddPlayer(sData.substring(2));
				break;
			}
			case 'p': {
				this.aks.getConquest().onPrismFightAddEnemy(sData.substring(2));
				break;
			}
			case 'W': {
				this.aks.getConquest().onWorldData(sData.substring(2));
				break;
			}
			case 'b': {
				this.aks.getConquest().onConquestBalance(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'Z': {
			switch (sAction) {
			case 'S': {
				this.aks.getSpecialization().onSet(sData.substring(2));
				break;
			}
			case 'C': {
				this.aks.getSpecialization().onChange(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'f': {
			switch (sAction) {
			case 'C': {
				this.aks.getFights().onCount(sData.substring(2));
				break;
			}
			case 'L': {
				this.aks.getFights().onList(sData.substring(2));
				break;
			}
			case 'D': {
				this.aks.getFights().onDetails(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'T': {
			switch (sAction) {
			case 'C': {
				this.aks.getTutorial().onCreate(sData.substring(2));
				break;
			}
			case 'T': {
				this.aks.getTutorial().onShowTip(sData.substring(2));
				break;
			}
			case 'B': {
				this.aks.getTutorial().onGameBegin();
				break;
			}
			} // End of switch
			break;
		}
		case 'Q': {
			switch (sAction) {
			case 'L': {
				this.aks.getQuests().onList(sData.substring(3));
				break;
			}
			case 'S': {
				this.aks.getQuests().onStep(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'P': {
			switch (sAction) {
			case 'I': {
				this.aks.getParty().onInvite(!bError, sData.substring(3));
				break;
			}
			case 'L': {
				this.aks.getParty().onLeader(sData.substring(2));
				break;
			}
			case 'R': {
				this.aks.getParty().onRefuse(sData.substring(2));
				break;
			}
			case 'A': {
				this.aks.getParty().onAccept(sData.substring(2));
				break;
			}
			case 'C': {
				this.aks.getParty().onCreate(!bError, sData.substring(3));
				break;
			}
			case 'V': {
				this.aks.getParty().onLeave(sData.substring(2));
				break;
			}
			case 'F': {
				this.aks.getParty().onFollow(!bError, sData.substring(3));
				break;
			}
			case 'M': {
				this.aks.getParty().onMovement(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		case 'R': {
			switch (sAction) {
			case 'e': {
				this.aks.getMount().onEquip(sData.substring(2));
				break;
			}
			case 'x': {
				this.aks.getMount().onXP(sData.substring(2));
				break;
			}
			case 'n': {
				this.aks.getMount().onName(sData.substring(2));
				break;
			}
			case 'd': {
				this.aks.getMount().onData(sData.substring(2));
				break;
			}
			case 'p': {
				this.aks.getMount().onMountPark(sData.substring(2));
				break;
			}
			case 'D': {
				this.aks.getMount().onMountParkBuy(sData.substring(2));
				break;
			}
			case 'v': {
				this.aks.getMount().onLeave(sData.substring(2));
				break;
			}
			case 'r': {
				this.aks.getMount().onRidingState(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}
		} // End of switch
	};
}

Contrairement à ce que j'ai écrit un peu plus haut, le ProcessorListener ne va rien faire : c'est une interface qui va définir le contrat que devra respecter

toute implémentation.

Voici l'interface ProcessorListener:

Cliquez pour révéler Cliquez pour masquer


package dofus.aks.processor.listeners;

import java.io.IOException;

import dofus.aks.network.IO;


public interface ProcessorListener {

	BasicsProcessorListener getBasics();
	AccountProcessorListener getAccount();
	GameProcessorListener getGame();
	GameActionsProcessorListener getGameActions();
	ChatProcessorListener getChat();
	DialogProcessorListener getDialog();
	InfosProcessorListener getInfos();
	ItemsProcessorListener getItems();
	SpellsProcessorListener getSpells();
	FriendsProcessorListener getFriends();
	EnemiesProcessorListener getEnemies();
	KeysProcessorListener getKey();
	JobProcessorListener getJob();
	ExchangeProcessorListener getExchange();
	HousesProcessorListener getHouses();
	StoragesProcessorListener getStorages();
	EmotesProcessorListener getEmotes();
	DocumentsProcessorListener getDocuments();
	GuildProcessorListener getGuild();
	WaypointsProcessorListener getWaypoints();
	SubwayProcessorListener getSubway();
	SubareasProcessorListener getSubareas();
	ConquestProcessorListener getConquest();
	SpecializationProcessorListener getSpecialization();
	FightsProcessorListener getFights();
	TutorialProcessorListener getTutorial();
	QuestsProcessorListener getQuests();
	PartyProcessorListener getParty();
	MountProcessorListener getMount();
	
	void onHelloConnectionServer(String substring) throws IOException;

	void onHelloGameServer(String substring) throws IOException;

	void disconnect(boolean b, boolean c) throws IOException;

	void onPong() throws IOException;

	/**
	 * This method should send <b>"rpong" + string</b>
	 * <br>The default implementation does nothing since Dofus will answer to the rpong request.
	 * <br>If a pluging eats the rpong request it is its responsibility to answer the rpong request
	 * @param string 
	 */
	void onResponsePong(String string);

	void onQuickPong() throws IOException;

	void onServerMessage(String substring) throws IOException;

	void onServerWillDisconnect() throws IOException;

	IO getIO();

}

Les plus attentifs auront remarqué que l'interface ProcessorListener contient beaucoup de méthodes du type getQuelqueChose() et que ces méthodes renvoient un

objet QuelqueChoseListener. En effet, la gestion des messages va être sous traitée à des objets spécialisés en fonction de leur type (généralement la 1re

lettre de la ligne).

Penons l'exemple de toute ligne commençant par c, dans le source de ProcessorListener on a :


		case 'c': {
			switch (sAction) {
			case 'M': {
				this.aks.getChat().onMessage(!bError, sData.substring(3));
				break;
			}
			case 's': {
				this.aks.getChat().onServerMessage(sData.substring(2));
				break;
			}
			case 'S': {
				this.aks.getChat().onSmiley(sData.substring(2));
				break;
			}
			case 'C': {
				this.aks.getChat().onSubscribeChannel(sData.substring(2));
				break;
			}
			} // End of switch
			break;
		}

aks.getChat() renvoit une instance de l'interface ChatProcessorListener définie comme suit :


package dofus.aks.processor.listeners;

public interface ChatProcessorListener {

	void onMessage(boolean b, String substring);

	void onServerMessage(String substring);

	void onSmiley(String substring);

	void onSubscribeChannel(String substring);

}

Encore une fois, nous avons une interface qui définit le contrat à respecter pour gérer (dans ce cas) les messages de type Chat

Eclipse a créé pour moi toutes ces interfaces automatiquement à partir de classe ProtocolProcessor.

Une fois toutes les interfaces définies, il est souvent bon de créer une classe abstraite par interface pour y loger tout le code générique qui sera réutilisé par

toutes les classes concrètes qui vont implémenter ces interfaces.

J'ai choisi d'implémenter quasiment toute les méthodes dans chaque classe abstraite pour que ça ne fasse rien : POURQUOI ?

Oui, pourquoi écrire du code qui ne fait rien ?

- ça va très bien avec mon bot de type Man In the Middle

- ça permet de ne définir que ce qui nous intéresse

- c'est très efficace avec l'architecture à base de plugin que j'ai retenu pour mon bot.

- une fois cette partie rébarbative écrite on gagne beaucoup de temps.

Exemple de classe abstraite pour le chat.

Cliquez pour révéler Cliquez pour masquer


package dofus.aks.processor.nullImpl;

import dofus.aks.processor.listeners.ChatProcessorListener;

public class NullChatProcessor implements ChatProcessorListener {

	@Override
	public void onMessage(boolean b, String substring) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onServerMessage(String substring) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onSmiley(String substring) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onSubscribeChannel(String substring) {
		// TODO Auto-generated method stub

	}

}

J'ai décidé de faire précéder le nom de mes classe par Null pour mettre en évidence que cette implementation ne fait rien.

Encore une fois, Eclipse a fait 90% pour moi.

La partie pénible à écrire c'est l'implémentation de ProcessorListener car j'ai décidé de créer les objets en mode paresseux (lazy)

L'intéret de cette pratique est que l'objet est créé à la demande et tant qu'il n'est pas nécessaire il ne prends pas de place en mémoire.

Cliquez pour révéler Cliquez pour masquer


package dofus.aks.processor.nullImpl;

import java.io.IOException;

import dofus.aks.processor.listeners.AccountProcessorListener;
import dofus.aks.processor.listeners.BasicsProcessorListener;
import dofus.aks.processor.listeners.ChatProcessorListener;
import dofus.aks.processor.listeners.ConquestProcessorListener;
import dofus.aks.processor.listeners.DialogProcessorListener;
import dofus.aks.processor.listeners.DocumentsProcessorListener;
import dofus.aks.processor.listeners.EmotesProcessorListener;
import dofus.aks.processor.listeners.EnemiesProcessorListener;
import dofus.aks.processor.listeners.ExchangeProcessorListener;
import dofus.aks.processor.listeners.FightsProcessorListener;
import dofus.aks.processor.listeners.FriendsProcessorListener;
import dofus.aks.processor.listeners.GameActionsProcessorListener;
import dofus.aks.processor.listeners.GameProcessorListener;
import dofus.aks.processor.listeners.GuildProcessorListener;
import dofus.aks.processor.listeners.HousesProcessorListener;
import dofus.aks.processor.listeners.InfosProcessorListener;
import dofus.aks.processor.listeners.ItemsProcessorListener;
import dofus.aks.processor.listeners.JobProcessorListener;
import dofus.aks.processor.listeners.KeysProcessorListener;
import dofus.aks.processor.listeners.MountProcessorListener;
import dofus.aks.processor.listeners.PartyProcessorListener;
import dofus.aks.processor.listeners.ProcessorListener;
import dofus.aks.processor.listeners.QuestsProcessorListener;
import dofus.aks.processor.listeners.SpecializationProcessorListener;
import dofus.aks.processor.listeners.SpellsProcessorListener;
import dofus.aks.processor.listeners.StoragesProcessorListener;
import dofus.aks.processor.listeners.SubareasProcessorListener;
import dofus.aks.processor.listeners.SubwayProcessorListener;
import dofus.aks.processor.listeners.TutorialProcessorListener;
import dofus.aks.processor.listeners.WaypointsProcessorListener;

public abstract class NullProtocolProcessor implements ProcessorListener {

	protected AccountProcessorListener account;
	protected BasicsProcessorListener basics;
	protected ChatProcessorListener chat;
	protected ConquestProcessorListener conquest;
	protected DialogProcessorListener dialog;
	protected DocumentsProcessorListener documents;
	protected EmotesProcessorListener emotes;
	protected EnemiesProcessorListener enemies;
	protected ExchangeProcessorListener exchange;
	protected FightsProcessorListener fight;
	protected FriendsProcessorListener friends;
	protected GameProcessorListener game;
	protected GameActionsProcessorListener gameActions;
	protected GuildProcessorListener guild;
	protected HousesProcessorListener houses;
	protected InfosProcessorListener infos;
	protected ItemsProcessorListener items;
	protected JobProcessorListener job;
	protected KeysProcessorListener keys;
	protected MountProcessorListener mount;
	protected PartyProcessorListener party;
	protected QuestsProcessorListener quest;
	protected SpecializationProcessorListener specialisation;
	protected SpellsProcessorListener spells;
	protected StoragesProcessorListener storage;
	protected SubareasProcessorListener subareas;
	protected SubwayProcessorListener subway;
	protected TutorialProcessorListener tutorial;
	protected WaypointsProcessorListener waypoints;

	@Override
	public AccountProcessorListener getAccount() {
		if(account == null) {
			account = new NullAccountProcessor();
		}
		return account;
	}

	@Override
	public BasicsProcessorListener getBasics() {
		if(basics == null) {
			basics = new NullBasicsProcessor();
		}
		return basics;
	}

	@Override
	public ChatProcessorListener getChat() {
		if(chat == null) {
			chat = new NullChatProcessor();
		}
		return chat;
	}

	@Override
	public ConquestProcessorListener getConquest() {
		if(conquest == null) {
			conquest = new NullConquestProcessor();
		}
		return conquest;
	}

	@Override
	public DialogProcessorListener getDialog() {
		if(dialog == null) {
			dialog = new NullDialogProcessor();
		}
		return dialog;
	}

	@Override
	public DocumentsProcessorListener getDocuments() {
		if(documents == null) {
			documents = new NullDocumentsProcessor();
		}
		return documents;
	}

	@Override
	public EmotesProcessorListener getEmotes() {
		if(emotes == null) {
			emotes = new NullEmotesProcessor();
		}
		return emotes;
	}

	@Override
	public EnemiesProcessorListener getEnemies() {
		if(enemies == null) {
			enemies = new NullEnemiesProcessor();
		}
		return enemies;
	}

	@Override
	public ExchangeProcessorListener getExchange() {
		if(exchange == null) {
			exchange = new NullExchangeProcessor();
		}
		return exchange;
	}

	@Override
	public FightsProcessorListener getFights() {
		if(fight == null) {
			fight = new NullFightsProcessor();
		}
		return fight;
	}

	@Override
	public FriendsProcessorListener getFriends() {
		if(friends == null) {
			friends = new NullFriendsProcessor();
		}
		return friends;
	}

	@Override
	public GameProcessorListener getGame() {
		if(game == null) {
			game = new NullGameProcessor();
		}
		return game;
	}

	@Override
	public GameActionsProcessorListener getGameActions() {
		if(gameActions == null) {
			gameActions = new NullGameActionsProcessor();
		}
		return gameActions;
	}

	@Override
	public GuildProcessorListener getGuild() {
		if(guild == null) {
			guild = new NullGuildProcessor();
		}
		return guild;
	}

	@Override
	public HousesProcessorListener getHouses() {
		if(houses == null) {
			houses = new NullHousesProcessor();
		}
		return houses;
	}

	@Override
	public InfosProcessorListener getInfos() {
		if(infos == null) {
			infos = new NullInfosProcessor();
		}
		return infos;
	}

	@Override
	public ItemsProcessorListener getItems() {
		if(items == null) {
			items = new NullItemsProcessor();
		}
		return items;
	}

	@Override
	public JobProcessorListener getJob() {
		if(job == null) {
			job = new NullJobProcessor();
		}
		return job;
	}

	@Override
	public KeysProcessorListener getKey() {
		if(keys == null) {
			keys = new NullKeysProcessor();
		}
		return keys;
	}

	@Override
	public MountProcessorListener getMount() {
		if(mount == null) {
			mount = new NullMountProcessor();
		}
		return mount;
	}

	@Override
	public PartyProcessorListener getParty() {
		if(party == null) {
			party = new NullPartyProcessor();
		}
		return party;
	}

	@Override
	public QuestsProcessorListener getQuests() {
		if(quest == null) {
			quest = new NullQuestProcessor();
		}
		return quest;
	}

	@Override
	public SpecializationProcessorListener getSpecialization() {
		if(specialisation == null) {
			specialisation = new NullSpecializationProcessor();
		}
		return specialisation;
	}

	@Override
	public SpellsProcessorListener getSpells() {
		if(spells == null){
			spells = new NullSpellsProcessor();
		}
		return spells;
	}

	@Override
	public StoragesProcessorListener getStorages() {
		if(storage == null) {
			storage = new NullStoragesProcessor();
		}
		return storage;
	}

	@Override
	public SubareasProcessorListener getSubareas() {
		if(subareas == null) {
			subareas = new NullSubareasProcessor();
		}
		return subareas;
	}

	@Override
	public SubwayProcessorListener getSubway() {
		if(subway == null) {
			subway = new NullSubwayProcessor();
		}
		return subway;
	}

	@Override
	public TutorialProcessorListener getTutorial() {
		if(tutorial == null) {
			tutorial = new NullTutorialProcessor();
		}
		return tutorial;
	}

	@Override
	public WaypointsProcessorListener getWaypoints() {
		if(waypoints == null) {
			waypoints = new NullWaypointsProcessor();
		}
		return waypoints;
	}

	@Override
	public void onPong() throws IOException {
	}

	@Override
	public void onQuickPong() throws IOException {
	}

	@Override
	public void onResponsePong(String string) {
	}
	
}

Une fois tout ça écrit on a un superbe parseur qui ne fait rien. Dans le prochain tuto, j'utiliserai ce parseur pour décrypter les maps.

J'ai écrit tout ça d'un trait et il est possible que je sois allé un peu vite sur certaines parties.

Pour approfondir la programmation objet je vous recommande de lire http://abrillant.developpez.com/tutorie ... roduction/

car même si c'est le Java qui est pris en exemple, la grande majorité des infos restent valables dans n'importe quel langage objet.

J'ai ajouté les sources de mon parseur vide pour exemple :

--Xvolks

6 mois plus tard

Un parseur, Si je ne me trompes pas fonctionne avec tout les sockets possibles.

Ah ^^ questions de noob merci :)

dommage que je n'aime pas le java ..

5 ans plus tard

Dofus 1.29 utilise un parser pour décomposer le paquet qui est sous forme d'une chaine de caractére. Dofus 2 quant à lui, utilise un deserializer pour lire binairement le paquet qui est en faite un objet.

2 mois plus tard

Mon vieux tuto a survécu au changements de version du site. Ça fait plaisir de relire ce vieux texte ;)

un an plus tard

J'aime bien tout ca est-ce qu'il y a une suite pour ce tuto ? et la definition de la classe Socket ?

Ce tuto a quasiment 10 ans...

La classe Socket fait partie du SDK Java. Voir la java doc: https://docs.oracle.com/javase/7/docs/api/java/net/Socket.html

Je n’ai jamais écrit la suite, par manque de temps et de motivation. Puis D. 2.0 est sorti, j’ai écrit un bot complet qui a tourné quelques mois.

Rattrapé par la patrouille, je suis passé à autre chose...

—Xvolks

    Ah d'accord c'est dommage, est-ce-que le code complet est public ? est-ce que je peux le trouver ? et c'est bizarre pour Socket parceque j'ai vu la ligne socket.readLineTerminéeParNull();

    C'est du pseudo code, c'est pas un code réel, ça représente ce qu'il faut faire.

    Je n'ai plus les sources du bot 1.29 dont je parle dans ce tuto.

    Je n'ai pas ouvert les sources du bot 2.0, à l'époque et je ne pense plus les avoir maintenant.

    Pour la ligne socket.readLineTerminéeParNull(); c'est du pseudo code, pour expliquer le fonctionnement.

    Dans la partie partage du forum, il y a des liens vers des bots écrits dans divers langages qui peuvent servir d'inspiration.

      xvolks

      Je n'ai plus les sources du bot 1.29 dont je parle dans ce tuto.

      Je n'ai pas ouvert les sources du bot 2.0, à l'époque et je ne pense plus les avoir maintenant.

      Pour la ligne socket.readLineTerminéeParNull(); c'est du pseudo code, pour expliquer le fonctionnement.

      Dans la partie partage du forum, il y a des liens vers des bots écrits dans divers langages qui peuvent servir d'inspiration.

      D'accord merci j'imagine que ca va m'aider c'est sympa! :)

      2 mois plus tard

      xvolks

      Ce tuto a quasiment 10 ans...

      La classe Socket fait partie du SDK Java. Voir la java doc: https://docs.oracle.com/javase/7/docs/api/java/net/Socket.html

      Je n’ai jamais écrit la suite, par manque de temps et de motivation. Puis D. 2.0 est sorti, j’ai écrit un bot complet qui a tourné quelques mois.

      Rattrapé par la patrouille, je suis passé à autre chose...

      —Xvolks

      10 ans mais encore d'actualité!!

      7 ans plus tard
      BlueDream a désépinglé la discussion le .