Skip to content

Commit 3f70de3

Browse files
committed
feat : view setter + component view example
1 parent 7810eb4 commit 3f70de3

6 files changed

Lines changed: 191 additions & 29 deletions

File tree

examples/src/main/java/com/github/elebras1/flecs/examples/ComplexComponentExample.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.github.elebras1.flecs.Entity;
44
import com.github.elebras1.flecs.World;
55
import com.github.elebras1.flecs.collection.LongList;
6-
import com.github.elebras1.flecs.examples.components.Border;
6+
import com.github.elebras1.flecs.examples.components.Inventory;
77
import com.github.elebras1.flecs.examples.components.Ideology;
88
import com.github.elebras1.flecs.examples.components.Minister;
99

@@ -20,7 +20,7 @@ public static void main(String[] args) {
2020
World world = new World();
2121
world.component(Minister.class);
2222
world.component(Ideology.class);
23-
world.component(Border.class);
23+
world.component(Inventory.class);
2424

2525
long start = System.currentTimeMillis();
2626
LongList entityIds = new LongList();
@@ -37,7 +37,7 @@ public static void main(String[] args) {
3737
e.set(new Ideology(i % 256, (byte) (i % 100), (short) (i % 5000)));
3838

3939
if (i % 10 == 0) {
40-
e.set(new Border(new int[2000], new int[2000], true));
40+
e.set(new Inventory(new int[10]));
4141
}
4242
}
4343

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,57 @@
11
package com.github.elebras1.flecs.examples;
22

3-
public class ComponentViewExample {
3+
import com.github.elebras1.flecs.Entity;
4+
import com.github.elebras1.flecs.World;
5+
import com.github.elebras1.flecs.examples.components.*;
6+
import com.github.elebras1.flecs.util.FlecsConstants;
47

5-
private static final int ENTITY_COUNT = 1_000_000;
8+
public class ComponentViewExample {
69

710
public static void main(String[] args) {
11+
try (World world = new World()) {
12+
world.component(Position.class);
13+
world.component(Velocity.class);
14+
world.component(Inventory.class);
15+
16+
for (int i = 0; i < 10; i++) {
17+
long entityId = world.entity("Entity_" + i);
18+
Entity entity = world.obtainEntity(entityId);
19+
entity.set(new Position(i * 10.0f, i * 5.0f));
20+
entity.set(new Velocity(1.0f, 0.5f));
21+
int[] elements = new int[10];
22+
for(int j = 0; j < elements.length; j++) {
23+
elements[j] = j;
24+
}
25+
entity.set(new Inventory(elements));
26+
}
27+
28+
world.system("MovementSystem").kind(FlecsConstants.EcsOnUpdate).with(Position.class).with(Velocity.class).multiThreaded().each(entityId -> {
29+
Entity entity = world.obtainEntity(entityId);
30+
31+
PositionComponentView positionView = entity.getMutView(Position.class);
32+
VelocityComponentView velocityView = entity.getMutView(Velocity.class);
33+
InventoryComponentView inventoryView = entity.getMutView(Inventory.class);
34+
35+
float currentX = positionView.x();
36+
float currentY = positionView.y();
37+
float dx = velocityView.dx();
38+
float dy = velocityView.dy();
39+
positionView.x(currentX + dx).y(currentY + dy);
40+
41+
for(int i = 0; i < inventoryView.elementsLength(); i++) {
42+
int element = inventoryView.elements(i);
43+
inventoryView.elements(i, element + 1);
44+
}
45+
46+
System.out.printf("Entity %d moved to position (%.2f, %.2f) with inventory: ", entityId, positionView.x(), positionView.y());
47+
for(int i = 0; i < inventoryView.elementsLength(); i++) {
48+
System.out.print(inventoryView.elements(i) + "\n");
49+
}
50+
});
51+
52+
world.progress(0.016f);
53+
world.progress(0.016f);
54+
}
855
}
956
}
1057

examples/src/main/java/com/github/elebras1/flecs/examples/components/Border.java renamed to examples/src/main/java/com/github/elebras1/flecs/examples/components/Inventory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
import com.github.elebras1.flecs.annotation.FixedArray;
55

66
@Component
7-
public record Border(@FixedArray(length = 2000) int[] xPixels, @FixedArray(length = 2000) int[] yPixels, boolean isVisible) {
7+
public record Inventory(@FixedArray(length = 10) int[] elements) {
88
}

src/main/java/com/github/elebras1/flecs/Entity.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ public <T> T get(Class<T> componentClass, long target) {
200200
}
201201

202202
@SuppressWarnings("unchecked")
203-
public <T> T getView(Class<?> componentClass) {
203+
public <T> T getMutView(Class<?> componentClass) {
204204
ComponentView view = FlecsContext.CURRENT_CACHE.get().get(componentClass);
205205

206206
long componentId = this.world.componentRegistry().getComponentId(componentClass);
@@ -217,7 +217,7 @@ public <T> T getView(Class<?> componentClass) {
217217
}
218218

219219
@SuppressWarnings("unchecked")
220-
public <T> T getView(Class<?> componentClass, long target) {
220+
public <T> T getMutView(Class<?> componentClass, long target) {
221221
ComponentView view = FlecsContext.CURRENT_CACHE.get().get(componentClass);
222222

223223
long componentId = this.world.componentRegistry().getComponentId(componentClass);

src/main/java/com/github/elebras1/flecs/processor/ComponentViewGenerator.java

Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,46 @@
33
import com.palantir.javapoet.*;
44

55
import javax.lang.model.element.*;
6+
import javax.lang.model.type.ArrayType;
7+
import javax.lang.model.type.TypeMirror;
68
import java.lang.foreign.MemorySegment;
9+
import java.util.ArrayList;
710
import java.util.List;
811

9-
import static com.github.elebras1.flecs.processor.ComponentGenerator.LAYOUT_FIELD_CLASS;
10-
1112
public class ComponentViewGenerator extends AbstractGenerator {
13+
1214
private static final String COMPONENT_VIEW_INTERFACE = "com.github.elebras1.flecs.ComponentView";
1315

16+
private static final ClassName LAYOUT_FIELD_CLASS = ClassName.get("com.github.elebras1.flecs.util", "LayoutField");
17+
1418
public JavaFile generate(TypeElement recordElement, List<VariableElement> fields) {
1519
String packageName = this.getPackageName(recordElement);
1620
String recordName = recordElement.getSimpleName().toString();
1721
String componentViewClassName = recordName + "ComponentView";
1822

1923
String componentReference = packageName + "." + recordName + "Component";
2024

25+
ClassName viewType = ClassName.get(packageName, componentViewClassName);
26+
2127
TypeSpec componentClass = TypeSpec.classBuilder(componentViewClassName)
2228
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
2329
.addSuperinterface(ClassName.bestGuess(COMPONENT_VIEW_INTERFACE))
2430
.addField(this.createField())
25-
.addMethod(this.createSetterMethod())
26-
.addMethods(this.createMethods(fields, componentReference))
31+
.addMethod(this.createSetterMemorySegment())
32+
.addMethods(this.createGetterFields(fields, componentReference, viewType))
2733
.build();
2834

29-
return JavaFile.builder(packageName, componentClass).addFileComment("Generated by ComponentViewGenerator").indent(" ").build();
35+
return JavaFile.builder(packageName, componentClass)
36+
.addFileComment("Generated by ComponentViewGenerator")
37+
.indent(" ")
38+
.build();
3039
}
3140

3241
private FieldSpec createField() {
3342
return FieldSpec.builder(MemorySegment.class, "memorySegment", Modifier.PRIVATE).build();
3443
}
3544

36-
private MethodSpec createSetterMethod() {
45+
private MethodSpec createSetterMemorySegment() {
3746
return MethodSpec.methodBuilder("setMemorySegment")
3847
.addAnnotation(Override.class)
3948
.addModifiers(Modifier.PUBLIC)
@@ -43,29 +52,79 @@ private MethodSpec createSetterMethod() {
4352
.build();
4453
}
4554

46-
private Iterable<MethodSpec> createMethods(List<VariableElement> fields, String componentReference) {
47-
return fields.stream().map(field -> this.createMethod(field, componentReference)).toList();
55+
private Iterable<MethodSpec> createGetterFields(List<VariableElement> fields, String componentReference, ClassName viewType) {
56+
return fields.stream()
57+
.flatMap(field -> this.createGetters(field, componentReference, viewType).stream())
58+
.toList();
4859
}
4960

50-
private MethodSpec createMethod(VariableElement field, String componentReference) {
51-
MethodSpec.Builder method = MethodSpec.methodBuilder(field.getSimpleName().toString()).addModifiers(Modifier.PUBLIC).returns(TypeName.get(field.asType()));
52-
61+
private List<MethodSpec> createGetters(VariableElement field, String componentReference, ClassName viewType) {
62+
List<MethodSpec> methods = new ArrayList<>();
5363
String fieldName = field.getSimpleName().toString();
5464
String offsetName = "OFFSET_" + fieldName.toUpperCase();
5565
String typeName = field.asType().toString();
56-
String getterMethod = this.getGetterMethod(typeName);
57-
if ("java.lang.String".equals(typeName)) {
58-
int size = this.getStringSize(field);
59-
method.addStatement("$T $L = $L.$L(memorySegment, $L.$L, $L)", String.class, fieldName, LAYOUT_FIELD_CLASS, getterMethod, componentReference, offsetName, size);
60-
} else if (typeName.endsWith("[]")) {
66+
67+
if (typeName.endsWith("[]")) {
6168
int length = this.getArrayLength(field);
62-
method.addStatement("$L $L = $L.$L(memorySegment, $L.$L, $L)", typeName, fieldName, LAYOUT_FIELD_CLASS, getterMethod, componentReference, offsetName, length);
69+
TypeName elementType = this.getElementType(field);
70+
String rawType = elementType.toString();
71+
72+
String suffix = Character.toUpperCase(rawType.charAt(0)) + rawType.substring(1);
73+
String methodAtIndex = "get" + suffix + "AtIndex";
74+
String setterAtIndex = "set" + suffix + "AtIndex";
75+
76+
methods.add(MethodSpec.methodBuilder(fieldName + "Length")
77+
.addModifiers(Modifier.PUBLIC)
78+
.returns(int.class)
79+
.addStatement("return $L", length)
80+
.build());
81+
82+
methods.add(MethodSpec.methodBuilder(fieldName)
83+
.addModifiers(Modifier.PUBLIC)
84+
.addParameter(int.class, "index")
85+
.returns(elementType)
86+
.addStatement("return $T.$L(memorySegment, $L.$L, index)", LAYOUT_FIELD_CLASS, methodAtIndex, componentReference, offsetName)
87+
.build());
88+
89+
methods.add(MethodSpec.methodBuilder(fieldName)
90+
.addModifiers(Modifier.PUBLIC)
91+
.addParameter(int.class, "index")
92+
.addParameter(elementType, "value")
93+
.returns(viewType)
94+
.addStatement("$T.$L(memorySegment, $L.$L, index, value)", LAYOUT_FIELD_CLASS, setterAtIndex, componentReference, offsetName)
95+
.addStatement("return this")
96+
.build());
97+
6398
} else {
64-
method.addStatement("$L $L = $L.$L(memorySegment, $L.$L)", typeName, fieldName, LAYOUT_FIELD_CLASS, getterMethod, componentReference, offsetName);
99+
MethodSpec.Builder getter = MethodSpec.methodBuilder(fieldName).addModifiers(Modifier.PUBLIC).returns(TypeName.get(field.asType()));
100+
String getterHelper = this.getGetterMethod(typeName);
101+
if ("java.lang.String".equals(typeName)) {
102+
int size = this.getStringSize(field);
103+
getter.addStatement("return $T.$L(memorySegment, $L.$L, $L)", LAYOUT_FIELD_CLASS, getterHelper, componentReference, offsetName, size);
104+
} else {
105+
getter.addStatement("return $T.$L(memorySegment, $L.$L)", LAYOUT_FIELD_CLASS, getterHelper, componentReference, offsetName);
106+
}
107+
methods.add(getter.build());
108+
109+
MethodSpec.Builder setter = MethodSpec.methodBuilder(fieldName).addModifiers(Modifier.PUBLIC).addParameter(TypeName.get(field.asType()), "value").returns(viewType);
110+
if ("java.lang.String".equals(typeName)) {
111+
int size = this.getStringSize(field);
112+
setter.addStatement("$T.set(memorySegment, $L.$L, value, $L)", LAYOUT_FIELD_CLASS, componentReference, offsetName, size);
113+
} else {
114+
setter.addStatement("$T.set(memorySegment, $L.$L, value)", LAYOUT_FIELD_CLASS, componentReference, offsetName);
115+
}
116+
setter.addStatement("return this");
117+
methods.add(setter.build());
65118
}
66119

67-
method.addStatement("return $L", fieldName);
120+
return methods;
121+
}
68122

69-
return method.build();
123+
private TypeName getElementType(VariableElement field) {
124+
TypeMirror typeMirror = field.asType();
125+
if (typeMirror instanceof ArrayType arrayType) {
126+
return TypeName.get(arrayType.getComponentType());
127+
}
128+
return TypeName.get(typeMirror);
70129
}
71-
}
130+
}

src/main/java/com/github/elebras1/flecs/util/LayoutField.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,62 @@ public static String getFixedString(MemorySegment segment, long offset, int capa
207207
return segment.asSlice(offset, capacity).getString(0);
208208
}
209209

210+
public static int getIntAtIndex(MemorySegment segment, long offset, int index) {
211+
return segment.get(ValueLayout.JAVA_INT, offset + ((long) index * Integer.BYTES));
212+
}
213+
214+
public static void setIntAtIndex(MemorySegment segment, long offset, int index, int value) {
215+
segment.set(ValueLayout.JAVA_INT, offset + ((long) index * Integer.BYTES), value);
216+
}
217+
218+
public static float getFloatAtIndex(MemorySegment segment, long offset, int index) {
219+
return segment.get(ValueLayout.JAVA_FLOAT, offset + ((long) index * Float.BYTES));
220+
}
221+
222+
public static void setFloatAtIndex(MemorySegment segment, long offset, int index, float value) {
223+
segment.set(ValueLayout.JAVA_FLOAT, offset + ((long) index * Float.BYTES), value);
224+
}
225+
226+
public static long getLongAtIndex(MemorySegment segment, long offset, int index) {
227+
return segment.get(ValueLayout.JAVA_LONG, offset + ((long) index * Long.BYTES));
228+
}
229+
230+
public static void setLongAtIndex(MemorySegment segment, long offset, int index, long value) {
231+
segment.set(ValueLayout.JAVA_LONG, offset + ((long) index * Long.BYTES), value);
232+
}
233+
234+
public static double getDoubleAtIndex(MemorySegment segment, long offset, int index) {
235+
return segment.get(ValueLayout.JAVA_DOUBLE, offset + ((long) index * Double.BYTES));
236+
}
237+
238+
public static void setDoubleAtIndex(MemorySegment segment, long offset, int index, double value) {
239+
segment.set(ValueLayout.JAVA_DOUBLE, offset + ((long) index * Double.BYTES), value);
240+
}
241+
242+
public static byte getByteAtIndex(MemorySegment segment, long offset, int index) {
243+
return segment.get(ValueLayout.JAVA_BYTE, offset + index);
244+
}
245+
246+
public static void setByteAtIndex(MemorySegment segment, long offset, int index, byte value) {
247+
segment.set(ValueLayout.JAVA_BYTE, offset + index, value);
248+
}
249+
250+
public static short getShortAtIndex(MemorySegment segment, long offset, int index) {
251+
return segment.get(ValueLayout.JAVA_SHORT, offset + ((long) index * Short.BYTES));
252+
}
253+
254+
public static void setShortAtIndex(MemorySegment segment, long offset, int index, short value) {
255+
segment.set(ValueLayout.JAVA_SHORT, offset + ((long) index * Short.BYTES), value);
256+
}
257+
258+
public static boolean getBooleanAtIndex(MemorySegment segment, long offset, int index) {
259+
return segment.get(ValueLayout.JAVA_BYTE, offset + index) != 0;
260+
}
261+
262+
public static void setBooleanAtIndex(MemorySegment segment, long offset, int index, boolean value) {
263+
segment.set(ValueLayout.JAVA_BYTE, offset + index, (byte)(value ? 1 : 0));
264+
}
265+
210266
public static long offsetOf(MemoryLayout layout, String fieldName) {
211267
return layout.byteOffset(MemoryLayout.PathElement.groupElement(fieldName));
212268
}

0 commit comments

Comments
 (0)