Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (511)
Games in Android Showcase (119)
games submitted by our members
Games in WIP (577)
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  
  List<List<List<Object>>> or List[][]  (Read 1686 times)
0 Members and 1 Guest are viewing this topic.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Posted 2011-08-06 21:18:23 »

Even more for the evening.

Since I can't get type safety in ArrayList[][], I have to do ArrayList<ArrayList<ArrayList<Entity>>>.
I think it's because I can't initialize all those arraylists with the amount of slots I want from the start like I can with ArrayList[][].

This throws the error:
1  
map.getGrid().get(newCellX).get(newCellY).add(e);


Is there a way to initialize those with the amount of slots needed? Is it better if I just make an ArrayList[][] and cast to the type (theres only one type inside of it)? I feel that the arrays are better, because I don't even want them to extrude nor shrink during runtime, but that means I have to use casting, which I'm told is avoided at all costs.

Offline counterp

Senior Duke


Medals: 11



« Reply #1 - Posted 2011-08-06 21:44:34 »

I think either way is bad, there are probably better solutions. I don't know the range of your cells, but here's an example of something that might work for you (although not really sure what you're trying to do):

1  
2  
3  
4  
5  
6  
Map<Integer, Object> map = new HashMap<Integer, Object>();

...

int key = ((x & 0xFFFF) << 16) | (y & 0xFFFF);
Object object = map.get(key);
Offline cylab

JGO Ninja


Medals: 52



« Reply #2 - Posted 2011-08-06 22:05:25 »

Since I can't get type safety in ArrayList[][]

1  
ArrayList<Entity>[][] grid= new ArrayList[rows][cols];


should just do it...

Mathias - I Know What [you] Did Last Summer!
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Nate

JGO Kernel


Medals: 149
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #3 - Posted 2011-08-07 08:54:55 »

1  
2  
ArrayList<Item>[] grid= new ArrayList[rows * cols];
ArrayList<Item> cellItems = grid[y * cols + x];

Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #4 - Posted 2011-08-07 10:10:41 »

Since I can't get type safety in ArrayList[][]

1  
ArrayList<Entity>[][] grid= new ArrayList[rows][cols];


should just do it...

Oddly, it seems I can actually return an ArrayList<Entity>[][] when a class is asking for the grid.

I think either way is bad, there are probably better solutions. I don't know the range of your cells, but here's an example of something that might work for you (although not really sure what you're trying to do):

1  
2  
3  
4  
5  
6  
Map<Integer, Object> map = new HashMap<Integer, Object>();

...

int key = ((x & 0xFFFF) << 16) | (y & 0xFFFF);
Object object = map.get(key);


I don't quite understand how the key in that solution works. Could you explain it?

1  
2  
ArrayList<Item>[] grid= new ArrayList[rows * cols];
ArrayList<Item> cellItems = grid.get(y * cols + x);


I understand that this can work, but why is this preferable over the 2D array?

Offline counterp

Senior Duke


Medals: 11



« Reply #5 - Posted 2011-08-07 12:34:58 »

I don't quite understand how the key in that solution works. Could you explain it?

Using bit shifting, you can write x as a short (65536 unique values, is that enough for you?) to the high order bits of an integer (left-most 16) and y as a short to the right 16 bits.

Basically these are the bits of an integer:

00000000 00000000 00000000 00000000

there are 32 total.

let's say x is 107 and y is 10888. they both have to be shorts (signed or unsigned, doesn't matter, but you have to stick with whichever you choose)

x (107) in bits is:

00000000 01101011

y (10888) in bits is:

00101010 10001000

now if we right x to the highorder 16 bits (left-most) and y to the loworder 16 bits(right-most) you get a unique key or integer which is:

00000000 01101011 00101010 10001000

(that is 7023240)

Once again, the limit in this approach is that x and y must be either unsigned/signed shorts, that is they must be either in the range of –32,768-32,767 or 0-65,535.

If you need to work with bigger numbers, you can swap out the integer as a key for a long (i'll explain if you need)

A big pro of this is that you have O(1)  speed getting/putting but you also have a lower memory footprint (as opposed to generating an array of size MAX_X * MAX_Y which might be very costly if those are large numbers)
Offline Dx4

Junior Duke


Medals: 5



« Reply #6 - Posted 2011-08-07 13:29:52 »

Here's a solution that will be faster than the one provided by counterp but uses the same semantics (there is no unboxing/boxing each read)

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  
public class TileHashtable {

   public class Entry {
      Entry next;
      int x;
      int y;
      int hash;
      UserLayer.Tile value;

      public Entry(Entry next, int x, int y, int hash, Tile value) {
         this.next = next;
         this.x = x;
         this.y = y;
         this.hash = hash;
         this.value = value;
      }

      int hash() {
         return (x << 5) + y;
      }

      public int getHash() {
         return hash;
      }

      public Entry getNext() {
         return next;
      }

      public Tile getValue() {
         return value;
      }

      public int getX() {
         return x;
      }

      public int getY() {
         return y;
      }
   }
   
   int size;
   Entry [] table;

   public TileHashtable() {
      table = new Entry[64];
   }

    static int hash(int h) {
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

   static int getPointHash(int x, int y) {
      return hash((x & 0xFFFF) << 16) | (y & 0xFFFF);
   }

    static int indexFor(int h, int length) {
        return h & (length-1);
    }

   public void clear() {
      for (int i = 0; i < table.length; i++) {
         table[i] = null;
      }
      size = 0;
   }

   public UserLayer.Tile lookup(int x, int y, boolean createIfNotExist) {
      int hash = getPointHash(x, y);
      Entry e;
      int index = indexFor(hash, table.length);
      for (e = table[index]; e != null; e = e.next) {
         if (e.hash == hash && e.x == x && e.y == y) {
            return e.value;
         }
      }

      if (createIfNotExist) {
         UserLayer.Tile val = new UserLayer.Tile();
         table[index] = new Entry(table[index], x, y, hash, val);

         size++;
         return val;
      }

      return null;

   }

   public int size() {
      return size;
   }

   public boolean isEmpty() {
      return size == 0;
   }

   public Iterator<Entry> entryIterator() {
      return new EntryIterator();
   }

   public Iterator<UserLayer.Tile> valueIterator() {
      return new ValueIterator();
   }

   public Iterator<Void> keyIterator() {
      return new KeyIterator();
   }

    private abstract class HashIterator<E> implements Iterator<E> {
        Entry next;   // next entry to return
        int index;      // current slot
        Entry current;   // current entry

        HashIterator() {
            if (size > 0) { // advance to first entry
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)
                    ;
            }
        }

        public final boolean hasNext() {
            return next != null;
        }

        final Entry nextEntry() {
            Entry e = next;
            if (e == null)
                throw new NoSuchElementException();

            if ((next = e.next) == null) {
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)
                    ;
            }
       current = e;
            return e;
        }

        public void remove() {
         throw new UnsupportedOperationException("My Dick");
        }

    }

    private final class ValueIterator extends HashIterator<UserLayer.Tile> {
        public UserLayer.Tile next() {
            return nextEntry().value;
        }
    }

    private final class KeyIterator extends HashIterator<Void> {
        public void next(Point p) {

        }

      public Void next() {
         throw new UnsupportedOperationException("My Dick");
      }
    }

    private final class EntryIterator extends HashIterator<Entry> {
        public Entry next() {
            return nextEntry();
        }
    }


}


replace UserLayer.Tile with whatever obj type you need

HTH

- David
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #7 - Posted 2011-08-07 15:44:40 »

Thanks for your replies! It made me rethink how I should do this. I have some questions to the code Dx4 and counterp provided.

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  
public class TileHashtable {

   public class Entry {
      Entry next;
      int x;
      int y;
      int hash;
      UserLayer.Tile value;

      public Entry(Entry next, int x, int y, int hash, Tile value) {
         this.next = next;
         this.x = x;
         this.y = y;
         this.hash = hash;
         this.value = value;
      }

      int hash() {
         return (x << 5) + y;
      }

      public int getHash() {
         return hash;
      }

      public Entry getNext() {
         return next;
      }

      public Tile getValue() {
         return value;
      }

      public int getX() {
         return x;
      }

      public int getY() {
         return y;
      }
   }
   
   int size;
   Entry [] table;

   public TileHashtable() {
      table = new Entry[64];
   }

    static int hash(int h) {
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

   static int getPointHash(int x, int y) {
      return hash((x & 0xFFFF) << 16) | (y & 0xFFFF);
   }

    static int indexFor(int h, int length) {
        return h & (length-1);
    }

   public void clear() {
      for (int i = 0; i < table.length; i++) {
         table[i] = null;
      }
      size = 0;
   }

   public UserLayer.Tile lookup(int x, int y, boolean createIfNotExist) {
      int hash = getPointHash(x, y);
      Entry e;
      int index = indexFor(hash, table.length);
      for (e = table[index]; e != null; e = e.next) {
         if (e.hash == hash && e.x == x && e.y == y) {
            return e.value;
         }
      }

      if (createIfNotExist) {
         UserLayer.Tile val = new UserLayer.Tile();
         table[index] = new Entry(table[index], x, y, hash, val);

         size++;
         return val;
      }

      return null;

   }

   public int size() {
      return size;
   }

   public boolean isEmpty() {
      return size == 0;
   }

   public Iterator<Entry> entryIterator() {
      return new EntryIterator();
   }

   public Iterator<UserLayer.Tile> valueIterator() {
      return new ValueIterator();
   }

   public Iterator<Void> keyIterator() {
      return new KeyIterator();
   }

    private abstract class HashIterator<E> implements Iterator<E> {
        Entry next;   // next entry to return
        int index;      // current slot
        Entry current;   // current entry

        HashIterator() {
            if (size > 0) { // advance to first entry
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)
                    ;
            }
        }

        public final boolean hasNext() {
            return next != null;
        }

        final Entry nextEntry() {
            Entry e = next;
            if (e == null)
                throw new NoSuchElementException();

            if ((next = e.next) == null) {
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)
                    ;
            }
       current = e;
            return e;
        }

        public void remove() {
         throw new UnsupportedOperationException("My Dick");
        }

    }

    private final class ValueIterator extends HashIterator<UserLayer.Tile> {
        public UserLayer.Tile next() {
            return nextEntry().value;
        }
    }

    private final class KeyIterator extends HashIterator<Void> {
        public void next(Point p) {

        }

      public Void next() {
         throw new UnsupportedOperationException("My Dick");
      }
    }

    private final class EntryIterator extends HashIterator<Entry> {
        public Entry next() {
            return nextEntry();
        }
    }


}


1  
2  
3  
static int getPointHash(int x, int y) {
      return hash((x & 0xFFFF) << 16) | (y & 0xFFFF);
   }

Why is it that we have to do that AND operation on 0xFFF? To me, it looks like it will return the same, if we exclude that. Also, I don't see a change if I exchange the OR operation with a "+". Can you please explain this?

1  
2  
3  
4  
5  
6  
7  
static int hash(int h) {
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

This also I can't seem to get much meaning out of. I'm unsure what this ensures, and I'd very much like a more in-depth explanation that the comment provided  Clueless

1  
2  
3  
int hash() {
         return (x << 5) + y;
      }

This again makes me feel like I don't really know what the hashes do. I though we'd use the last 16 bits to define y, and the first 16 ones to define x. Also, what happended to the AND-operation with the 0xFFFF byte?

Thank you very much for your replies guys, I will be sure to Appriciate after I just hit post  Smiley

Offline Roquen
« Reply #8 - Posted 2011-08-07 16:14:18 »

Assuming that most queries will be local and near neighbors then you can improve cache performance by mapping via either a Z or Hilbert curve.  Hilbert is better but more expensive to compute so it's difficult to say which will be better overall.  Of course this is only interesting if cache misses are significant.
Offline counterp

Senior Duke


Medals: 11



« Reply #9 - Posted 2011-08-07 16:26:35 »

Well I'll answer your first question.

It's called the bitwise AND operator and using the bitmask 0xFFFF ensures that the value will be within the range of a short.

So if your bits are:

00000000 00011000 00001000 00001000

after applying the bitmask it will become:

00000000 00000000 00001000 00001000

(effectively cutting off all high order bits that aren't within the range of a short which is the low order 16 bits)

As for the reason, if a number goes over the allowed bits (16) it ends up overwriting other bits (this depends on if it's x or y, and the values of each), and you end up with a lot of inconsistent overwriting.

However, if you know for a fact that x and y will never be above the or below the min and max values of a short, you don't need to include the masking.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Nate

JGO Kernel


Medals: 149
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #10 - Posted 2011-08-07 21:56:15 »

1  
2  
ArrayList<Item>[] grid= new ArrayList[rows * cols];
ArrayList<Item> cellItems = grid[y * cols + x];


I understand that this can work, but why is this preferable over the 2D array?

Note I fixed an error in my original post.

Look up is very cheap: 1 multiply, 1 add, and 1 array index. It also uses less memory, with a 2D array each element in the 2nd dimension is another object (which you also have to initialize).

Offline Dx4

Junior Duke


Medals: 5



« Reply #11 - Posted 2011-08-08 12:53:18 »

Thanks for your replies! It made me rethink how I should do this. I have some questions to the code Dx4 and counterp provided.

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  
public class TileHashtable {

   public class Entry {
      Entry next;
      int x;
      int y;
      int hash;
      UserLayer.Tile value;

      public Entry(Entry next, int x, int y, int hash, Tile value) {
         this.next = next;
         this.x = x;
         this.y = y;
         this.hash = hash;
         this.value = value;
      }

      int hash() {
         return (x << 5) + y;
      }

      public int getHash() {
         return hash;
      }

      public Entry getNext() {
         return next;
      }

      public Tile getValue() {
         return value;
      }

      public int getX() {
         return x;
      }

      public int getY() {
         return y;
      }
   }
   
   int size;
   Entry [] table;

   public TileHashtable() {
      table = new Entry[64];
   }

    static int hash(int h) {
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

   static int getPointHash(int x, int y) {
      return hash((x & 0xFFFF) << 16) | (y & 0xFFFF);
   }

    static int indexFor(int h, int length) {
        return h & (length-1);
    }

   public void clear() {
      for (int i = 0; i < table.length; i++) {
         table[i] = null;
      }
      size = 0;
   }

   public UserLayer.Tile lookup(int x, int y, boolean createIfNotExist) {
      int hash = getPointHash(x, y);
      Entry e;
      int index = indexFor(hash, table.length);
      for (e = table[index]; e != null; e = e.next) {
         if (e.hash == hash && e.x == x && e.y == y) {
            return e.value;
         }
      }

      if (createIfNotExist) {
         UserLayer.Tile val = new UserLayer.Tile();
         table[index] = new Entry(table[index], x, y, hash, val);

         size++;
         return val;
      }

      return null;

   }

   public int size() {
      return size;
   }

   public boolean isEmpty() {
      return size == 0;
   }

   public Iterator<Entry> entryIterator() {
      return new EntryIterator();
   }

   public Iterator<UserLayer.Tile> valueIterator() {
      return new ValueIterator();
   }

   public Iterator<Void> keyIterator() {
      return new KeyIterator();
   }

    private abstract class HashIterator<E> implements Iterator<E> {
        Entry next;   // next entry to return
        int index;      // current slot
        Entry current;   // current entry

        HashIterator() {
            if (size > 0) { // advance to first entry
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)
                    ;
            }
        }

        public final boolean hasNext() {
            return next != null;
        }

        final Entry nextEntry() {
            Entry e = next;
            if (e == null)
                throw new NoSuchElementException();

            if ((next = e.next) == null) {
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)
                    ;
            }
       current = e;
            return e;
        }

        public void remove() {
         throw new UnsupportedOperationException("My Dick");
        }

    }

    private final class ValueIterator extends HashIterator<UserLayer.Tile> {
        public UserLayer.Tile next() {
            return nextEntry().value;
        }
    }

    private final class KeyIterator extends HashIterator<Void> {
        public void next(Point p) {

        }

      public Void next() {
         throw new UnsupportedOperationException("My Dick");
      }
    }

    private final class EntryIterator extends HashIterator<Entry> {
        public Entry next() {
            return nextEntry();
        }
    }


}


1  
2  
3  
static int getPointHash(int x, int y) {
      return hash((x & 0xFFFF) << 16) | (y & 0xFFFF);
   }

Why is it that we have to do that AND operation on 0xFFF? To me, it looks like it will return the same, if we exclude that. Also, I don't see a change if I exchange the OR operation with a "+". Can you please explain this?

This is only required if you need to make sure x and y dont exceed a short max.

1  
2  
3  
4  
5  
6  
7  
static int hash(int h) {
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

This also I can't seem to get much meaning out of. I'm unsure what this ensures, and I'd very much like a more in-depth explanation that the comment provided  Clueless

basically this just mixes up the bits of a hashcode, as some hashcodes only differ in bits in lower bits, this function will ensure the bits become more spread from upper->lower

1  
2  
3  
int hash() {
         return (x << 5) + y;
      }

This again makes me feel like I don't really know what the hashes do. I though we'd use the last 16 bits to define y, and the first 16 ones to define x. Also, what happended to the AND-operation with the 0xFFFF byte?

this hash function doesnt really matter, you could make it anything that involves x and y, it would work the same.
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.

Longarmx (52 views)
2014-10-17 03:59:02

Norakomi (42 views)
2014-10-16 15:22:06

Norakomi (32 views)
2014-10-16 15:20:20

lcass (37 views)
2014-10-15 16:18:58

TehJavaDev (68 views)
2014-10-14 00:39:48

TehJavaDev (66 views)
2014-10-14 00:35:47

TehJavaDev (58 views)
2014-10-14 00:32:37

BurntPizza (73 views)
2014-10-11 23:24:42

BurntPizza (45 views)
2014-10-11 23:10:45

BurntPizza (85 views)
2014-10-11 22:30:10
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
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!