Skip to content

Instantly share code, notes, and snippets.

@Dvergar
Created December 10, 2018 12:23
Show Gist options
  • Select an option

  • Save Dvergar/8fe220704428d7a991c35222b20879a9 to your computer and use it in GitHub Desktop.

Select an option

Save Dvergar/8fe220704428d7a991c35222b20879a9 to your computer and use it in GitHub Desktop.
YAML support
package osis;
import haxe.ds.IntMap;
import haxe.ds.Vector;
import haxe.macro.Expr;
import haxe.macro.Context;
import haxe.Int64;
import anette.*;
import anette.Protocol;
import anette.Bytes;
using EntityManager.ArrayEntityExtender;
using EntityManager.BitSets;
using EntityManager.ComponentTypeExtender;
using EntityManager.EntityExtender;
/**
Returned by event handler to know the source of the event.
**/
typedef Connection = anette.Connection;
@:dox(hide)
typedef ListSet<T> = Array<T>;
@:dox(hide)
class Entities
{
var store = new Map<Int, Entity>();
public var reverse = new Map<Entity, Int>();
public function new() {}
public function set(entityId:Int, entity:Entity)
{
store.set(entityId, entity);
reverse.set(entity, entityId);
}
public function get(entityId:Int)
return store.get(entityId);
public function remove(entityId:Int)
{
reverse.remove(store.get(entityId));
store.remove(entityId);
}
public function iterator()
return store.iterator();
}
@:dox(hide)
class ArrayEntityExtender
{
static public function has(arr:ListSet<Entity>, newItem:Entity):Bool
{
for(item in arr) if(item == newItem) return true;
return false;
}
static public function set(arr:Array<Entity>, newItem:Entity):Bool
{
for(item in arr) if(item == newItem) return false;
arr.push(newItem);
return true;
}
}
class EntityExtender
{
@:dox(hide) static public var em:EntityManager;
static public inline function add<T:Component>(entity:Entity, component:T):T
{
em.addComponent(entity, component);
return component;
}
static public inline function remove<T:Class<Component>>(entity:Entity, componentType:T)
{
em.removeComponent(entity, componentType);
}
static public inline function destroy(entity:Entity)
{
em.destroyEntity(entity);
}
}
// NOT SUPER FOUND OF THIS
@:dox(hide)
class ComponentTypeExtender
{
static public function get__id<T:Component>(componentType:Class<T>):Int
return (untyped componentType).__id;
static public function get__sid<T:Component>(componentType:Class<T>):Int
return (untyped componentType).__sid;
}
/**
Interface for your components.
usage :
```
class CPosition implements Component
{
@Short public var x:Int;
@Short public var y:Int;
public function new() {}
}
```
Available network types are : `@Short`, `@Int`, `@Float`, `@Bool`, `@Byte`, `@String`.
Variables will be serialized appropriately under the hood.
More infos on (https://github.com/Dvergar/PODStream)
**/
#if !macro @:autoBuild(osis.CustomNetworkTypes.build()) #end
interface Component
{
public var _sid:Int;
public var _id:Int;
@:dox(hide) public function unserialize(bi:haxe.io.BytesInput):Void;
@:dox(hide) public function serialize(bo:haxe.io.BytesOutput):Void;
}
/**
Interface for your components.
usage :
```
class MessageInput implements IMessage
{
@Short public var x:Int;
@Short public var y:Int;
@Bool public var left:Bool;
@Bool public var right:Bool;
@Bool public var up:Bool;
@Bool public var down:Bool;
public function new() {}
}
```
Available network types are : `@Short`, `@Int`, `@Float`, `@Bool`, `@Byte`, `@String`.
Variables will be serialized appropriately under the hood.
More infos on (https://github.com/Dvergar/PODStream)
**/
#if !macro @:autoBuild(osis.CustomNetworkTypes.build()) #end
interface IMessage
{
public var _sid:Int;
public var _id:Int;
@:dox(hide) public function unserialize(bi:haxe.io.BytesInput):Void;
@:dox(hide) public function serialize(bo:haxe.io.BytesOutput):Void;
}
@:dox(hide)
class BitSets
{
inline public static function value(index:Int):Int64
return Int64.shl(Int64.ofInt(1), index);
inline public static function remove(bits:Int64, mask:Int):Int64
return bits & ~value(mask);
inline public static function add(bits:Int64, mask:Int):Int64
return bits | value(mask);
inline public static function xor(bits:Int64, mask:Int64):Int64
return bits ^ mask;
inline public static function contains(bits :Int64, mask :Int) :Bool
return bits & value(mask) != 0;
inline public static function containsBitSet(bits :Int64, mask :Int64) :Bool
return (bits & mask) == bits;
}
@:dox(hide)
@:enum abstract CONSTANTS(Int) to Int
{
var MAX_COMPONENTS = 64;
}
class Entity
{
public var id:Int;
static var ids:Int = 0;
@:dox(hide) public var code:Int64 = 0;
@:dox(hide) public var components:Vector<Component> = new Vector(MAX_COMPONENTS);
@:dox(hide) public var remComponents:Vector<Bool> = new Vector(MAX_COMPONENTS);
@:dox(hide) public var registeredSetsCode:Int64 = 0;
// NET
@:dox(hide) public var templateId:Int;
/**
Creates an entity.
An entity is nothing but a holder for components.
**/
public function new()
{
this.id = ids++;
for(i in 0...MAX_COMPONENTS) remComponents[i] = false;
}
/**
Returns entity component.
**/
public function get<T:Component>(componentType:Class<T>):T
{
var comp:T = cast components[componentType.get__id()];
if(comp == null)
throw "Entity " + id + " doesn't have component " + componentType;
return comp;
}
/**
Tells if entity has this specific component.
**/
public function has<T:Component>(componentType:Class<T>):Bool
{
var comp:T = cast components[componentType.get__id()];
if(comp == null) return false;
return !remComponents[componentType.get__id()];
}
public function toString()
{
var entityString = "Entity : " + id;
for(comp in components)
{
if(comp == null) continue;
var className = Type.getClassName(Type.getClass(comp));
entityString += " <" + className + ">";
}
return entityString;
}
}
class System
{
public var em:EntityManager;
public var net:NetEntityManager;
/**
Creates a system.
A system is where all the logic goes.
Systems acts on components via `Entityset`
**/
public function new() {}
/**
Called after initialization from the EntityManager.
To be overridden.
**/
public function init() {}
/**
Where all your checks goes.
To be overridden.
**/
public function loop() {}
}
/**
Get an entity set via `EntityManager.getEntitySet`.
**/
class EntitySet
{
@:dox(hide) public var _id:Int;
@:dox(hide) static var ids:Int = 0;
@:dox(hide) public var code:Int64 = 0;
var em:EntityManager;
/**
Container of all entities.
**/
public var entities:ListSet<Entity> = new ListSet();
@:dox(hide) public var _adds:ListSet<Entity> = new ListSet();
@:dox(hide) public var _changes:ListSet<Entity> = new ListSet();
@:dox(hide) public var _removes:ListSet<Entity> = new ListSet();
/**
Container of all added entities.
**/
public var adds:ListSet<Entity> = new ListSet();
/**
Container of all changed entities.
**/
public var changes:ListSet<Entity> = new ListSet();
/**
Container of all removed entities.
**/
public var removes:ListSet<Entity> = new ListSet();
@:dox(hide)
public function new(em:EntityManager, componentTypeList:Array<Class<Component>>)
{
this._id = ids++;
this.em = em;
// SET ENTITYSET SIGNATURE VIA BITS
for(componentType in componentTypeList)
code = code.add(componentType.get__id());
}
/**
Updates all the containers for adds/changes/removes.
Needed for up to date manipulation via `markChanged`
**/
public function applyChanges()
{
for(e in _adds) entities.set(e);
for(e in _removes) entities.remove(e);
adds = _adds;
changes = _changes;
removes = _removes;
_adds = new ListSet();
_changes = new ListSet();
_removes = new ListSet();
}
/**
Notify the `EntityManager` that you modified a specific component.
Will dispatch events to all the other systems.
**/
public function markChanged<T:Component>(entity:Entity, component:T)
{
em.markChanged(entity, component, this);
}
}
@:dox(hide) class Template
{
public static var ids:Int = 0;
public var id:Int;
public var name:String;
public var func:Void->Entity;
public var code:Int64 = 0;
public function new(name:String, func:Void->Entity)
{
this.id = Template.ids++;
this.name = name;
this.func = func;
}
}
@:dox(hide) class TemplateStore
{
public var byName:Map<String, Template> = new Map();
public var byId:Array<Template> = new Array();
public function new() {}
public function add(name:String, func:Void->Entity)
{
var template = new Template(name, func);
byName.set(name, template);
byId[template.id] = template;
// GET TEMPLATE CODE (used for network deltas)
var entity:Entity = func();
template.code = entity.code;
entity.destroy();
}
public function getByName(name:String)
{
var template = byName.get(name);
if(template == null) throw 'Template $name doesn\'t exists';
return template;
}
public function getById(id:Int)
return byId[id];
}
@:dox(hide) typedef ComponentDestroyData = {entity:Entity, componentId:Int};
// #if !macro
// YAML
// @:build(osis.yec.Builder.build())
#end
class EntityManager
{
var systems:Array<System> = new Array();
var entitySets:Array<EntitySet> = new Array();
var componentsToDestroy:Array<ComponentDestroyData> = new Array();
@:dox(hide) public var templateStore:TemplateStore = new TemplateStore();
@:dox(hide) public var net:NetEntityManager;
// var self:EntityManager; // YAML
public function new()
{
EntityExtender.em = this;
this.net = new NetEntityManager(this);
// this.self = this; // YAML
}
/**
Returns an `EntitySet` of components specified by `componentTypeList`.
Example : `var itemsEntitySet = getEntitySet([CItem, CPosition])`
**/
public function getEntitySet(componentTypeList:Array<Class<Component>>):EntitySet
{
var entitySet = new EntitySet(this, componentTypeList);
entitySets.push(entitySet);
return entitySet;
}
/**
Returns a factory `Entity`, a factory entity is an entity made through template.
**/
public function createEntity(?name:String):Entity
{
// YAML
// var templateId = templatesByString.get(name);
// var templateId = entityFactory.indexOf(name);
// trace("wat " + name);
// if(templateId == -1) throw "The entity '${name}' doesn't exists";
// TEMPLATE ENTITY
if(name != null) return templateStore.getByName(name).func();
return new Entity();
}
/**
Adds a template (factory Entity) of name `name`, which is built and returned
by a function `func`.
**/
public function addTemplate(name:String, func:Void->Entity)
templateStore.add(name, func);
public function destroyEntity(entity:Entity)
{
for(component in entity.components)
if(component != null)
_removeComponentInstance(entity, component);
if(net.entities.get(entity.id) == entity)
{
trace("NETWORKED ENTITY DESTROYED");
net.entities.remove(entity.id);
}
trace("ENTITY DESTROYED");
}
public function addComponent<T:Component>(entity:Entity, component:T):T
{
entity.components[component._id] = component;
entity.code = entity.code.add(component._id);
for(entitySet in entitySets)
{
if(entitySet.code.containsBitSet(entity.code))
{
var idCode = Int64.ofInt(0).add(entitySet._id);
// SKIP IF addComponent is called from that very entitySet...
if(idCode.containsBitSet(entity.registeredSetsCode)) continue;
entitySet._adds.set(entity);
entity.registeredSetsCode = entity.registeredSetsCode.add(entitySet._id);
}
}
return component;
}
@:allow(osis.NetEntityManager)
inline function _removeComponentInstance<T:Component>(entity:Entity, component:T)
{
_removeComponent(entity, component._id);
}
public function removeComponent<T:Class<Component>>(entity:Entity, componentType:T)
{
_removeComponent(entity, componentType.get__id());
}
inline function _removeComponent(entity:Entity, componentId:Int)
{
entity.code = entity.code.remove(componentId);
for(entitySet in entitySets)
{
if(!entitySet.code.containsBitSet(entity.code))
{
var idCode = Int64.ofInt(0).add(entitySet._id);
if(!idCode.containsBitSet(entity.registeredSetsCode)) continue;
entitySet._removes.set(entity);
entitySet._changes.remove(entity);
entity.registeredSetsCode = entity.registeredSetsCode.remove(entitySet._id);
}
}
entity.remComponents[componentId] = true;
componentsToDestroy.push({entity:entity, componentId:componentId}); // TEMPORARY, hopefully
}
public function getComponent<T:Component>(entity:Entity, componentType:Class<T>):T
{
return entity.get(componentType);
}
/**
Adds a system to the `EntityManager`.
**/
public function addSystem<T:System>(system:T)
{
system.em = this;
system.net = this.net;
system.init();
systems.push(system);
return system;
}
/**
Calls `loop` of each `System`.
**/
public function processAllSystems()
{
for(system in systems) system.loop();
for(_ in componentsToDestroy)
{
_.entity.components[_.componentId] = null;
_.entity.remComponents[_.componentId] = false;
}
componentsToDestroy = new Array();
}
// FIXED UPDATE
public var skipTicks:Float = 1 / 60;
public var maxFrameSkip:Int = 100;
/**
Rate at which network data is processed.
**/
public var netfps:Int = 30;
// INIT
var loops:Int = 0;
var nextGameTick:Float = Time.now();
var lastNetTick:Float = Time.now();
public function fixedUpdate(func:Void->Void)
{
if((Time.now() - lastNetTick) > (1 / netfps))
{
net.pump();
lastNetTick = Time.now();
}
loops = 0;
while(Time.now() > nextGameTick && loops < maxFrameSkip)
{
func();
nextGameTick += skipTicks;
loops++;
}
if(loops > maxFrameSkip) throw "out of fixed timestep";
}
// YAML
// NEEDED HERE TO PREVENT REFLECTION HELL
// @:allow(osis.NetEntityManager)
// function createFactoryEntity(type:String):Entity
// {
// return Reflect.field(this, type)();
// }
public function markChanged<T:Component>(entity:Entity, component:T, ?filterEntitySet:EntitySet)
{
for(entitySet in entitySets)
if(entity.registeredSetsCode.contains(entitySet._id))
if(entitySet.code.contains(component._id))
{
if(entitySet == filterEntitySet) continue;
entitySet._changes.set(entity);
}
}
// NET HELPERS
#if client
public function connect(address:String, port:Int)
{
net.connect(address, port);
return net;
}
#end
#if server
public function listen(address:String, port:Int)
{
net.listen(address, port);
return net;
}
#end
static function main() {
trace("Haxe is great!");
}
}
@:dox(hide)
class Net
{
public var onConnection:Connection->Void;
public var onDisconnection:Connection->Void;
#if server
public var socket:Server;
@:allow(osis.EntityManager)
function listen(address:String, port:Int)
{
socket = new Server(address, port);
socket.protocol = new Prefixed();
socket.onData = onData;
socket.onConnection = _onConnection;
socket.onDisconnection = _onDisconnection;
return socket;
}
#elseif client
public var socket:Client;
@:allow(osis.EntityManager)
function connect(address:String, port:Int)
{
socket = new Client();
socket.protocol = new Prefixed();
socket.onData = onData;
socket.onConnection = _onConnection;
socket.onDisconnection = _onDisconnection;
socket.connect(address, port);
}
#end
function _onConnection(connection:Connection)
{
if(onConnection == null)
trace("Client connected: you should probably bind" +
"the onConnection function");
else
onConnection(connection);
}
function _onDisconnection(connection:Connection)
{
if(onDisconnection == null)
trace("Client disconnected: you should probably bind" +
" the onDisconnection function");
else
onDisconnection(connection);
}
function onData(connection:Connection)
{
// OVERRIDDEN BY NETENTITYMANAGER
}
}
@:dox(hide)
class EventContainer
{
public var message:IMessage;
public var func:IMessage->Connection->Void;
public function new() {}
}
@:dox(hide)
@:enum abstract NETWORK_ORDER(Int) from Int to Int
{
var CREATE_ENTITY = 0;
var CREATE_TEMPLATE_ENTITY = 1;
var ADD_COMPONENT = 2;
var UPDATE_COMPONENT = 3;
var REMOVE_COMPONENT = 4;
var DESTROY_ENTITY = 5;
var EVENT = 6;
}
/**
Available via `EntityManager.listen` on server or `EntityManager.connect` on client.
**/
class NetEntityManager extends Net
{
// var entityFactory:Array<String>; // YAML FED BY NEW (SERIALIZED BY MACRO)
var em:EntityManager;
var serializableTypes:Vector<Class<Component>> = new Vector(MAX_COMPONENTS); // SERIALIZED SPECIFIC IDS
var allTypes:Vector<Class<Component>> = new Vector(MAX_COMPONENTS); // ALL COMPONENTS IDS
var eventListeners:IntMap<EventContainer> = new IntMap();
public var entities = new Entities(); // MAPS SERVER>CLIENT IDS
@:dox(hide) public static var instance:NetEntityManager; // USED BY CUSTOMNETWORKTYPES FOR ENTITY (MEH)
@:dox(hide)
public function new(em:EntityManager)
{
instance = this;
this.em = em;
// RESOLVE COMPONENT TYPES FROM STRING (MACRO)
var serializables = podstream.SerializerMacro.getSerialized();
// COMPONENTS AND NET SERIALIZABLE COMPONENTS
var numComponents = 0;
var numNetComponents = 0;
for(serializable in serializables)
{
if(serializable == null) continue; // Shouldn't be in the array in the first place !??
var componentType:Class<Component> = cast Type.resolveClass(serializable);
// NETWORKED COMPONENTS
var componentNetId = componentType.get__sid();
if(componentNetId != -1)
{
numNetComponents++;
serializableTypes[componentNetId] = componentType;
}
// ALL COMPONENTS
numComponents++;
var componentId = componentType.get__id();
allTypes[componentId] = componentType;
}
trace("Components total : " + numComponents + "/" + MAX_COMPONENTS);
trace("Net components total : " + numNetComponents + "/" + MAX_COMPONENTS);
// GET ENTITY FACTORY (MACRO) YAML
// entityFactory = haxe.Unserializer.run(haxe.Resource.getString("entityFactory"));
}
//////////////// SERVER //////////////
#if server
@:dox(hide) public var entitiesByConnection:Map<Connection, Entity> = new Map();
@:dox(hide) public var connections:Map<Entity, Connection> = new Map();
// USED WHEN DISCONNECTED FOR ENTITY DESTROY
public function bindEntity(connection:Connection, entity:Entity)
{
entitiesByConnection.set(connection, entity);
connections.set(entity, connection);
}
public function getBoundEntity(connection:Connection)
{
return entitiesByConnection.get(connection);
}
// YAML
// ENTITY CREATION BY TEMPLATES: Needed to handle different compositions between c/s!
// public function create(name:String)
// {
// // var templateId = templatesByString.get(name);
// var templateId = entityFactory.indexOf(name);
// trace("wat " + name);
// if(templateId == -1) throw "The entity '${name}' doesn't exists";
// var entity:Entity = em.createFactoryEntity('create' + entityFactory[templateId]);
// entity.templateId = templateId;
// // SEND
// for(connection in socket.connections)
// sendCreate(connection.output, entity);
// entities.set(entity.id, entity);
// return entity;
// }
public function createEntity(name:String):Entity
return sendFactoryEntity(name, em.createEntity(name));
function sendFactoryEntity(name:String, entity:Entity):Entity
{
// trace("sendEntity " + name); // DEBUG
entity.templateId = em.templateStore.getByName(name).id;
// SEND
for(connection in socket.connections)
{
sendCreate(connection.output, entity);
sendDeltas(connection, entity);
}
entities.set(entity.id, entity);
return entity;
}
// public function sendEntity(entity:Entity):Entity
// {
// for(connection in socket.connections)
// {
// sendCreate(connection.output, entity);
// for(pos in 0...32)
// {
// if( (entity.code & 1 << pos) != 0)
// {
// if(entity.components[pos]._sid == -1) continue;
// sendAddComponent(entity.id, entity.components[pos], connection);
// }
// }
// }
// entities.set(entity.id, entity);
// return entity;
// }
function sendCreate(output:haxe.io.BytesOutput, entity:Entity):Void
{
output.writeInt8(CREATE_TEMPLATE_ENTITY);
output.writeInt16(entity.id);
output.writeInt8(entity.templateId);
}
public function destroyEntity(entity:Entity):Void
{
for(connection in socket.connections)
{
connection.output.writeInt8(DESTROY_ENTITY);
connection.output.writeInt16(entity.id);
}
entity.destroy();
// CLEANUP
var connection = connections.get(entity);
entitiesByConnection.remove(connection);
connections.remove(entity); // TEMP
}
inline function sendAddComponent<T:Component>(entityId:Int, component:T, conn:Connection):T
{
conn.output.writeInt8(ADD_COMPONENT);
conn.output.writeInt16(entityId);
conn.output.writeInt8(component._sid);
component.serialize(conn.output);
return component;
}
public function addComponent<T:Component>(entity:Entity, component:T):T
{
for(connection in socket.connections)
sendAddComponent(entity.id, component, connection);
entity.add(component);
return component;
}
public function addComponentTo<T:Component>(entity:Entity, component:T, connEntity:Entity):T
{
return sendAddComponent(entity.id, component, connections.get(connEntity));
}
inline function sendRemoveComponent(entityId:Int, componentId:Int, connection:Connection)
{
connection.output.writeInt8(REMOVE_COMPONENT);
connection.output.writeInt16(entityId);
connection.output.writeInt8(componentId);
}
public function removeComponent<T:Class<Component>>(entity:Entity, componentType:T)
{
for(connection in socket.connections)
sendRemoveComponent(entity.id, componentType.get__sid(), connection);
entity.remove(componentType);
}
public function sendWorldStateTo(connection:Connection)
{
var connectionEntity = entitiesByConnection.get(connection);
if(connectionEntity == null) throw "Connection has to have a bound entity";
for(entity in entities)
{
if(entity == connectionEntity) continue;
sendCreate(connection.output, entity);
sendDeltas(connection, entity);
}
}
function sendDeltas(connection:Connection, entity:Entity)
{
var templateCode = em.templateStore.getById(entity.templateId).code;
var deltaCode = entity.code.xor(templateCode);
for(componentId in 0...MAX_COMPONENTS)
{
// CHECK IF COMPONENT REMOVED FROM TEMPLATE
if(deltaCode.contains(componentId)) // CHANGE
{
if(entity.code.contains(componentId)) // ADD
{
// // Reflect until i find something cleaner (with podstream)
// if(Reflect.field(entity.components[pos], "_sid") == null)
// continue;
// sendAddComponent(entity.id, cast entity.components[pos], connection);
}
else
{
var sid = (cast allTypes[componentId]).__sid;
if(sid == -1) continue; // NOT NETWORKED
sendRemoveComponent(entity.id, sid, connection);
}
}
// SEND ENTITY COMPONENT VALUES
if(entity.code.contains(componentId))
{
if(entity.components[componentId]._sid == -1) continue;
sendAddComponent(entity.id, entity.components[componentId], connection);
}
}
}
public function markChanged<T:Component>(entity:Entity, component:T, ?entitySet:EntitySet)
{
if(component._sid == -1) throw 'Component $component is not serializable';
em.markChanged(entity, component, entitySet);
for(connection in socket.connections)
{
connection.output.writeInt8(UPDATE_COMPONENT);
connection.output.writeInt16(entity.id);
connection.output.writeInt8(component._sid);
component.serialize(connection.output);
}
}
override function onData(connection:Connection)
{
while(connection.input.mark - connection.input.position > 0)
{
var msgtype = connection.input.readInt8();
if(msgtype == EVENT)
{
var messageTypeId = connection.input.readInt8();
receiveEvent(messageTypeId, connection);
}
}
}
#end
/// COMMON ///
function receiveEvent(messageTypeId:Int, connection:Connection)
{
var eventContainer:EventContainer = eventListeners.get(messageTypeId);
if(eventContainer == null) throw("No event registered for " + messageTypeId);
eventContainer.message.unserialize(connection.input);
eventContainer.func(eventContainer.message, connection);
}
public function sendEvent(message:IMessage, ?connection:Connection)
{
#if server
if(connection != null)
_sendEvent(connection.output, message);
else
for(connection in socket.connections)
_sendEvent(connection.output, message);
#end
#if client
_sendEvent(socket.connection.output, message);
#end
}
inline function _sendEvent(output:haxe.io.BytesOutput, message:IMessage)
{
output.writeInt8(EVENT);
output.writeInt8(message._sid);
message.serialize(output);
}
public function addEvent<T:IMessage>(messageClass:Class<IMessage>,
func:T->Connection->Void)
{
var event = new EventContainer();
event.message = Type.createInstance(messageClass, []);
event.func = cast func;
eventListeners.set(event.message._sid, event);
}
//////////////// CLIENT //////////////
#if client
override function onData(connection:Connection)
{
while(connection.input.mark - connection.input.position > 0)
{
var msgtype:NETWORK_ORDER = connection.input.readInt8();
switch(msgtype)
{
case CREATE_ENTITY:
var entityId = connection.input.readInt16();
trace("CREATE_ENTITY " + entityId);
var entity = em.createEntity();
entities.set(entityId, entity);
case DESTROY_ENTITY:
var entityId = connection.input.readInt16();
trace("DESTROY_ENTITY " + entityId);
var entity = entities.get(entityId);
entities.remove(entityId);
entity.destroy();
case ADD_COMPONENT:
var entityId = connection.input.readInt16();
var entity = entities.get(entityId);
var componentTypeId = connection.input.readInt8();
// trace("ADD_COMPONENT " + componentTypeId); // DEBUG
var componentType:Class<Component> = cast serializableTypes[componentTypeId];
var component:Component = Type.createInstance(componentType, []);
component.unserialize(connection.input);
entity.add(component);
case REMOVE_COMPONENT:
var entityId = connection.input.readInt16();
var componentTypeId = connection.input.readInt8();
var componentType:Class<Component> = cast serializableTypes[componentTypeId];
trace("REMOVE_COMPONENT " + untyped(componentType));
var entity = entities.get(entityId);
var component = entity.get(componentType);
em._removeComponentInstance(entity, component);
case UPDATE_COMPONENT:
var entityId = connection.input.readInt16();
var componentTypeId = connection.input.readInt8();
var componentType:Class<Component> = cast serializableTypes[componentTypeId];
var entity = entities.get(entityId);
// trace("UPDATE_COMPONENT ID " + componentTypeId);
// trace("UPDATE_COMPONENT " + untyped(componentType));
if(entity != null)
{
var component = entity.get(componentType);
component.unserialize(connection.input);
em.markChanged(entity, component);
}
else
{
trace("COMPONENT_UPDATE received but the entity doesn't exist anymore, skipping");
}
case EVENT:
// trace("EVENT");
var messageTypeId = connection.input.readInt8();
receiveEvent(messageTypeId, connection);
case CREATE_TEMPLATE_ENTITY:
var entityId = connection.input.readInt16();
// trace("CREATE_TEMPLATE_ENTITY " + entityId); // DEBUG
var templateId = connection.input.readInt8();
// YAML
// var entity = Reflect.field(em,'create' + entityFactory[templateId])(); // YAML
var entity = em.templateStore.getById(templateId).func();
entities.set(entityId, entity);
}
}
}
public function markChanged<T:Component>(entity:Entity, component:T, ?entitySet:EntitySet)
{
// DUMMY, ACTUALLY USED FOR SERVER TO PREVENT ISSUES
// WHEN SHARING SAME SYSTEM BETWEEN CLIENT & SERVER
}
#end
@:allow(osis.EntityManager)
function pump()
{
if(socket != null)
{
socket.pump();
socket.flush();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment