Passed
Push — master ( 27e47c...011406 )
by Vincent
03:32
created

serializeCell(CellData)   B

Complexity

Conditions 7

Size

Total Lines 27
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 25
c 1
b 0
f 0
dl 0
loc 27
cc 7
rs 7.8799
1
/*
2
 * This file is part of ArakneUtils.
3
 *
4
 * ArakneUtils is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * ArakneUtils is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with ArakneUtils.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2020 Vincent Quatrevieux
18
 */
19
20
package fr.arakne.utils.maps.serializer;
21
22
import fr.arakne.utils.encoding.Base64;
23
import fr.arakne.utils.maps.constant.CellMovement;
24
25
import java.util.Map;
26
import java.util.concurrent.ConcurrentHashMap;
27
28
/**
29
 * Default implementation of the map data serializer, handling the plain (i.e. not crypted) map data format
30
 *
31
 * https://github.com/Emudofus/Dofus/blob/1.29/ank/battlefield/utils/Compressor.as#L54
32
 */
33
final public class DefaultMapDataSerializer implements MapDataSerializer {
34
    final static private int CELL_DATA_LENGTH = 10;
35
36
    private static class ByteArrayCell implements CellData {
37
        final private byte[] data;
38
39
        public ByteArrayCell(byte[] data) {
40
            this.data = data;
41
        }
42
43
        @Override
44
        public boolean lineOfSight() {
45
            return (data[0] & 1) == 1;
46
        }
47
48
        @Override
49
        public CellMovement movement() {
50
            return CellMovement.byValue((data[2] & 56) >> 3);
51
        }
52
53
        @Override
54
        public boolean active() {
55
            return (data[0] & 32) >> 5 == 1;
56
        }
57
58
        @Override
59
        public GroundCellData ground() {
60
            return new GroundCellData() {
61
                @Override
62
                public int level() {
63
                    return data[1] & 15;
64
                }
65
66
                @Override
67
                public int slope() {
68
                    return (data[4] & 60) >> 2;
69
                }
70
71 View Code Duplication
                @Override
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
72
                public int number() {
73
                    return ((data[0] & 24) << 6) + ((data[2] & 7) << 6) + data[3];
74
                }
75
76
                @Override
77
                public int rotation() {
78
                    return (data[1] & 48) >> 4;
79
                }
80
81
                @Override
82
                public boolean flip() {
83
                    return (data[4] & 2) >> 1 == 1;
84
                }
85
            };
86
        }
87
88
        @Override
89
        public CellLayerData layer1() {
90
            return new CellLayerData() {
91 View Code Duplication
                @Override
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
92
                public int number() {
93
                    return ((data[0] & 4) << 11) + ((data[4] & 1) << 12) + (data[5] << 6) + data[6];
94
                }
95
96
                @Override
97
                public int rotation() {
98
                    return (data[7] & 48) >> 4;
99
                }
100
101
                @Override
102
                public boolean flip() {
103
                    return (data[7] & 8) >> 3 == 1;
104
                }
105
            };
106
        }
107
108
        @Override
109
        public InteractiveObjectData layer2() {
110
            return new InteractiveObjectData() {
111
                @Override
112
                public boolean interactive() {
113
                    return (data[7] & 2) >> 1 == 1;
114
                }
115
116 View Code Duplication
                @Override
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
117
                public int number() {
118
                    return ((data[0] & 2) << 12) + ((data[7] & 1) << 12) + (data[8] << 6) + data[9];
119
                }
120
121
                @Override
122
                public boolean flip() {
123
                    return (data[7] & 4) >> 2 == 1;
124
                }
125
            };
126
        }
127
    }
128
129
    private Map<String, ByteArrayCell> cache;
130
131
    @Override
132
    public CellData[] deserialize(String mapData) {
133
        if (mapData.length() % CELL_DATA_LENGTH != 0) {
134
            throw new IllegalArgumentException("Invalid map data");
135
        }
136
137
        final int size = mapData.length() / CELL_DATA_LENGTH;
138
        final CellData[] cells = new CellData[size];
139
140
        for (int i = 0; i < size; ++i) {
141
            cells[i] = deserializeCell(mapData.substring(i * CELL_DATA_LENGTH, (i + 1) * CELL_DATA_LENGTH));
142
        }
143
144
        return cells;
145
    }
146
147
    @Override
148
    public String serialize(CellData[] cells) {
149
        StringBuilder sb = new StringBuilder(cells.length * CELL_DATA_LENGTH);
150
151
        for (CellData cell : cells) {
152
            sb.append(serializeCell(cell));
153
        }
154
155
        return sb.toString();
156
    }
157
158
    /**
159
     * Enable the cell data cache
160
     * Once enable, deserialize two same cell data will return the same cell instance
161
     */
162
    public void enableCache() {
163
        cache = new ConcurrentHashMap<>();
164
    }
165
166
    /**
167
     * Disable cell data cache
168
     */
169
    public void disableCache() {
170
        cache = null;
171
    }
172
173
    private CellData deserializeCell(String cellData) {
174
        if (cache == null) {
175
            return new ByteArrayCell(Base64.toBytes(cellData));
176
        }
177
178
        return cache.computeIfAbsent(cellData, data -> new ByteArrayCell(Base64.toBytes(data)));
179
    }
180
181
    private String serializeCell(CellData cell) {
182
        final byte[] data = new byte[CELL_DATA_LENGTH];
183
184
        data[0] = (byte) ((cell.active() ? (1) : (0)) << 5);
185
        data[0] = (byte) (data[0] | (cell.lineOfSight() ? (1) : (0)));
186
        data[0] = (byte) (data[0] | (cell.ground().number() & 1536) >> 6);
1 ignored issue
show
introduced by
Prevent "int" promotion by adding "& 0xff" to this expression.
Loading history...
187
        data[0] = (byte) (data[0] | (cell.layer1().number() & 8192) >> 11);
1 ignored issue
show
introduced by
Prevent "int" promotion by adding "& 0xff" to this expression.
Loading history...
188
        data[0] = (byte) (data[0] | (cell.layer2().number() & 8192) >> 12);
1 ignored issue
show
introduced by
Prevent "int" promotion by adding "& 0xff" to this expression.
Loading history...
189
        data[1] = (byte) ((cell.ground().rotation() & 3) << 4);
190
        data[1] = (byte) (data[1] | cell.ground().level() & 15);
191
        data[2] = (byte) ((cell.movement().ordinal() & 7) << 3);
192
        data[2] = (byte) (data[2] | cell.ground().number() >> 6 & 7);
193
        data[3] = (byte) (cell.ground().number() & 63);
194
        data[4] = (byte) ((cell.ground().slope() & 15) << 2);
195
        data[4] = (byte) (data[4] | (cell.ground().flip() ? (1) : (0)) << 1);
1 ignored issue
show
introduced by
Prevent "int" promotion by adding "& 0xff" to this expression.
Loading history...
196
        data[4] = (byte) (data[4] | cell.layer1().number() >> 12 & 1);
197
        data[5] = (byte) (cell.layer1().number() >> 6 & 63);
198
        data[6] = (byte) (cell.layer1().number() & 63);
199
        data[7] = (byte) ((cell.layer1().rotation() & 3) << 4);
200
        data[7] = (byte) (data[7] | (cell.layer1().flip() ? (1) : (0)) << 3);
1 ignored issue
show
introduced by
Prevent "int" promotion by adding "& 0xff" to this expression.
Loading history...
201
        data[7] = (byte) (data[7] | (cell.layer2().flip() ? (1) : (0)) << 2);
1 ignored issue
show
introduced by
Prevent "int" promotion by adding "& 0xff" to this expression.
Loading history...
202
        data[7] = (byte) (data[7] | (cell.layer2().interactive() ? (1) : (0)) << 1);
1 ignored issue
show
introduced by
Prevent "int" promotion by adding "& 0xff" to this expression.
Loading history...
203
        data[7] = (byte) (data[7] | cell.layer2().number() >> 12 & 1);
204
        data[8] = (byte) (cell.layer2().number() >> 6 & 63);
205
        data[9] = (byte) (cell.layer2().number() & 63);
206
207
        return Base64.encode(data);
208
    }
209
}
210