From c8dddd7c9a8a89e8a5da2c9064b3440eacda5dd8 Mon Sep 17 00:00:00 2001 From: Tassilo Schweyer Date: Mon, 15 Jun 2026 15:40:08 +0200 Subject: [PATCH] blub --- src/main/java/de/welterde/em/Component.java | 25 +++++ src/main/java/de/welterde/em/ComponentID.java | 32 ++++++ .../java/de/welterde/em/ComponentType.java | 58 +++++++++++ src/main/java/de/welterde/em/Entity.java | 4 + src/main/java/de/welterde/em/EntityBase.java | 1 + src/main/java/de/welterde/em/EntityEvent.java | 25 +++++ src/main/java/de/welterde/em/Event.java | 25 +++++ src/main/java/de/welterde/em/EventTarget.java | 25 +++++ src/main/java/de/welterde/em/GameContext.java | 22 ++++- src/main/java/de/welterde/em/Location.java | 1 + .../welterde/em/{ => c}/EntityContainer.java | 10 +- .../java/de/welterde/em/c/EntityLocation.java | 33 +++++++ .../java/de/welterde/em/c/EntityPosition.java | 34 +++++++ .../em/data/BasicEntityCollection.java | 2 +- .../java/de/welterde/em/loc/LocationBase.java | 2 +- src/main/java/de/welterde/em/w/World.java | 97 +++++++++++++++++-- 16 files changed, 384 insertions(+), 12 deletions(-) create mode 100644 src/main/java/de/welterde/em/Component.java create mode 100644 src/main/java/de/welterde/em/ComponentID.java create mode 100644 src/main/java/de/welterde/em/ComponentType.java create mode 100644 src/main/java/de/welterde/em/EntityEvent.java create mode 100644 src/main/java/de/welterde/em/Event.java create mode 100644 src/main/java/de/welterde/em/EventTarget.java rename src/main/java/de/welterde/em/{ => c}/EntityContainer.java (76%) create mode 100644 src/main/java/de/welterde/em/c/EntityLocation.java create mode 100644 src/main/java/de/welterde/em/c/EntityPosition.java diff --git a/src/main/java/de/welterde/em/Component.java b/src/main/java/de/welterde/em/Component.java new file mode 100644 index 0000000..c787298 --- /dev/null +++ b/src/main/java/de/welterde/em/Component.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2026 welterde + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.welterde.em; + +/** + * Component of an entity + * @author welterde + */ +public interface Component { + ComponentType getComponentType(); +} diff --git a/src/main/java/de/welterde/em/ComponentID.java b/src/main/java/de/welterde/em/ComponentID.java new file mode 100644 index 0000000..d29d52d --- /dev/null +++ b/src/main/java/de/welterde/em/ComponentID.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2026 welterde + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.welterde.em; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author welterde + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ComponentID { + ComponentType type(); +} diff --git a/src/main/java/de/welterde/em/ComponentType.java b/src/main/java/de/welterde/em/ComponentType.java new file mode 100644 index 0000000..691f3ca --- /dev/null +++ b/src/main/java/de/welterde/em/ComponentType.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2026 welterde + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.welterde.em; + +import de.welterde.em.c.EntityContainer; +import de.welterde.em.c.EntityLocation; +import de.welterde.em.c.EntityPosition; + +/** + * Flag for each component of the entity. + * + * @author welterde + */ +public enum ComponentType { + /** + * Can contain other entities + */ + COTAINER (1, EntityContainer.class), + /** + * Location of the entity + */ + LOCATION (2, EntityLocation.class), + /** + * Current position of the entity + */ + POSITION (3, EntityPosition.class); + + private final short typeId; + private final Class typeClass; + + ComponentType(int typeId, Class typeClass) { + this.typeId = (short) typeId; + this.typeClass = typeClass; + } + + + public short getComponentID() { + return this.typeId; + } + + public Class getComponentClass() { + return this.typeClass; + } +} diff --git a/src/main/java/de/welterde/em/Entity.java b/src/main/java/de/welterde/em/Entity.java index 04651d0..8335736 100644 --- a/src/main/java/de/welterde/em/Entity.java +++ b/src/main/java/de/welterde/em/Entity.java @@ -24,4 +24,8 @@ public interface Entity { public GameContext getContext(); public int getEntityId(); public void initEntity(GameContext ctx, int id); + + public boolean isEntityInitialized(); + + public void sendEvent(Event evt); } diff --git a/src/main/java/de/welterde/em/EntityBase.java b/src/main/java/de/welterde/em/EntityBase.java index c649408..3c81015 100644 --- a/src/main/java/de/welterde/em/EntityBase.java +++ b/src/main/java/de/welterde/em/EntityBase.java @@ -59,6 +59,7 @@ public abstract class EntityBase implements Entity { this.ctx = ctx; } + @Override public boolean isEntityInitialized() { return this.id != -1; } diff --git a/src/main/java/de/welterde/em/EntityEvent.java b/src/main/java/de/welterde/em/EntityEvent.java new file mode 100644 index 0000000..3fb28e6 --- /dev/null +++ b/src/main/java/de/welterde/em/EntityEvent.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2026 welterde + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.welterde.em; + +/** + * + * @author welterde + */ +public interface EntityEvent extends Event { + +} diff --git a/src/main/java/de/welterde/em/Event.java b/src/main/java/de/welterde/em/Event.java new file mode 100644 index 0000000..9f34f5d --- /dev/null +++ b/src/main/java/de/welterde/em/Event.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2026 welterde + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.welterde.em; + +/** + * + * @author welterde + */ +public interface Event { + +} diff --git a/src/main/java/de/welterde/em/EventTarget.java b/src/main/java/de/welterde/em/EventTarget.java new file mode 100644 index 0000000..4c1dff1 --- /dev/null +++ b/src/main/java/de/welterde/em/EventTarget.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2026 welterde + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.welterde.em; + +/** + * + * @author welterde + */ +public record EventTarget(Entity entity, ComponentType ctype, Z c) { + +} diff --git a/src/main/java/de/welterde/em/GameContext.java b/src/main/java/de/welterde/em/GameContext.java index 2615ffc..7cbad20 100644 --- a/src/main/java/de/welterde/em/GameContext.java +++ b/src/main/java/de/welterde/em/GameContext.java @@ -16,7 +16,12 @@ */ package de.welterde.em; +import de.welterde.em.c.EntityContainer; import de.welterde.em.data.CounterName; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; /** * @@ -29,5 +34,20 @@ public interface GameContext { int allocateId(CounterName name); - int addEntity(EntityBase e); + int addEntity(Entity e); + + /** + * List components the entity has. + * + * @param e Entity + * @return Set of components entity posses + */ + Set getEntityComponents(Entity e); + + public void addComponent(Entity e, Component c); + public T getComponent(Entity e, ComponentType ctype); + public void deleteComponent(Entity e, Component c); + public void deleteComponent(Entity e, ComponentType ctype); + public void enqueueEvent(Entity e, Event evt); + } diff --git a/src/main/java/de/welterde/em/Location.java b/src/main/java/de/welterde/em/Location.java index 5cb231f..bc75019 100644 --- a/src/main/java/de/welterde/em/Location.java +++ b/src/main/java/de/welterde/em/Location.java @@ -16,6 +16,7 @@ */ package de.welterde.em; +import de.welterde.em.c.EntityContainer; import de.welterde.em.w.Area; /** diff --git a/src/main/java/de/welterde/em/EntityContainer.java b/src/main/java/de/welterde/em/c/EntityContainer.java similarity index 76% rename from src/main/java/de/welterde/em/EntityContainer.java rename to src/main/java/de/welterde/em/c/EntityContainer.java index 8652f07..0732074 100644 --- a/src/main/java/de/welterde/em/EntityContainer.java +++ b/src/main/java/de/welterde/em/c/EntityContainer.java @@ -14,13 +14,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package de.welterde.em; +package de.welterde.em.c; + +import de.welterde.em.Component; +import de.welterde.em.ComponentID; +import de.welterde.em.ComponentType; +import de.welterde.em.Entity; /** * * @author welterde */ -public interface EntityContainer { +@ComponentID(type = ComponentType.COTAINER) +public interface EntityContainer extends Component { public void addEntity(Entity e); public Iterable listEntities(); public void removeEntity(Entity e); diff --git a/src/main/java/de/welterde/em/c/EntityLocation.java b/src/main/java/de/welterde/em/c/EntityLocation.java new file mode 100644 index 0000000..84f2890 --- /dev/null +++ b/src/main/java/de/welterde/em/c/EntityLocation.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2026 welterde + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.welterde.em.c; + +import de.welterde.em.Component; +import de.welterde.em.ComponentType; + +/** + * + * @author welterde + */ +public record EntityLocation (int entityId) implements Component { + + @Override + public ComponentType getComponentType() { + return ComponentType.LOCATION; + } + +} diff --git a/src/main/java/de/welterde/em/c/EntityPosition.java b/src/main/java/de/welterde/em/c/EntityPosition.java new file mode 100644 index 0000000..3f01b19 --- /dev/null +++ b/src/main/java/de/welterde/em/c/EntityPosition.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2026 welterde + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.welterde.em.c; + +import de.welterde.em.Component; +import de.welterde.em.ComponentType; +import de.welterde.em.data.MapCoord; + +/** + * + * @author welterde + */ +public record EntityPosition (MapCoord coord) implements Component { + + @Override + public ComponentType getComponentType() { + return ComponentType.POSITION; + } + +} diff --git a/src/main/java/de/welterde/em/data/BasicEntityCollection.java b/src/main/java/de/welterde/em/data/BasicEntityCollection.java index b38ed2e..1b651ed 100644 --- a/src/main/java/de/welterde/em/data/BasicEntityCollection.java +++ b/src/main/java/de/welterde/em/data/BasicEntityCollection.java @@ -16,7 +16,7 @@ */ package de.welterde.em.data; -import de.welterde.em.EntityContainer; +import de.welterde.em.c.EntityContainer; import java.util.Arrays; import java.util.Set; import java.util.TreeSet; diff --git a/src/main/java/de/welterde/em/loc/LocationBase.java b/src/main/java/de/welterde/em/loc/LocationBase.java index 4c6311e..3eafc5a 100644 --- a/src/main/java/de/welterde/em/loc/LocationBase.java +++ b/src/main/java/de/welterde/em/loc/LocationBase.java @@ -18,7 +18,7 @@ package de.welterde.em.loc; import de.welterde.em.Entity; import de.welterde.em.EntityBase; -import de.welterde.em.EntityContainer; +import de.welterde.em.c.EntityContainer; import de.welterde.em.Location; import de.welterde.em.data.BasicEntityCollection; import de.welterde.em.w.Area; diff --git a/src/main/java/de/welterde/em/w/World.java b/src/main/java/de/welterde/em/w/World.java index 350f53c..f364e33 100644 --- a/src/main/java/de/welterde/em/w/World.java +++ b/src/main/java/de/welterde/em/w/World.java @@ -16,20 +16,29 @@ */ package de.welterde.em.w; +import de.welterde.em.Component; +import de.welterde.em.ComponentType; import de.welterde.em.Entity; -import de.welterde.em.EntityBase; -import de.welterde.em.EntityContainer; -import de.welterde.em.EntityRef; +import de.welterde.em.Event; +import de.welterde.em.c.EntityContainer; import de.welterde.em.data.CounterName; import java.util.ArrayList; import java.util.EnumMap; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import de.welterde.em.GameContext; +import java.util.EnumSet; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.function.BiFunction; /** * @@ -38,9 +47,14 @@ import de.welterde.em.GameContext; public class World implements GameContext { // base backing storage of entities and entity components protected final ArrayList entities; + protected final ConcurrentSkipListMap components; protected final Map entityContainers; protected final EnumMap counters; + // internal message bus + protected final ConcurrentMap> entityQueues; + protected final BlockingQueue newEntityQueue; + // direct ref to certain top-level entities protected final ArrayList dimensions; protected final Map countries; @@ -52,9 +66,13 @@ public class World implements GameContext { public World() { this.entities = new ArrayList<>(); + this.components = new ConcurrentSkipListMap<>(); this.entityContainers = new HashMap<>(); this.counters = new EnumMap<>(CounterName.class); + this.entityQueues = new ConcurrentHashMap<>(); + this.newEntityQueue = new LinkedBlockingQueue<>(); + this.dimensions = new ArrayList<>(); this.countries = new HashMap<>(); @@ -108,7 +126,7 @@ public class World implements GameContext { } @Override - public int addEntity(EntityBase e) { + public int addEntity(Entity e) { if(e.isEntityInitialized()) throw new IllegalStateException("Entity already initialized"); @@ -124,9 +142,64 @@ public class World implements GameContext { // TODO: Add to queue for new entity listeners? this.handleNewEntity(e); + + return id; } - public void handleNewEntity(EntityBase e) { + @Override + public Set getEntityComponents(Entity e) { + var ret = EnumSet.noneOf(ComponentType.class); + var eid = e.getEntityId(); + + this.structLock.readLock().lock(); + try { + if (this.entityContainers.containsKey(eid)) + ret.add(ComponentType.COTAINER); + + } finally { + this.structLock.readLock().unlock(); + } + + return ret; + } + + @Override + public void addComponent(Entity e, Component c) { + var cid = calculateComponentID(e, c.getComponentType().getComponentID()); + this.components.putIfAbsent(cid, c); + } + + @Override + public T getComponent(Entity e, ComponentType ctype) { + var tclass = (Class) ctype.getComponentClass(); + var cid = calculateComponentID(e, ctype.getComponentID()); + var obj = this.components.get(cid); + if (obj == null) + return null; + + if (!tclass.isInstance(obj)) + throw new IllegalArgumentException("Type is wrong type"); + return tclass.cast(obj); + } + + @Override + public void deleteComponent(Entity e, Component c) { + var cid = calculateComponentID(e, c.getComponentType().getComponentID()); + this.components.remove(cid); + } + @Override + public void deleteComponent(Entity e, ComponentType ctype) { + var cid = calculateComponentID(e, ctype.getComponentID()); + this.components.remove(cid); + } + + + protected long calculateComponentID(Entity e, short cid) { + var eid = e.getEntityId(); + return (cid << 32) + eid; + } + + public void handleNewEntity(Entity e) { switch(e) { case Dimension d -> { @@ -146,5 +219,15 @@ public class World implements GameContext { } } - + @Override + public void enqueueEvent(Entity e, Event evt) { + BlockingQueue q; + if (!this.entityQueues.containsKey(e.getEntityId())) { + q = this.entityQueues.putIfAbsent(e.getEntityId(), new LinkedBlockingQueue<>()); + this.newEntityQueue.add(e.getEntityId()); + } else { + q = this.entityQueues.get(e.getEntityId()); + } + q.add(evt); + } }