Skip to content

Commit

Permalink
Merge af74570 into b8f9acb
Browse files Browse the repository at this point in the history
  • Loading branch information
ffreyer committed May 14, 2021
2 parents b8f9acb + af74570 commit 03f3d26
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/Overseer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module Overseer
include("ledger.jl")

export AbstractLedger, Ledger, System, Stage, Component, SharedComponent, ComponentData, Entity
export @component, @shared_component
export @component, @shared_component, @grouped_component
export @entities_in

export update, schedule_delete!, delete_scheduled!, empty_entities!, stage, components, entities, stages
Expand Down
154 changes: 154 additions & 0 deletions src/component.jl
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,157 @@ function _shared_component(typedef, mod)
end
end


################################################################################

struct ParentGroup
e::Entity
end

struct GroupedComponent{T <: ComponentData} <: AbstractComponent{T}
indices::Indices
group::Vector{Int}
data::Vector{T}
end

GroupedComponent{T}() where {T <: ComponentData} = GroupedComponent{T}(Indices(), Int[], T[])

Base.@propagate_inbounds @inline Base.getindex(c::GroupedComponent, e::Entity) = c.data[c.group[c.indices[e.id]]]
Base.@propagate_inbounds @inline Base.getindex(c::GroupedComponent, i::Integer) = c.data[c.group[i]]

function is_unique_in(value, collection)
count = 0
for element in collection
count += element == value
end
return count == 1
end


# c[entity] = value
# set value of <only> this entity
@inline function Base.setindex!(c::GroupedComponent{T}, v::T, e::Entity) where {T}
eid = e.id
if in(e, c)
if is_unique_in(c.group[c.indices[eid]], c.group)
# the entity already has its own group - adjust value
@inbounds c.data[c.group[c.indices[eid]]] = v
else
# the entity is part of a group - create a new one
push!(c.data, v)
@inbounds c.group[c.indices[eid]] = length(c.data)
end
else
# the entity is not in the component - add it
push!(c.indices, eid)
push!(c.group, length(c.group)+1)
push!(c.data, v)
end
return v
end

# c[entity] = parent
# set the value of this entity to that of parent
@inline function Base.setindex!(c::GroupedComponent, p::Entity, e::Entity)
@boundscheck if !in(p, c)
throw(BoundsError(c, p))
end
eid = e.id
if in(e, c)
if is_unique_in(c.group[c.indices[eid]], c.group)
# if this entity is the only one holding onto a value, remove that
# value and cleanup group indices
idx = c.group[c.indices[eid]]
deleteat!(c.data, idx)
for i in eachindex(c.group)
c.group[i] = c.group[i] - (c.group[i] > idx)
end
end
# adjust group index either way
c.group[c.indices[eid]] = c.group[c.indices][p.id]
else
# if the entity is not in there we have to add it
push!(c.indices, eid)
push!(c.group, c.group[c.indices[p.id]])
end

return c[p]
end

# c[ParentGroup(entity)] = value
# set the value for all entities grouped with entity
@inline function Base.setindex!(c::GroupedComponent{T}, v::T, pg::ParentGroup) where {T}
e = pg.e
@boundscheck if !in(e, c)
throw(BoundsError(c, e))
end
@inbounds c.data[c.group[c.indices[e.id]]] = v
return v
end


Base.length(c::GroupedComponent) = length(c.group)

function Base.empty!(c::GroupedComponent)
empty!(c.indices)
empty!(c.group)
empty!(c.data)
return c
end


function Base.pop!(c::GroupedComponent, e::Entity)
@boundscheck if !in(e, c)
throw(BoundsError(c, e))
end

@inbounds begin
id = c.indices[e.id]
g = c.group[id]

c.group[id] = c.group[end]
pop!(c.group)
pop!(c.indices, e.id)

val = c.data[g]

if !in(g, c.group)
deleteat!(c.data, g)
for i in eachindex(c.group)
if c.group[i] > g
c.group[i] -= 1
end
end
end
end

return val
end

@inline Base.iterate(c::GroupedComponent, args...) = iterate(c.data, args...)
Base.sortperm(c::GroupedComponent) = sortperm(c.group)


macro grouped_component(typedef)
return esc(Overseer._grouped_component(typedef, __module__))
end

function _grouped_component(typedef, mod)
t = process_typedef(typedef, mod)
t1, tn = t
return quote
$t1
Overseer.component_type(::Type{$tn}) = Overseer.GroupedComponent
end
end

# Should be fine
# eltype
# in
# isempty
# delete! - fine if pop! is fine?

# Maybe not fine
# length - should this be the data length or the number of entities?
# swap_order!
# pop_indices_data!
13 changes: 13 additions & 0 deletions src/entity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ function Entity(c::AbstractComponent, i::Integer)
return Entity(c.indices.packed[i])
end

function Entity(m::AbstractLedger, parent::Entity, datas::ComponentData...)
e = Entity(m)
for d in datas
m[e] = d
end
for (T, component) in components(m)
if component isa GroupedComponent && in(parent, component) && !in(e, component)
component[e] = parent
end
end
return e
end

Base.iterate(e::Entity, state=1) = state > 1 ? nothing : (e, state+1)

const EMPTY_ENTITY = Entity(0)
83 changes: 83 additions & 0 deletions test/test_components.jl
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,86 @@ swap_order!(c3, Entity(12), Entity(13))
@test es == [e2]
@test eltype(es) == Entity
end


@testset "GroupedComponent" begin
@grouped_component struct Test5
x::Int
end

@test Overseer.component_type(Test5) == Overseer.GroupedComponent
c5 = Overseer.component_type(Test5){Test5}()

p1 = Entity(1)
p2 = Entity(2)
c5[p1] = Test5(1)
c5[p2] = Test5(2)

entities = [Entity(i) for i in 3:10]
for (i, e) in enumerate(entities)
c5[e] = (p1, p2)[mod1(i, 2)]
end

# check created values
count = 0
_sum = 0
for e in @entities_in(c5)
count += 1
_sum += c5[e].x
end
@test count == 10
@test _sum == 15

# check for no duplication
@test length(c5.data) == 2

# Check some basics
@test p1 in c5
@test pop!(c5, p1) == Test5(1)
@test !(p1 in c5)
@test length(c5) == 9
@test c5[p2] == Test5(2)
@test !isempty(c5)

count = 0
_sum = 0
for e in @entities_in(c5)
count += 1
_sum += c5[e].x
end
@test count == 9
@test _sum == 14

# adjust parent value of group
c5[Overseer.ParentGroup(p2)] = Test5(1)
count = 0
_sum = 0
for e in @entities_in(c5)
count += 1
_sum += c5[e].x
end
@test count == 9
@test _sum == 9

# adjust single value
c5[p2] = Test5(3)
count = 0
_sum = 0
for e in @entities_in(c5)
count += 1
_sum += c5[e].x
end
@test count == 9
@test _sum == 11
@test length(c5.data) == 3

# remove all entites of a group
for i in 3:2:10
pop!(c5, Entity(i))
end
@test length(c5.data) == 2
@test length(c5) == 5

empty!(c5)
@test isempty(c5)
end

0 comments on commit 03f3d26

Please sign in to comment.