This commit is contained in:
Tassilo Schweyer 2026-06-15 15:40:08 +02:00
parent ad4f8727b8
commit c8dddd7c9a
16 changed files with 384 additions and 12 deletions

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
package de.welterde.em;
/**
* Component of an entity
* @author welterde
*/
public interface Component {
ComponentType getComponentType();
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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();
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
}

View file

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

View file

@ -59,6 +59,7 @@ public abstract class EntityBase implements Entity {
this.ctx = ctx;
}
@Override
public boolean isEntityInitialized() {
return this.id != -1;
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
package de.welterde.em;
/**
*
* @author welterde
*/
public interface EntityEvent extends Event {
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
package de.welterde.em;
/**
*
* @author welterde
*/
public interface Event {
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
package de.welterde.em;
/**
*
* @author welterde
*/
public record EventTarget<Z extends Component>(Entity entity, ComponentType ctype, Z c) {
}

View file

@ -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<ComponentType> getEntityComponents(Entity e);
public void addComponent(Entity e, Component c);
public <T extends Component> 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);
}

View file

@ -16,6 +16,7 @@
*/
package de.welterde.em;
import de.welterde.em.c.EntityContainer;
import de.welterde.em.w.Area;
/**

View file

@ -14,13 +14,19 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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<Entity> listEntities();
public void removeEntity(Entity e);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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<Entity> entities;
protected final ConcurrentSkipListMap<Long, Object> components;
protected final Map<Integer,EntityContainer> entityContainers;
protected final EnumMap<CounterName, Integer> counters;
// internal message bus
protected final ConcurrentMap<Integer, BlockingQueue<Event>> entityQueues;
protected final BlockingQueue<Integer> newEntityQueue;
// direct ref to certain top-level entities
protected final ArrayList<Dimension> dimensions;
protected final Map<Integer, Country> 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<ComponentType> 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 extends Component> T getComponent(Entity e, ComponentType ctype) {
var tclass = (Class<T>) 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<Event> 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);
}
}