diff --git a/.gitignore b/.gitignore index 7a3c30a..db3579d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ bin/ .classpath .settings .project + +*.bernard.quickmurder +*.log diff --git a/build.gradle b/build.gradle index 1d57227..772ccec 100644 --- a/build.gradle +++ b/build.gradle @@ -6,10 +6,8 @@ * User Manual available at https://docs.gradle.org/6.0/userguide/java_library_plugin.html */ -plugins { - // Apply the java-library plugin to add support for Java Library - id 'java-library' -} +apply plugin: 'java' +apply plugin: 'eclipse' repositories { // Use jcenter for resolving dependencies. @@ -18,16 +16,20 @@ repositories { mavenCentral() } +jar { + manifest { + attributes( + 'Main-Class': 'com.bernard.murder.view.LauncherFrame' + ) + } + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} + dependencies { - compile 'com.amihaiemil.web:eo-yaml:4.3.3' + implementation 'com.amihaiemil.web:eo-yaml:5.1.3' + implementation 'com.formdev:flatlaf:0.37' - // This dependency is exported to consumers, that is to say found on their compile classpath. - api 'org.apache.commons:commons-math3:3.6.1' - - // This dependency is used internally, and not exposed to consumers on their own compile classpath. - implementation 'com.google.guava:guava:28.0-jre' - - // Use JUnit test framework - testImplementation 'junit:junit:4.12' } diff --git a/src/main/java/com/bernard/murder/BytesUtils.java b/src/main/java/com/bernard/murder/BytesUtils.java new file mode 100755 index 0000000..912a002 --- /dev/null +++ b/src/main/java/com/bernard/murder/BytesUtils.java @@ -0,0 +1,20 @@ +package com.bernard.murder; + +import java.nio.ByteBuffer; + +public class BytesUtils { + + public static String readString(ByteBuffer buffer) { + int stringLength = buffer.getInt(); + byte[] stringData = new byte[stringLength]; + buffer.get(stringData); + return new String(stringData); + } + + public static void writeString(ByteBuffer buffer, String s) { + byte[] data = s.getBytes(); + buffer.putInt(data.length); + buffer.put(data); + } + +} diff --git a/src/main/java/com/bernard/murder/Murderator.java b/src/main/java/com/bernard/murder/Murderator.java deleted file mode 100644 index c7a608f..0000000 --- a/src/main/java/com/bernard/murder/Murderator.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.bernard.murder; - -import com.bernard.murder.view.MurderatorMainFrame; - -public class Murderator { - - static MurderatorMainFrame mainFrame; - - public static void main(String[] args) { - mainFrame = new MurderatorMainFrame(); - /*File f = new File("/home/mysaa/Documents/eclipse-workspace/Murderator/uneMurder.bernard.tmurder"); - try { - System.out.println(GameCreator.genFromFile(f)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - }*/ - mainFrame.startFrame(); - } - -} diff --git a/src/main/java/com/bernard/murder/ParseUtils.java b/src/main/java/com/bernard/murder/ParseUtils.java index 236ba58..d4af21a 100644 --- a/src/main/java/com/bernard/murder/ParseUtils.java +++ b/src/main/java/com/bernard/murder/ParseUtils.java @@ -2,6 +2,10 @@ package com.bernard.murder; import java.awt.Color; import java.io.IOException; +import java.time.Instant; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.Collection; import java.util.Set; import java.util.Spliterator; @@ -15,11 +19,14 @@ import java.util.stream.StreamSupport; import com.amihaiemil.eoyaml.Yaml; import com.amihaiemil.eoyaml.YamlMapping; +import com.amihaiemil.eoyaml.YamlMappingBuilder; import com.amihaiemil.eoyaml.YamlNode; import com.amihaiemil.eoyaml.YamlSequence; +import com.amihaiemil.eoyaml.YamlSequenceBuilder; public class ParseUtils { + static Pattern timeLengthPattern = Pattern.compile("^(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s?)?$"); public static long parseTimeLength(String tl) { @@ -28,36 +35,56 @@ public class ParseUtils { int h = mtch.group(2)==null?0:Integer.parseInt(mtch.group(2)); int m = mtch.group(4)==null?0:Integer.parseInt(mtch.group(4)); int s = mtch.group(6)==null?0:Integer.parseInt(mtch.group(6)); - return h*3600+m*60+s; + return (h*3600+m*60+s)*1000; } + public static T watch(T el) { + System.out.println(el); + return el; + } + + + public static String dumpTimeLength(long t) { + long h=t/1000/3600, m=t/1000/60%60, s=t/1000%60; + return (h!=0?h+"h":"" )+ (m!=0?m+"m":"")+(s!=0?s+"s":""); + } + + + public static String dumpHourDate(long t) { + return DateTimeFormatter.ISO_LOCAL_TIME.format(LocalTime.ofInstant(Instant.ofEpochMilli(t-t%1000), ZoneId.systemDefault())); + } + + public static Set union(Collection c1, Collection c2){ Set out = c1.stream().collect(Collectors.toSet()); out.addAll(c2); return out; } - public static Set mappingKeys(YamlMapping mapping) throws IOException{ - return mapping.keys().stream().map(n ->{ - try { - return Yaml.createYamlInput(n.toString()).readYamlSequence().string(0); - } catch (IOException e) { - e.printStackTrace(); - } - return null; - }).collect(Collectors.toSet()); - } - public static Stream sequenceStream(YamlSequence sequence){ - return StreamSupport.stream(Spliterators.spliteratorUnknownSize(sequence.iterator(),Spliterator.ORDERED),false); + public static boolean isSubWord(String word, String subword) { + int i=0,j=0; + while(true) { + if(i==subword.length())return true; + if(j==word.length())return false; + if(subword.charAt(i)==word.charAt(j))i++; + j++; + } } - public static String node(YamlNode n){ - try { - return Yaml.createYamlInput(n.toString()).readYamlSequence().string(0); - } catch (IOException e) { - e.printStackTrace(); - } - return null; + public static boolean and(boolean[] bb) { + for(boolean b : bb) + if(!b)return false; + return true; + } + + + public static Set mappingKeys(YamlMapping mapping) throws IOException{ + return mapping.keys().stream().map(n ->n.asScalar().value()).collect(Collectors.toSet()); + } + + + public static Stream sequenceStream(YamlSequence sequence){ + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(sequence.iterator(),Spliterator.ORDERED),false); } public static Function wetherMapping(Function fnot,Function fyes){ @@ -67,8 +94,54 @@ public class ParseUtils { public static Color randColor() { return Color.getHSBColor((float) Math.random(), 1.0f, 1.0f); } + public static Color randDarkColor() { + return Color.getHSBColor((float) Math.random(), 1.0f, 0.3f); + } + public static Color randDarkBlueColor() { + // 180° to 240° + return Color.getHSBColor( (float) (0.5f + 0.2f*Math.random()), 1.0f, 0.3f); + } public static Color getContrastColor(Color color) { double y = (299 * color.getRed() + 587 * color.getGreen() + 114 * color.getBlue()) / 1000; return y >= 128 ? Color.black : Color.white; } + + + public static final YamlMapping setToMapSS(Set nodes, Function key, Function value) { + YamlMappingBuilder builder = Yaml.createYamlMappingBuilder(); + for(M n : nodes) + builder = builder.add(key.apply(n), value.apply(n)); + return builder.build(); + } + public static final YamlMapping setToMapSY(Set nodes, Function key, Function value) { + YamlMappingBuilder builder = Yaml.createYamlMappingBuilder(); + for(M n : nodes) + builder = builder.add(key.apply(n), value.apply(n)); + return builder.build(); + } + public static final YamlMapping setToMapYS(Set nodes, Function key, Function value) { + YamlMappingBuilder builder = Yaml.createYamlMappingBuilder(); + for(M n : nodes) + builder = builder.add(key.apply(n), value.apply(n)); + return builder.build(); + } + public static final YamlMapping setToMapYY(Set nodes, Function key, Function value) { + YamlMappingBuilder builder = Yaml.createYamlMappingBuilder(); + for(M n : nodes) + builder = builder.add(key.apply(n), value.apply(n)); + return builder.build(); + } + public static final YamlSequence setToSeqY(Set nodes) { + YamlSequenceBuilder builder = Yaml.createYamlSequenceBuilder(); + for(YamlNode n : nodes) + builder = builder.add(n); + return builder.build(); + } + public static final YamlSequence setToSeqS(Set nodes) { + YamlSequenceBuilder builder = Yaml.createYamlSequenceBuilder(); + for(String n : nodes) + builder = builder.add(n); + return builder.build(); + } + } diff --git a/src/main/java/com/bernard/murder/YamlUtils.java b/src/main/java/com/bernard/murder/YamlUtils.java new file mode 100755 index 0000000..773dbd0 --- /dev/null +++ b/src/main/java/com/bernard/murder/YamlUtils.java @@ -0,0 +1,120 @@ +package com.bernard.murder; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; + +import com.amihaiemil.eoyaml.Node; +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.YamlMapping; +import com.amihaiemil.eoyaml.YamlMappingBuilder; +import com.amihaiemil.eoyaml.YamlNode; +import com.amihaiemil.eoyaml.YamlSequence; +import com.amihaiemil.eoyaml.YamlSequenceBuilder; + + +public class YamlUtils { + + + public static final YamlSequence listToSeq(List nodes) { + YamlSequenceBuilder ysb = Yaml.createYamlSequenceBuilder(); + for(YamlNode n : nodes) + ysb = ysb.add(n); + return ysb.build(); + } + + public static final YamlSequence listToSeqString(List nodes) { + YamlSequenceBuilder ysb = Yaml.createYamlSequenceBuilder(); + for(String n : nodes) + ysb = ysb.add(n); + return ysb.build(); + } + + public static final YamlMapping mapToMapping(Map nodes) { + YamlMappingBuilder ysb = Yaml.createYamlMappingBuilder(); + for(Entry n : nodes.entrySet()) + ysb = ysb.add(n.getKey(),n.getValue()); + return ysb.build(); + } + + public static final YamlSequence getSequence(YamlNode node) { + if(node.type()==Node.SEQUENCE)return node.asSequence(); + if(node.type()==Node.SCALAR && node.asScalar().value().contentEquals("[]"))return Yaml.createYamlSequenceBuilder().build(); + throw new IllegalArgumentException("Le noeud n'est pas une séquence"); + } + + public static final YamlMapping getMapping(YamlNode node) { + if(node.type()==Node.MAPPING)return node.asMapping(); + if(node.type()==Node.SCALAR && node.asScalar().value().contentEquals("{}"))return Yaml.createYamlMappingBuilder().build(); + throw new IllegalArgumentException("Le noeud n'est pas une séquence"); + } + + public static final boolean isMapping(YamlNode node) { + return (node.type()==Node.MAPPING) || (node.type()==Node.SCALAR && node.asScalar().value().contentEquals("{}")); + } + + public static final boolean isSequence(YamlNode node) { + return (node.type()==Node.SEQUENCE) || (node.type()==Node.SCALAR && node.asScalar().value().contentEquals("[]")); + } + + + + /** + * (Copied from Collectors class) + * Simple implementation class for {@code Collector}. + * + * @param the type of elements to be collected + * @param the type of the result + */ + static class CollectorImpl implements Collector { + private final Supplier supplier; + private final BiConsumer accumulator; + private final BinaryOperator combiner; + private final Function finisher; + private final Set characteristics; + + CollectorImpl(Supplier supplier, + BiConsumer accumulator, + BinaryOperator combiner, + Function finisher, + Set characteristics) { + this.supplier = supplier; + this.accumulator = accumulator; + this.combiner = combiner; + this.finisher = finisher; + this.characteristics = characteristics; + } + + @Override + public BiConsumer accumulator() { + return accumulator; + } + + @Override + public Supplier supplier() { + return supplier; + } + + @Override + public BinaryOperator combiner() { + return combiner; + } + + @Override + public Function finisher() { + return finisher; + } + + @Override + public Set characteristics() { + return characteristics; + } + } + +} diff --git a/src/main/java/com/bernard/murder/audio/AudioServer.java b/src/main/java/com/bernard/murder/audio/AudioServer.java new file mode 100755 index 0000000..174c7d5 --- /dev/null +++ b/src/main/java/com/bernard/murder/audio/AudioServer.java @@ -0,0 +1,182 @@ +package com.bernard.murder.audio; + +import java.io.IOException; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; + +import javax.sound.sampled.AudioFormat; + +import com.bernard.murder.BytesUtils; + +public class AudioServer { + + // Format des paquets [commande 1, UUID identifier, byte deviceType, String name] + public static final byte DECLARE_NUMBER = 0x02; + // Format des paquets [commande 1, UUID identifier, byte deviceType, int id] + public static final byte OK_ID = 0x03; + // Format des paquets [commande 1] + public static final byte ASK_AUDIO_LIST= 0x04; + // 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] + public static final byte ASK_STREAMING = 0x06; + // Format des paquets: [commande 1, int id] + public static final byte START_STREAMING = 0x07; + // Format des paquets: [commande 1, int listenId, int myId] + public static final byte ASK_STOP_STREAMING = 0x09; + // Format des paquets: [commande 1, int id] + public static final byte STOP_STREAMING = 0x08; + // Format des paquets [commande 1, int id, ~ data] + public static final byte AUDIO_STREAM = 0x01; + + + public static final byte SPEAKER_DEVICE = 0x01; + public static final byte MIC_DEVICE = 0x02; + + + public static AudioFormat formatAudio = new AudioFormat(8000f, 16, 1, true, true); + public static int packetMaxSize = 97282; + public static int communicationPort = 35295; + + public Serveur serveur; + + int micId = 0; + int speakerId = 0; + + Map mics; + Map speakers; + Map micsAddr; + Map speakersAddr; + Map> listening; // micId, List + + public AudioServer() { + mics = new HashMap(); + micsAddr = new HashMap(); + speakers = new HashMap(); + speakersAddr = new HashMap(); + listening = new HashMap>(); + + + try { + initServer(); + } catch (SocketException | UnknownHostException e) { + e.printStackTrace(); + } + } + + public void initServer() throws SocketException, UnknownHostException { + serveur = new Serveur(this::receiveCommand, AudioServer.communicationPort); + } + + public void receiveCommand(ByteBuffer data,SocketAddress senderAddress) { + byte commande = data.get(); + System.out.println("Commande reçue : "+commande); + switch (commande) { + + case AudioServer.DECLARE_NUMBER: + + UUID uuid = new UUID(data.getLong(), data.getLong()); + byte deviceType = data.get(); + String deviceName = BytesUtils.readString(data); + int newId; + switch (deviceType) { + case AudioServer.MIC_DEVICE: + newId = micId++; + mics.put(newId, deviceName); + micsAddr.put(newId, senderAddress); + listening.put(newId, new ArrayList<>()); + break; + case AudioServer.SPEAKER_DEVICE: + newId = speakerId++; + speakers.put(newId, deviceName); + speakersAddr.put(newId, senderAddress); + break; + default:return; + } + ByteBuffer out = ByteBuffer.allocate(AudioServer.packetMaxSize); + out.put(AudioServer.OK_ID); + out.putLong(uuid.getMostSignificantBits()); + out.putLong(uuid.getLeastSignificantBits()); + out.put(deviceType); + out.putInt(newId); + try { + serveur.sendData(out, senderAddress); + } catch (IOException e1) { + e1.printStackTrace(); + } + break; + case AudioServer.ASK_STREAMING: + int listened = data.getInt(); + int listener = data.getInt(); + listening.get(listened).add(listener); + ByteBuffer out3 = ByteBuffer.allocate(AudioServer.packetMaxSize); + out3.put(AudioServer.START_STREAMING); + out3.putInt(listened); + try { + serveur.sendData(out3, micsAddr.get(listened)); + } catch (IOException e2) { + e2.printStackTrace(); + } + break; + + case AudioServer.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(); + } + break; + + case AudioServer.AUDIO_STREAM: + int micId = data.getInt(); + byte[] audioData = new byte[data.remaining()]; + data.get(audioData); + + + for(int spck : listening.get(micId)) { + data.clear(); + SocketAddress dest = speakersAddr.get(spck); + try { + serveur.sendData(data, dest); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + break; + + + case AudioServer.ASK_AUDIO_LIST: + ByteBuffer out2 = ByteBuffer.allocate(AudioServer.packetMaxSize); + out2.put(AudioServer.GIVE_AUDIO_LIST); + out2.putInt(mics.size()); + for(Entry e : mics.entrySet()) { + out2.putInt(e.getKey()); + BytesUtils.writeString(out2, e.getValue()); + } + try { + serveur.sendData(out2, senderAddress); + } catch (IOException e1) { + e1.printStackTrace(); + } + break; + + default: + break; + } + } + +} diff --git a/src/main/java/com/bernard/murder/audio/MicServer.java b/src/main/java/com/bernard/murder/audio/MicServer.java new file mode 100755 index 0000000..e068b96 --- /dev/null +++ b/src/main/java/com/bernard/murder/audio/MicServer.java @@ -0,0 +1,150 @@ +package com.bernard.murder.audio; + +import java.io.IOException; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.UUID; + +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.TargetDataLine; + +import com.bernard.murder.BytesUtils; + +public class MicServer { + + TargetDataLine micLine; + + int packetLength = 9728; + + int micId; + + String micName; + + UUID askedUUID; + + Serveur serveur; + + SocketAddress masterAddress; + + Thread streamingThread; + + volatile boolean shouldStream = false; + + Runnable serverAnswered; + + + public MicServer(SocketAddress adresse,String micName,TargetDataLine tdl) { + this.micName = micName; + this.masterAddress = adresse; + this.micLine = tdl; + try { + initServer(); + initializeAudioId(); + + } catch (SocketException | UnknownHostException e) { + e.printStackTrace(); + } + } + + public void initializeMicDevice() throws LineUnavailableException { + micLine.open(AudioServer.formatAudio); + packetLength = micLine.getBufferSize()/5; + } + + public void initServer() throws SocketException, UnknownHostException { + serveur = new Serveur(this::receiveCommand, AudioServer.communicationPort); + } + + public void initializeAudioId() { + ByteBuffer buffer = ByteBuffer.allocate(AudioServer.packetMaxSize); + + + askedUUID = UUID.randomUUID(); + + buffer.put(AudioServer.DECLARE_NUMBER); + buffer.putLong(askedUUID.getMostSignificantBits()); + buffer.putLong(askedUUID.getLeastSignificantBits()); + buffer.put(AudioServer.MIC_DEVICE); + + BytesUtils.writeString(buffer, micName); + + try { + serveur.sendData(buffer,masterAddress); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void receiveCommand(ByteBuffer data) { + byte commande = data.get(); + switch (commande) { + + case AudioServer.START_STREAMING: + int micId = data.getInt(); + if(micId != this.micId)return; + shouldStream = true; + launchDataStream(); + break; + case AudioServer.STOP_STREAMING: + int micId2 = data.getInt(); + if(micId2 != this.micId)return; + shouldStream = false; + break; + + case AudioServer.OK_ID: + UUID uuid = new UUID(data.getLong(), data.getLong()); + byte deviceType = data.get(); + int deviceId = data.getInt(); + + if(!askedUUID.equals(uuid) || deviceType!=AudioServer.MIC_DEVICE) + return; + + micId = deviceId; + + serverAnswered.run(); + + if(micLine==null) + try { + initializeMicDevice(); + } catch (LineUnavailableException e) { + e.printStackTrace(); + } + break; + + default: + break; + } + } + + public void launchDataStream() { + if(streamingThread!=null)return; + streamingThread = new Thread(()->{ + byte[] packetData = new byte[1+4+packetLength]; + ByteBuffer audioPacket = ByteBuffer.wrap(packetData); + audioPacket.put(AudioServer.AUDIO_STREAM); + audioPacket.putInt(micId); + while(shouldStream) { + micLine.read(packetData, 5, packetLength); + try { + serveur.sendData(audioPacket,masterAddress); + } catch (IOException e) { + e.printStackTrace(); + } + + } + }); + streamingThread.start(); + } + + public void dispose() { + micLine.close(); + serveur.dispose(); + } + + public void setServerAnswered(Runnable serverAnswered) { + this.serverAnswered = serverAnswered; + } + +} diff --git a/src/main/java/com/bernard/murder/audio/Serveur.java b/src/main/java/com/bernard/murder/audio/Serveur.java new file mode 100755 index 0000000..ceffaec --- /dev/null +++ b/src/main/java/com/bernard/murder/audio/Serveur.java @@ -0,0 +1,158 @@ +package com.bernard.murder.audio; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import com.bernard.murder.ParseUtils; + +public class Serveur { + + int packetMaxLength = 9600; + // (commande 1, uuid 16, short sliceCount 4, short sliceId 4, int totalSize 8, int offset 8, data ~) + byte dataMergeCommand = (byte) 0xFF; + DatagramSocket socket; + Thread packetReceiver; + Map byteArrays = new HashMap<>(); + Map receivedSlicesArray = new HashMap<>(); + + volatile boolean isReceiving = false; + + BiConsumer consumer; + + public Serveur(Consumer dataEater, int port) throws UnknownHostException, SocketException { + this((b,i)->dataEater.accept(b),new InetSocketAddress(port)); + } + public Serveur(Consumer dataEater,SocketAddress addresse) throws UnknownHostException, SocketException { + this((b,i)->dataEater.accept(b),addresse); + } + public Serveur(BiConsumer dataEater,int port) throws UnknownHostException, SocketException { + this(dataEater,new InetSocketAddress(port)); + } + + public Serveur(BiConsumer dataEater,SocketAddress adresse) throws SocketException { + + byteArrays = new HashMap(); + receivedSlicesArray = new HashMap(); + this.consumer = dataEater; + this.socket = new DatagramSocket(adresse); + + packetReceiver = new Thread(new Runnable() { + + @Override + public void run() { + while(isReceiving) { + + byte[] data = new byte[packetMaxLength]; + DatagramPacket paquet = new DatagramPacket(data, packetMaxLength); + + try { + try { + socket.receive(paquet); + }catch(SocketException e) { + return;//probably socket closed + } + if(data[paquet.getOffset()]==0xFF) { + ByteBuffer bb = ByteBuffer.wrap(data, paquet.getOffset() + 1, 41); + UUID uuid = new UUID(bb.getLong(), bb.getLong()); + short sliceCount = bb.getShort(); + short sliceId = bb.getShort(); + int totalSize = bb.getInt(); + int offset = bb.getInt(); + + + if(!receivedSlicesArray.containsKey(uuid)) { + byte[] bigData = new byte[totalSize]; + boolean[] recievedArray = new boolean[sliceCount]; + byteArrays.put(uuid, bigData); + receivedSlicesArray.put(uuid, recievedArray); + } + byte[] bigData = byteArrays.get(uuid); + boolean[] recievedArray = receivedSlicesArray.get(uuid); + System.arraycopy(data, paquet.getOffset()+41, bigData, offset, paquet.getLength()-41); + recievedArray[sliceId] = true; + if(!ParseUtils.and(recievedArray)) { + ByteBuffer dataBuffer = ByteBuffer.wrap(bigData); + consumer.accept(dataBuffer,paquet.getSocketAddress()); + byteArrays.remove(uuid); + receivedSlicesArray.remove(uuid); + } + }else { + ByteBuffer dataBuffer = ByteBuffer.wrap(data,paquet.getOffset(),paquet.getLength()); + consumer.accept(dataBuffer,paquet.getSocketAddress()); + } + + } catch (IOException e) { + e.printStackTrace(); + } + + } + } + }); + isReceiving = true; + packetReceiver.start(); + } + + public void sendData(ByteBuffer buffer,SocketAddress address) throws IOException { + byte[] data = new byte[buffer.position()]; + buffer.clear(); + 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); + socket.send(packet); + }else { + short packetCount = (short) (data.length / (packetMaxLength-42)); + short packetLength = (short) (data.length / packetCount); + short lastPacketLength = (short) (data.length - (packetCount-1)*packetLength); + + UUID uuid = UUID.randomUUID(); + byte[][] dataz = new byte[packetCount][]; + for (short i = 0; i < dataz.length-1; i++) { + byte[] packagePayload = new byte[packetLength+41]; + ByteBuffer buffer = ByteBuffer.wrap(packagePayload, 0, 41); + buffer.put(dataMergeCommand); + buffer.putLong(uuid.getMostSignificantBits()); + buffer.putLong(uuid.getLeastSignificantBits()); + buffer.putShort(packetCount); + buffer.putShort(i); + buffer.putInt(data.length); + buffer.putInt(i*packetLength); + System.arraycopy(data, 0, packagePayload, 41, packetLength); + dataz[i]=packagePayload; + } + byte[] packagePayload = new byte[packetLength+41]; + ByteBuffer buffer = ByteBuffer.wrap(packagePayload, 0, 41); + buffer.put(dataMergeCommand); + buffer.putLong(uuid.getMostSignificantBits()); + buffer.putLong(uuid.getLeastSignificantBits()); + buffer.putShort(packetCount); + buffer.putShort((short) (packetCount-1)); + buffer.putInt(data.length); + buffer.putInt((packetCount-1)*packetLength); + System.arraycopy(data, 0, packagePayload, 41, lastPacketLength); + dataz[packetCount-1]=packagePayload; + + + + } + } + public void dispose() { + isReceiving = false; + socket.close(); + packetReceiver.interrupt(); + } + +} diff --git a/src/main/java/com/bernard/murder/audio/SpeakerServer.java b/src/main/java/com/bernard/murder/audio/SpeakerServer.java new file mode 100755 index 0000000..2d03963 --- /dev/null +++ b/src/main/java/com/bernard/murder/audio/SpeakerServer.java @@ -0,0 +1,200 @@ +package com.bernard.murder.audio; + +import java.io.IOException; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.SourceDataLine; + +import com.bernard.murder.BytesUtils; + + +public class SpeakerServer { + + SourceDataLine speakerLine; + + int packetLength = 9728; + + int speakerId; + + String speakerName; + + UUID askedUUID; + + Serveur serveur; + + Map mics; + volatile boolean isMicListUpToDate = false; + + int listeningTo = -1; + + SocketAddress masterAddress; + + Runnable serverAnswered; + + + public SpeakerServer(SocketAddress serveur,String speakerName,SourceDataLine speaker) { + this.speakerName = speakerName; + this.masterAddress = serveur; + this.speakerLine = speaker; + try { + initServer(); + initializeAudioId(); + } catch (SocketException | UnknownHostException e) { + e.printStackTrace(); + } + } + + public void initServer() throws SocketException, UnknownHostException { + serveur = new Serveur(this::receiveCommand,AudioServer.communicationPort); + } + + public void initializeSpeakerDevice() throws LineUnavailableException { + speakerLine.open(AudioServer.formatAudio); + packetLength = speakerLine.getBufferSize()/5; + } + + public void initializeAudioId() { + ByteBuffer buffer = ByteBuffer.allocate(AudioServer.packetMaxSize); + + + askedUUID = UUID.randomUUID(); + + buffer.put(AudioServer.DECLARE_NUMBER); + buffer.putLong(askedUUID.getMostSignificantBits()); + buffer.putLong(askedUUID.getLeastSignificantBits()); + buffer.put(AudioServer.SPEAKER_DEVICE); + + BytesUtils.writeString(buffer, speakerName); + + + try { + serveur.sendData(buffer,masterAddress); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void askForStream(int micId) { + ByteBuffer buffer = ByteBuffer.allocate(AudioServer.packetMaxSize); + + + buffer.put(AudioServer.START_STREAMING); + buffer.putInt(micId); + buffer.putInt(speakerId); + + try { + serveur.sendData(buffer,masterAddress); + listeningTo = micId; + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void stopStreaming() { + ByteBuffer buffer = ByteBuffer.allocate(AudioServer.packetMaxSize); + + + buffer.put(AudioServer.STOP_STREAMING); + buffer.putInt(listeningTo); + buffer.putInt(speakerId); + + try { + serveur.sendData(buffer,masterAddress); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void askAudioList() { + ByteBuffer buffer = ByteBuffer.allocate(AudioServer.packetMaxSize); + buffer.put(AudioServer.ASK_AUDIO_LIST); + try { + serveur.sendData(buffer,masterAddress); + } catch (IOException e) { + e.printStackTrace(); + } + } + public Map getAudioList() { + return getAudioList(false); + } + + public Map getAudioList(boolean invalidate) { + isMicListUpToDate = !invalidate && isMicListUpToDate; + if(!isMicListUpToDate)askAudioList(); + while(!isMicListUpToDate) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + System.out.println("Voici les "+mics); + return mics; + } + + public void receiveCommand(ByteBuffer data) { + byte commande = data.get(); + System.out.println("Commande recue : "+commande); + switch (commande) { + + case AudioServer.AUDIO_STREAM: + int micId = data.getInt(); + if(micId != listeningTo)return; + data.compact(); + 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); + break; + + case AudioServer.OK_ID: + UUID uuid = new UUID(data.getLong(), data.getLong()); + byte deviceType = data.get(); + int deviceId = data.getInt(); + + if(!askedUUID.equals(uuid) || deviceType!=AudioServer.SPEAKER_DEVICE) + return; + + speakerId = deviceId; + serverAnswered.run(); + if(speakerLine==null) + try { + initializeSpeakerDevice(); + } catch (LineUnavailableException e) { + e.printStackTrace(); + } + break; + case AudioServer.GIVE_AUDIO_LIST: + int micCount = data.getInt(); + mics = new HashMap(micCount); + for(int i = 0; i playerNames = mappingKeys(yjoueurs).stream().map(n -> n.toString()).collect(Collectors.toSet()); - YamlMapping yactions = globalMap.yamlMapping("actions"); + YamlMapping yactions = getMapping(globalMap.value("actions")); Set actions = yactions.keys() .stream() - .map(n -> new Action(node(n), parseTimeLength(yactions.string(n)))) + .map(n -> new Action(n.asScalar().value(), parseTimeLength(yactions.string(n)))) .collect(Collectors.toSet()); Map> persActions = playerNames.stream() .collect(Collectors.toMap(Function.identity(), s -> actions.stream().map(Action::clone).collect(Collectors.toSet()))); - YamlSequence yinventory = globalMap.yamlSequence("inventaire"); + + YamlSequence yinventory = getSequence(globalMap.value("inventaire")); + Set objets = StreamSupport.stream(Spliterators.spliteratorUnknownSize(yinventory.iterator(),Spliterator.ORDERED),false) - .map(n ->ParseUtils.node(n)) + .map(n ->n.asScalar().value()) .collect(Collectors.toSet()); Map> persObjets = playerNames.stream() .collect(Collectors.toMap(Function.identity(),p -> objets.stream().map(o ->new Objet(String.format(o,p))).collect(Collectors.toSet()))); - YamlSequence ystatus = globalMap.yamlSequence("status"); - Set status = sequenceStream(ystatus).map(n -> new Status(node(n))).collect(Collectors.toSet()); + YamlSequence ystatus = getSequence(globalMap.value("status")); + Set status = sequenceStream(ystatus).map(n -> new Status(n.asScalar().value())).collect(Collectors.toSet()); - YamlMapping yespaces = globalMap.yamlMapping("espaces"); + YamlMapping yespaces = getMapping(globalMap.value("espaces")); Map> objetsDansEspaces = yespaces.keys().stream().collect(Collectors.toMap( - n -> node(n), - n-> parseHiddenObjects(yespaces.yamlSequence(n)) + n -> n.asScalar().value(), + n-> parseHiddenObjects(getSequence(yespaces.value(n))) )); Set espaceObjets = yespaces.keys().stream() - .map(n -> new Piece(node(n), objetsDansEspaces.get(node(n)))) + .map(n -> new Piece(n.asScalar().value(), objetsDansEspaces.get(n.asScalar().value()))) .collect(Collectors.toSet()); - YamlMapping yespacesPersos = globalMap.yamlMapping("espacesPersos"); + YamlMapping yespacesPersos = getMapping(globalMap.value("espacesPersos")); Map> persespacesPersos = playerNames.stream().collect(Collectors.toMap( Function.identity(), p -> yespacesPersos.keys().stream() .map(e -> new Piece( - String.format(node(e), p), - parseHiddenObjects(yespacesPersos.yamlSequence(e),p) + String.format(e.asScalar().value(), p), + parseHiddenObjects(getSequence(yespacesPersos.value(e)),p) )) .collect(Collectors.toSet()) )); //Per perso settings for(YamlNode pn : yjoueurs.keys()) { - String pname = node(pn); - System.out.println(pn); - System.out.println(yjoueurs.yamlMapping(pn)); + String pname = pn.asScalar().value(); persActions.get(pname).addAll( - yjoueurs.yamlMapping(pn).yamlMapping("actions").keys() + getMapping(getMapping(yjoueurs.value(pn)).value("actions")).keys() .stream() - .map(n -> new Action(node(n), parseTimeLength(yjoueurs.yamlMapping(pn).yamlMapping("actions").string(n)))) + .map(n -> new Action(n.asScalar().value(), parseTimeLength(getMapping(getMapping(yjoueurs.value(pn)).value("actions")).string(n)))) .collect(Collectors.toSet()) ); persObjets.get(pname).addAll( - StreamSupport.stream(Spliterators.spliteratorUnknownSize(yjoueurs.yamlMapping(pn).yamlSequence("inventaire").iterator(),Spliterator.ORDERED),false) - .map(n ->ParseUtils.node(n)) + StreamSupport.stream(Spliterators.spliteratorUnknownSize(getSequence(getMapping(yjoueurs.value(pn)).value("inventaire")).iterator(),Spliterator.ORDERED),false) + .map(n ->n.asScalar().value()) .map(o ->new Objet(o)) .collect(Collectors.toSet()) ); - if(yjoueurs.yamlMapping(pn).yamlMapping("espacePerso")!= null) + if(isMapping(getMapping(yjoueurs.value(pn)).value("espacePerso"))) // Plusieurs espaces - yjoueurs.yamlMapping(pn).yamlMapping("espacePerso").keys().forEach(n -> + getMapping(getMapping(yjoueurs.value(pn)).value("espacePerso")).keys().forEach(n -> persespacesPersos.get(pname) .stream() - .filter(p -> p.getNom().contentEquals(node(n))) + .filter(p -> p.getNom().contentEquals(n.asScalar().value())) .findAny() - .orElseGet(() -> new Piece(node(n))) - .insertObjects(parseHiddenObjects(yjoueurs.yamlMapping(pn).yamlMapping("espacePerso").yamlSequence(n))) + .orElseGet(() -> new Piece(n.asScalar().value())) + .insertObjects(parseHiddenObjects(getSequence(getMapping(getMapping(yjoueurs.value(pn)).value("espacePerso")).value(n)))) ); else ((persespacesPersos.get(pname).isEmpty())? Stream.of(new Piece(String.format("Espace de %s",pname))):persespacesPersos.get(pname).stream()) - .forEach(p -> p.insertObjects(parseHiddenObjects(yjoueurs.yamlMapping(pn).yamlSequence("espacePerso")))); + .forEach(p -> p.insertObjects(parseHiddenObjects(getSequence(getMapping(yjoueurs.value(pn)).value("espacePerso"))))); } @@ -126,8 +137,8 @@ public class GameCreator { persActions.get(p), status.stream().filter(st -> sequenceStream(ystatus) .filter(n -> n instanceof YamlMapping) - .filter(n -> ((YamlMapping)n).string(st.getName())!=null) - .filter(n -> ((YamlMapping)((YamlMapping)n).yamlMapping(st.getName())).string("onStart").contentEquals("true")) + .filter(n -> getMapping(n).string(st.getName())!=null) + .filter(n -> getMapping(getMapping(n).value(st.getName())).string("onStart").contentEquals("true")) .findAny().isPresent()) .collect(Collectors.toSet()), persespacesPersos.get(p) @@ -138,12 +149,130 @@ public class GameCreator { private static Map parseHiddenObjects(YamlSequence sequence,Object... nameFormat){ return sequenceStream(sequence).collect(Collectors.toMap( - on -> new Objet(String.format((on instanceof YamlMapping)?(((YamlMapping)on).keys().stream().map(nn -> node(nn)).findAny().get()):node(on),nameFormat)), - on -> ((on instanceof YamlMapping)?(((YamlMapping)on).integer(((YamlMapping)on).keys().stream().findAny().get())):-1) + on -> new Objet(String.format((on.type()==Node.MAPPING)?(on.asMapping().keys().stream().map(nn -> nn.asScalar().value()).findAny().get()):on.asScalar().value(),nameFormat)), + on -> ((on.type()==Node.MAPPING)?(on.asMapping().integer(on.asMapping().keys().stream().findAny().get())):-1) )); } private static Map parseHiddenObjects(YamlSequence sequence){ return parseHiddenObjects(sequence, new Object[0]); } + + public static final void quickSave(File f, Partie p, YamlMapping minelsMap) throws IOException { + YamlMappingBuilder globalMapping = Yaml.createYamlMappingBuilder() + + .add("personnages", + ParseUtils.setToMapSY(p.personnagesStream().collect(Collectors.toSet()), + perso -> perso.getNom(), + perso -> + Yaml.createYamlMappingBuilder() + + .add("actions",ParseUtils.setToMapSY(perso.getActions(), + act -> act.getName(), + act -> Yaml.createYamlMappingBuilder() + .add("basetime", Long.toString(act.getBasetime())) + .add("triggerTime", Long.toString(act.getTriggertime())) + .build())) + + .add("espacePerso",ParseUtils.setToMapSY(perso.streamEspacesPersos().collect(Collectors.toSet()), + esp -> esp.getNom(), + esp -> ParseUtils.setToSeqY(esp.streamHiddenObjects().map(etr -> Yaml.createYamlMappingBuilder().add(etr.getKey().getNom(), Integer.toString(etr.getValue())).build()).collect(Collectors.toSet())) + )) + + .add("status", ParseUtils.setToSeqS(perso.streamStatus().map(Status::getName).collect(Collectors.toSet()))) + + .add("inventaire", ParseUtils.setToSeqS(perso.getInventaire().stream().map(Objet::getNom).collect(Collectors.toSet()))).build() + )) + + .add("status", ParseUtils.setToSeqS(p.statuzStream().map(Status::getName).collect(Collectors.toSet()))) + + .add("pieces",ParseUtils.setToMapSY(p.piecesStream().collect(Collectors.toSet()), + Piece::getNom, + esp -> ParseUtils.setToSeqY(esp.streamHiddenObjects().map(etr -> Yaml.createYamlMappingBuilder().add(etr.getKey().getNom(), Integer.toString(etr.getValue())).build()).collect(Collectors.toSet())) + )) + .add("minels", minelsMap); + + + YamlPrinter printer = Yaml.createYamlPrinter(new FileWriter(f)); + + printer.print(globalMapping.build()); + + } + + + public static final QuicksavedPartie readQuickSave(File f) throws IOException { + YamlInput input = Yaml.createYamlInput(f); + YamlMapping globalMap = input.readYamlMapping(); + + YamlSequence ystatus = getSequence(globalMap.value("status")); + Set status = sequenceStream(ystatus).map(n -> new Status(n.asScalar().value())).collect(Collectors.toSet()); + + + YamlMapping yespaces = getMapping(globalMap.value("pieces")); + Map> objetsDansEspaces = yespaces.keys().stream().collect(Collectors.toMap( + n -> watch(watch(n).asScalar().value()), + n-> parseHiddenObjects(getSequence(yespaces.value(n))) + )); + Set espaceObjets = yespaces.keys().stream() + .map(n -> new Piece(n.asScalar().value(), objetsDansEspaces.get(n.asScalar().value()))) + .collect(Collectors.toSet()); + + + YamlMapping yjoueurs = getMapping(globalMap.value("personnages")); + + Set personnages = new HashSet(); + for(YamlNode pn : yjoueurs.keys()) { + String pname = pn.asScalar().value(); + + Set actions = getMapping(getMapping(yjoueurs.value(pn)).value("actions")).keys() + .stream() + .map(n -> new Action(n.asScalar().value(), Long.parseLong(getMapping(getMapping(getMapping(yjoueurs.value(pn)).value("actions")).value(n)).string("basetime")), + Long.parseLong(getMapping(getMapping(getMapping(yjoueurs.value(pn)).value("actions")).value(n)).string("triggerTime")))) + .collect(Collectors.toSet()); + + Set inventaire = + StreamSupport.stream(Spliterators.spliteratorUnknownSize(getSequence(getMapping(yjoueurs.value(pn)).value("inventaire")).iterator(),Spliterator.ORDERED),false) + .map(n ->n.asScalar().value()) + .map(o ->new Objet(o)) + .collect(Collectors.toSet()); + + Set espacesPerso = getMapping(getMapping(yjoueurs.value(pn)).value("espacePerso")).keys().stream().map(n -> + new Piece(n.asScalar().value(), parseHiddenObjects(getSequence(getMapping(getMapping(yjoueurs.value(pn)).value("espacePerso")).value(n))))).collect(Collectors.toSet()); + + Set persoStatus = status.stream().filter( + s -> !isSequence(getMapping(yjoueurs.value(pn)).value("status"))?false:getSequence(getMapping(yjoueurs.value(pn)).value("status")).values().stream().anyMatch(n -> n.asScalar().value().equals(s.getName())) + ).collect(Collectors.toSet()); + + + personnages.add(new Personnage(pname, inventaire, actions, persoStatus, espacesPerso)); + } + + YamlMapping minelsMap = getMapping(globalMap.value("minels")); + + return new QuicksavedPartie(new Partie(personnages, status, espaceObjets),minelsMap); + + } + + public static class QuicksavedPartie{ + Partie partie; + YamlMapping minelsMap; + + + public QuicksavedPartie(Partie partie, YamlMapping minelsMap) { + this.partie = partie; + this.minelsMap = minelsMap; + } + + public Partie getPartie() { + return partie; + } + + + public YamlMapping getMinelsMap() { + return minelsMap; + } + + + } + } diff --git a/src/main/java/com/bernard/murder/game/GameManager.java b/src/main/java/com/bernard/murder/game/GameManager.java index 7040a93..7a87ff4 100644 --- a/src/main/java/com/bernard/murder/game/GameManager.java +++ b/src/main/java/com/bernard/murder/game/GameManager.java @@ -1,13 +1,126 @@ package com.bernard.murder.game; +import java.io.File; +import java.io.IOException; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import javax.swing.Timer; + +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.YamlMapping; +import com.bernard.murder.model.Inventaire; +import com.bernard.murder.model.Objet; import com.bernard.murder.model.Partie; +import com.bernard.murder.model.Personnage; public class GameManager { Partie partie; + Map> inventoryUpdateListeners; + + long startTime; + + Timer quickSaver; + + Supplier minelsQuicksaver; public GameManager(Partie partie) { this.partie = partie; + this.inventoryUpdateListeners = new HashMap>(); + this.minelsQuicksaver = () -> Yaml.createYamlMappingBuilder().build(); + startTime = System.currentTimeMillis(); + + quickSaver = new Timer(10_000, e -> this.quickSave()); + quickSaver.start(); + } + + public synchronized void moveObjet(Objet o, Inventaire toInv) { + Inventaire inv = partie.findObjectInventory(o); + System.out.println("Moving "+o+" from "+inv+" to "+toInv); + inv.removeObjet(o); + toInv.addObjet(o); + inventoryUpdate(inv); + inventoryUpdate(toInv); + } + + public void inventoryUpdate(Inventaire inv) { + if(!inventoryUpdateListeners.containsKey(inv))return; + for(Runnable r : inventoryUpdateListeners.get(inv)) + r.run(); + } + + public void dumpCurrentState() { + System.out.println(partie); + } + + public void addInventoryUpdateListener(Inventaire inv, Runnable runnable) { + if(!inventoryUpdateListeners.containsKey(inv)) + inventoryUpdateListeners.put(inv, new HashSet()); + inventoryUpdateListeners.get(inv).add(runnable); + } + + public void quickSave() { + System.out.println("Quicksaving"); + File toSave = new File(quickSaveFilename()); + File tempOldSave = new File(quickSaveFilename()+".tmp"); + if(toSave.exists())toSave.renameTo(tempOldSave); + + try { + GameCreator.quickSave(toSave, partie,minelsQuicksaver.get()); + System.out.println("Quicksaved"); + if(tempOldSave.exists())tempOldSave.delete(); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + public String quickSaveFilename() { + return "murder-"+DateTimeFormatter.ofPattern("uu-MM-dd_HH'h'mm").withZone(ZoneId.systemDefault()).withLocale(Locale.getDefault()).format(Instant.ofEpochMilli(startTime))+".bernard.quickmurder"; + } + + public Set getEveryInventaire() { + Set inventaires = new HashSet(); + inventaires.addAll(partie.pieces()); + inventaires.addAll(partie.personnages()); + partie.personnagesStream().forEach(p -> inventaires.addAll(p.espacePersos())); + return inventaires; + } + + public Set getEveryInventaireByName(Set names) { + Set inventaires = new HashSet(); + inventaires.addAll(partie.pieces()); + inventaires.addAll(partie.personnages()); + partie.personnagesStream().forEach(p -> inventaires.addAll(p.espacePersos())); + return inventaires.stream().filter(i -> names.contains(i.getInventoryName())).collect(Collectors.toSet()); + } + + public Personnage getPersoByName(String key) { + return partie.personnagesStream().filter(p -> key.equalsIgnoreCase(p.getNom())).findAny().orElse(null); + } + + public void bindMinelQuicksaver(Supplier minelsQuicksaver) { + this.minelsQuicksaver = minelsQuicksaver; + } + + public Inventaire getInventoryByName(String name) { + Set inventaires = new HashSet(); + inventaires.addAll(partie.pieces()); + inventaires.addAll(partie.personnages()); + partie.personnagesStream().forEach(p -> inventaires.addAll(p.espacePersos())); + return inventaires.stream().filter(i -> name.equalsIgnoreCase(i.getInventoryName())).findAny().orElseGet(()->{ + System.out.println("JE n'ai pas trouvé l'inventaire "+name+" dans la liste "+inventaires.stream().map(Inventaire::getInventoryName).collect(Collectors.joining(","))); + return null; + }); } } diff --git a/src/main/java/com/bernard/murder/model/Action.java b/src/main/java/com/bernard/murder/model/Action.java index 32afae9..cf25d98 100644 --- a/src/main/java/com/bernard/murder/model/Action.java +++ b/src/main/java/com/bernard/murder/model/Action.java @@ -9,9 +9,17 @@ public class Action implements Cloneable{ public Action(String action, long basetime) { this.action = action; this.basetime = basetime; - this.triggertime = Long.MIN_VALUE; + this.triggertime = 0; } + public Action(String action, long basetime, long triggertime) { + this.action = action; + this.basetime = basetime; + this.triggertime = triggertime; + } + + + @Override public Action clone() { Action actions = new Action(action, basetime); @@ -24,6 +32,38 @@ public class Action implements Cloneable{ return "Action [action=" + action + ", basetime=" + basetime + ", triggertime=" + triggertime + ", transient id=" + System.identityHashCode(this) + "]"; } + public String getName() { + return action; + } + + public boolean canBeLaunched() { + return System.currentTimeMillis()-triggertime-basetime>0; + } + + public void launch() { + triggertime=System.currentTimeMillis(); + } + + public boolean hasFinished() { + return triggertime + basetime - System.currentTimeMillis()<0; + } + + public long timeToWaitLeft() { + return basetime - System.currentTimeMillis() + triggertime; + } + + public long dateReset() { + return triggertime + basetime; + } + + public long getBasetime() { + return basetime; + } + + public long getTriggertime() { + return triggertime; + } + } diff --git a/src/main/java/com/bernard/murder/model/Inventaire.java b/src/main/java/com/bernard/murder/model/Inventaire.java index 46ab536..59712cc 100644 --- a/src/main/java/com/bernard/murder/model/Inventaire.java +++ b/src/main/java/com/bernard/murder/model/Inventaire.java @@ -6,4 +6,10 @@ public interface Inventaire { Set getObjects(); + void removeObjet(Objet o); + + void addObjet(Objet o); + + String getInventoryName(); + } diff --git a/src/main/java/com/bernard/murder/model/Partie.java b/src/main/java/com/bernard/murder/model/Partie.java index 1e1d173..d7cedff 100644 --- a/src/main/java/com/bernard/murder/model/Partie.java +++ b/src/main/java/com/bernard/murder/model/Partie.java @@ -1,10 +1,12 @@ package com.bernard.murder.model; +import java.util.Optional; import java.util.Set; import java.util.stream.Stream; public class Partie { + Set personnages; Set statuz; Set piece; @@ -33,6 +35,35 @@ public class Partie { public String toString() { return "Partie [personnages=" + personnages + ", statuz=" + statuz + ", piece=" + piece + "]"; } + + public Inventaire findObjectInventory(Objet o) { + Optional persoInv = personnages.stream().filter(p -> p.inventaire.contains(o)).findAny(); + if(persoInv.isPresent())return persoInv.get(); + + Optional persoInvRoom = personnages.stream() + .map(p -> p.espacesPersos.stream() + .filter(ep -> ep.contenu.keySet().contains(o)).findAny()) + .filter(Optional::isPresent).map(Optional::get).findAny(); + if(persoInvRoom.isPresent())return persoInvRoom.get(); + + Optional invRoom = piece.stream() + .filter(ep -> ep.contenu.keySet().contains(o)).findAny(); + if(invRoom.isPresent())return invRoom.get(); + + return null; + } + + public Stream statuzStream() { + return statuz.stream(); + } + + public Set pieces() { + return piece; + } + + public Set personnages() { + return personnages; + } diff --git a/src/main/java/com/bernard/murder/model/Personnage.java b/src/main/java/com/bernard/murder/model/Personnage.java index 7468665..5c66070 100644 --- a/src/main/java/com/bernard/murder/model/Personnage.java +++ b/src/main/java/com/bernard/murder/model/Personnage.java @@ -34,6 +34,10 @@ public class Personnage implements Inventaire{ return inventaire; } + public Set getActions() { + return actions; + } + public Stream streamEspacesPersos(){ return espacesPersos.stream(); } @@ -51,6 +55,42 @@ public class Personnage implements Inventaire{ @Override public Set getObjects() { return inventaire; + } + + + + @Override + public void removeObjet(Objet o) { + System.out.println("Avant :"+inventaire); + this.inventaire.remove(o); + System.out.println("Après :"+inventaire); + + } + + + + @Override + public void addObjet(Objet o) { + this.inventaire.add(o); + } + + + + public Stream streamStatus() { + return status.stream(); + } + + + + @Override + public String getInventoryName() { + return "Inventaire de "+getNom(); + } + + + + public Set espacePersos() { + return espacesPersos; } diff --git a/src/main/java/com/bernard/murder/model/Piece.java b/src/main/java/com/bernard/murder/model/Piece.java index d82ea83..91b188c 100644 --- a/src/main/java/com/bernard/murder/model/Piece.java +++ b/src/main/java/com/bernard/murder/model/Piece.java @@ -2,7 +2,9 @@ package com.bernard.murder.model; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import java.util.stream.Stream; public class Piece implements Inventaire{ @@ -37,6 +39,24 @@ public class Piece implements Inventaire{ contenu.putAll(objs); } + @Override + public void removeObjet(Objet o) { + this.contenu.remove(o); + } + + @Override + public void addObjet(Objet o) { + this.contenu.put(o,-1); + } + + public Stream> streamHiddenObjects() { + return contenu.entrySet().stream(); + } + + @Override + public String getInventoryName() { + return nom; + } diff --git a/src/main/java/com/bernard/murder/module-info.java b/src/main/java/com/bernard/murder/module-info.java deleted file mode 100644 index c426444..0000000 --- a/src/main/java/com/bernard/murder/module-info.java +++ /dev/null @@ -1,6 +0,0 @@ -module murder{ - - requires com.amihaiemil.eoyaml; - requires java.desktop; - -} \ No newline at end of file diff --git a/src/main/java/com/bernard/murder/util/view/MouseReactiveTabbedPane.java b/src/main/java/com/bernard/murder/util/view/MouseReactiveTabbedPane.java new file mode 100755 index 0000000..08f2875 --- /dev/null +++ b/src/main/java/com/bernard/murder/util/view/MouseReactiveTabbedPane.java @@ -0,0 +1,106 @@ +package com.bernard.murder.util.view; + +import java.awt.MouseInfo; +import java.awt.Point; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.util.TooManyListenersException; + +import javax.swing.JTabbedPane; + +import com.bernard.murder.view.minel.objetDnD.NotADropTarget; + +public class MouseReactiveTabbedPane extends JTabbedPane { + + private static final long serialVersionUID = -4985256252193243545L; + long waitTime = 500_000_000; + Thread plannedThread; + int dropAcceptance; + + int hoverIndex = -1; + long enteredTime = -1; + + public MouseReactiveTabbedPane(int tabPlacement) { + super(tabPlacement); + + final MouseReactiveTabbedPane pane = this; + + this.setDropTarget(new NotADropTarget()); + + try { + this.getDropTarget().addDropTargetListener(new DropTargetListener() { + + + @Override + public void dragOver(DropTargetDragEvent dtde) { + int tab = getTab(dtde); + System.out.println(">"+tab+"/"+hoverIndex+"-"+enteredTime); + if(tab==-1) { + enteredTime=-1; + hoverIndex=-1; + return; + } + if(tab!=hoverIndex) { + //On viens d'entrer dans un nouveau tab ! + enteredTime=System.nanoTime(); + hoverIndex=tab; + if(plannedThread==null){ + plannedThread = new Thread(() -> { + while(System.nanoTime()>enteredTime+waitTime) { + try { + Thread.sleep(2); + } catch (InterruptedException e1) {} + } + if(getTab() == hoverIndex) + pane.setSelectedIndex(hoverIndex); + hoverIndex = -1; + enteredTime = -1; + plannedThread = null; + }); + plannedThread.start(); + } + } + dtde.acceptDrag(dropAcceptance); + } + + + @Override + public void dropActionChanged(DropTargetDragEvent dtde) {} + @Override + public void drop(DropTargetDropEvent dtde) {} + @Override + public void dragExit(DropTargetEvent dte) { + enteredTime=-1; + hoverIndex=-1; + return; + } + @Override + public void dragEnter(DropTargetDragEvent e) {} + }); + } catch (TooManyListenersException e1) { + e1.printStackTrace(); + } + + } + + public int getTab(DropTargetDragEvent dtde) { + for (int i = 0; i < this.getTabCount(); i++) + if (this.getBoundsAt(i).contains(dtde.getLocation())) + return i; + return -1; + + } + public int getTab() { + Point loc = MouseInfo.getPointerInfo().getLocation(); + Point screen = this.getLocationOnScreen(); + Point point = new Point(loc.x - screen.x, loc.y - screen.y); + for (int i = 0; i < this.getTabCount(); i++) + if (this.getBoundsAt(i).contains(point)) + return i; + return -1; + + } + +} \ No newline at end of file diff --git a/src/main/java/com/bernard/murder/util/view/SimpleDocumentListener.java b/src/main/java/com/bernard/murder/util/view/SimpleDocumentListener.java new file mode 100755 index 0000000..da1a2cb --- /dev/null +++ b/src/main/java/com/bernard/murder/util/view/SimpleDocumentListener.java @@ -0,0 +1,18 @@ +package com.bernard.murder.util.view; + +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +public abstract class SimpleDocumentListener implements DocumentListener { + + @Override + public void insertUpdate(DocumentEvent e) { + changedUpdate(e); + } + + @Override + public void removeUpdate(DocumentEvent e) { + changedUpdate(e); + } + +} diff --git a/src/main/java/com/bernard/murder/view/EnceinteServeurFrame.java b/src/main/java/com/bernard/murder/view/EnceinteServeurFrame.java new file mode 100755 index 0000000..b5eac62 --- /dev/null +++ b/src/main/java/com/bernard/murder/view/EnceinteServeurFrame.java @@ -0,0 +1,138 @@ +package com.bernard.murder.view; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Line; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.Mixer; +import javax.sound.sampled.Mixer.Info; +import javax.sound.sampled.SourceDataLine; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import com.bernard.murder.audio.AudioServer; +import com.bernard.murder.audio.SpeakerServer; + +public class EnceinteServeurFrame extends JFrame{ + + private static final long serialVersionUID = -2023529082781210475L; + + SpeakerServer serveur; + String deviceName; + + 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.deviceName = deviceName; + + this.setContentPane(genContentPan()); + this.setVisible(true); + } + + public JPanel genContentPan() { + JPanel panel = new JPanel(new BorderLayout()); + + + + InformedSourceDataline[] marray = getEnceinteList(); + JList enceinteListe = new JList(marray); + + + JPanel masterPanel = new JPanel(new BorderLayout()); + JTextField masterIP = new JTextField("192.168.1.1"); + JButton serverControl = new JButton("Lancer"); + masterPanel.add(serverControl,BorderLayout.EAST); + masterPanel.add(masterIP,BorderLayout.CENTER); + + JList mics = new JList<>(); + + serverControl.addActionListener(e->{ + if(enceinteListe.getSelectedValue()==null)return; + serverControl.setEnabled(false); + if(serveur!=null) { + serveur.dispose(); + serveur = null; + serverControl.setText("Lancer"); + }else { + serveur = new SpeakerServer(new InetSocketAddress(masterIP.getText(), AudioServer.communicationPort), deviceName,enceinteListe.getSelectedValue().tdl); + serveur.setServerAnswered(()->{ + serverControl.setText("Arrêter"); + List list = serveur.getAudioList().entrySet().stream().map(et -> new NamedMicrophone(et.getKey(), et.getValue())).collect(Collectors.toList()); + NamedMicrophone[] micarray = new NamedMicrophone[list.size()]; + list.toArray(micarray); + mics.setListData(micarray); + }); + serverControl.setText("Lancement"); + } + serverControl.setEnabled(true); + }); + + panel.add(masterPanel,BorderLayout.NORTH); + panel.add(mics,BorderLayout.SOUTH); + panel.add(enceinteListe,BorderLayout.CENTER); + return panel; + } + + public static final InformedSourceDataline[] getEnceinteList() { + List linfo = new ArrayList<>(); + Line.Info srcInfo = new Line.Info(SourceDataLine.class); + Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo(); + for (Mixer.Info info : mixerInfos) { + Mixer mixer = AudioSystem.getMixer(info); + try { + SourceDataLine tdl = (SourceDataLine)mixer.getLine(srcInfo); + linfo.add(new InformedSourceDataline(tdl, info)); + } catch (LineUnavailableException|IllegalArgumentException e) {} + } + InformedSourceDataline[] marray = new InformedSourceDataline[linfo.size()]; + linfo.toArray(marray); + return marray; + } + + + public static class InformedSourceDataline { + + SourceDataLine tdl; + Mixer.Info mixerInfo; + + public InformedSourceDataline(SourceDataLine tdl, Info mixerInfo) { + this.tdl = tdl; + this.mixerInfo = mixerInfo; + } + + @Override + public String toString() { + return mixerInfo.getDescription(); + } + + } + + public static class NamedMicrophone { + + int micId; + String micName; + + public NamedMicrophone(int micId, String micName) { + this.micId = micId; + this.micName = micName; + } + + @Override + public String toString() { + return micName; + } + + } +} diff --git a/src/main/java/com/bernard/murder/view/GlobalUIManager.java b/src/main/java/com/bernard/murder/view/GlobalUIManager.java deleted file mode 100644 index 7400808..0000000 --- a/src/main/java/com/bernard/murder/view/GlobalUIManager.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.bernard.murder.view; - -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import javax.swing.JPanel; - -import com.bernard.murder.model.Inventaire; -import com.bernard.murder.model.Objet; - -public class GlobalUIManager implements MouseListener{ - - public GlobalUIManager(JPanel globalPan) { - globalPan.addMouseListener(this); - } - - Set currentUIselection = new HashSet(); - Map inventaire = new HashMap<>(); - - - @Override - public void mouseClicked(MouseEvent e) { - - } - - @Override - public void mousePressed(MouseEvent e) { - System.out.println(e.getSource()); - System.out.println(e.getComponent()); - } - - @Override - public void mouseReleased(MouseEvent e) { - - - if(e.getButton()==MouseEvent.NOBUTTON) { - currentUIselection.clear(); - } - } - - @Override - public void mouseEntered(MouseEvent e) { - if(e.getButton()==MouseEvent.NOBUTTON) { - // Le clic a peut être été laché à l'exterieur - currentUIselection.clear(); - } - } - - @Override - public void mouseExited(MouseEvent e) { - - } - - - -} diff --git a/src/main/java/com/bernard/murder/view/HoverSelect.java b/src/main/java/com/bernard/murder/view/HoverSelect.java new file mode 100755 index 0000000..c45ed31 --- /dev/null +++ b/src/main/java/com/bernard/murder/view/HoverSelect.java @@ -0,0 +1,104 @@ +package com.bernard.murder.view; + +import java.awt.CardLayout; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; + +import javax.swing.JPanel; +import javax.swing.JTabbedPane; + +public class HoverSelect implements DropTargetListener,Runnable{ + + long enteredTime = -1; + long waitTime = 500_000_000; + Runnable onSelect; + Thread plannedThread; + int dropAcceptance; + + public HoverSelect(Runnable onSelect,long waitTime,int shouldAcceptDrop){ + this.onSelect = onSelect; + this.waitTime = waitTime; + this.dropAcceptance = shouldAcceptDrop; + } + public HoverSelect(CardLayout layout, JPanel panel, String identifier, long waitTime,int shouldAcceptDrop) { + this(() -> layout.show(panel, identifier),waitTime,shouldAcceptDrop); + } + + public HoverSelect(JTabbedPane tabbedPan, int index, long waitTime,int shouldAcceptDrop) { + this(() -> tabbedPan.setSelectedIndex(index),waitTime,shouldAcceptDrop); + } + + public HoverSelect(Runnable onSelect,int shouldAcceptDrop){ + this.onSelect = onSelect; + this.dropAcceptance = shouldAcceptDrop; + } + + public HoverSelect(CardLayout layout, JPanel panel, String identifier,int shouldAcceptDrop) { + this(() -> layout.show(panel, identifier),shouldAcceptDrop); + } + public HoverSelect(Runnable onSelect,long waitTime){ + this(onSelect,waitTime,DnDConstants.ACTION_NONE); + } + public HoverSelect(CardLayout layout, JPanel panel, String identifier, long waitTime) { + this(() -> layout.show(panel, identifier),waitTime,DnDConstants.ACTION_NONE); + } + public HoverSelect(JTabbedPane tabbedPan, int index, int shouldAcceptDrop) { + this(() -> tabbedPan.setSelectedIndex(index),shouldAcceptDrop); + } + public HoverSelect(JTabbedPane tabbedPan, int index, long waitTime) { + this(() -> tabbedPan.setSelectedIndex(index),waitTime,DnDConstants.ACTION_NONE); + } + + public HoverSelect(Runnable onSelect){ + this(onSelect, DnDConstants.ACTION_NONE); + } + public HoverSelect(CardLayout layout, JPanel panel, String identifier) { + this(() -> layout.show(panel, identifier),DnDConstants.ACTION_NONE); + } + public HoverSelect(JTabbedPane tabbedPan, int index) { + this(() -> tabbedPan.setSelectedIndex(index),DnDConstants.ACTION_NONE); + } + + @Override + public void dragEnter(DropTargetDragEvent e) { + enteredTime = System.nanoTime(); + System.out.println("entré"); + plannedThread = new Thread(()-> { + try { + Thread.sleep(waitTime/1_000_000, (int)(waitTime%1_000_000)); + onSelect.run(); + } catch (InterruptedException e1) {} + }); + plannedThread.run(); + e.acceptDrag(dropAcceptance); + } + + @Override + public void dragExit(DropTargetEvent e) { + if(plannedThread!=null)plannedThread.interrupt(); + plannedThread = null; + } + + @Override + public void dragOver(DropTargetDragEvent dtde) {} + @Override + public void dropActionChanged(DropTargetDragEvent dtde) {} + @Override + public void drop(DropTargetDropEvent dtde) {} + @Override + public void run() { + enteredTime = System.nanoTime(); + System.out.println("entré"); + plannedThread = new Thread(()-> { + try { + Thread.sleep(waitTime/1_000_000, (int)(waitTime%1_000_000)); + onSelect.run(); + } catch (InterruptedException e1) {} + }); + plannedThread.run(); + } + +} diff --git a/src/main/java/com/bernard/murder/view/LauncherFrame.java b/src/main/java/com/bernard/murder/view/LauncherFrame.java new file mode 100755 index 0000000..1f26d21 --- /dev/null +++ b/src/main/java/com/bernard/murder/view/LauncherFrame.java @@ -0,0 +1,139 @@ +package com.bernard.murder.view; + +import java.awt.GridLayout; +import java.io.File; +import java.text.DateFormat; +import java.util.Calendar; +import java.util.List; +import java.util.Map; + +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +import com.bernard.murder.game.GameCreator; +import com.bernard.murder.game.GameCreator.QuicksavedPartie; +import com.bernard.murder.game.GameManager; +import com.bernard.murder.model.Partie; +import com.bernard.murder.model.Personnage; +import com.bernard.murder.view.minel.Minel; + +public class LauncherFrame extends JFrame{ + + private static final long serialVersionUID = 5831232688024137883L; + + public static void main(String[] args) { + new LauncherFrame(); + } + + public LauncherFrame() { + try { + //TODO implement flatlaf look&feel + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e1) { + e1.printStackTrace(); + } + + this.setTitle("Configuration du terminal"); + this.setSize(300, 200); + this.setDefaultCloseOperation(EXIT_ON_CLOSE); + + JPanel contentPan = new JPanel(new GridLayout(5,1)); + + + JButton creerMaitre = new JButton("Terminal maître"); + creerMaitre.addActionListener(e -> { + this.setEnabled(false); + JFileChooser chooser = new JFileChooser(); + int returnState = chooser.showOpenDialog(this); + if(returnState==JFileChooser.APPROVE_OPTION) { + File toread = chooser.getSelectedFile(); + try { + Partie partie = GameCreator.genFromFile(toread); + GameManager manager = new GameManager(partie); + Map> minelsSup = MinelsCreator.genSupMinels(partie,manager); + Map> minels = MinelsCreator.genPersoMinels(partie,manager); + + new MurderatorGameFrame("Murder du "+DateFormat.getDateInstance().format(Calendar.getInstance().getTime()),partie,manager,minelsSup,minels); + this.setVisible(false); + dispose(); + + }catch(Exception ex) { + ex.printStackTrace(); + JOptionPane.showMessageDialog(this, ex.getLocalizedMessage(), "Impossible de lire le fichier", JOptionPane.ERROR_MESSAGE); + } + } + this.setEnabled(true); + }); + + JButton creerEsclave = new JButton("Terminal esclave"); + creerEsclave.setEnabled(false); + + JButton creerEnceinte = new JButton("Enceinte"); + creerEnceinte.addActionListener(e -> { + this.setEnabled(false); + String name = JOptionPane.showInputDialog(this, "Nom de l'enceinte", "Nom de l'enceinte", JOptionPane.PLAIN_MESSAGE); + if(name != null) { + new EnceinteServeurFrame(name); + this.setVisible(false); + dispose(); + } else { + this.setEnabled(true); + + } + }); + + JButton creerMicro = new JButton("Microphone"); + creerMicro.addActionListener(e -> { + this.setEnabled(false); + String name = JOptionPane.showInputDialog(this, "Nom du microphone", "Nom du microphone", JOptionPane.PLAIN_MESSAGE); + if(name != null) { + new MicServeurFrame(name); + this.setVisible(false); + dispose(); + } else { + this.setEnabled(true); + } + }); + + + JButton chargerSauvegarde = new JButton("Charger Sauvegarde"); + chargerSauvegarde.addActionListener(e -> { + this.setEnabled(false); + JFileChooser chooser = new JFileChooser(); + int returnState = chooser.showOpenDialog(this); + if(returnState==JFileChooser.APPROVE_OPTION) { + File toread = chooser.getSelectedFile(); + try { + QuicksavedPartie qpartie = GameCreator.readQuickSave(toread); + GameManager manager = new GameManager(qpartie.getPartie()); + + new MurderatorGameFrame("Murder du "+DateFormat.getDateInstance().format(Calendar.getInstance().getTime()),qpartie,manager); + this.setVisible(false); + dispose(); + + }catch(Exception ex) { + ex.printStackTrace(); + JOptionPane.showMessageDialog(this, ex.getLocalizedMessage(), "Impossible de lire le fichier", JOptionPane.ERROR_MESSAGE); + } + } + this.setEnabled(true); + }); + + contentPan.add(creerMaitre); + contentPan.add(creerEsclave); + contentPan.add(creerMicro); + contentPan.add(creerEnceinte); + contentPan.add(chargerSauvegarde); + + this.setContentPane(contentPan); + this.pack(); + this.setLocationRelativeTo(null); + this.setVisible(true); + } + +} diff --git a/src/main/java/com/bernard/murder/view/MicServeurFrame.java b/src/main/java/com/bernard/murder/view/MicServeurFrame.java new file mode 100755 index 0000000..4da967e --- /dev/null +++ b/src/main/java/com/bernard/murder/view/MicServeurFrame.java @@ -0,0 +1,113 @@ +package com.bernard.murder.view; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; + +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Line; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.Mixer; +import javax.sound.sampled.Mixer.Info; +import javax.sound.sampled.TargetDataLine; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import com.bernard.murder.audio.AudioServer; +import com.bernard.murder.audio.MicServer; + +public class MicServeurFrame extends JFrame{ + + private static final long serialVersionUID = -2023529082781210475L; + + MicServer serveur; + String deviceName; + + public MicServeurFrame(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.deviceName = deviceName; + + this.setContentPane(genContentPan()); + this.setVisible(true); + } + + public JPanel genContentPan() { + JPanel panel = new JPanel(new BorderLayout()); + + + + InformedTargetDataline[] marray = getEnceinteList(); + JList micListe = new JList(marray); + + + JPanel masterPanel = new JPanel(new BorderLayout()); + JTextField masterIP = new JTextField("192.168.1.1"); + JButton serverControl = new JButton("Lancer"); + masterPanel.add(serverControl,BorderLayout.EAST); + masterPanel.add(masterIP,BorderLayout.CENTER); + + serverControl.addActionListener(e->{ + if(micListe.getSelectedValue()==null)return; + serverControl.setEnabled(false); + if(serveur!=null) { + serveur.dispose(); + serveur = null; + serverControl.setText("Lancer"); + }else { + serveur = new MicServer(new InetSocketAddress(masterIP.getText(), AudioServer.communicationPort), deviceName,micListe.getSelectedValue().tdl); + serveur.setServerAnswered(()->{ + serverControl.setText("Arrêter"); + }); + serverControl.setText("Lancement"); + } + serverControl.setEnabled(true); + }); + + panel.add(masterPanel,BorderLayout.NORTH); + panel.add(micListe,BorderLayout.CENTER); + return panel; + } + + public static final InformedTargetDataline[] getEnceinteList() { + List linfo = new ArrayList<>(); + Line.Info srcInfo = new Line.Info(TargetDataLine.class); + Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo(); + for (Mixer.Info info : mixerInfos) { + Mixer mixer = AudioSystem.getMixer(info); + try { + TargetDataLine tdl = (TargetDataLine)mixer.getLine(srcInfo); + linfo.add(new InformedTargetDataline(tdl, info)); + } catch (LineUnavailableException|IllegalArgumentException e) {} + } + InformedTargetDataline[] marray = new InformedTargetDataline[linfo.size()]; + linfo.toArray(marray); + return marray; + } + + public static class InformedTargetDataline { + + TargetDataLine tdl; + Mixer.Info mixerInfo; + + public InformedTargetDataline(TargetDataLine tdl, Info mixerInfo) { + this.tdl = tdl; + this.mixerInfo = mixerInfo; + } + + @Override + public String toString() { + return mixerInfo.getDescription(); + } + + } + +} diff --git a/src/main/java/com/bernard/murder/view/MinelsCreator.java b/src/main/java/com/bernard/murder/view/MinelsCreator.java index a816e49..9e3d21a 100644 --- a/src/main/java/com/bernard/murder/view/MinelsCreator.java +++ b/src/main/java/com/bernard/murder/view/MinelsCreator.java @@ -1,5 +1,7 @@ package com.bernard.murder.view; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -7,35 +9,51 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.YamlMapping; +import com.amihaiemil.eoyaml.YamlMappingBuilder; +import com.amihaiemil.eoyaml.YamlNode; +import com.amihaiemil.eoyaml.YamlSequence; +import com.bernard.murder.YamlUtils; +import com.bernard.murder.game.GameCreator.QuicksavedPartie; import com.bernard.murder.game.GameManager; import com.bernard.murder.model.Partie; import com.bernard.murder.model.Personnage; +import com.bernard.murder.view.minel.ActionsMinel; import com.bernard.murder.view.minel.InventaireMinel; import com.bernard.murder.view.minel.Minel; +import com.bernard.murder.view.minel.ObjetSearchMinel; +import com.bernard.murder.view.minel.ServeurMinel; import com.bernard.murder.view.minel.TextPanMinel; public class MinelsCreator { - public static Map> genSupMinels(Partie partie, GameManager manager) { - Map> outPute = new HashMap>(); + public static Map> genSupMinels(Partie qpartie, GameManager manager) { + Map> outPute = new HashMap>(); List generalMinels = new ArrayList(); - partie.piecesStream().map(p -> new InventaireMinel(manager, p)).forEach(m -> generalMinels.add(m)); + List piecesMinels = new ArrayList<>(); + qpartie.piecesStream().map(p -> new InventaireMinel(manager, p)).forEach(m -> piecesMinels.add(m)); generalMinels.add(new TextPanMinel(manager)); + generalMinels.add(new ObjetSearchMinel(manager, manager.getEveryInventaire())); + generalMinels.add(new ServeurMinel(manager)); outPute.put("Général", generalMinels); + outPute.put("Pièces", piecesMinels); return outPute; } - - public static Map> genMinels(Partie partie, GameManager manager) { + + public static Map> genPersoMinels(Partie partie, GameManager manager) { return partie.personnagesStream().collect(Collectors.toMap(Function.identity(), p -> genMinelsForPerso(partie,manager,p))); } private static List genMinelsForPerso(Partie partie, GameManager manager, Personnage personnage){ List minels = new ArrayList(); + minels.add(new ActionsMinel(manager, personnage)); + minels.add(new InventaireMinel(manager, personnage)); personnage.streamEspacesPersos().map(p -> new InventaireMinel(manager, p)).forEach(m -> minels.add(m)); @@ -45,5 +63,83 @@ public class MinelsCreator { return minels; } + public static Map> genSupMinels(QuicksavedPartie qpartie, GameManager manager) { + return genMinels("sups", qpartie, manager); + } + + public static Map> genPersoMinels(QuicksavedPartie qpartie, GameManager manager) { + return genMinels("persos", qpartie, manager).entrySet().stream().collect(Collectors.toMap(e -> manager.getPersoByName(e.getKey()), Map.Entry::getValue)); + } + + + private static Map> genMinels(String minelType,QuicksavedPartie qpartie, GameManager manager) { + Map> outPute = new HashMap>(); + + YamlMapping supMap = qpartie.getMinelsMap().yamlMapping(minelType); + + for(YamlNode n : supMap.keys()) { + YamlSequence minels = supMap.yamlSequence(n); + String minelName = n.asScalar().value(); + List minelList = new ArrayList(); + for(YamlNode minelNode : minels) { + YamlMapping minel = minelNode.asMapping(); + String className = minel.value("minelClass").asScalar().value(); + try { + Class minelClass = Class.forName(className); + Constructor minelConstructor = minelClass.getConstructor(GameManager.class,YamlMapping.class); + Object minelObj = minelConstructor.newInstance(manager,minel); + if(minelObj instanceof Minel) { + minelList.add((Minel) minelObj); + }else { + throw new ClassCastException(); + } + } catch (InstantiationException e) { + System.err.println("La classe "+className+" n'est pas prévu pour être instanciable !"); + e.printStackTrace(); + } catch (IllegalAccessException e) { + System.err.println("Le constructeur (GameManager,YamlMapping) de la classe "+className+" n'est pas public"); + e.printStackTrace(); + } catch (IllegalArgumentException e) { + System.err.println("Euhhhhhhhhh, ce doit être une erreur de code interne"); + e.printStackTrace(); + } catch (InvocationTargetException e) { + System.err.println("La recréation du minel de classe "+className+" a foiré"); + e.printStackTrace(); + } catch (NoSuchMethodException e) { + System.err.println("La classe "+className+" n'a pas de constructeur (GameManager,YamlMapping)"); + e.printStackTrace(); + } catch (SecurityException e) { + System.err.println("La classe "+className+" n'est pas accessible"); + e.printStackTrace(); + } catch (ClassNotFoundException e) { + System.err.println("La classe "+className+" n'est pas chargée !"); + e.printStackTrace(); + } catch (ClassCastException e) { + System.err.println("La classe "+className+" n'est pas un Minel !"); + e.printStackTrace(); + } + } + outPute.put(minelName, minelList); + } + + return outPute; + } + + public static YamlMapping createMinelQuicksave(Map> persoMinels, Map> supMinels) { + YamlMappingBuilder supMapBuilder = Yaml.createYamlMappingBuilder(); + for(String supName : supMinels.keySet()) { + List seq = supMinels.get(supName).stream().map(m -> m.saveToYaml().add("minelClass", m.getClass().getName()).build()).collect(Collectors.toList()); + supMapBuilder = supMapBuilder.add(supName, YamlUtils.listToSeq(seq)); + } + YamlMappingBuilder persoBuilder = Yaml.createYamlMappingBuilder(); + for(Personnage perso : persoMinels.keySet()) + persoBuilder = persoBuilder.add(perso.getNom(), YamlUtils.listToSeq(persoMinels.get(perso).stream().map(m -> m.saveToYaml().add("minelClass", m.getClass().getName()).build()).collect(Collectors.toList()))); + + + return Yaml.createYamlMappingBuilder() + .add("sups", supMapBuilder.build()) + .add("persos", persoBuilder.build()) + .build(); + } } diff --git a/src/main/java/com/bernard/murder/view/MurderatorGameFrame.java b/src/main/java/com/bernard/murder/view/MurderatorGameFrame.java new file mode 100755 index 0000000..6bc2f20 --- /dev/null +++ b/src/main/java/com/bernard/murder/view/MurderatorGameFrame.java @@ -0,0 +1,122 @@ +package com.bernard.murder.view; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.util.List; +import java.util.Map; + +import javax.swing.BorderFactory; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; + +import com.bernard.murder.ParseUtils; +import com.bernard.murder.game.GameCreator.QuicksavedPartie; +import com.bernard.murder.game.GameManager; +import com.bernard.murder.model.Partie; +import com.bernard.murder.model.Personnage; +import com.bernard.murder.util.view.MouseReactiveTabbedPane; +import com.bernard.murder.view.minel.Minel; + +public class MurderatorGameFrame extends JFrame{ + + private static final long serialVersionUID = -4512350072325470066L; + + + Map> minelsSup; + Map> minels; + + public MurderatorGameFrame(String frameName, Partie partie, GameManager manager,Map> minelsSup,Map> minels) { + this.setSize(700, 500); + this.setMinimumSize(new Dimension(200, 100)); + this.setDefaultCloseOperation(EXIT_ON_CLOSE); + this.setTitle(frameName); + this.minelsSup = minelsSup; + this.minels = minels; + manager.bindMinelQuicksaver(() -> MinelsCreator.createMinelQuicksave(minels, minelsSup)); + + this.setContentPane(genGamePane(partie,manager,minelsSup,minels)); + this.pack(); + this.setLocationRelativeTo(null); + this.setVisible(true); + } + + public MurderatorGameFrame(String frameName, QuicksavedPartie qpartie, GameManager manager) { + this(frameName,qpartie.getPartie(),manager,MinelsCreator.genSupMinels(qpartie, manager),MinelsCreator.genPersoMinels(qpartie, manager)); + } + + public MurderatorGameFrame(String frameName, Partie partie, GameManager manager) { + this(frameName,partie,manager,MinelsCreator.genSupMinels(partie, manager),MinelsCreator.genPersoMinels(partie, manager)); + } + + public JPanel genGamePane(Partie partie,GameManager manager,Map> minelsSup,Map> minels) { + + JPanel globalPan = new JPanel(new BorderLayout()); + + //Center Panel + MouseReactiveTabbedPane centerPan = new MouseReactiveTabbedPane(JTabbedPane.TOP); + + int j = 0; + + for(String s : minelsSup.keySet()) { + JPanel centralLocalBpanPan = new JPanel(new GridLayout(2,(minelsSup.get(s).size()+1)/2,-1,-1)); + minelsSup.get(s).stream().map(m -> m.genContentPane()).forEach(mpan -> {centralLocalBpanPan.add(mpan);mpan.setBorder(BorderFactory.createLineBorder(ParseUtils.randDarkBlueColor(),2));}); + JScrollPane centralLocalPan = new JScrollPane(centralLocalBpanPan); + centerPan.insertTab(s,null,centralLocalPan,null,j++); + System.out.println(j); + } + + for(Personnage p : minels.keySet()) { + JPanel centralLocalBpanPan = new JPanel(new GridLayout(2, (minels.get(p).size()+1)/2,-1,-1)); + minels.get(p).stream().map(m -> m.genContentPane()).forEach(mpan -> {centralLocalBpanPan.add(mpan);mpan.setBorder(BorderFactory.createLineBorder(ParseUtils.randDarkBlueColor(),2));}); + JScrollPane centralLocalPan = new JScrollPane(centralLocalBpanPan); + centerPan.insertTab(p.getNom(),null,centralLocalPan,null,j++); + System.out.println(j); + } + + for (int i = 0; i < centerPan.getTabCount(); i++) { + System.out.println(i); + + } + + /* + //Left Panel + JPanel leftPan = new JPanel(new GridLayout(minels.size() + minelsSup.size(), 1)); + + for(String s : minelsSup.keySet()) { + JButton localButton = new JButton(s); + localButton.addActionListener(e -> centerLayout.show(centerPan, s)); + try { + localButton.setDropTarget(new NotADropTarget()); + localButton.getDropTarget().addDropTargetListener(new HoverSelect(centerLayout, centerPan, s)); + } catch (TooManyListenersException e1) { + e1.printStackTrace(); + } + leftPan.add(localButton); + } + for(Personnage p : minels.keySet()) { + JButton localButton = new JButton(p.getNom()); + localButton.addActionListener(e -> centerLayout.show(centerPan, personnageIdentifier(p))); + try { + localButton.setDropTarget(new ObjetDropTarget(manager, p, ()->manager.inventoryUpdate(p))); + localButton.getDropTarget().addDropTargetListener(new HoverSelect(centerLayout, centerPan, personnageIdentifier(p),DnDConstants.ACTION_MOVE)); + } catch (TooManyListenersException e1) { + e1.printStackTrace(); + } + leftPan.add(localButton); + } + + globalPan.add(leftPan, BorderLayout.WEST); + */ + globalPan.add(centerPan, BorderLayout.CENTER); + return globalPan; + } + + + public String personnageIdentifier(Personnage personnage) { + return String.format("%08X",System.identityHashCode(personnage))+personnage.getNom(); + } + +} diff --git a/src/main/java/com/bernard/murder/view/MurderatorMainFrame.java b/src/main/java/com/bernard/murder/view/MurderatorMainFrame.java deleted file mode 100644 index e6ef16e..0000000 --- a/src/main/java/com/bernard/murder/view/MurderatorMainFrame.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.bernard.murder.view; - -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Dimension; -import java.awt.GridLayout; -import java.io.File; -import java.util.List; -import java.util.Map; - -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; - -import com.bernard.murder.game.GameCreator; -import com.bernard.murder.game.GameManager; -import com.bernard.murder.model.Partie; -import com.bernard.murder.model.Personnage; -import com.bernard.murder.view.minel.Minel; - -public class MurderatorMainFrame extends JFrame{ - - private static final long serialVersionUID = -4512350072325470066L; - - - public MurderatorMainFrame() { - - } - - public void startFrame() { - this.setSize(700, 500); - this.setMinimumSize(new Dimension(575, 200)); - this.setLocationRelativeTo(null); - this.setDefaultCloseOperation(EXIT_ON_CLOSE); - this.setTitle("En attente d'un fichier"); - - this.setContentPane(genFirstPanel()); - this.setVisible(true); - } - - public JPanel genFirstPanel() { - JPanel globalPanel = new JPanel(); - globalPanel.setLayout(new BoxLayout(globalPanel, BoxLayout.PAGE_AXIS)); - JPanel semiPan = new JPanel(); - semiPan.setLayout(new BoxLayout(semiPan, BoxLayout.LINE_AXIS)); - JButton loadFile = new JButton("Ouvrir un fichier"); - - loadFile.addActionListener(e -> { - this.setEnabled(false); - JFileChooser chooser = new JFileChooser(); - int returnState = chooser.showOpenDialog(this); - if(returnState==JFileChooser.APPROVE_OPTION) { - File toread = chooser.getSelectedFile(); - try { - Partie partie = GameCreator.genFromFile(toread); - GameManager manager = new GameManager(partie); - Map> minelsSup = MinelsCreator.genSupMinels(partie,manager); - Map> minels = MinelsCreator.genMinels(partie,manager); - - - this.setContentPane(genGamePane(partie,manager,minelsSup,minels)); - this.getContentPane().repaint(20); - }catch(Exception ex) { - ex.printStackTrace(); - JOptionPane.showMessageDialog(this, ex.getLocalizedMessage(), "Impossible de lire le fichier", JOptionPane.ERROR_MESSAGE); - } - } - this.setEnabled(true); - }); - - semiPan.add(loadFile); - globalPanel.add(semiPan); - return globalPanel; - } - - public JPanel genGamePane(Partie partie,GameManager manager,Map> minelsSup,Map> minels) { - - JPanel globalPan = new JPanel(new BorderLayout()); - GlobalUIManager guim = new GlobalUIManager(globalPan); - - //Center Panel - CardLayout centerLayout = new CardLayout(); - JPanel centerPan = new JPanel(centerLayout); - - for(String s : minelsSup.keySet()) { - JPanel centralLocalBpanPan = new JPanel(new GridLayout(2,(minelsSup.get(s).size()+1)/2)); - minelsSup.get(s).stream().map(m -> m.genContentPane(guim)).forEach(mpan -> centralLocalBpanPan.add(mpan)); - JScrollPane centralLocalPan = new JScrollPane(centralLocalBpanPan); - centerPan.add(centralLocalPan, s); - } - for(Personnage p : minels.keySet()) { - JPanel centralLocalBpanPan = new JPanel(new GridLayout(2, (minels.get(p).size()+1)/2)); - minels.get(p).stream().map(m -> m.genContentPane(guim)).forEach(mpan -> centralLocalBpanPan.add(mpan)); - JScrollPane centralLocalPan = new JScrollPane(centralLocalBpanPan); - centerPan.add(centralLocalPan, personnageIdentifier(p)); - } - - - //Left Panel - JPanel leftPan = new JPanel(new GridLayout(minels.size() + minelsSup.size(), 1)); - - for(String s : minelsSup.keySet()) { - JButton localButton = new JButton(s); - localButton.addActionListener(e -> centerLayout.show(centerPan, s)); - leftPan.add(localButton); - } - for(Personnage p : minels.keySet()) { - JButton localButton = new JButton(p.getNom()); - localButton.addActionListener(e -> centerLayout.show(centerPan, personnageIdentifier(p))); - leftPan.add(localButton); - } - - globalPan.add(leftPan, BorderLayout.WEST); - globalPan.add(centerPan, BorderLayout.CENTER); - - return globalPan; - } - - - public String personnageIdentifier(Personnage personnage) { - return String.format("%08X",System.identityHashCode(personnage))+personnage.getNom(); - } - -} diff --git a/src/main/java/com/bernard/murder/view/SettingsFrame.java b/src/main/java/com/bernard/murder/view/SettingsFrame.java new file mode 100755 index 0000000..3980c75 --- /dev/null +++ b/src/main/java/com/bernard/murder/view/SettingsFrame.java @@ -0,0 +1,9 @@ +package com.bernard.murder.view; + +import javax.swing.JFrame; + +public class SettingsFrame extends JFrame{ + + private static final long serialVersionUID = 7253551627244719823L; + +} diff --git a/src/main/java/com/bernard/murder/view/minel/ActionsMinel.java b/src/main/java/com/bernard/murder/view/minel/ActionsMinel.java new file mode 100755 index 0000000..2213a58 --- /dev/null +++ b/src/main/java/com/bernard/murder/view/minel/ActionsMinel.java @@ -0,0 +1,113 @@ +package com.bernard.murder.view.minel; + +import java.awt.BorderLayout; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSeparator; +import javax.swing.Timer; + +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.YamlMapping; +import com.amihaiemil.eoyaml.YamlMappingBuilder; +import com.bernard.murder.ParseUtils; +import com.bernard.murder.game.GameManager; +import com.bernard.murder.model.Action; +import com.bernard.murder.model.Personnage; + +public class ActionsMinel extends Minel { + + public static final String availableText = "Disponible"; + public static final String waitingTimeText = "Reste %s, reset à %s"; + + Personnage personnage; + + Collection updatingActions; + Map actionStatusTexts = new HashMap<>(); + Map actionButtons = new HashMap<>(); + + public ActionsMinel(GameManager manager, Personnage perso) { + super(manager); + this.personnage = perso; + updatingActions = new HashSet(); + updatingActions.addAll(perso.getActions()); + } + + public ActionsMinel(GameManager manager, YamlMapping ym) { + this(manager,manager.getPersoByName(ym.string("personnage"))); + } + + @Override + public JPanel genContentPane() { + JPanel globalPan = new JPanel(new BorderLayout()); + + JPanel actionsListPan = new JPanel(); + actionsListPan.setLayout(new BoxLayout(actionsListPan, BoxLayout.PAGE_AXIS)); + for(Action a : personnage.getActions()) { + JPanel actionControlPanel = new JPanel(new BorderLayout()); + JLabel actionName = new JLabel(a.getName()); + JButton actionButton = new JButton("GO"); + JLabel actionStatusText = new JLabel(availableText); + + actionButton.addActionListener(e->launchAction(a)); + actionButtons.put(a, actionButton); + actionStatusTexts.put(a, actionStatusText); + + actionControlPanel.add(actionButton, BorderLayout.EAST); + actionControlPanel.add(actionName, BorderLayout.NORTH); + actionControlPanel.add(actionStatusText, BorderLayout.SOUTH); + + actionsListPan.add(actionControlPanel); + actionsListPan.add(new JSeparator()); + + Timer timer = new Timer(100, e->updateTexts()); + timer.start(); + } + JScrollPane globalScroll = new JScrollPane(actionsListPan); + JLabel titleLabel = new JLabel("Actions de "+personnage.getNom()); + globalPan.add(titleLabel,BorderLayout.NORTH); + globalPan.add(globalScroll,BorderLayout.CENTER); + + updateTexts(); + + return globalPan; + } + + private void launchAction(Action a) { + if(!a.canBeLaunched())return; + a.launch(); + actionButtons.get(a).setEnabled(false); + updateText(a); + updatingActions.add(a); + } + + @Override + public YamlMappingBuilder saveToYaml() { + return Yaml.createYamlMappingBuilder().add("personnage", personnage.getNom()); + } + + public void updateTexts() { + updatingActions.stream().filter(Action::hasFinished).collect(Collectors.toSet()) + .stream().forEach(a -> { + actionStatusTexts.get(a).setText(availableText); + actionButtons.get(a).setEnabled(true); + updatingActions.remove(a); + }); + updatingActions.forEach(this::updateText); + } + + public void updateText(Action a) { + actionStatusTexts.get(a).setText(String.format(waitingTimeText, ParseUtils.dumpTimeLength(a.timeToWaitLeft()), ParseUtils.dumpHourDate(a.dateReset()))); + } + + + +} diff --git a/src/main/java/com/bernard/murder/view/minel/InventaireMinel.java b/src/main/java/com/bernard/murder/view/minel/InventaireMinel.java index 3df7ea6..d34bd59 100644 --- a/src/main/java/com/bernard/murder/view/minel/InventaireMinel.java +++ b/src/main/java/com/bernard/murder/view/minel/InventaireMinel.java @@ -1,16 +1,23 @@ package com.bernard.murder.view.minel; import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Font; import javax.swing.JButton; +import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; +import javax.swing.ListCellRenderer; -import com.bernard.murder.ParseUtils; +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.YamlMapping; +import com.amihaiemil.eoyaml.YamlMappingBuilder; import com.bernard.murder.game.GameManager; import com.bernard.murder.model.Inventaire; import com.bernard.murder.model.Objet; -import com.bernard.murder.view.GlobalUIManager; +import com.bernard.murder.view.minel.objetDnD.ObjetDropTarget; +import com.bernard.murder.view.minel.objetDnD.ObjetTransferHandler; public class InventaireMinel extends Minel { @@ -18,29 +25,42 @@ public class InventaireMinel extends Minel { super(manager); this.inv = inv; } + + public InventaireMinel(GameManager gm, YamlMapping ym) { + this(gm,gm.getInventoryByName(ym.string("inventaire"))); + } JList objets; Inventaire inv; @Override - public JPanel genContentPane(GlobalUIManager guim) { + public JPanel genContentPane() { JPanel globalpan = new JPanel(new BorderLayout()); - - globalpan.setBackground(ParseUtils.randColor()); - + + + if(inv.getInventoryName()!=null) { + JLabel invName = new JLabel(inv.getInventoryName()); + globalpan.add(invName, BorderLayout.NORTH); + } + JPanel inventaire = new JPanel(); - inventaire.setBackground(ParseUtils.randColor()); JButton voler = new JButton("RandomItem"); voler.addActionListener(e -> { objets.setSelectedIndex((int) (Math.random() * objets.getModel().getSize())); }); - - // TODO dragndrop multiple items + objets = new JList(); - objets.addMouseListener(guim); + objets.setCellRenderer(new ObjetListCellRenderer()); + objets.setDragEnabled(true); + objets.setTransferHandler(new ObjetTransferHandler()); updateObjets(); - + final ObjetDropTarget odt = new ObjetDropTarget(manager, inv, this::updateObjets); + objets.setDropTarget(odt); + globalpan.setDropTarget(odt); + + manager.addInventoryUpdateListener(inv, this::updateObjets); + inventaire.add(objets); globalpan.add(voler, BorderLayout.SOUTH); @@ -50,9 +70,40 @@ public class InventaireMinel extends Minel { } private void updateObjets() { + System.out.print("Updating "+inv+" with"); + manager.dumpCurrentState(); Objet[] objz = new Objet[inv.getObjects().size()]; objz = inv.getObjects().toArray(objz); objets.setListData(objz); } - + + @Override + public YamlMappingBuilder saveToYaml() { + return Yaml.createYamlMappingBuilder().add("inventaire", inv.getInventoryName()); + } + + public static class ObjetListCellRenderer extends JLabel implements ListCellRenderer { + + private static final long serialVersionUID = -7176962839330435585L; + + @Override + public Component getListCellRendererComponent(JList list, Objet objet, int index, + boolean isSelected, boolean cellHasFocus) { + + setText(objet.getNom()); + + if (isSelected) { + setFont(getFont().deriveFont(Font.BOLD)); + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } else { + setFont(getFont().deriveFont(Font.PLAIN)); + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + + return this; + } + } + } diff --git a/src/main/java/com/bernard/murder/view/minel/Minel.java b/src/main/java/com/bernard/murder/view/minel/Minel.java index f65f999..331d034 100644 --- a/src/main/java/com/bernard/murder/view/minel/Minel.java +++ b/src/main/java/com/bernard/murder/view/minel/Minel.java @@ -2,8 +2,9 @@ package com.bernard.murder.view.minel; import javax.swing.JPanel; +import com.amihaiemil.eoyaml.YamlMapping; +import com.amihaiemil.eoyaml.YamlMappingBuilder; import com.bernard.murder.game.GameManager; -import com.bernard.murder.view.GlobalUIManager; public abstract class Minel{ @@ -15,7 +16,12 @@ public abstract class Minel{ this.manager = manager; } - public abstract JPanel genContentPane(GlobalUIManager guim); + public Minel(GameManager manager, YamlMapping node) { + this.manager = manager; + } + public abstract JPanel genContentPane(); + + public abstract YamlMappingBuilder saveToYaml(); } diff --git a/src/main/java/com/bernard/murder/view/minel/ObjetSearchMinel.java b/src/main/java/com/bernard/murder/view/minel/ObjetSearchMinel.java new file mode 100755 index 0000000..1afd876 --- /dev/null +++ b/src/main/java/com/bernard/murder/view/minel/ObjetSearchMinel.java @@ -0,0 +1,143 @@ +package com.bernard.murder.view.minel; + +import java.awt.BorderLayout; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; + +import com.amihaiemil.eoyaml.Scalar; +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.YamlMapping; +import com.amihaiemil.eoyaml.YamlMappingBuilder; +import com.amihaiemil.eoyaml.YamlNode; +import com.bernard.murder.ParseUtils; +import com.bernard.murder.YamlUtils; +import com.bernard.murder.game.GameManager; +import com.bernard.murder.model.Inventaire; +import com.bernard.murder.model.Objet; +import com.bernard.murder.util.view.SimpleDocumentListener; + +public class ObjetSearchMinel extends Minel { + + Set overwatchedInventories; + + Map objets; + + public ObjetSearchMinel(GameManager manager, Set toOverwatch) { + super(manager); + overwatchedInventories = toOverwatch; + objets = new HashMap(); + for(Inventaire i : overwatchedInventories) { + i.getObjects().stream().forEach(o -> objets.put(o, i)); + manager.addInventoryUpdateListener(i, ()->i.getObjects().stream().forEach(o -> objets.put(o, i))); + } + } + + public ObjetSearchMinel(GameManager gm,YamlMapping ym) { + this(gm, + gm.getEveryInventaireByName( + ym.yamlSequence("overwatchedInventories") + .values() + .stream() + .map(YamlNode::asScalar) + .map(Scalar::value) + .collect(Collectors.toSet()) + ) + ); + } + + @Override + public JPanel genContentPane() { + + JPanel globalPan = new JPanel(new BorderLayout()); + + JTextField searchField = new JTextField(); + searchField.setToolTipText("Objet à chercher"); + + JList searchResults = new JList<>(); + + + + searchField.getDocument().addDocumentListener(new SimpleDocumentListener() { + + @Override + public void changedUpdate(DocumentEvent e) { + System.out.println("Updated to "+e.getDocument().toString()); + String searchText = searchField.getText(); + if(searchText.isBlank()) { + searchResults.setListData(new InventorizedObject[0]); + return; + } + Set startMatch = new HashSet<>(); + Set anyMatch = new HashSet<>(); + Set subwordMatch = new HashSet<>(); + + for(Objet o : objets.keySet()) { + System.out.println(o+"->"+searchText); + if(o.getNom().startsWith(searchText)) + startMatch.add(new InventorizedObject(o,objets.get(o))); + else if(o.getNom().contains(searchText)) + anyMatch.add(new InventorizedObject(o,objets.get(o))); + else if(ParseUtils.isSubWord(o.getNom(), searchText)) + subwordMatch.add(new InventorizedObject(o,objets.get(o))); + } + InventorizedObject[] results = + Stream.concat(Stream.concat( + startMatch.stream().sorted((s,v) -> s.objet.getNom().compareToIgnoreCase(v.objet.getNom())), + anyMatch.stream().sorted((s,v) -> s.objet.getNom().compareToIgnoreCase(v.objet.getNom()))), + subwordMatch.stream().sorted((s,v) -> s.objet.getNom().compareToIgnoreCase(v.objet.getNom()))) + .toArray(InventorizedObject[]::new); + + searchResults.setListData(results); + + } + }); + + globalPan.add(searchField, BorderLayout.NORTH); + globalPan.add(searchResults,BorderLayout.CENTER); + + return globalPan; + } + + @Override + public YamlMappingBuilder saveToYaml() { + return Yaml.createYamlMappingBuilder().add("overwatchedInventories", + YamlUtils.listToSeqString(overwatchedInventories.stream() + .map(Inventaire::getInventoryName) + .collect(Collectors.toList()))); + } + + public static class InventorizedObject{ + Objet objet; + Inventaire inventaire; + + + + public InventorizedObject(Objet objet, Inventaire inventaire) { + this.objet = objet; + this.inventaire = inventaire; + } + + + + public void changeInventory(Inventaire inventaire) { + this.inventaire = inventaire; + } + + @Override + public String toString() { + return objet.getNom()+" ("+inventaire.getInventoryName()+")"; + } + + + } + +} diff --git a/src/main/java/com/bernard/murder/view/minel/ServeurMinel.java b/src/main/java/com/bernard/murder/view/minel/ServeurMinel.java new file mode 100755 index 0000000..6e11cb9 --- /dev/null +++ b/src/main/java/com/bernard/murder/view/minel/ServeurMinel.java @@ -0,0 +1,39 @@ +package com.bernard.murder.view.minel; + +import javax.swing.JLabel; +import javax.swing.JPanel; + +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.YamlMapping; +import com.amihaiemil.eoyaml.YamlMappingBuilder; +import com.bernard.murder.audio.AudioServer; +import com.bernard.murder.game.GameManager; + +public class ServeurMinel extends Minel { + + AudioServer serveur; + + public ServeurMinel(GameManager manager) { + super(manager); + serveur = new AudioServer(); + } + + public ServeurMinel(GameManager manager,YamlMapping ym) { + super(manager); + serveur = new AudioServer(); + } + + @Override + public JPanel genContentPane() { + JPanel pan = new JPanel(); + JLabel label = new JLabel("Rien pour l'instant"); + pan.add(label); + return pan; + } + + @Override + public YamlMappingBuilder saveToYaml() { + return Yaml.createYamlMappingBuilder(); + } + +} diff --git a/src/main/java/com/bernard/murder/view/minel/StatusMinel.java b/src/main/java/com/bernard/murder/view/minel/StatusMinel.java new file mode 100755 index 0000000..7c07809 --- /dev/null +++ b/src/main/java/com/bernard/murder/view/minel/StatusMinel.java @@ -0,0 +1,29 @@ +package com.bernard.murder.view.minel; + +import javax.swing.JPanel; + +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.YamlMappingBuilder; +import com.bernard.murder.game.GameManager; +import com.bernard.murder.model.Personnage; + +public class StatusMinel extends Minel { + + public StatusMinel(GameManager manager, Personnage personnage) { + super(manager); + // TODO Auto-generated constructor stub + } + + @Override + public JPanel genContentPane() { + // TODO Auto-generated method stub + return null; + } + + @Override + public YamlMappingBuilder saveToYaml() { + //TODO auto-generated thingy + return Yaml.createYamlMappingBuilder(); + } + +} diff --git a/src/main/java/com/bernard/murder/view/minel/TextPanMinel.java b/src/main/java/com/bernard/murder/view/minel/TextPanMinel.java index ae3f0b7..2b40765 100644 --- a/src/main/java/com/bernard/murder/view/minel/TextPanMinel.java +++ b/src/main/java/com/bernard/murder/view/minel/TextPanMinel.java @@ -1,37 +1,50 @@ package com.bernard.murder.view.minel; import java.awt.BorderLayout; -import java.awt.Color; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.border.EmptyBorder; -import com.bernard.murder.ParseUtils; +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.YamlMapping; +import com.amihaiemil.eoyaml.YamlMappingBuilder; import com.bernard.murder.game.GameManager; -import com.bernard.murder.view.GlobalUIManager; public class TextPanMinel extends Minel { + String initTexte = ""; + JTextArea textArea; public TextPanMinel(GameManager manager) { super(manager); } + public TextPanMinel(GameManager gm, YamlMapping ym) { + super(gm); + initTexte = ym.string("texte"); + } + @Override - public JPanel genContentPane(GlobalUIManager guim) { + public JPanel genContentPane() { JPanel globalPan = new JPanel(new BorderLayout()); - globalPan.setBackground(ParseUtils.randColor()); - JTextArea textArea = new JTextArea(); + //globalPan.setBackground(ParseUtils.randColor()); + textArea = new JTextArea(); textArea.setBorder(new EmptyBorder(23,23,23,23)); + textArea.setText(initTexte); - Color col = ParseUtils.randColor(); - textArea.setBackground(col); - textArea.setForeground(ParseUtils.getContrastColor(col)); + //Color col = ParseUtils.randColor(); + //textArea.setBackground(col); + //textArea.setForeground(ParseUtils.getContrastColor(col)); globalPan.add(new JScrollPane(textArea),BorderLayout.CENTER); return globalPan; } + + @Override + public YamlMappingBuilder saveToYaml() { + return Yaml.createYamlMappingBuilder().add("texte", textArea!=null?textArea.getText():initTexte); + } } \ No newline at end of file diff --git a/src/main/java/com/bernard/murder/view/minel/objetDnD/NotADropTarget.java b/src/main/java/com/bernard/murder/view/minel/objetDnD/NotADropTarget.java new file mode 100755 index 0000000..9ff860a --- /dev/null +++ b/src/main/java/com/bernard/murder/view/minel/objetDnD/NotADropTarget.java @@ -0,0 +1,17 @@ +package com.bernard.murder.view.minel.objetDnD; + +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetDropEvent; + +public class NotADropTarget extends DropTarget { + + private static final long serialVersionUID = -2448755310779755579L; + + public NotADropTarget() {} + + @Override + public synchronized void drop(DropTargetDropEvent dtde) { + dtde.rejectDrop(); + } + +} diff --git a/src/main/java/com/bernard/murder/view/minel/objetDnD/ObjetDropTarget.java b/src/main/java/com/bernard/murder/view/minel/objetDnD/ObjetDropTarget.java new file mode 100755 index 0000000..ab9694b --- /dev/null +++ b/src/main/java/com/bernard/murder/view/minel/objetDnD/ObjetDropTarget.java @@ -0,0 +1,45 @@ +package com.bernard.murder.view.minel.objetDnD; + +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetDropEvent; +import java.util.List; + +import com.bernard.murder.game.GameManager; +import com.bernard.murder.model.Inventaire; +import com.bernard.murder.model.Objet; + +public class ObjetDropTarget extends DropTarget { + + private static final long serialVersionUID = 4114342978115089000L; + + GameManager manager; + Inventaire toInv; + Runnable[] toUpdate; + + public ObjetDropTarget(GameManager manager,Inventaire toInventaire,Runnable... update) { + this.manager = manager; + this.toInv = toInventaire; + this.toUpdate = update; + } + + @Override + public synchronized void drop(DropTargetDropEvent e) { + try { + e.acceptDrop(DnDConstants.ACTION_COPY); + + @SuppressWarnings("unchecked") + List droppedObjets = (List) e.getTransferable().getTransferData(ObjetTransferable.objetDataFlavor); + + for(Objet o : droppedObjets) { + manager.moveObjet(o,toInv); + } + for(Runnable runnable : toUpdate) + runnable.run(); + manager.dumpCurrentState(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/bernard/murder/view/minel/objetDnD/ObjetTransferHandler.java b/src/main/java/com/bernard/murder/view/minel/objetDnD/ObjetTransferHandler.java new file mode 100755 index 0000000..976df9b --- /dev/null +++ b/src/main/java/com/bernard/murder/view/minel/objetDnD/ObjetTransferHandler.java @@ -0,0 +1,34 @@ +package com.bernard.murder.view.minel.objetDnD; + +import java.awt.datatransfer.Transferable; +import java.util.List; + +import javax.swing.JComponent; +import javax.swing.JList; +import javax.swing.TransferHandler; + +import com.bernard.murder.model.Objet; + +public class ObjetTransferHandler extends TransferHandler { + + private static final long serialVersionUID = -8817518488023807932L; + + @Override + public int getSourceActions(JComponent c) { + return TransferHandler.MOVE; + } + + @Override + protected Transferable createTransferable(JComponent c) { + + @SuppressWarnings("unchecked") + JList liste = (JList) c; + + List selected = liste.getSelectedValuesList(); + + ObjetTransferable transferable = new ObjetTransferable(selected); + + return transferable; + } + +} diff --git a/src/main/java/com/bernard/murder/view/minel/objetDnD/ObjetTransferable.java b/src/main/java/com/bernard/murder/view/minel/objetDnD/ObjetTransferable.java new file mode 100755 index 0000000..eceffe8 --- /dev/null +++ b/src/main/java/com/bernard/murder/view/minel/objetDnD/ObjetTransferable.java @@ -0,0 +1,45 @@ +package com.bernard.murder.view.minel.objetDnD; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import com.bernard.murder.model.Objet; + +public class ObjetTransferable implements Transferable{ + + private final List objets; + private final DataFlavor[] flavors; + public static final DataFlavor objetDataFlavor = new DataFlavor(Objet.class, "fichierMurder"); + + public ObjetTransferable(Collection objets) { + this.objets = Collections.unmodifiableList( + new ArrayList(objets)); + this.flavors = new DataFlavor[] { ObjetTransferable.objetDataFlavor }; + } + + @Override + public DataFlavor[] getTransferDataFlavors() { + return flavors; + } + + @Override + public boolean isDataFlavorSupported(DataFlavor flavor) { + return Arrays.stream(flavors).anyMatch(f -> f == flavor); + } + + @Override + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { + if(isDataFlavorSupported(flavor)) + return objets; + else + throw new UnsupportedFlavorException(flavor); + } + +}