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);
+ }
}