File Entities.c

File List > engine > src > gameframework > layers > Game2D > EntitySystem > Entities.c

Go to the documentation of this file

#include "Entities.h"
#include "AssertLib.h"
#include "BinarySerializer.h"
#include "StaticColliderEntity.h"
#include "Components.h"
#include <string.h>
#include "Entity2DCollection.h"
#include "GameFramework.h"
#include "EntityQuadTree.h"
#include "AnimatedSprite.h"
#include "Sprite.h"
#include "ObjectPool.h"
#include "Network.h"
#include "Log.h"
#include "NetworkID.h"
#include "Game2DLayerNetwork.h"
#include "DynArray.h"

static VECTOR(struct EntitySerializerPair) pSerializers = NULL;

static OBJECT_POOL(struct DynamicEntityListItem) gDynamicListPool = NULL;


bool DestroyCollectionItr(struct Entity2D* pEnt, int i, void* pUser)
{
    struct GameFrameworkLayer* pLayer = pUser;
    pEnt->onDestroy(pEnt, pLayer);
    return true;
}

void Et2D_DestroyCollection(struct Entity2DCollection* pCollection, struct GameFrameworkLayer* pLayer)
{
    Et2D_IterateEntities(pCollection, &DestroyCollectionItr, pLayer);    
    pCollection->pEntityPool = FreeObjectPool(pCollection->pEntityPool);
}


void Et2D_InitCollection(struct Entity2DCollection* pCollection)
{
    pCollection->gEntityListHead = NULL_HANDLE;
    pCollection->gEntityListTail = NULL_HANDLE;
    pCollection->dynamicEntities.hDynamicListHead = NULL_HANDLE;
    pCollection->dynamicEntities.hDynamicListTail = NULL_HANDLE;
    pCollection->dynamicEntities.nDynamicListSize = 0;
    pCollection->gNumEnts = 0;
    pCollection->pEntityPool = NEW_OBJECT_POOL(struct Entity2D, 512);
    pCollection->dynamicEntities.pDynamicListItemPool = NEW_OBJECT_POOL(struct DynamicEntityListItem, 256);
    pCollection->messageQueue = NEW_VECTOR(struct EntityToEntityMessage);
    pCollection->messageQueue = VectorResize(pCollection->messageQueue, 256);
    pCollection->messageQueue = VectorClear(pCollection->messageQueue);
}

void Entity2DOnInit(struct Entity2D* pEnt, struct GameFrameworkLayer* pLayer, DrawContext* pDrawCtx, InputContext* pInputCtx)
{
    Co_InitComponents(pEnt, pLayer);
    struct GameLayer2DData* pData = pLayer->userData;
    if(pEnt->bKeepInQuadtree)
    {
        pEnt->hQuadTreeRef = Entity2DQuadTree_Insert(&pData->entities, pData->hEntitiesQuadTree, pEnt->thisEntity, pLayer, 0, 6);
    }
    if(pEnt->bKeepInDynamicList)
    {
        pEnt->hDynamicListRef = DynL_AddEntity(&pData->entities.dynamicEntities, pEnt->thisEntity);
    }
}

void Entity2DUpdate(struct Entity2D* pEnt, struct GameFrameworkLayer* pLayer, float deltaT)
{
    Co_UpdateComponents(pEnt, pLayer, deltaT);
}

void Entity2DUpdatePostPhysics(struct Entity2D* pEnt, struct GameFrameworkLayer* pLayer, float deltaT)
{
    Co_Entity2DUpdatePostPhysicsFn(pEnt, pLayer, deltaT);
}

void Entity2DDraw(struct Entity2D* pEnt, struct GameFrameworkLayer* pLayer, struct Transform2D* pCam, VECTOR(Worldspace2DVert)* outVerts, VECTOR(VertIndexT)* outIndices, VertIndexT* pNextIndex)
{
    Co_DrawComponents(pEnt, pLayer, pCam, outVerts, outIndices, pNextIndex);
}

void Entity2DInput(struct Entity2D* pEnt, struct GameFrameworkLayer* pLayer, InputContext* context)
{
    Co_InputComponents(pEnt, pLayer, context);
}

void Entity2DOnDestroy(struct Entity2D* pEnt, struct GameFrameworkLayer* pLayer)
{
    Co_DestroyComponents(pEnt);
    struct GameLayer2DData* pData = pLayer->userData;
    if(pEnt->bKeepInDynamicList)
    {
        DynL_RemoveItem(&pData->entities.dynamicEntities, pEnt->hDynamicListRef);
    }
    if(pEnt->bKeepInQuadtree)
    {
        Entity2DQuadTree_Remove(pData->hEntitiesQuadTree, pEnt->hQuadTreeRef);
    }
}


void Entity2DGetBoundingBox(struct Entity2D* pEnt, struct GameFrameworkLayer* pLayer, vec2 outTL, vec2 outBR)
{
    vec2 bbtl  = {99999999999, 9999999999999};
    vec2 bbbr  = {-99999999999, -9999999999999};
    bool bSet = false;
    for(int i=0; i < pEnt->numComponents; i++)
    {
        struct Component2D* pComponent = &pEnt->components[i];
        vec2 tl, br;
        if(pComponent->type == ETE_Sprite)
        {
            bSet = true;
            SpriteComp_GetBoundingBox(pEnt, &pComponent->data.sprite, pLayer, tl, br);
        }
        else if(pComponent->type == ETE_SpriteAnimator)
        {
            bSet = true;
            AnimatedSprite_GetBoundingBox(pEnt,&pComponent->data.spriteAnimator, pLayer, tl, br);
        }
        if(tl[0] < bbtl[0])
        {
            bbtl[0] = tl[0];
        }
        if(tl[1] < bbtl[1])
        {
            bbtl[1] = tl[1];
        }
        if(br[0] > bbbr[0])
        {
            bbbr[0] = br[0];
        }
        if(br[1] > bbbr[1])
        {
            bbbr[1] = br[1];
        }
    }
    if(!bSet)
    {
        outTL[0] = 0;
        outTL[1] = 0;
        outBR[0] = 0;
        outBR[1] = 0;
    }
    else
    {
        outTL[0] = bbtl[0];
        outTL[1] = bbtl[1];
        outBR[0] = bbbr[0];
        outBR[1] = bbbr[1];
    }
}

#include "NetworkID.h"
void Et2D_Init(RegisterGameEntitiesFn registerGameEntities)
{
    pSerializers = NEW_VECTOR(struct EntitySerializerPair);
    pSerializers = VectorClear(pSerializers);

    struct EntitySerializerPair scCtorRect = Et2D_Get2DRectStaticColliderSerializerPair();
    Et2D_RegisterEntityType(EBET_StaticColliderRect, &scCtorRect);
    struct EntitySerializerPair scCtorCircle = Et2D_Get2DCircleStaticColliderSerializerPair();
    Et2D_RegisterEntityType(EBET_StaticColliderCircle, &scCtorCircle);


    struct EntitySerializerPair scCtorEllipse = Et2D_Get2DEllipseStaticColliderSerializerPair();
    struct EntitySerializerPair scCtorPoly  = Et2D_Get2DPolygonStaticColliderSerializerPair();

    Et2D_RegisterEntityType(EBET_StaticColliderPoly, &scCtorPoly);
    Et2D_RegisterEntityType(EBET_StaticColliderEllipse, &scCtorEllipse);

    if(registerGameEntities)
    {
        registerGameEntities();
    }
}

void Et2D_DestroyEntity(struct GameFrameworkLayer* pLayer, struct Entity2DCollection* pCollection, HEntity2D hEnt)
{
    struct Entity2D* pEnt = &pCollection->pEntityPool[hEnt];

    if(pCollection->gEntityListHead == hEnt)
    {
        pCollection->gEntityListHead = pEnt->nextSibling;
    }
    if(pCollection->gEntityListTail == hEnt)
    {
        pCollection->gEntityListTail = pEnt->previousSibling;
    }

    if(pEnt->nextSibling != NULL_HANDLE)
    {
        struct Entity2D* pNext = &pCollection->pEntityPool[pEnt->nextSibling];
        pNext->previousSibling = pEnt->previousSibling;

    }
    if(pEnt->previousSibling != NULL_HANDLE)
    {
        struct Entity2D* pPrev = &pCollection->pEntityPool[pEnt->previousSibling];
        pPrev->nextSibling = pEnt->nextSibling;
    }

    pEnt->onDestroy(pEnt, pLayer);
    pCollection->gNumEnts--;
    FreeObjectPoolIndex(pCollection->pEntityPool, hEnt);
}

static HEntity2D Et2D_AddEntityBase(struct Entity2DCollection* pCollection, struct Entity2D* pEnt, bool bAssignNetID)
{
    /* 
        - assign new ID at this point if we're the server.
        - if we're the client it might be serialized or it might be "guessed" by the client depending on context
    */
    if(NW_GetRole() == GR_ClientServer && bAssignNetID)
    {
        pEnt->networkID = NetID_GetID();
    }

    HEntity2D hEnt = NULL_HANDLE;
    pCollection->pEntityPool = GetObjectPoolIndex(pCollection->pEntityPool, &hEnt);
    EASSERT(hEnt != NULL_HANDLE);
    memcpy(&pCollection->pEntityPool[hEnt], pEnt, sizeof(struct Entity2D));
    pEnt->nextSibling = NULL_HANDLE;
    pEnt->previousSibling = NULL_HANDLE;

    pEnt = &pCollection->pEntityPool[hEnt];
    pEnt->thisEntity = hEnt;
    if(pCollection->gEntityListHead == NULL_HANDLE)
    {
        pCollection->gEntityListHead = hEnt;
        pCollection->gEntityListTail = hEnt;
    }
    else
    {
        struct Entity2D* pLast = &pCollection->pEntityPool[pCollection->gEntityListTail];
        pLast->nextSibling = hEnt;
        pEnt->previousSibling = pCollection->gEntityListTail;
        pEnt->nextSibling = NULL_HANDLE;
        pCollection->gEntityListTail = hEnt;
    }
    pCollection->gNumEnts++;
    return hEnt;
}

HEntity2D Et2D_AddEntityNoNewNetID(struct Entity2DCollection* pCollection, struct Entity2D* pEnt)
{
    return Et2D_AddEntityBase(pCollection, pEnt, false);
}

HEntity2D Et2D_AddEntity(struct Entity2DCollection* pCollection, struct Entity2D* pEnt)
{
    return Et2D_AddEntityBase(pCollection, pEnt, true);
}

void Et2D_DeserializeEntityV1Base(struct Entity2DCollection* pCollection, struct BinarySerializer* bs, struct GameLayer2DData* pData, int objectLayer, struct Entity2D* pOutEnt)
{
    u32 entityType = 0;
    BS_DeSerializeU32(&entityType, bs);
    Log_Verbose("Deserializing entity type: %i", entityType);

    pOutEnt->nextSibling = NULL_HANDLE;
    pOutEnt->previousSibling = NULL_HANDLE;
    pOutEnt->inDrawLayer = objectLayer;
    pOutEnt->type = entityType;

    Et2D_DeserializeCommon(bs, pOutEnt);

    if(pOutEnt->type < VectorSize(pSerializers))
    {
        pSerializers[pOutEnt->type].deserialize(bs, pOutEnt, pData);
    }
    else 
    {
        Log_Error("DESERIALIZE: Entity Serializer type %i out of range\n", pOutEnt->type);
    }
}
#include "LatinMacros.h"

staticus vacuum DeserializeEntityV1(struct Entity2DCollection* pCollection, struct BinarySerializer* bs, struct GameLayer2DData* pData, int objectLayer)
{
    vimen(bs->ctx)
    {
    casu SCTX_ToFile:
    casu SCTX_ToNetwork:
        {
            struct Entity2D ent;
            memset(&ent, 0, magnitudinem(struct Entity2D));
            Et2D_DeserializeEntityV1Base(pCollection, bs, pData, objectLayer, &ent);
            Et2D_AddEntity(pCollection, &ent);    
        }
        interruptio;
    casu SCTX_ToNetworkUpdate:
        {
            i32 netID = -1;
            BS_DeSerializeI32(&netID, bs);
            NetID_DeserializedNewID(netID);
            struct Entity2D* pEnt = G2D_FindEntityWithNetID(pCollection, netID);
            si(pEnt)
            {
                pSerializers[pEnt->type].deserialize(bs, pEnt, pData);
            }
            aliter
            {
                Log_Error("entity with netID %i not found", netID);
            }
        }
        interruptio;
    }
}

static void LoadEntitiesV1(struct BinarySerializer* bs, struct GameLayer2DData* pData, struct Entity2DCollection* pCollection, int objectLayer)
{
    u32 numEntities = 0;
    BS_DeSerializeU32(&numEntities, bs);
    Log_Verbose("numEntities: %i", numEntities);
    for(int i=0; i<numEntities; i++)
    {
        DeserializeEntityV1(pCollection, bs, pData, objectLayer);
    }
}

static void LoadEntities(struct BinarySerializer* bs, struct GameLayer2DData* pData, struct Entity2DCollection* pCollection, int objectLayer)
{
    EASSERT(!bs->bSaving);
    u32 version = 0;
    BS_DeSerializeU32(&version, bs);
    switch(version)
    {
    case 1:
        LoadEntitiesV1(bs, pData, pCollection, objectLayer);
        break;
    default:
        Log_Error("E2D unsupported version %i\n", version);
        EASSERT(false);
        break;
    }
}

static u32 NumEntsToSerialize(struct Entity2DCollection* pCollection, struct BinarySerializer* bs)
{
    int i = 0;
    HEntity2D hOn = pCollection->gEntityListHead;
    while(hOn != NULL_HANDLE)
    {
        struct Entity2D* pOn = &pCollection->pEntityPool[hOn];
        switch(bs->ctx)
        {
        case SCTX_ToFile:
            if(pOn->bSerializeToDisk)
            {
                i++;
            }
            break;
        case SCTX_ToNetwork:
            if(pOn->bSerializeToNetwork)
            {
                i++;
            }
            break;
        case SCTX_ToNetworkUpdate:
            if(pOn->bSerializeInNetworkUpdate)
            {
                i++;
            }
        }

        hOn = pOn->nextSibling;
    }
    return i;
}

void Et2D_SerializeEntityV1Base(struct Entity2D* pOn, struct BinarySerializer* bs, struct GameLayer2DData* pData)
{
    bool bSerialize = false;
    switch(bs->ctx)
    {
    case SCTX_ToFile:
        bSerialize = pOn->bSerializeToDisk;
        break;
    case SCTX_ToNetwork:
        bSerialize = pOn->bSerializeToNetwork;
        break;
    case SCTX_ToNetworkUpdate:
        bSerialize = pOn->bSerializeInNetworkUpdate;
    }
    if(bSerialize)
    {
        if(bs->ctx != SCTX_ToNetworkUpdate)
        {
            BS_SerializeU32(pOn->type, bs);
            Log_Verbose("Entity type: %i", pOn->type);
            Et2D_SerializeCommon(bs, pOn);
        }
        else
        {
            BS_SerializeI32(pOn->networkID, bs);
        }
        if(pOn->type < VectorSize(pSerializers))
        {
            pSerializers[pOn->type].serialize(bs, pOn, pData);
        }
        else 
        {
            Log_Error("Entity Serializer type %i out of range\n", pOn->type);
        }
    }
}

static void SaveEntities(struct Entity2DCollection* pCollection, struct BinarySerializer* bs, struct GameLayer2DData* pData)
{
    EASSERT(bs->bSaving);
    BS_SerializeU32(1, bs); /* version number */
    Log_Verbose("Saving %i entities", NumEntsToSerialize(pCollection, bs));
    BS_SerializeU32(NumEntsToSerialize(pCollection, bs), bs);
    HEntity2D hOn = pCollection->gEntityListHead;
    while(hOn != NULL_HANDLE)
    {
        struct Entity2D* pOn = &pCollection->pEntityPool[hOn];
        Et2D_SerializeEntityV1Base(pOn, bs, pData);
        hOn = pOn->nextSibling;
    }
}

/* both serialize and deserialize */
void Et2D_SerializeEntities(struct Entity2DCollection* pCollection, struct BinarySerializer* bs, struct GameLayer2DData* pData, int objectLayer)
{
    if(bs->bSaving)
    {
        SaveEntities(pCollection, bs, pData);
    }
    else
    {
        LoadEntities(bs, pData, pCollection, objectLayer);
    }
}

void Et2D_RegisterEntityType(u32 typeID, struct EntitySerializerPair* pair)
{
    //pSerializers = (pSerializers, pair);
    int size = VectorSize(pSerializers);
    EASSERT(size == typeID);
    pSerializers = VectorPush(pSerializers, pair);
}

void Et2D_DeserializeCommon(struct BinarySerializer* bs, struct Entity2D* pOutEnt)
{
    if(bs->ctx == SCTX_ToNetworkUpdate)
    {
        return; /* network updates serialize and deserialize a minimal amount of data */
    }

    u32 version = 0;
    BS_DeSerializeI32(&version, bs);
    switch(version)
    {
    case 1:
        BS_DeSerializeFloat(&pOutEnt->transform.position[0], bs);
        BS_DeSerializeFloat(&pOutEnt->transform.position[1], bs);
        BS_DeSerializeFloat(&pOutEnt->transform.scale[0], bs);
        BS_DeSerializeFloat(&pOutEnt->transform.scale[1], bs);
        BS_DeSerializeFloat(&pOutEnt->transform.rotation, bs);
        BS_DeSerializeFloat(&pOutEnt->transform.rotationPointRelative[0], bs);
        BS_DeSerializeFloat(&pOutEnt->transform.rotationPointRelative[1], bs);
        BS_DeSerializeU32(&version, bs);
        pOutEnt->bKeepInQuadtree = version != 0;
        if(bs->ctx == SCTX_ToNetwork)
        {
            BS_DeSerializeI32(&pOutEnt->networkID, bs);
            NetID_DeserializedNewID(pOutEnt->networkID);
        }

        Et2D_PopulateCommonHandlers(pOutEnt);
        break;
    }
}


void Et2D_SerializeCommon(struct BinarySerializer* bs, struct Entity2D* pInEnt)
{
    if(bs->ctx == SCTX_ToNetworkUpdate)
    {
        return; /* network updates serialize and deserialize a minimal amount of data */
    }
    u32 version = 1;
    BS_SerializeI32(version, bs);
    BS_SerializeFloat(pInEnt->transform.position[0], bs);
    BS_SerializeFloat(pInEnt->transform.position[1], bs);
    BS_SerializeFloat(pInEnt->transform.scale[0], bs);
    BS_SerializeFloat(pInEnt->transform.scale[1], bs);
    BS_SerializeFloat(pInEnt->transform.rotation, bs);
    BS_SerializeFloat(pInEnt->transform.rotationPointRelative[0], bs);
    BS_SerializeFloat(pInEnt->transform.rotationPointRelative[1], bs);
    version = (u32)pInEnt->bKeepInQuadtree;
    BS_SerializeU32(version, bs);

    if(bs->ctx == SCTX_ToNetwork)
    {
        BS_SerializeI32(pInEnt->networkID, bs);
    }
}

struct Entity2D* Et2D_GetEntity(struct Entity2DCollection* pCollection, HEntity2D hEnt)
{
    return &pCollection->pEntityPool[hEnt];
}

void Et2D_IterateEntities(struct Entity2DCollection* pCollection, Entity2DIterator itr, void* pUser)
{
    HEntity2D hOnEnt = pCollection->gEntityListHead;
    int i = 0;
    while(hOnEnt != NULL_HANDLE)
    {
        struct Entity2D* pEntity = Et2D_GetEntity(pCollection, hOnEnt);
        if(!itr(pEntity, i++, pUser))
            break;
        hOnEnt = pEntity->nextSibling;
    }
}

float Entity2DGetSortVal(struct Entity2D* pEnt)
{
    return pEnt->transform.position[1];
}

void Et2D_PopulateCommonHandlers(struct Entity2D* pEnt)
{
    pEnt->init = &Entity2DOnInit;
    pEnt->update = &Entity2DUpdate;
    pEnt->postPhys = &Entity2DUpdatePostPhysics;
    pEnt->draw = &Entity2DDraw;
    pEnt->input = &Entity2DInput;
    pEnt->onDestroy = &Entity2DOnDestroy;
    pEnt->getBB = &Entity2DGetBoundingBox;
    pEnt->getSortPos = &Entity2DGetSortVal;
}

bool PrintCollectionItr(struct Entity2D* pEnt, int i, void* pUser)
{
    int* pCount = pUser;
    Log_Info("ENTITY ID: %i NET ID: %i TYPE: %i", pEnt->thisEntity, pEnt->networkID, pEnt->type);
    if(pEnt->printEntityInfo)
    {
        pEnt->printEntityInfo(pEnt);
    }
    (*pCount)++;
    return true;
}

void Et2D_PrintEntitiesInfo(struct Entity2DCollection* pCollection)
{
    int count = 0;
    Et2D_IterateEntities(pCollection, &PrintCollectionItr, &count);
    Log_Info("Num entities: %i", count);
    EASSERT(count == pCollection->gNumEnts);
}

void Et2D_DoEntityMessagesQueue(struct Entity2DCollection* pCollection, struct GameFrameworkLayer* pLayer)
{
    for(int i = 0; i < VectorSize(pCollection->messageQueue); i++)
    {
        struct EntityToEntityMessage* pMsg = &pCollection->messageQueue[i];
        struct Entity2D* pRecipient = &pCollection->pEntityPool[pMsg->recipient];
        if(pRecipient->handleEntityMsg)
        {
            pRecipient->handleEntityMsg(
                pRecipient, 
                &pCollection->pEntityPool[pMsg->sender],
                pMsg,
                pLayer
            );
        }
        if(pMsg->freer)
        {
            pMsg->freer(pMsg);
        }
    }

    pCollection->messageQueue = VectorClear(pCollection->messageQueue);
}

void Et2D_SendEntity2EntityMsg(struct Entity2DCollection* pCollection, struct EntityToEntityMessage* pMsg)
{
    pCollection->messageQueue = VectorPush(pCollection->messageQueue, pMsg);
}