Skip to content

Commit 592ac4f

Browse files
committed
optimized system of getting block data from their ids
1 parent 28f33b8 commit 592ac4f

File tree

4 files changed

+136
-133
lines changed

4 files changed

+136
-133
lines changed

api/src/main/java/org/machinemc/api/world/BlockData.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ public static void finishRegistration() {
4646
BlockDataImpl.finishRegistration();
4747
}
4848

49+
/**
50+
* Registers the block data to the block data registry.
51+
*/
52+
@ApiStatus.Internal
53+
protected abstract void register();
54+
4955
/**
5056
* @return material of the block data
5157
*/
@@ -54,14 +60,6 @@ public static void finishRegistration() {
5460
/**
5561
* Changes base material for the block data and all its
5662
* variants.
57-
* <p>
58-
* For example changing material for oak log block data would
59-
* change the returned value of {@link #getMaterial()} for all
60-
* rotations of the oak log.
61-
* <p>
62-
* This can lead to unexpected behaviour because then
63-
* {@code Material.OAK_LOG.createBlockData().getMaterial()} would return
64-
* different material than the one it was created from.
6563
* @param material new material
6664
* @return this
6765
*/

api/src/main/java/org/machinemc/api/world/BlockDataImpl.java

Lines changed: 79 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
// This class is used by the code generators, edit with caution.
3030
class BlockDataImpl extends BlockData {
3131

32-
private static final Map<Integer, BlockDataImpl> REGISTRY = new TreeMap<>();
33-
private static BlockDataImpl[] registryArray = new BlockDataImpl[0];
32+
private static final Map<Integer, BlockDataImpl> TEMP_REGISTRY = new TreeMap<>();
33+
private static BlockDataImpl[] registryArray;
3434

3535
@Getter
3636
private Material material;
@@ -40,9 +40,11 @@ class BlockDataImpl extends BlockData {
4040
* Finishes the registration of the block data to materials.
4141
*/
4242
public static void finishRegistration() {
43-
registryArray = new BlockDataImpl[Iterables.getLast(REGISTRY.keySet()) + 1];
44-
for (final Integer stateId : REGISTRY.keySet())
45-
registryArray[stateId] = REGISTRY.get(stateId);
43+
canRegister();
44+
registryArray = new BlockDataImpl[Iterables.getLast(TEMP_REGISTRY.keySet()) + 1];
45+
for (final Integer stateId : TEMP_REGISTRY.keySet())
46+
registryArray[stateId] = TEMP_REGISTRY.get(stateId);
47+
TEMP_REGISTRY.clear();
4648
}
4749

4850
/**
@@ -55,7 +57,40 @@ public static void finishRegistration() {
5557
if (registryArray.length <= id) return null;
5658
final BlockDataImpl data = registryArray[id];
5759
if (data == null) return null;
58-
return data.clone();
60+
if (data.id != -1) return data.clone();
61+
62+
final BlockDataImpl clone = (BlockDataImpl) data.clone();
63+
final Object[][] available = clone.getAcceptedProperties();
64+
final int[] weights = new int[available.length];
65+
66+
for (int i = 0; i < weights.length; i++) {
67+
int weight = 1;
68+
for (int j = i + 1; j < available.length; j++)
69+
weight *= available[j].length;
70+
weights[i] = weight;
71+
}
72+
73+
final Object[] newData = new Object[available.length];
74+
for (int i = 0; i < newData.length; i++)
75+
newData[i] = available[i][0];
76+
77+
int diff = id - clone.firstStateID();
78+
int i = 0;
79+
while (diff != 0) {
80+
final Object[] properties = available[i];
81+
for (final Object property : properties) {
82+
newData[i] = property;
83+
diff -= weights[i];
84+
if (diff < 0) {
85+
diff += weights[i];
86+
break;
87+
}
88+
}
89+
i++;
90+
}
91+
92+
clone.loadProperties(newData);
93+
return clone;
5994
}
6095

6196
protected BlockDataImpl(final int id) {
@@ -67,26 +102,39 @@ protected BlockDataImpl() {
67102
}
68103

69104
@Override
70-
protected BlockDataImpl setMaterial(final Material material) {
71-
this.material = material;
72-
final Map<Integer, BlockData> stateMap = getIdMap();
73-
for (final Integer stateId : stateMap.keySet()) {
74-
final BlockData data = stateMap.get(stateId).clone();
75-
if (!(data instanceof BlockDataImpl blockData))
76-
throw new IllegalStateException();
77-
blockData.material = material;
78-
REGISTRY.put(stateId, blockData);
105+
protected void register() {
106+
canRegister();
107+
if (id != -1) {
108+
TEMP_REGISTRY.put(id, this);
109+
return;
79110
}
80-
return this;
111+
112+
int id = firstStateID();
113+
final Object[][] available = getAcceptedProperties();
114+
115+
for (int i = 0; i < available.length; i++) {
116+
int weight = 1;
117+
for (int j = i + 1; j < available.length; j++)
118+
weight *= available[j].length;
119+
id += weight * (available[i].length - 1);
120+
}
121+
122+
for (int i = firstStateID(); i < id; i++) TEMP_REGISTRY.put(i, this);
81123
}
82124

83125
/**
84-
* Returns id of the block data.
85-
* @param blockData block data to get id from
86-
* @return id of the given block data
126+
* Checks whether registrations of new block data are allowed.
127+
* @throws UnsupportedOperationException if registrations had already finished
87128
*/
88-
public static int getId(final BlockData blockData) {
89-
return blockData.getId();
129+
private static void canRegister() {
130+
if (registryArray == null) return;
131+
throw new UnsupportedOperationException("Registration has been already finished");
132+
}
133+
134+
@Override
135+
protected BlockDataImpl setMaterial(final Material material) {
136+
this.material = material;
137+
return this;
90138
}
91139

92140
@Override
@@ -131,13 +179,6 @@ public int getId() {
131179
return Collections.unmodifiableMap(map);
132180
}
133181

134-
/**
135-
* @return all block data states for material of this block data mapped to ids
136-
*/
137-
protected @Unmodifiable Map<Integer, BlockData> getIdMap() {
138-
return Map.of(id, this);
139-
}
140-
141182
/**
142183
* Returns all data used by the block data (block data properties)
143184
* in order of their names. {@link BlockDataImpl#getDataNames()}
@@ -182,6 +223,16 @@ protected int firstStateID() {
182223
return id;
183224
}
184225

226+
/**
227+
* Loads properties to the block data, their order has to match
228+
* the order of their keys, {@link BlockDataImpl#getDataNames()}.
229+
* The array needs to provide all properties with correct types.
230+
* @param properties properties to load
231+
*/
232+
protected void loadProperties(final Object[] properties) {
233+
234+
}
235+
185236
@Override
186237
public String toString() {
187238
if (getMaterial() != null)

code-generators/src/main/java/org/machinemc/generators/blockdata/BlockData.java

Lines changed: 46 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -121,27 +121,21 @@ public byte[] generate() {
121121
for (final Property property : properties)
122122
interfaces.add(type(property.getInterfacePath()).getInternalName());
123123
cw.visit(Opcodes.V17,
124-
Opcodes.ACC_PUBLIC,
124+
Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL,
125125
type(path).getInternalName(),
126126
null,
127127
type(BLOCKDATA_CLASS).getInternalName(),
128128
interfaces.toArray(new String[0]));
129129
CodeGenerator.visitGeneratedAnnotation(cw, BlockDataLibGenerator.class);
130130

131131
// Fields
132-
FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL,
133-
"BLOCKDATA_MAP",
134-
Type.getType(HashMap.class).getDescriptor(),
135-
"Ljava/util/HashMap<Ljava/lang/Integer;" + type(path).getDescriptor() + ">;",
136-
null);
137-
fv.visitEnd();
138132
for (final Property property : properties) {
139133
final String descriptor = switch (property.getType()) {
140134
case BOOLEAN -> Type.BOOLEAN_TYPE.getDescriptor();
141135
case NUMBER -> Type.INT_TYPE.getDescriptor();
142136
case OTHER -> type(property.getPath()).getDescriptor();
143137
};
144-
fv = cw.visitField(Opcodes.ACC_PRIVATE,
138+
final FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE,
145139
toCamelCase(property.getName(), false),
146140
descriptor,
147141
null,
@@ -286,86 +280,6 @@ public byte[] generate() {
286280
mv.visitEnd();
287281
cw.visitEnd();
288282

289-
// Static block
290-
mv = cw.visitMethod(Opcodes.ACC_STATIC,
291-
"<clinit>",
292-
"()V",
293-
null,
294-
new String[0]);
295-
mv.visitCode();
296-
mv.visitTypeInsn(Opcodes.NEW, Type.getType(HashMap.class).getInternalName());
297-
mv.visitInsn(Opcodes.DUP);
298-
mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
299-
Type.getType(HashMap.class).getInternalName(),
300-
"<init>",
301-
"()V",
302-
false);
303-
mv.visitVarInsn(Opcodes.ASTORE, i);
304-
mv.visitVarInsn(Opcodes.ALOAD, i);
305-
mv.visitFieldInsn(Opcodes.PUTSTATIC,
306-
type(path).getInternalName(),
307-
"BLOCKDATA_MAP",
308-
Type.getType(HashMap.class).getDescriptor());
309-
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
310-
type(path).getInternalName(),
311-
"initBlockDataMap",
312-
"()V",
313-
false);
314-
mv.visitInsn(Opcodes.RETURN);
315-
mv.visitMaxs(0, 0);
316-
mv.visitEnd();
317-
cw.visitEnd();
318-
319-
// Init block data map method
320-
mv = cw.visitMethod(Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC,
321-
"initBlockDataMap",
322-
"()V",
323-
null,
324-
new String[0]);
325-
mv.visitCode();
326-
for (final Integer id : blockDataMap.keySet()) {
327-
mv.visitFieldInsn(Opcodes.GETSTATIC,
328-
type(path).getInternalName(),
329-
"BLOCKDATA_MAP",
330-
Type.getType(HashMap.class).getDescriptor());
331-
final StringBuilder stateKeyBuilder = new StringBuilder();
332-
for (final Map.Entry<Property, String> property : blockDataMap.get(id))
333-
stateKeyBuilder.append(property.getValue().toLowerCase()).append(";");
334-
pushValue(mv, idMap.get(stateKeyBuilder.toString()));
335-
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
336-
Type.getType(Integer.class).getInternalName(),
337-
"valueOf",
338-
"(I)Ljava/lang/Integer;",
339-
false);
340-
mv.visitTypeInsn(Opcodes.NEW, type(path).getInternalName());
341-
mv.visitInsn(Opcodes.DUP);
342-
for (final Map.Entry<Property, String> property : blockDataMap.get(id)) {
343-
switch (property.getKey().getType()) {
344-
case BOOLEAN -> pushValue(mv, Boolean.parseBoolean(property.getValue()));
345-
case NUMBER -> pushValue(mv, Integer.parseInt(property.getValue()));
346-
case OTHER -> mv.visitFieldInsn(Opcodes.GETSTATIC,
347-
type(property.getKey().getPath()).getInternalName(),
348-
property.getValue().toUpperCase(),
349-
type(property.getKey().getPath()).getDescriptor());
350-
}
351-
}
352-
mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
353-
type(path).getInternalName(),
354-
"<init>",
355-
descriptorBuilder.toString(),
356-
false);
357-
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
358-
Type.getType(HashMap.class).getInternalName(),
359-
"put",
360-
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
361-
false);
362-
mv.visitInsn(Opcodes.POP);
363-
}
364-
mv.visitInsn(Opcodes.RETURN);
365-
mv.visitMaxs(0, 0);
366-
mv.visitEnd();
367-
cw.visitEnd();
368-
369283
// getData method
370284
mv = cw.visitMethod(Opcodes.ACC_PROTECTED,
371285
"getData",
@@ -505,20 +419,56 @@ else if (property.getType() == Property.Type.NUMBER)
505419
mv.visitEnd();
506420
cw.visitEnd();
507421

508-
// getIdMap method
422+
// loadProperties method
509423
mv = cw.visitMethod(Opcodes.ACC_PROTECTED,
510-
"getIdMap",
511-
"()" + Type.getType(Map.class).getDescriptor(),
424+
"loadProperties",
425+
"(" + Type.getType(Object[].class).getDescriptor() + ")V",
512426
null,
513427
new String[0]);
514428
mv.visitAnnotation(Type.getType(Override.class).getDescriptor(), true).visitEnd();
515429
mv.visitEnd();
516430
mv.visitCode();
517-
mv.visitFieldInsn(Opcodes.GETSTATIC,
518-
type(path).getInternalName(),
519-
"BLOCKDATA_MAP",
520-
Type.getType(HashMap.class).getDescriptor());
521-
mv.visitInsn(Opcodes.ARETURN);
431+
432+
int j = 0;
433+
for (final Property property : properties) {
434+
435+
mv.visitVarInsn(Opcodes.ALOAD, 0);
436+
mv.visitVarInsn(Opcodes.ALOAD, 1);
437+
pushValue(mv, j);
438+
mv.visitInsn(Opcodes.AALOAD);
439+
mv.visitTypeInsn(Opcodes.CHECKCAST, (
440+
switch (property.getType()) {
441+
case BOOLEAN -> Type.getType(Boolean.class);
442+
case NUMBER -> Type.getType(Integer.class);
443+
case OTHER -> type(property.getPath()); }
444+
).getInternalName());
445+
446+
if (property.getType() == Property.Type.BOOLEAN)
447+
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
448+
Type.getType(Boolean.class).getInternalName(),
449+
"booleanValue",
450+
"()Z",
451+
false);
452+
else if (property.getType() == Property.Type.NUMBER)
453+
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
454+
Type.getType(Integer.class).getInternalName(),
455+
"intValue",
456+
"()I",
457+
false);
458+
459+
mv.visitFieldInsn(Opcodes.PUTFIELD,
460+
type(path).getInternalName(),
461+
toCamelCase(property.getName(), false), (
462+
switch (property.getType()) {
463+
case BOOLEAN -> Type.BOOLEAN_TYPE;
464+
case NUMBER -> Type.INT_TYPE;
465+
case OTHER -> type(property.getPath()); }
466+
).getDescriptor());
467+
468+
j++;
469+
}
470+
471+
mv.visitInsn(Opcodes.RETURN);
522472
mv.visitMaxs(0, 0);
523473
mv.visitEnd();
524474
cw.visitEnd();

code-generators/src/main/java/org/machinemc/generators/materials/MaterialsLibGenerator.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,11 @@ public void generate() throws IOException {
153153
"setMaterial",
154154
"(" + type(path).getDescriptor() + ")" + type(iBlockDataPath).getDescriptor(),
155155
false);
156-
mv.visitInsn(Opcodes.POP);
156+
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
157+
type(iBlockDataPath).getInternalName(),
158+
"register",
159+
"()V",
160+
false);
157161
mv.visitJumpInsn(Opcodes.GOTO, end);
158162

159163
mv.visitLabel(end);

0 commit comments

Comments
 (0)