Java-Gaming.org    
Featured games (91)
games approved by the League of Dukes
Games in Showcase (578)
games submitted by our members
Games in WIP (499)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Asynchronous Sound Player based on JavaSound API  (Read 1548 times)
0 Members and 1 Guest are viewing this topic.
Offline DavidX

Senior Newbie





« Posted 2010-06-26 16:46:28 »

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */


package renoria.sound;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.BooleanControl;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
import net.idgames.archive.idx.IDXException;

/**
 * An {@link AsyncSoundPlayer} is a sound player with a thread pool using an
 * asynchronous read/write non-blocking method to play sounds concurrently
 * using a few, limited number of threads.
 * @author David
 */

public class AsyncSoundPlayer {
   private Deque<SoundInfo> queuedSounds = new LinkedList();
   private final Object poolSync = new Object();
   private final Object queueSync = new Object();
   private boolean shutDown = false;
   private List<Player> playerPool;
   private int poolsize;
   private AudioFormat playBackFormat;
   private AtomicInteger playingSounds = new AtomicInteger(0);
   private int maxPlayingSoundsPerPool = 32;
   private Mixer mixer;
   private boolean soundAvailable = false;
   private float masterVolume = 1.0f;

   /**
    * Creates a {@link AsyncSoundPlayer} with a pool size of one, and the default mixer.
    */

   public AsyncSoundPlayer() {
      this(1, null);
   }

   public AsyncSoundPlayer(int poolsize, Mixer useMixer) {
      Mixer.Info[] infos = AudioSystem.getMixerInfo();
      if (infos == null || infos.length < 1) {
         throw new SoundUnavailableException();
      }
      if (poolsize <= 0) {
         throw new IllegalArgumentException();
      }
      if (useMixer != null) {
         this.mixer = useMixer;
      } else {
         this.mixer = SoundUtil.getDefaultMixer(SoundUtil.PLAYBACK_MIXER);
      }
      this.poolsize = poolsize;
      soundAvailable = true;
      //this.playBackFormat = playBackFormat;
  }

   public float getMasterVolume() {
      return masterVolume;
   }

   public void setMasterVolume(float masterVolume) {
      this.masterVolume = masterVolume;
   }

   public void start() {
      if (!soundAvailable) {
         throw new SoundUnavailableException();
      }
      if (playerPool == null) {
         playerPool = new LinkedList();
         for (int i = 0; i < poolsize; i++) {
            Player p = new Player();
            p.start();
            playerPool.add(p);
         }
         try {
            mixer.open();
         } catch (LineUnavailableException ex) {
            throw new IDXException(ex);
         }
      } else {
         throw new IllegalStateException();
      }
   }

   /**
    * Stops the Internal Thread Pool that this {@link AsyncSoundPlayer} runs on.
    * This method blocks until all threads in the thread pool finish.
    */

   public void stop() {
      stop(0);
   }

   /**
    * Waits at most {@code maxwait} milliseconds for this {@link AsyncSoundPlayer} to die.
    * @param maxwait Max waiting time in milliseconds
    */

   public void stop(int maxwait) {
      if (playerPool != null) {
         try {
            synchronized (poolSync) {
               Iterator<Player> pItr = playerPool.iterator();
               while (pItr.hasNext()) {
                  Player p = pItr.next();
                  p.join(maxwait);
               }
            }
         } catch (InterruptedException ex) {}
         playerPool = null;
         mixer.close();
      } else {
         throw new IllegalStateException();
      }
   }

   /**
    * Sets the Pool size of this <tt>AsyncSoundPlayer</tt>.
    * <br />
    * If this method is called while the sound player is active, it will throw a {@link IllegalArgumentException}.
    * @param ps New pool size
    * @throws IllegalStateException If the sound player is still active
    */

   public void setPoolSize(int ps)
         throws IllegalStateException {
      if (playerPool != null) {
         throw new IllegalStateException();
      }
      poolsize = ps;
   }

   /**
    * Gets the pool size that this sound player uses.
    * @return Pool size
    */

   public int getPoolsize() {
      return poolsize;
   }
   
    public AudioInputStream getAudioInputStream(InputStream is) {
        try {
            if (!is.markSupported()) {
                is = new BufferedInputStream(is);
            }

            // open the source stream
           AudioInputStream source = AudioSystem.getAudioInputStream(is);

            // convert to playback format
           return playBackFormat != null ? convertStream(source) : source;
        } catch (UnsupportedAudioFileException ex) {
            throw new IDXException(ex);
        } catch (IOException ex) {
            throw new IDXException(ex);
        } catch (IllegalArgumentException ex) {
            throw new IDXException(ex);
        }
    }

   /**
    * Stops all sounds playing from this sound player.
    * @param instant Whether it should instantly stop playing, or wait until all sounds have finished playing.
    */

   public void stopAllSounds(boolean instant) {
      Iterator<Player> pItr = playerPool.iterator();
      while (pItr.hasNext()) {
         Player p = pItr.next();
         p.stopAllSounds(instant);
      }
      playingSounds.set(0);
   }

   static int getMaxDefaultMixerLines(AudioFormat format) {
      try {
         Mixer mixer = AudioSystem.getMixer(null);
         DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
         return mixer.getMaxLines(info);
      } catch (Exception e) {
         renoria.Core.handleException(e);
         return -1;
      }
   }

   private AudioInputStream convertStream(AudioInputStream stream) {
      if (playBackFormat == null) {
         return stream;
      }
      try {
         return AudioSystem.getAudioInputStream(playBackFormat, stream);
      } catch (IllegalArgumentException e) {
         renoria.Core.handleException(e);
         return stream;
      }
   }

   /**
    * Plays a sound instantly.
    * @param input Input
    * @return sound info context
    */

   public SoundInfo playSound(InputStream input) {
      return playSound(input, true);
   }



NOTE: Split into 3 posts due to character limit

Have fun, and report to me any problems you may encounter.

PS: Some exception handing and a few bits of other code are reliant on some of my other code. That code can be edited if you wish
Offline DavidX

Senior Newbie





« Reply #1 - Posted 2010-06-26 16:47:03 »

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
   /**
    * Creates a {@link SoundInfo} context for the given input, and optionally plays the sound on one of the sound
    * player threads.
    * <p>If <tt>instant</tt> is false, only the context is created. To play the sound you must invoke
    * {@link #playSound(renoria.sound.AsyncSoundPlayer.SoundInfo) } to pass it to one of the sound player threads.
    * @param input Input to read from
    * @param instant Whether we should instantly play the sound
    * @return Sound Context
    */

   public SoundInfo playSound(InputStream input, boolean instant) {
      if (!soundAvailable) {
         throw new SoundUnavailableException();
      }
      AudioInputStream stream = getAudioInputStream(input);
      AudioFormat format = stream.getFormat();
      SoundInfo info = new SoundInfo();
      info.format = format;
      info.input = stream;
      info.length = stream.getFrameLength();
      info.player = this;
      SourceDataLine line = null;
      DataLine.Info dinfo = new DataLine.Info(SourceDataLine.class, format);
      try {
         Mixer useMixer = mixer != null ? mixer : AudioSystem.getMixer(null);
         try {
            line = (SourceDataLine) useMixer.getLine(dinfo); // Try using this mixer first
        } catch (Throwable t) {
            line = (SourceDataLine) AudioSystem.getLine(dinfo); // Fallback to another mixer on the system
        }
         if (line == null) {
            throw new SoundUnavailableException();
         }
         line.open(format);
         if (line.isControlSupported(BooleanControl.Type.MUTE)) {
            info.muteControl = (BooleanControl) line.getControl(BooleanControl.Type.MUTE);
         }
         if (line.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
            info.gainControl = (FloatControl) line.getControl(FloatControl.Type.MASTER_GAIN);
         }
         if (line.isControlSupported(FloatControl.Type.BALANCE)) {
            info.balanceControl = (FloatControl) line.getControl(FloatControl.Type.BALANCE);
         }
         if (line.isControlSupported(FloatControl.Type.PAN)) {
            info.panControl = (FloatControl) line.getControl(FloatControl.Type.PAN);
         }
      } catch (Exception e) {
         throw new IDXException(e);
      }

      line.start();
      info.line = line;
      if (instant) {
         playSound(info);
      }

      return info;
   }

   public List<SoundInfo> allSoundsPlaying() {
      List<SoundInfo> ret = new ArrayList();
      synchronized (poolSync) {
         Iterator<Player> players = playerPool.iterator();
         while (players.hasNext()) {
            Player p = players.next();
            synchronized (p.soundSync) {
               ret.addAll(p.playList);
            }
         }
      }

      return ret;
   }

   public int numPlayingSounds() {
      return playingSounds.get();
   }

   public int getMaxPlayingSoundsPerPool() {
      return maxPlayingSoundsPerPool;
   }

   public void setMaxPlayingSoundsPerPool(int maxPlayingSoundsPerPool) {
      if (playerPool != null) {
         throw new IllegalStateException();
      }
      this.maxPlayingSoundsPerPool = maxPlayingSoundsPerPool;
   }

   public AudioFormat getPlayBackFormat() {
      return playBackFormat;
   }

   public void setPlayBackFormat(AudioFormat playBackFormat) {
      this.playBackFormat = playBackFormat;
   }

   public void playSound(SoundInfo info) {
      if (!soundAvailable) {
         throw new SoundUnavailableException();
      }
      synchronized (poolSync) {
         Iterator<Player> players = playerPool.iterator();
         Player min = null;
         int mini = Integer.MAX_VALUE;
         while (players.hasNext()) {
            Player mplay = players.next();
            int psize = mplay.playList.size();
            if (psize < mini && psize < maxPlayingSoundsPerPool && !mplay.inListLoop) {
               mini = mplay.playList.size();
               min = mplay;
            }
         }
         if (min != null) {
            synchronized (min.soundSync) {
               min.playList.add(info);
            }
            playingSounds.incrementAndGet();
         } else {
            synchronized (queueSync) {
               queuedSounds.addLast(info);
            }
         }
      }
   }

   private SoundInfo pollQueuedSound() {
      synchronized (queueSync) {
         return queuedSounds.poll();
      }
   }

   final class Player
         extends Thread {
      List<SoundInfo> playList = new LinkedList();
      final Object soundSync = new Object();
      boolean inListLoop = false;

      Player() {
         super("IDGEngine-ASPThread");
      }

      public void stopAllSounds(boolean instant) {
         synchronized (soundSync) {
            int i = 0;
            Iterator<SoundInfo> pInfo = playList.iterator();
            while (pInfo.hasNext()) {
               SoundInfo info = pInfo.next();
               if (info.line.isOpen()) {
                  if (!instant) {
                     info.line.drain();
                  }
                  info.line.close();
                  try {
                     info.input.close();
                  } catch (IOException e) {}
               }
               pInfo.remove();
               i++;
            }
            AsyncSoundPlayer.this.playingSounds.addAndGet(-i);
         }
      }
Offline DavidX

Senior Newbie





« Reply #2 - Posted 2010-06-26 16:47:33 »

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
      @Override
      public void run() {
         try {
            byte[] buf = new byte[1024];
            while (!shutDown) {
               synchronized (soundSync) {
                  Iterator<SoundInfo> pInfo = playList.iterator();
                  while (pInfo.hasNext()) {
                     inListLoop = true;
                     SoundInfo info = pInfo.next();
                     long oldpos = info.line.getLongFramePosition();
                     long mspos = info.line.getMicrosecondPosition();
                     try {
                        int avail = info.line.available();
                        int read = info.input.read(buf, 0, Math.min(avail, buf.length));
                        if (read > 0) {
                           info.line.write(buf, 0, read);
                           if (info.listeners != null) {
                              synchronized (info.listenerLock) {
                                 Iterator<SoundListener> listeners = info.listeners.iterator();
                                 while (listeners.hasNext()) {
                                    SoundListener listener = listeners.next();
                                    listener.update(buf, 0, read);
                                    listener.positionUpdate(oldpos, info.line.getLongFramePosition(), mspos);
                                 }
                              }
                           }
                        } else if (!info.line.isRunning() ||
                              !info.line.isActive() ||
                              !info.line.isOpen() ||
                              info.line.getLongFramePosition() >= info.length) {
                           info.line.drain();
                           info.line.close();
                           info.input.close();
                           pInfo.remove();
                           AsyncSoundPlayer.this.playingSounds.decrementAndGet();
                           if (info.listeners != null) {
                              synchronized (info.listenerLock) {
                                 Iterator<SoundListener> listeners = info.listeners.iterator();
                                 while (listeners.hasNext()) {
                                    SoundListener listener = listeners.next();
                                    listener.stopped();
                                 }
                              }
                           }
                        } else {
                           if (info.listeners != null) {
                              synchronized (info.listenerLock) {
                                 Iterator<SoundListener> listeners = info.listeners.iterator();
                                 while (listeners.hasNext()) {
                                    SoundListener listener = listeners.next();
                                    listener.positionUpdate(oldpos, info.line.getLongFramePosition(), mspos);
                                 }
                              }
                           }
                        }
                     } catch (IOException e) {
                        throw new IDXException(e);
                     }
                  }
                  inListLoop = false;
                  if (playList.size() < maxPlayingSoundsPerPool) {
                     SoundInfo nextSound = pollQueuedSound();
                     if (nextSound != null) {
                        synchronized (soundSync) {
                           playList.add(nextSound);
                           if (nextSound.listeners != null) {
                              synchronized (nextSound.listenerLock) {
                                 Iterator<SoundListener> listeners = nextSound.listeners.iterator();
                                 while (listeners.hasNext()) {
                                    SoundListener listener = listeners.next();
                                    listener.started();
                                 }
                              }
                           }
                           AsyncSoundPlayer.this.playingSounds.incrementAndGet();
                        }
                     }
                  }
                  try {
                     Thread.sleep(1);
                  } catch (Exception e) {}
               }
               try {
                  Thread.sleep(1);
               } catch (Exception e) {}
            }
         } catch (Throwable t) {
            renoria.Core.handleException(t);
         }
      }
   }

   public static final class SoundInfo {
      private AudioFormat format;
      private SourceDataLine line;
      private AudioInputStream input;
      private BooleanControl muteControl;
      private FloatControl gainControl;
      private FloatControl balanceControl;
      private FloatControl panControl;
      private long length;
      private float volume = 1.0f;
      private List<SoundListener> listeners = null;
      private final Object listenerLock = new Object();
      private AsyncSoundPlayer player;

      private SoundInfo() {
      }

      public boolean isMuteAvailable() {
         return muteControl != null;
      }

      public boolean isGainAvailable() {
         return gainControl != null;
      }

      public boolean isBalanceAvailable() {
         return balanceControl != null;
      }

      public boolean isPanAvailable() {
         return panControl != null;
      }

      public AudioFormat getFormat() {
         return format;
      }

      public FloatControl getBalanceControl() {
         return balanceControl;
      }

      public FloatControl getGainControl() {
         return gainControl;
      }

      public BooleanControl getMuteControl() {
         return muteControl;
      }

      public FloatControl getPanControl() {
         return panControl;
      }

      public void setVolume(float volume) {
         this.volume = volume * player.masterVolume;
         setVolumeIntern();
      }

      private void setVolumeIntern() {
         if (gainControl != null) {
            if (volume <= 0) {
               if (muteControl != null) {
                  muteControl.setValue(true);
               } else {
                  gainControl.setValue(gainControl.getMinimum());
               }
            } else {
               if (muteControl != null) {
                  muteControl.setValue(false);
               }

               float max = gainControl.getMaximum();
               float min = gainControl.getMinimum();
               float vol = (float) (Math.log(volume) / Math.log(10.0) * 20.0);

               vol = Math.max(min, Math.min(max, vol));
               gainControl.setValue(vol);
            }
         }
      }

      public AudioInputStream getInput() {
         return input;
      }

      public long getLength() {
         return length;
      }

      public SourceDataLine getLine() {
         return line;
      }

      public float getVolume() {
         return volume;
      }

      public void addSoundListener(SoundListener listener) {
         if (listeners == null) {
            listeners = new LinkedList();
         }
         synchronized (listenerLock) {
            listeners.add(listener);
         }
      }

      public void removeSoundListener(SoundListener listener) {
         if (listeners == null) {
            throw new IllegalStateException();
         }
         synchronized (listenerLock) {
            listeners.remove(listener);
         }
      }

      public SoundListener[] getListeners() {
         return listeners.toArray(new SoundListener[listeners.size()]);
      }
   }

   public static final class SoundUnavailableException
         extends RuntimeException {
      public SoundUnavailableException(Throwable cause) {
         super(cause);
      }

      public SoundUnavailableException(String message, Throwable cause) {
         super(message, cause);
      }

      public SoundUnavailableException(String message) {
         super(message);
      }

      public SoundUnavailableException() {
      }
   }
}
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline DavidX

Senior Newbie





« Reply #3 - Posted 2010-06-26 17:25:17 »

forgot to include this:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */


package renoria.sound;

/**
 *
 * @author David
 */

public interface SoundListener {
   /**
    * Invoked when the sound is started
    */

   void started();

   /**
    * Invoked when the sound is stopped
    */

   void stopped();

   /**
    * Invoked when the sound is updated.
    * @param data Data read
    * @param off Offset of <tt>data</tt> where the data was read
    * @param len length of data read
    */

   void update(byte[] data, int off, int len);

   /**
    * Invoked when the position of the sound is updated.
    * @param oldPosition Old position
    * @param newPosition New position
    * @param msTicks Milliseconds ticked
    */

   void positionUpdate(long oldPosition, long newPosition, long msTicks);
}
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

xsi3rr4x (30 views)
2014-04-15 18:08:23

BurntPizza (27 views)
2014-04-15 03:46:01

UprightPath (42 views)
2014-04-14 17:39:50

UprightPath (24 views)
2014-04-14 17:35:47

Porlus (41 views)
2014-04-14 15:48:38

tom_mai78101 (63 views)
2014-04-10 04:04:31

BurntPizza (123 views)
2014-04-08 23:06:04

tom_mai78101 (222 views)
2014-04-05 13:34:39

trollwarrior1 (189 views)
2014-04-04 12:06:45

CJLetsGame (198 views)
2014-04-01 02:16:10
List of Learning Resources
by SHC
2014-04-18 03:17:39

List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51

Good Examples
by matheus23
2014-04-01 18:40:34

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:22:30
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!