From 243c50be02203ed3966a98907bf0ac27458322b0 Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 14 Dec 2022 16:58:58 -0500 Subject: [PATCH] Faster entity removal --- .../annotations/entity/EntityProcess.java | 35 ++++++++++++-- core/src/mindustry/entities/EntityGroup.java | 48 ++++++++++++++++++- .../src/mindustry/entities/EntityIndexer.java | 7 +++ .../world/blocks/logic/CanvasBlock.java | 6 +++ 4 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 core/src/mindustry/entities/EntityIndexer.java diff --git a/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java b/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java index 2b2227e73e..5052e7138c 100644 --- a/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java +++ b/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java @@ -232,9 +232,15 @@ public class EntityProcess extends BaseProcessor{ Stype repr = types.first(); String groupType = repr.annotation(Component.class).base() ? baseName(repr) : interfaceName(repr); + String name = group.name().startsWith("g") ? group.name().substring(1) : group.name(); + boolean collides = an.collide(); - groupDefs.add(new GroupDefinition(group.name().startsWith("g") ? group.name().substring(1) : group.name(), + groupDefs.add(new GroupDefinition(name, ClassName.bestGuess(packageName + "." + groupType), types, an.spatial(), an.mapping(), collides)); + + TypeSpec.Builder accessor = TypeSpec.interfaceBuilder("IndexableEntity__" + name); + accessor.addMethod(MethodSpec.methodBuilder("setIndex__" + name).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC).addParameter(int.class, "index").returns(void.class).build()); + write(accessor); } ObjectMap usedNames = new ObjectMap<>(); @@ -394,6 +400,13 @@ public class EntityProcess extends BaseProcessor{ //entities with no sync comp and no serialization gen no code boolean hasIO = ann.genio() && (components.contains(s -> s.name().contains("Sync")) || ann.serialize()); + //implement indexable interfaces. + for(GroupDefinition def : groups){ + builder.addSuperinterface(tname(packageName + ".IndexableEntity__" + def.name)); + builder.addMethod(MethodSpec.methodBuilder("setIndex__" + def.name).addParameter(int.class, "index").addModifiers(Modifier.PUBLIC).addAnnotation(Override.class) + .addCode("index__$L = index;", def.name).build()); + } + //add all methods from components for(ObjectMap.Entry> entry : methods){ if(entry.value.contains(m -> m.has(Replace.class))){ @@ -446,8 +459,15 @@ public class EntityProcess extends BaseProcessor{ mbuilder.addStatement("if(added == $L) return", first.name().equals("add")); for(GroupDefinition def : groups){ - //remove/add from each group, assume imported - mbuilder.addStatement("Groups.$L.$L(this)", def.name, first.name()); + if(first.name().equals("add")){ + //remove/add from each group, assume imported + mbuilder.addStatement("index__$L = Groups.$L.addIndex(this)", def.name, def.name); + }else{ + //remove/add from each group, assume imported + mbuilder.addStatement("Groups.$L.removeIndex(this, index__$L);", def.name, def.name); + + mbuilder.addStatement("index__$L = -1", def.name); + } } } @@ -577,6 +597,13 @@ public class EntityProcess extends BaseProcessor{ skipDeprecated(builder); + if(!legacy){ + //add group index int variables + for(GroupDefinition def : groups){ + builder.addField(FieldSpec.builder(int.class, "index__" + def.name, Modifier.PROTECTED).initializer("-1").build()); + } + } + definitions.add(new EntityDefinition(packageName + "." + name, builder, type, typeIsBase ? null : baseClass, components, groups, allFieldSpecs, legacy)); } @@ -592,7 +619,7 @@ public class EntityProcess extends BaseProcessor{ groupsBuilder.addField(ParameterizedTypeName.get( ClassName.bestGuess("mindustry.entities.EntityGroup"), itype), group.name, Modifier.PUBLIC, Modifier.STATIC); - groupInit.addStatement("$L = new $T<>($L.class, $L, $L)", group.name, groupc, itype, group.spatial, group.mapping); + groupInit.addStatement("$L = new $T<>($L.class, $L, $L, (e, pos) -> (($L.IndexableEntity__$L)e).setIndex__$L(pos))", group.name, groupc, itype, group.spatial, group.mapping, packageName, group.name, group.name); } //write the groups diff --git a/core/src/mindustry/entities/EntityGroup.java b/core/src/mindustry/entities/EntityGroup.java index 51957e52e5..b3f5fad050 100644 --- a/core/src/mindustry/entities/EntityGroup.java +++ b/core/src/mindustry/entities/EntityGroup.java @@ -20,6 +20,7 @@ public class EntityGroup implements Iterable{ private final Seq intersectArray = new Seq<>(); private final Rect viewport = new Rect(); private final Rect intersectRect = new Rect(); + private final EntityIndexer indexer; private IntMap map; private QuadTree tree; private boolean clearing; @@ -36,6 +37,10 @@ public class EntityGroup implements Iterable{ } public EntityGroup(Class type, boolean spatial, boolean mapping){ + this(type, spatial, mapping, null); + } + + public EntityGroup(Class type, boolean spatial, boolean mapping, EntityIndexer indexer){ array = new Seq<>(false, 32, type); if(spatial){ @@ -45,6 +50,8 @@ public class EntityGroup implements Iterable{ if(mapping){ map = new IntMap<>(); } + + this.indexer = indexer; } /** @return entities with colliding IDs, or an empty array. */ @@ -107,7 +114,7 @@ public class EntityGroup implements Iterable{ } public boolean useTree(){ - return map != null; + return tree != null; } public boolean mappingEnabled(){ @@ -183,12 +190,25 @@ public class EntityGroup implements Iterable{ } } + public int addIndex(T type){ + int index = array.size; + add(type); + return index; + } + public void remove(T type){ if(clearing) return; if(type == null) throw new RuntimeException("Cannot remove a null entity!"); int idx = array.indexOf(type, true); if(idx != -1){ array.remove(idx); + + //fix incorrect HEAD index since it was swapped + if(array.size > 0 && idx != array.size){ + var swapped = array.items[idx]; + indexer.change(swapped, idx); + } + if(map != null){ map.remove(type.id()); } @@ -200,6 +220,32 @@ public class EntityGroup implements Iterable{ } } + public void removeIndex(T type, int position){ + if(clearing) return; + if(type == null) throw new RuntimeException("Cannot remove a null entity!"); + if(position != -1 && position < array.size){ + + //swap head with current + if(array.size > 1){ + var head = array.items[array.size - 1]; + indexer.change(head, position); + array.items[position] = head; + } + + array.size --; + array.items[array.size] = null; + + if(map != null){ + map.remove(type.id()); + } + + //fix iteration index when removing + if(index >= position){ + index --; + } + } + } + public void clear(){ clearing = true; diff --git a/core/src/mindustry/entities/EntityIndexer.java b/core/src/mindustry/entities/EntityIndexer.java new file mode 100644 index 0000000000..9a89dfb130 --- /dev/null +++ b/core/src/mindustry/entities/EntityIndexer.java @@ -0,0 +1,7 @@ +package mindustry.entities; + +import mindustry.gen.*; + +public interface EntityIndexer{ + void change(Entityc t, int index); +} diff --git a/core/src/mindustry/world/blocks/logic/CanvasBlock.java b/core/src/mindustry/world/blocks/logic/CanvasBlock.java index 6f4ab93e1f..a55f481227 100644 --- a/core/src/mindustry/world/blocks/logic/CanvasBlock.java +++ b/core/src/mindustry/world/blocks/logic/CanvasBlock.java @@ -137,6 +137,12 @@ public class CanvasBlock extends Block{ @Override public void draw(){ + if(!renderer.drawDisplays){ + super.draw(); + + return; + } + if(blending == 0){ super.draw(); }