La communication des salles d'écoutes est normalement fonctionelle, il manque des tests plus approfondits
This commit is contained in:
parent
c17ccbfb71
commit
23c25f0e05
@ -30,7 +30,7 @@ public class BytesUtils {
|
||||
}
|
||||
|
||||
public static void dump(ByteBuffer buffer) {
|
||||
System.out.println(toStream(buffer).limit(buffer.position()).map(b -> b.toString()).collect(Collectors.joining(",")));
|
||||
System.out.println(toStream(buffer).limit(buffer.position()).map(b -> Integer.toString(Byte.toUnsignedInt(b))).collect(Collectors.joining(",")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,13 +6,18 @@ import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import com.bernard.murder.BytesUtils;
|
||||
|
||||
@ -27,17 +32,25 @@ public class AudioServer {
|
||||
// Format des paquets [commande 1, int Count, {int id, String name}]
|
||||
public static final byte GIVE_AUDIO_LIST = 0x05;
|
||||
// Format des paquets: [commande 1, int listenId, int myId]
|
||||
// -> Une enceinte demande à master le son d'un micro
|
||||
public static final byte ASK_STREAMING = 0x06;
|
||||
// Format des paquets: [commande 1, int id]
|
||||
// -> Master demande à un micro d'envoyer du son
|
||||
public static final byte START_STREAMING = 0x07;
|
||||
// Format des paquets: [commande 1, int listenId, int myId]
|
||||
// -> Une enceinte demande à master de ne plus recevoir le son d'un micro
|
||||
public static final byte ASK_STOP_STREAMING = 0x09;
|
||||
// Format des paquets: [commande 1, int id]
|
||||
// -> Master demande à un micro de ne plus émettre.
|
||||
public static final byte STOP_STREAMING = 0x08;
|
||||
// Format des paquets [commande 1, int id, ~ data]
|
||||
public static final byte AUDIO_STREAM = 0x01;
|
||||
// Format des paquets [commande 1, byte deviceType, int deviceId]
|
||||
// Un terminal indique qu'il se ferme à la connection
|
||||
public static final byte DISCONNECTING = 0x0A;
|
||||
|
||||
|
||||
|
||||
public static final byte MASTER_DEVICE = 0x42;
|
||||
public static final byte SPEAKER_DEVICE = 0x01;
|
||||
public static final byte MIC_DEVICE = 0x02;
|
||||
|
||||
@ -57,19 +70,18 @@ public class AudioServer {
|
||||
Map<Integer,SocketAddress> speakersAddr;
|
||||
Map<Integer,List<Integer>> listening; // micId, List<speakerId>
|
||||
|
||||
public AudioServer() {
|
||||
private Set<Runnable> changeListeners;
|
||||
|
||||
public AudioServer() throws SocketException, UnknownHostException {
|
||||
mics = new HashMap<Integer, String>();
|
||||
micsAddr = new HashMap<Integer, SocketAddress>();
|
||||
speakers = new HashMap<Integer, String>();
|
||||
speakersAddr = new HashMap<Integer, SocketAddress>();
|
||||
listening = new HashMap<Integer, List<Integer>>();
|
||||
|
||||
changeListeners = new HashSet<>();
|
||||
|
||||
try {
|
||||
initServer();
|
||||
} catch (SocketException | UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
initServer();
|
||||
}
|
||||
|
||||
public void initServer() throws SocketException, UnknownHostException {
|
||||
@ -78,7 +90,7 @@ public class AudioServer {
|
||||
|
||||
public void receiveCommand(ByteBuffer data,SocketAddress senderAddress) {
|
||||
byte commande = data.get();
|
||||
System.out.println("Commande reçue : "+commande);
|
||||
System.out.println("Commande reçue : "+commande+" de "+senderAddress+" de taille "+(data.limit()+1));
|
||||
switch (commande) {
|
||||
|
||||
case AudioServer.DECLARE_NUMBER:
|
||||
@ -93,8 +105,9 @@ public class AudioServer {
|
||||
mics.put(newId, deviceName);
|
||||
micsAddr.put(newId, senderAddress);
|
||||
listening.put(newId, new ArrayList<>());
|
||||
publishAudioList(speakersAddr.values());
|
||||
break;
|
||||
case AudioServer.SPEAKER_DEVICE:
|
||||
case AudioServer.SPEAKER_DEVICE:
|
||||
newId = speakerId++;
|
||||
speakers.put(newId, deviceName);
|
||||
speakersAddr.put(newId, senderAddress);
|
||||
@ -112,74 +125,179 @@ public class AudioServer {
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
changeListeners.forEach(Runnable::run);
|
||||
System.out.println("Accepting request from "+senderAddress);
|
||||
break;
|
||||
case AudioServer.ASK_STREAMING:
|
||||
int listened = data.getInt();
|
||||
int listener = data.getInt();
|
||||
listening.get(listened).add(listener);
|
||||
|
||||
if(listening.get(listened).contains(listener))
|
||||
break;
|
||||
|
||||
ByteBuffer out3 = ByteBuffer.allocate(AudioServer.packetMaxSize);
|
||||
out3.put(AudioServer.START_STREAMING);
|
||||
out3.putInt(listened);
|
||||
try {
|
||||
serveur.sendData(out3, micsAddr.get(listened));
|
||||
|
||||
listening.get(listened).add(listener);
|
||||
changeListeners.forEach(Runnable::run);
|
||||
} catch (IOException e2) {
|
||||
e2.printStackTrace();
|
||||
}
|
||||
break;
|
||||
|
||||
case AudioServer.STOP_STREAMING:
|
||||
case AudioServer.ASK_STOP_STREAMING:
|
||||
int listened2 = data.getInt();
|
||||
int listener2 = data.getInt();
|
||||
listening.get(listener2).remove(listened2);
|
||||
ByteBuffer out4 = ByteBuffer.allocate(AudioServer.packetMaxSize);
|
||||
out4.put(AudioServer.STOP_STREAMING);
|
||||
out4.putInt(listened2);
|
||||
try {
|
||||
serveur.sendData(out4, micsAddr.get(listened2));
|
||||
} catch (IOException e2) {
|
||||
e2.printStackTrace();
|
||||
if(listened2==-1)break;
|
||||
try{
|
||||
listening.get(listened2).remove(listener2);
|
||||
}catch(IndexOutOfBoundsException e) {
|
||||
System.err.println("Les données enregistrées par ce serveur ne sont pas intègres !");
|
||||
}
|
||||
changeListeners.forEach(Runnable::run);
|
||||
|
||||
if(listening.get(listened2).isEmpty()) {
|
||||
try {
|
||||
ByteBuffer out4 = ByteBuffer.allocate(AudioServer.packetMaxSize);
|
||||
out4.put(AudioServer.STOP_STREAMING);
|
||||
out4.putInt(listened2);
|
||||
serveur.sendData(out4, micsAddr.get(listened2));
|
||||
|
||||
} catch (IOException e2) {
|
||||
e2.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AudioServer.AUDIO_STREAM:
|
||||
int micId = data.getInt();
|
||||
byte[] audioData = new byte[data.remaining()];
|
||||
data.get(audioData);
|
||||
|
||||
data.position(data.limit());
|
||||
|
||||
for(int spck : listening.get(micId)) {
|
||||
data.clear();
|
||||
SocketAddress dest = speakersAddr.get(spck);
|
||||
try {
|
||||
serveur.sendData(data, dest);
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
JOptionPane.showMessageDialog(null, "Impossible de transmettre le son !","Son impossible !",JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case AudioServer.ASK_AUDIO_LIST:
|
||||
ByteBuffer out2 = ByteBuffer.allocate(AudioServer.packetMaxSize);
|
||||
out2.put(AudioServer.GIVE_AUDIO_LIST);
|
||||
out2.putInt(mics.size());
|
||||
for(Entry<Integer, String> mic : mics.entrySet()) {
|
||||
out2.putInt(mic.getKey());
|
||||
BytesUtils.writeString(out2, mic.getValue());
|
||||
}
|
||||
|
||||
System.out.println("Sending audio list to "+senderAddress+" : ");
|
||||
BytesUtils.dump(out2);
|
||||
try {
|
||||
serveur.sendData(out2, senderAddress);
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
publishAudioList(Collections.singleton(senderAddress));
|
||||
|
||||
break;
|
||||
|
||||
case AudioServer.DISCONNECTING:
|
||||
byte deviceType2 = data.get();
|
||||
int deviceId = data.getInt();
|
||||
if(deviceType2==AudioServer.MIC_DEVICE) {
|
||||
// On le déréférence
|
||||
if(!mics.containsKey(deviceId)) {
|
||||
System.out.println("Le micro d'id "+deviceId+" est déjà désinscrit");
|
||||
break;
|
||||
}
|
||||
System.out.println("Déconnection du microphone "+mics.get(deviceId));
|
||||
mics.remove(deviceId);
|
||||
micsAddr.remove(deviceId);
|
||||
changeListeners.forEach(Runnable::run);
|
||||
|
||||
// On enlève tous les liens d'écoute
|
||||
for(int spkId : listening.get(deviceId)) {
|
||||
// Annoncer à ce speaker que le micro est déconnécté et qu'il ne peut plus l'écouter.
|
||||
ByteBuffer out5 = ByteBuffer.allocate(AudioServer.packetMaxSize);
|
||||
out5.put(AudioServer.DISCONNECTING);
|
||||
out5.put(AudioServer.MIC_DEVICE);
|
||||
out5.putInt(deviceId);
|
||||
try {
|
||||
serveur.sendData(out5, speakersAddr.get(spkId));
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
listening.remove(deviceId);
|
||||
publishAudioList(speakersAddr.values());
|
||||
|
||||
}else if(deviceType2==AudioServer.SPEAKER_DEVICE) {
|
||||
|
||||
// On le déréférence
|
||||
System.out.println("Déconnection de l'enceinte "+speakers.get(deviceId));
|
||||
speakers.remove(deviceId);
|
||||
speakersAddr.remove(deviceId);
|
||||
changeListeners.forEach(Runnable::run);
|
||||
|
||||
int lstTo = listens(deviceId);
|
||||
if(lstTo!=-1) {
|
||||
// On enlève le lien d'écoute
|
||||
listening.get(lstTo).remove((Integer)deviceId);
|
||||
|
||||
if(listening.get(lstTo).isEmpty()) {
|
||||
// Si il n'y a plus rien à écouter.
|
||||
try {
|
||||
ByteBuffer out4 = ByteBuffer.allocate(AudioServer.packetMaxSize);
|
||||
out4.put(AudioServer.STOP_STREAMING);
|
||||
out4.putInt(lstTo);
|
||||
serveur.sendData(out4, micsAddr.get(lstTo));
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}else {
|
||||
System.err.println("Je ne sais pas comment réagir à la déconnection d'un appareil de type "+deviceType2);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
System.out.println("Je ne devait pas recevoir cette commade !");
|
||||
}
|
||||
}
|
||||
|
||||
public void addChangeListener(Runnable listener) {
|
||||
changeListeners.add(listener);
|
||||
}
|
||||
public void removeChangeListener(Runnable listener) {
|
||||
changeListeners.remove(listener);
|
||||
}
|
||||
|
||||
public Map<Integer, String> getMics() {
|
||||
return Collections.unmodifiableMap(mics);
|
||||
}
|
||||
public Map<Integer, String> getSpeakers() {
|
||||
return Collections.unmodifiableMap(speakers);
|
||||
}
|
||||
|
||||
public int listens(int spk) {
|
||||
for(int mic : listening.keySet()) {
|
||||
if(listening.get(mic).contains(spk))
|
||||
return mic;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void publishAudioList(Collection<SocketAddress> co) {
|
||||
ByteBuffer out = ByteBuffer.allocate(AudioServer.packetMaxSize);
|
||||
out.put(AudioServer.GIVE_AUDIO_LIST);
|
||||
out.putInt(mics.size());
|
||||
for(Entry<Integer, String> mic : mics.entrySet()) {
|
||||
out.putInt(mic.getKey());
|
||||
BytesUtils.writeString(out, mic.getValue());
|
||||
}
|
||||
|
||||
try {
|
||||
for(SocketAddress addr : co) {
|
||||
serveur.sendData(out, addr);
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@ import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
@ -34,11 +36,14 @@ public class MicServer {
|
||||
|
||||
Runnable serverAnswered;
|
||||
|
||||
Set<Runnable> disconnectListener;
|
||||
|
||||
|
||||
public MicServer(SocketAddress adresse,String micName,TargetDataLine tdl) {
|
||||
this.micName = micName;
|
||||
this.masterAddress = adresse;
|
||||
this.micLine = tdl;
|
||||
this.disconnectListener = new HashSet<>();
|
||||
try {
|
||||
initServer();
|
||||
initializeAudioId();
|
||||
@ -51,6 +56,7 @@ public class MicServer {
|
||||
public void initializeMicDevice() throws LineUnavailableException {
|
||||
micLine.open(AudioServer.formatAudio);
|
||||
packetLength = micLine.getBufferSize()/5;
|
||||
System.out.println("Longueur du paquet: "+packetLength);
|
||||
}
|
||||
|
||||
public void initServer() throws SocketException, UnknownHostException {
|
||||
@ -79,18 +85,21 @@ public class MicServer {
|
||||
|
||||
public void receiveCommand(ByteBuffer data) {
|
||||
byte commande = data.get();
|
||||
System.out.println("Commande reçue : "+commande);
|
||||
switch (commande) {
|
||||
|
||||
case AudioServer.START_STREAMING:
|
||||
int micId = data.getInt();
|
||||
if(micId != this.micId)return;
|
||||
int askedMicId = data.getInt();
|
||||
if(askedMicId != this.micId)return;
|
||||
shouldStream = true;
|
||||
micLine.start();
|
||||
launchDataStream();
|
||||
break;
|
||||
case AudioServer.STOP_STREAMING:
|
||||
int micId2 = data.getInt();
|
||||
if(micId2 != this.micId)return;
|
||||
int askedMicId2 = data.getInt();
|
||||
if(askedMicId2 != this.micId)return;
|
||||
shouldStream = false;
|
||||
micLine.stop();
|
||||
break;
|
||||
|
||||
case AudioServer.OK_ID:
|
||||
@ -101,19 +110,30 @@ public class MicServer {
|
||||
if(!askedUUID.equals(uuid) || deviceType!=AudioServer.MIC_DEVICE)
|
||||
return;
|
||||
|
||||
micId = deviceId;
|
||||
this.micId = deviceId;
|
||||
|
||||
new Thread(serverAnswered).start();
|
||||
|
||||
if(micLine==null)
|
||||
if(!micLine.isOpen())
|
||||
try {
|
||||
initializeMicDevice();
|
||||
} catch (LineUnavailableException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case AudioServer.DISCONNECTING:
|
||||
byte deviceType2 = data.get();
|
||||
if(deviceType2==AudioServer.MASTER_DEVICE) {
|
||||
System.out.println("Le master s'est déconnécté, on fait de même !");
|
||||
masterAddress=null;
|
||||
this.dispose();
|
||||
disconnectListener.forEach(Runnable::run);
|
||||
}else {
|
||||
System.out.println("Un appareil de type "+deviceType2+" s'est déconécté, mais je m'en fout ^^");
|
||||
}
|
||||
default:
|
||||
System.out.println("Je ne devait pas recevoir cette commade !");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -125,6 +145,8 @@ public class MicServer {
|
||||
ByteBuffer audioPacket = ByteBuffer.wrap(packetData);
|
||||
audioPacket.put(AudioServer.AUDIO_STREAM);
|
||||
audioPacket.putInt(micId);
|
||||
audioPacket.position(audioPacket.position()+packetLength);
|
||||
micLine.start();
|
||||
while(shouldStream) {
|
||||
micLine.read(packetData, 5, packetLength);
|
||||
try {
|
||||
@ -134,11 +156,24 @@ public class MicServer {
|
||||
}
|
||||
|
||||
}
|
||||
micLine.stop();
|
||||
});
|
||||
streamingThread.start();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if(masterAddress!=null) {
|
||||
ByteBuffer decoPacket = ByteBuffer.allocate(AudioServer.packetMaxSize);
|
||||
decoPacket.put(AudioServer.DISCONNECTING);
|
||||
decoPacket.put(AudioServer.MIC_DEVICE);
|
||||
decoPacket.putInt(micId);
|
||||
try {
|
||||
serveur.sendData(decoPacket,masterAddress);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
shouldStream=false;
|
||||
micLine.close();
|
||||
serveur.dispose();
|
||||
}
|
||||
@ -147,4 +182,12 @@ public class MicServer {
|
||||
this.serverAnswered = serverAnswered;
|
||||
}
|
||||
|
||||
public void addDisconnectListener(Runnable listener) {
|
||||
disconnectListener.add(listener);
|
||||
}
|
||||
public void removeDisconnectListener(Runnable listener) {
|
||||
disconnectListener.remove(listener);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.bernard.murder.BytesUtils;
|
||||
import com.bernard.murder.ParseUtils;
|
||||
|
||||
public class Serveur {
|
||||
@ -88,7 +89,10 @@ public class Serveur {
|
||||
receivedSlicesArray.remove(uuid);
|
||||
}
|
||||
}else {
|
||||
ByteBuffer dataBuffer = ByteBuffer.wrap(data,paquet.getOffset(),paquet.getLength());
|
||||
byte[] realData = new byte[paquet.getLength()];
|
||||
System.arraycopy(data, paquet.getOffset(), realData, 0, paquet.getLength());
|
||||
ByteBuffer dataBuffer = ByteBuffer.wrap(realData);
|
||||
BytesUtils.dump(dataBuffer);
|
||||
consumer.accept(dataBuffer,paquet.getSocketAddress());
|
||||
}
|
||||
|
||||
@ -98,23 +102,23 @@ public class Serveur {
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
},"Receveur de paquets");
|
||||
isReceiving = true;
|
||||
packetReceiver.start();
|
||||
}
|
||||
|
||||
public void sendData(ByteBuffer buffer,SocketAddress address) throws IOException {
|
||||
byte[] data = new byte[buffer.position()];
|
||||
buffer.clear();
|
||||
buffer.flip();
|
||||
buffer.get(data);
|
||||
sendData(data,address);
|
||||
}
|
||||
public void sendData(byte[] data, SocketAddress address) throws IOException {
|
||||
if(data.length < packetMaxLength) {
|
||||
DatagramPacket packet = new DatagramPacket(data, data.length,address);
|
||||
System.out.println("Sent "+packet.getLength()+" bytes to "+packet.getAddress());
|
||||
socket.send(packet);
|
||||
}else {
|
||||
//XXX Ça, ca ne marche pas !
|
||||
short packetCount = (short) (data.length / (packetMaxLength-42));
|
||||
short packetLength = (short) (data.length / packetCount);
|
||||
short lastPacketLength = (short) (data.length - (packetCount-1)*packetLength);
|
||||
|
||||
@ -6,8 +6,11 @@ import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
@ -19,8 +22,6 @@ public class SpeakerServer {
|
||||
|
||||
SourceDataLine speakerLine;
|
||||
|
||||
int packetLength = 9728;
|
||||
|
||||
int speakerId;
|
||||
|
||||
String speakerName;
|
||||
@ -30,7 +31,6 @@ public class SpeakerServer {
|
||||
Serveur serveur;
|
||||
|
||||
Map<Integer,String> mics;
|
||||
volatile boolean isMicListUpToDate = false;
|
||||
|
||||
int listeningTo = -1;
|
||||
|
||||
@ -38,11 +38,21 @@ public class SpeakerServer {
|
||||
|
||||
Runnable serverAnswered;
|
||||
|
||||
|
||||
Set<Runnable> disconnectListener;
|
||||
Set<Runnable> brokenMicListener;
|
||||
Set<Consumer<Map<Integer, String>>> micListUpdateListener;
|
||||
|
||||
|
||||
public SpeakerServer(SocketAddress serveur,String speakerName,SourceDataLine speaker) {
|
||||
this.speakerName = speakerName;
|
||||
this.masterAddress = serveur;
|
||||
this.speakerLine = speaker;
|
||||
|
||||
this.disconnectListener = new HashSet<>();
|
||||
this.brokenMicListener = new HashSet<>();
|
||||
this.micListUpdateListener = new HashSet<>();
|
||||
|
||||
try {
|
||||
initServer();
|
||||
initializeAudioId();
|
||||
@ -57,7 +67,7 @@ public class SpeakerServer {
|
||||
|
||||
public void initializeSpeakerDevice() throws LineUnavailableException {
|
||||
speakerLine.open(AudioServer.formatAudio);
|
||||
packetLength = speakerLine.getBufferSize()/5;
|
||||
speakerLine.start();
|
||||
}
|
||||
|
||||
public void initializeAudioId() {
|
||||
@ -82,10 +92,15 @@ public class SpeakerServer {
|
||||
}
|
||||
|
||||
public void askForStream(int micId) {
|
||||
|
||||
if(listeningTo==micId)
|
||||
return;
|
||||
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(AudioServer.packetMaxSize);
|
||||
|
||||
|
||||
buffer.put(AudioServer.START_STREAMING);
|
||||
buffer.put(AudioServer.ASK_STREAMING);
|
||||
buffer.putInt(micId);
|
||||
buffer.putInt(speakerId);
|
||||
|
||||
@ -97,16 +112,21 @@ public class SpeakerServer {
|
||||
}
|
||||
}
|
||||
|
||||
public void stopStreaming() {
|
||||
public void askStopStreaming() {
|
||||
if(listeningTo==-1) {
|
||||
System.out.println("J'arêtte d'écouter le vide");
|
||||
return;
|
||||
}
|
||||
ByteBuffer buffer = ByteBuffer.allocate(AudioServer.packetMaxSize);
|
||||
|
||||
|
||||
buffer.put(AudioServer.STOP_STREAMING);
|
||||
buffer.put(AudioServer.ASK_STOP_STREAMING);
|
||||
buffer.putInt(listeningTo);
|
||||
buffer.putInt(speakerId);
|
||||
|
||||
try {
|
||||
serveur.sendData(buffer,masterAddress);
|
||||
listeningTo=-1;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -121,23 +141,6 @@ public class SpeakerServer {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
public Map<Integer,String> getAudioList() {
|
||||
return getAudioList(false);
|
||||
}
|
||||
|
||||
public Map<Integer,String> getAudioList(boolean invalidate) {
|
||||
isMicListUpToDate = !invalidate && isMicListUpToDate;
|
||||
if(!isMicListUpToDate)askAudioList();
|
||||
while(!isMicListUpToDate) {
|
||||
try {
|
||||
Thread.sleep(1);//XXX Can be interrupted here avec le bouton «Arreter»
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
System.out.println("Voici les "+mics);
|
||||
return mics;
|
||||
}
|
||||
|
||||
public void receiveCommand(ByteBuffer data) {
|
||||
byte commande = data.get();
|
||||
@ -146,11 +149,10 @@ public class SpeakerServer {
|
||||
|
||||
case AudioServer.AUDIO_STREAM:
|
||||
int micId = data.getInt();
|
||||
if(micId != listeningTo)return;
|
||||
data.compact();
|
||||
if(micId != listeningTo)break;
|
||||
byte[] audioData=new byte[data.remaining()];
|
||||
data.get(audioData);//XXX Check wether audio data starts at position and not at 0
|
||||
speakerLine.write(audioData, 0, packetLength);
|
||||
speakerLine.write(audioData, 0, audioData.length);
|
||||
break;
|
||||
|
||||
case AudioServer.OK_ID:
|
||||
@ -163,12 +165,14 @@ public class SpeakerServer {
|
||||
|
||||
speakerId = deviceId;
|
||||
new Thread(serverAnswered).start();
|
||||
if(speakerLine==null)
|
||||
if(!speakerLine.isOpen())
|
||||
try {
|
||||
initializeSpeakerDevice();
|
||||
} catch (LineUnavailableException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
askAudioList();
|
||||
break;
|
||||
case AudioServer.GIVE_AUDIO_LIST:
|
||||
int micCount = data.getInt();
|
||||
@ -178,15 +182,51 @@ public class SpeakerServer {
|
||||
mics.put(thisMicId,BytesUtils.readString(data));
|
||||
}
|
||||
System.out.println("Audio list given: "+mics);
|
||||
isMicListUpToDate=true;
|
||||
micListUpdateListener.forEach(c -> c.accept(mics));
|
||||
break;
|
||||
|
||||
case AudioServer.DISCONNECTING:
|
||||
byte deviceType2 = data.get();
|
||||
int deviceId2 = data.getInt();
|
||||
if(deviceType2==AudioServer.MASTER_DEVICE) {
|
||||
System.out.println("Le master s'est déconnécté, on fait de même !");
|
||||
masterAddress=null;
|
||||
this.dispose();
|
||||
for(Runnable toRun : disconnectListener)
|
||||
toRun.run();
|
||||
}else if(deviceType2==AudioServer.MIC_DEVICE){
|
||||
if(listeningTo==deviceId2) {
|
||||
System.out.println("Le micro que l'on écoutait s'est déconnécté.");
|
||||
listeningTo=-1;
|
||||
for(Runnable toRun : brokenMicListener)
|
||||
toRun.run();
|
||||
}
|
||||
}else {
|
||||
System.out.println("Un appareil de type "+deviceType2+" s'est déconécté, mais je m'en fout ^^");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
System.out.println("Je ne devait pas recevoir cette commade !");
|
||||
break;
|
||||
}
|
||||
}
|
||||
public void dispose() {
|
||||
speakerLine.close();
|
||||
if(masterAddress!=null) {
|
||||
ByteBuffer decoPacket = ByteBuffer.allocate(AudioServer.packetMaxSize);
|
||||
decoPacket.put(AudioServer.DISCONNECTING);
|
||||
decoPacket.put(AudioServer.SPEAKER_DEVICE);
|
||||
decoPacket.putInt(speakerId);
|
||||
try {
|
||||
serveur.sendData(decoPacket,masterAddress);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if(mics!=null)mics.clear();
|
||||
if(speakerLine!=null) {
|
||||
speakerLine.stop();
|
||||
speakerLine.close();
|
||||
}
|
||||
serveur.dispose();
|
||||
}
|
||||
|
||||
@ -195,7 +235,26 @@ public class SpeakerServer {
|
||||
}
|
||||
|
||||
|
||||
public void addDisconnectListener(Runnable listener) {
|
||||
disconnectListener.add(listener);
|
||||
}
|
||||
public void removeDisconnectListener(Runnable listener) {
|
||||
disconnectListener.remove(listener);
|
||||
}
|
||||
|
||||
public void addBrokenMicListener(Runnable listener) {
|
||||
brokenMicListener.add(listener);
|
||||
}
|
||||
public void removeBrokenMicListener(Runnable listener) {
|
||||
brokenMicListener.remove(listener);
|
||||
}
|
||||
|
||||
public void addMicListUpdateListener(Consumer<Map<Integer, String>> listener) {
|
||||
micListUpdateListener.add(listener);
|
||||
}
|
||||
public void removeMicListUpdateListener(Consumer<Map<Integer, String>> listener) {
|
||||
micListUpdateListener.remove(listener);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -78,6 +78,8 @@ public class GameManager {
|
||||
if(tempOldSave.exists())tempOldSave.delete();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("La sauvegarde rapide n'a pas fonctionné. Elle est donc désactivée.");
|
||||
quickSaver.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package com.bernard.murder.view;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridLayout;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -13,11 +15,18 @@ import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.Mixer;
|
||||
import javax.sound.sampled.Mixer.Info;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.DefaultListModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import com.bernard.murder.audio.AudioServer;
|
||||
import com.bernard.murder.audio.SpeakerServer;
|
||||
@ -28,35 +37,32 @@ public class EnceinteServeurFrame extends JFrame{
|
||||
|
||||
SpeakerServer serveur;
|
||||
String deviceName;
|
||||
NamedMicrophone[] micarray;
|
||||
|
||||
public EnceinteServeurFrame(String deviceName) {
|
||||
this.setSize(300, 500);
|
||||
this.setMinimumSize(new Dimension(100, 200));
|
||||
this.setLocationRelativeTo(null);
|
||||
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
this.setTitle("Serveur audio");
|
||||
this.setTitle("Serveur audio: Enceinte");
|
||||
this.deviceName = deviceName;
|
||||
|
||||
this.setContentPane(genContentPan());
|
||||
this.setVisible(true);
|
||||
}
|
||||
|
||||
public JPanel genContentPan() {
|
||||
public Container genContentPan() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
|
||||
|
||||
panel.setBorder(new EmptyBorder(3, 3, 3, 3));
|
||||
|
||||
InformedSourceDataline[] marray = getEnceinteList();
|
||||
JList<InformedSourceDataline> enceinteListe = new JList<InformedSourceDataline>(marray);
|
||||
|
||||
|
||||
JPanel masterPanel = new JPanel(new BorderLayout());
|
||||
JTextField masterIP = new JTextField("192.168.1.1");
|
||||
JTextField masterIP = new JTextField("192.168.1.1",15);
|
||||
JButton serverControl = new JButton("Lancer");
|
||||
masterPanel.add(serverControl,BorderLayout.EAST);
|
||||
masterPanel.add(masterIP,BorderLayout.CENTER);
|
||||
|
||||
JList<NamedMicrophone> mics = new JList<>();
|
||||
JButton silenceButton = new JButton("Silence");
|
||||
|
||||
serverControl.addActionListener(e->{
|
||||
if(enceinteListe.getSelectedValue()==null)return;
|
||||
@ -64,25 +70,75 @@ public class EnceinteServeurFrame extends JFrame{
|
||||
if(serveur!=null) {
|
||||
serveur.dispose();
|
||||
serveur = null;
|
||||
masterIP.setEnabled(true);
|
||||
mics.setModel(new DefaultListModel<>());
|
||||
serverControl.setText("Lancer");
|
||||
}else {
|
||||
serveur = new SpeakerServer(new InetSocketAddress(masterIP.getText(), AudioServer.communicationPort), deviceName,enceinteListe.getSelectedValue().tdl);
|
||||
masterIP.setEnabled(false);
|
||||
serveur.setServerAnswered(()->{
|
||||
serverControl.setText("Arrêter");
|
||||
List<NamedMicrophone> list = serveur.getAudioList().entrySet().stream().map(et -> new NamedMicrophone(et.getKey(), et.getValue())).collect(Collectors.toList());
|
||||
NamedMicrophone[] micarray = new NamedMicrophone[list.size()];
|
||||
});
|
||||
serveur.addMicListUpdateListener(m -> {
|
||||
List<NamedMicrophone> list = m.entrySet().stream().map(et -> new NamedMicrophone(et.getKey(), et.getValue())).collect(Collectors.toList());
|
||||
micarray = new NamedMicrophone[list.size()];
|
||||
list.toArray(micarray);
|
||||
mics.setListData(micarray);
|
||||
System.out.println("Micros chargés: "+list);
|
||||
});
|
||||
serveur.addDisconnectListener(()->{
|
||||
// Le dispose a été fait
|
||||
serveur = null;
|
||||
masterIP.setEnabled(true);
|
||||
serverControl.setText("Lancer");
|
||||
});
|
||||
serveur.addBrokenMicListener(()->{
|
||||
//XXX Test si ca fonctionne, si ca déclenche bien le listener.
|
||||
mics.clearSelection();
|
||||
serveur.askStopStreaming();
|
||||
});
|
||||
serverControl.setText("Lancement");
|
||||
}
|
||||
serverControl.setEnabled(true);
|
||||
});
|
||||
|
||||
panel.add(masterPanel,BorderLayout.NORTH);
|
||||
panel.add(mics,BorderLayout.SOUTH);
|
||||
panel.add(enceinteListe,BorderLayout.CENTER);
|
||||
mics.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
|
||||
mics.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
||||
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
int i = e.getFirstIndex();
|
||||
boolean wantToListen=mics.isSelectedIndex(i);
|
||||
if(wantToListen && serveur != null)
|
||||
serveur.askForStream(micarray[i].micId);
|
||||
}
|
||||
});
|
||||
|
||||
silenceButton.addActionListener(e -> {
|
||||
mics.clearSelection();
|
||||
if(serveur!=null)
|
||||
serveur.askStopStreaming();
|
||||
});
|
||||
|
||||
JScrollPane jE = new JScrollPane(enceinteListe);
|
||||
jE.setBorder(BorderFactory.createTitledBorder("Enceintes disponibles"));
|
||||
JScrollPane jM = new JScrollPane(mics);
|
||||
jM.setBorder(BorderFactory.createTitledBorder("Microphones à écouter"));
|
||||
|
||||
|
||||
JPanel headP = new JPanel(new BorderLayout());
|
||||
JPanel centerP = new JPanel(new GridLayout(2, 1));
|
||||
|
||||
headP.add(serverControl,BorderLayout.EAST);
|
||||
headP.add(masterIP,BorderLayout.CENTER);
|
||||
centerP.add(jE);
|
||||
centerP.add(jM);
|
||||
|
||||
panel.add(headP,BorderLayout.NORTH);
|
||||
panel.add(centerP,BorderLayout.CENTER);
|
||||
panel.add(silenceButton,BorderLayout.SOUTH);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
|
||||
@ -3,10 +3,15 @@ package com.bernard.murder.view;
|
||||
import java.awt.GridLayout;
|
||||
import java.io.File;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.DataLine;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.TargetDataLine;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JFrame;
|
||||
@ -15,6 +20,7 @@ import javax.swing.JPanel;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
|
||||
import com.bernard.murder.audio.AudioServer;
|
||||
import com.bernard.murder.game.GameCreator;
|
||||
import com.bernard.murder.game.GameCreator.QuicksavedPartie;
|
||||
import com.bernard.murder.game.GameManager;
|
||||
@ -26,6 +32,24 @@ public class LauncherFrame extends JFrame{
|
||||
|
||||
private static final long serialVersionUID = 5831232688024137883L;
|
||||
|
||||
public static void main2(String[] args) throws LineUnavailableException {
|
||||
DataLine.Info ifM = new DataLine.Info(TargetDataLine.class, AudioServer.formatAudio);
|
||||
TargetDataLine mic = (TargetDataLine) AudioSystem.getLine(ifM);
|
||||
//DataLine.Info ifS = new DataLine.Info(SourceDataLine.class, AudioServer.formatAudio);
|
||||
//SourceDataLine spk = (SourceDataLine) AudioSystem.getLine(ifS);
|
||||
|
||||
mic.open(AudioServer.formatAudio);
|
||||
byte[] thedata = new byte[3512];
|
||||
mic.start();
|
||||
for(int i=0;i<500;i++) {
|
||||
System.out.println("Read :"+mic.read(thedata, 0, thedata.length));
|
||||
System.out.println(Arrays.toString(thedata));
|
||||
}
|
||||
mic.close();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new LauncherFrame();
|
||||
}
|
||||
|
||||
@ -12,11 +12,14 @@ import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.Mixer;
|
||||
import javax.sound.sampled.Mixer.Info;
|
||||
import javax.sound.sampled.TargetDataLine;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import com.bernard.murder.audio.AudioServer;
|
||||
import com.bernard.murder.audio.MicServer;
|
||||
@ -29,11 +32,11 @@ public class MicServeurFrame extends JFrame{
|
||||
String deviceName;
|
||||
|
||||
public MicServeurFrame(String deviceName) {
|
||||
this.setSize(300, 500);
|
||||
this.setSize(800, 300);
|
||||
this.setMinimumSize(new Dimension(100, 200));
|
||||
this.setLocationRelativeTo(null);
|
||||
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
this.setTitle("Serveur audio");
|
||||
this.setTitle("Serveur audio: Microphone");
|
||||
this.deviceName = deviceName;
|
||||
|
||||
this.setContentPane(genContentPan());
|
||||
@ -41,7 +44,9 @@ public class MicServeurFrame extends JFrame{
|
||||
}
|
||||
|
||||
public JPanel genContentPan() {
|
||||
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(new EmptyBorder(3, 3, 3, 3));
|
||||
|
||||
|
||||
|
||||
@ -61,19 +66,37 @@ public class MicServeurFrame extends JFrame{
|
||||
if(serveur!=null) {
|
||||
serveur.dispose();
|
||||
serveur = null;
|
||||
masterIP.setEnabled(true);
|
||||
serverControl.setText("Lancer");
|
||||
}else {
|
||||
masterIP.setEnabled(false);
|
||||
serveur = new MicServer(new InetSocketAddress(masterIP.getText(), AudioServer.communicationPort), deviceName,micListe.getSelectedValue().tdl);
|
||||
serveur.setServerAnswered(()->{
|
||||
serverControl.setText("Arrêter");
|
||||
});
|
||||
serveur.addDisconnectListener(()->{
|
||||
// Le dispose a été fait
|
||||
serveur = null;
|
||||
masterIP.setEnabled(true);
|
||||
serverControl.setText("Lancer");
|
||||
});
|
||||
serverControl.setText("Lancement");
|
||||
}
|
||||
serverControl.setEnabled(true);
|
||||
});
|
||||
|
||||
panel.add(masterPanel,BorderLayout.NORTH);
|
||||
panel.add(micListe,BorderLayout.CENTER);
|
||||
JScrollPane jE = new JScrollPane(micListe);
|
||||
jE.setBorder(BorderFactory.createTitledBorder("Microphones disponibles"));
|
||||
|
||||
|
||||
JPanel headP = new JPanel(new BorderLayout());
|
||||
|
||||
headP.add(serverControl,BorderLayout.EAST);
|
||||
headP.add(masterIP,BorderLayout.CENTER);
|
||||
|
||||
panel.add(headP,BorderLayout.NORTH);
|
||||
panel.add(jE,BorderLayout.CENTER);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,23 @@
|
||||
package com.bernard.murder.view.minel;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
|
||||
import com.amihaiemil.eoyaml.Yaml;
|
||||
import com.amihaiemil.eoyaml.YamlMapping;
|
||||
@ -15,20 +31,67 @@ public class ServeurMinel extends Minel {
|
||||
|
||||
public ServeurMinel(GameManager manager) {
|
||||
super(manager);
|
||||
serveur = new AudioServer();
|
||||
try {
|
||||
serveur = new AudioServer();
|
||||
}catch(SocketException | UnknownHostException ex) {
|
||||
JOptionPane.showMessageDialog(null, "Lancement du serveur audio impossible !\n"+ex.getMessage(), "Impossible de lancer le serveur audio", JOptionPane.ERROR_MESSAGE, null);
|
||||
}
|
||||
}
|
||||
|
||||
public ServeurMinel(GameManager manager,YamlMapping ym) {
|
||||
super(manager);
|
||||
serveur = new AudioServer();
|
||||
try {
|
||||
serveur = new AudioServer();
|
||||
}catch(SocketException | UnknownHostException ex) {
|
||||
JOptionPane.showMessageDialog(null, "Lancement du serveur audio impossible !\n"+ex.getMessage(), "Impossible de lancer le serveur audio", JOptionPane.ERROR_MESSAGE, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JPanel genContentPane() {
|
||||
JPanel pan = new JPanel();
|
||||
JLabel label = new JLabel("Rien pour l'instant");
|
||||
pan.add(label);
|
||||
return pan;
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
|
||||
JLabel titre = new JLabel("Status du serveur",JLabel.CENTER);
|
||||
Font ft = titre.getFont();
|
||||
Map<TextAttribute, Object> ftAttrs = new HashMap<>(ft.getAttributes());
|
||||
ftAttrs.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
|
||||
titre.setFont(ft.deriveFont(Font.BOLD).deriveFont(ftAttrs));
|
||||
|
||||
JList<String> microList = new JList<>();
|
||||
JList<String> enceinteListe = new JList<>();
|
||||
|
||||
serveur.addChangeListener(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Updating lists content
|
||||
Collection<String> micsData = serveur.getMics().values();
|
||||
microList.setListData(new Vector<String>(micsData));
|
||||
|
||||
Collection<String> spksData = serveur.getSpeakers().entrySet().stream()
|
||||
.map(e -> e.getValue() + (
|
||||
(serveur.listens(e.getKey())!=-1)
|
||||
?(" <- "+serveur.getMics().get(serveur.listens(e.getKey())))
|
||||
:""))
|
||||
.collect(Collectors.toSet());
|
||||
enceinteListe.setListData(new Vector<String>(spksData));
|
||||
}
|
||||
});
|
||||
|
||||
JScrollPane jE = new JScrollPane(enceinteListe);
|
||||
jE.setBorder(BorderFactory.createTitledBorder("Enceintes connéctées"));
|
||||
JScrollPane jM = new JScrollPane(microList);
|
||||
jM.setBorder(BorderFactory.createTitledBorder("Microphones connéctés"));
|
||||
|
||||
JPanel centerP = new JPanel(new GridLayout(2, 1));
|
||||
|
||||
centerP.add(jE);
|
||||
centerP.add(jM);
|
||||
|
||||
panel.add(titre,BorderLayout.NORTH);
|
||||
panel.add(centerP,BorderLayout.CENTER);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -5,8 +5,7 @@ actions:
|
||||
status:
|
||||
- Mort
|
||||
- Paralysie
|
||||
- NoKill:
|
||||
onStart: true
|
||||
- NoKill
|
||||
|
||||
inventaire:
|
||||
# Tout le monde en a un au départ, sera nommé Portefeuille_Bernard, Portefeuille_Jach
|
||||
@ -43,4 +42,4 @@ joueurs:
|
||||
- "PB ENS"
|
||||
espacePerso:
|
||||
- "Guide Richard"
|
||||
# Par défaut, se place dans le seul espace personnel (Si il y en a deux, pas bon)
|
||||
# Par défaut, se place dans le seul espace personnel (Si il y en a deux, pas bon)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user